From 704fd9a64fb8a8b1e929700c3e7413f8c3aaa2c2 Mon Sep 17 00:00:00 2001 From: yum Date: Sun, 2 Oct 2022 17:24:16 -0700 Subject: Add parameters to resize board (likely broken) ... and a bunch of bugfixes: * Shader is now transparent * Simplify shader row/column calculation * Add punctuation to texture * Fix generate.sh * Add lorum_ipsum.txt * Fix how long text is scrolled * Simplify encoding logic in osc_ctrl.py --- osc_ctrl.py | 181 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 144 insertions(+), 37 deletions(-) (limited to 'osc_ctrl.py') diff --git a/osc_ctrl.py b/osc_ctrl.py index 23b63c2..fcb2445 100644 --- a/osc_ctrl.py +++ b/osc_ctrl.py @@ -4,6 +4,7 @@ import argparse import random import time import fileinput +import generate_utils from pythonosc import udp_client from math import ceil @@ -16,8 +17,8 @@ from generate_utils import NUM_LAYERS from generate_utils import BOARD_ROWS from generate_utils import BOARD_COLS -#MSG_DELAY_S=0.3 -MSG_DELAY_S=0.1 +#CELL_TX_TIME_S=1.0 +CELL_TX_TIME_S=0.6 def usage(): print("python3 -m pip install python-osc") @@ -30,30 +31,57 @@ args = parser.parse_args() client = udp_client.SimpleUDPClient(args.i, args.p) +class EvilGlobalState(): + # Mapping from ascii char to encoded byte. + encoding = {} +state = EvilGlobalState() + +def generateEncoding(state): + for i in range(0, 26): + state.encoding[chr(ord('A') + i)] = i + for i in range(26, 52): + state.encoding[chr(ord('a') + i - 26)] = i + for i in range(52, 62): + state.encoding[chr(ord('0') + i - 52)] = i + state.encoding[','] = 62 + state.encoding['.'] = 63 + state.encoding[' '] = 64 + state.encoding['?'] = 65 + state.encoding['!'] = 66 + state.encoding[';'] = 67 + state.encoding[':'] = 68 + state.encoding['-'] = 69 + state.encoding['_'] = 70 + state.encoding["'"] = 71 + state.encoding['"'] = 72 + def encodeMessage(lines): result = [] for line in lines: for char in line: - char_int = ord(char) - if char_int >= ord('A') and char_int <= ord('Z'): - result.append(ord(char) - ord('A')) - elif char >= 'a' and char <= 'z': - result.append((ord(char) - ord('a')) + 26) - elif char >= '0' and char <= '9': - result.append((ord(char) - ord('0')) + 52) - elif char == '.': - result.append(63) - elif char == ',': - result.append(62) - elif char == ' ': - result.append(64) - # Pad message with spaces so that it overwrites any leftover text. - result += [65] * (BOARD_COLS - len(line)) + result.append(state.encoding[char]) + result += [state.encoding[' ']] * (BOARD_COLS - len(line)) #print("Encoded message: {}".format(result)) return result +def updateCell(cell_idx, letter_encoded, s0, s1, s2): + addr="/avatar/parameters/" + getLayerParam(cell_idx) + client.send_message(addr, letter_encoded) + + addr="/avatar/parameters/" + getSelectParam(cell_idx, 0) + client.send_message(addr, s0) + + addr="/avatar/parameters/" + getSelectParam(cell_idx, 1) + client.send_message(addr, s1) + + addr="/avatar/parameters/" + getSelectParam(cell_idx, 2) + client.send_message(addr, s2) + +# Send a cell all at once. # `which_cell` is an integer in the range [0,8). -def sendMessageCell(msg_cell, which_cell): +def sendMessageCellDiscrete(msg_cell, which_cell): + # Really long messages just wrap back around. + which_cell = (which_cell % 8) s0 = ((floor(which_cell / 4) % 2) == 1) s1 = ((floor(which_cell / 2) % 2) == 1) @@ -62,22 +90,10 @@ def sendMessageCell(msg_cell, which_cell): print("Cell s0/s1/s2: {}/{}/{}".format(s0,s1,s2)) # Seek each layer to the current cell. for i in range(0, len(msg_cell)): - print("Board index: {}".format(getBoardIndex(i, s0, s1, s2))) - - addr="/avatar/parameters/" + getLayerParam(i) - client.send_message(addr, msg_cell[i]) - - addr="/avatar/parameters/" + getSelectParam(i, 0) - client.send_message(addr, (floor(which_cell / 4) % 2) == 1) - - addr="/avatar/parameters/" + getSelectParam(i, 1) - client.send_message(addr, (floor(which_cell / 2) % 2) == 1) - - addr="/avatar/parameters/" + getSelectParam(i, 2) - client.send_message(addr, (which_cell % 2) == 1) + updateCell(i, msg_cell[i], s0, s1, s2) # Wait for convergence. - time.sleep(MSG_DELAY_S) + time.sleep(CELL_TX_TIME_S / 3.0) # Enable each layer. # TODO(yum_food) for some reason, if we don't active every layer, the @@ -87,7 +103,7 @@ def sendMessageCell(msg_cell, which_cell): client.send_message(addr, True) # Wait for convergence. - time.sleep(MSG_DELAY_S) + time.sleep(CELL_TX_TIME_S / 3.0) # Disable each layer. for i in range(0, NUM_LAYERS): @@ -95,7 +111,50 @@ def sendMessageCell(msg_cell, which_cell): client.send_message(addr, False) # Wait for convergence. - time.sleep(MSG_DELAY_S) + time.sleep(CELL_TX_TIME_S / 3.0) + +# Send a cell smoothly spread out over the course of CELL_TX_TIME_S. +# `which_cell` is an integer in the range [0,8). +# TODO(yum_food) because we can only reliably update entire cells at once, +# this method does not work :( +def sendMessageCellContinuous(msg_cell, which_cell): + s0 = ((floor(which_cell / 4) % 2) == 1) + s1 = ((floor(which_cell / 2) % 2) == 1) + s2 = ((floor(which_cell / 1) % 2) == 1) + + time_quanta = 100 + dt = CELL_TX_TIME_S / (time_quanta * 1.0) + + # key: time quantum \elem [0, 100) + # value: idx to handle + update_times = {} + enable_times = {} + disable_times = {} + + for i in range(0, len(msg_cell)): + update_time = int(((i / NUM_LAYERS) + 0.000) * time_quanta) % time_quanta + enable_time = int(((i / NUM_LAYERS) + 0.333) * time_quanta) % time_quanta + disable_time = int(((i / NUM_LAYERS) + 0.666) * time_quanta) % time_quanta + + update_times[update_time] = i + enable_times[enable_time] = i + disable_times[disable_time] = i + + for t in range(0, time_quanta): + if t in update_times: + which_cell = update_times[t] + print("which cell: {}".format(which_cell)) + updateCell(which_cell, msg_cell[which_cell], s0, s1, s2) + if t in enable_times: + which_cell = enable_times[t] + addr="/avatar/parameters/" + getEnableParam(which_cell) + client.send_message(addr, True) + if t in disable_times: + which_cell = disable_times[t] + addr="/avatar/parameters/" + getEnableParam(which_cell) + client.send_message(addr, False) + + time.sleep(dt) # The board is broken down into contiguous collections of characters called # cells. Each cell contains `NUM_LAYERS` characters. We can update one cell @@ -139,19 +198,67 @@ def sendMessage(msg): print("Encoded message: {}".format(msg)) + openBoard() + n_cells = ceil(msg_len / NUM_LAYERS) print("n_cells: {}".format(n_cells)) for cell in range(0, n_cells): - # Really long messages just wrap back around. - cell = cell % NUM_LAYERS + cell_begin = cell * NUM_LAYERS + cell_end = (cell + 1) * NUM_LAYERS + cell_msg = msg[cell_begin:cell_end] + print("Send cell {}".format(cell)) + sendMessageCellDiscrete(cell_msg, cell) + + closeBoard() +def sendRawMessage(msg): + n_cells = ceil(len(msg) / NUM_LAYERS) + for cell in range(0, n_cells): cell_begin = cell * NUM_LAYERS cell_end = (cell + 1) * NUM_LAYERS cell_msg = msg[cell_begin:cell_end] print("Send cell {}".format(cell)) - sendMessageCell(cell_msg, cell) + sendMessageCellDiscrete(cell_msg, cell) + +def clear(): + sendRawMessage([state.encoding[' ']] * BOARD_ROWS * BOARD_COLS) + +def openBoard(): + addr="/avatar/parameters/" + generate_utils.getResize0Param() + client.send_message(addr, False) + addr="/avatar/parameters/" + generate_utils.getResize1Param() + client.send_message(addr, False) + + time.sleep(0.1) + + addr="/avatar/parameters/" + generate_utils.getResizeEnableParam() + client.send_message(addr, True) + + time.sleep(0.1) + + addr="/avatar/parameters/" + generate_utils.getResizeEnableParam() + client.send_message(addr, False) + +def closeBoard(): + addr="/avatar/parameters/" + generate_utils.getResize0Param() + client.send_message(addr, True) + addr="/avatar/parameters/" + generate_utils.getResize1Param() + client.send_message(addr, True) + + time.sleep(0.1) + + addr="/avatar/parameters/" + generate_utils.getResizeEnableParam() + client.send_message(addr, True) + + time.sleep(0.1) + + addr="/avatar/parameters/" + generate_utils.getResizeEnableParam() + client.send_message(addr, False) if __name__ == "__main__": + generateEncoding(state) + clear() + for line in fileinput.input(): sendMessage(line) -- cgit v1.2.3