Skip to content

Instantly share code, notes, and snippets.

@matthewryanscott
Created October 14, 2016 17:12
Show Gist options
  • Save matthewryanscott/8d92faaa6ee1c71e570e6018bd7896af to your computer and use it in GitHub Desktop.
Save matthewryanscott/8d92faaa6ee1c71e570e6018bd7896af to your computer and use it in GitHub Desktop.
from collections import deque
import sys
import time
import threading
import mido
import numpy as np
from orbitant import backend as midi
from orbitant.transport import START, STOP, CLOCK
import sounddevice as sd
import sunvox as sv
import sunvox.buffered
SIZE = 512
FREQ = 44100
SUNVOX_FILE = 'ticks.sunvox'
def main():
clock_p = sv.buffered.BufferedProcess(size=SIZE, freq=FREQ)
player_p = sv.buffered.BufferedProcess(size=SIZE, freq=FREQ)
clock_slot = sv.Slot(process=clock_p)
player_slot = sv.Slot(process=player_p)
buf_len_seconds = SIZE / FREQ
tick_events = deque()
seq_tick_events = deque()
ports = [
midi.open_output('studiomux @ Cadence', virtual=False),
midi.open_output('USB2.0-MIDI Port 2', virtual=False),
]
def callback(outdata, frames, t, status):
clock_buf = clock_p.fill_buffer()
player_buf = player_p.fill_buffer()
outdata[:] = np.tile(player_buf, outdata.shape[1] // 2)
ticks, = (clock_buf.transpose()[0] != 0).nonzero()
if ticks.any():
last = None
for tick in ticks:
if last is None or tick - last > 1:
wait = buf_len_seconds * (tick / SIZE)
until = time.time() + wait
tick_events.append(until)
last = tick
def clock():
print('clock started')
ticks = -1
while True:
if tick_events:
e = tick_events.popleft()
while time.time() < e:
time.sleep(0)
for port in ports:
port.send(CLOCK)
seq_tick_events.append(e)
ticks += 1
if ticks % 24 == 0:
sys.stdout.write('\n')
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(0)
def sequencer():
print('sequencer started')
ticks = -1
while True:
if seq_tick_events:
seq_tick_events.popleft()
ticks += 1
if ticks % 24 == 0:
player_slot.send_event(0, sv.NOTECMD.C4, 32, 3, 0, 0)
sys.stdout.write('C')
else:
sys.stdout.write(' ')
if ticks % 96 == 0:
player_slot.send_event(1, sv.NOTECMD.G3, 48, 3, 0, 0)
sys.stdout.write('G')
else:
sys.stdout.write(' ')
if ticks % 32 == 0:
sys.stdout.write('E')
player_slot.send_event(2, sv.NOTECMD.E4, 64, 3, 0, 0)
else:
sys.stdout.write(' ')
if ticks % 12 == 0:
sys.stdout.write('F')
player_slot.send_event(3, sv.NOTECMD.F4, 64, 3, 0, 0)
else:
sys.stdout.write(' ')
sys.stdout.flush()
time.sleep(0)
clock_thread = threading.Thread(None, clock, 'clock', daemon=True)
clock_thread.start()
sequencer_thread = threading.Thread(None, sequencer, 'sequencer', daemon=True)
sequencer_thread.start()
clock_slot.load(SUNVOX_FILE)
player_slot.load(SUNVOX_FILE)
clock_slot.play_from_beginning()
for port in ports:
port.send(mido.Message('songpos', pos=0))
port.send(START)
with sd.OutputStream(FREQ, SIZE, callback=callback):
input()
for port in ports:
port.send(STOP)
clock_p.kill()
player_p.kill()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment