In case of running out of prefetch pipes, truncate the sample to the preloaded part.
[calfbox.git] / novabox.py
blobe0e23e34d990612e27b4b38ef35dd3f8c3d5f5de
1 # A primitive drum machine interface for Novation Nocturn.
3 # Usage:
4 # - make sure that Novation Nocturn is connected *and* that the USB device
5 # that corresponds to it can be opened by the current user
6 # - create an ini file containing a scene with a single instrument using
7 # a sampler or fluidsynth engine + some drum mappings in SFZ or SF2
8 # - ensure that the ini file contains 7 drum patterns, pat1..pat7, these
9 # can be copied from cboxrc-example
10 # - user buttons 1..7 start drum patterns
11 # - user button 8 stops the playback
12 # - encoder knob 1 adjusts the volume
13 # - mixer button exits the application
15 import math
16 import sys
18 sys.path = ["./py"] + sys.path
20 import nocturn
21 import cbox
23 quit = False
25 instr_name = cbox.Document.get_scene().status().instruments.keys()[0]
27 def clamp(val, min, max):
28 if val < min:
29 val = min
30 elif val > max:
31 val = max
32 return val
34 class NovaBox:
35 def __init__(self):
36 self.nocturn = nocturn.Nocturn()
37 self.cur_pattern = None
38 self.handlers = {}
39 self.handlers[83] = self.on_xfade_touch
40 for i in range(7):
41 self.handlers[112 + i] = lambda cmd, val: self.on_buttonN_press(cmd - 112) if val > 0 else None
42 self.handlers[112 + 7] = lambda cmd, val: self.on_button8_press() if val > 0 else None
43 self.handlers[64] = self.on_knob1_change
44 self.handlers[65] = self.on_knob2_change
45 self.handlers[127] = lambda cmd, val: self.on_mixer_press() if val > 0 else None
47 def on_knob1_change(self, cmd, val):
48 scene = cbox.Document.get_scene()
49 instr = scene.status().instruments[instr_name][1]
50 gain = instr.get_things('/output/1/status', ['gain']).gain
51 if val > 63:
52 val = -128 + val
53 instr.cmd('/output/1/gain', None, gain + val * 0.5)
55 def on_knob2_change(self, cmd, val):
56 tempo = cbox.GetThings("/master/status", ['tempo'], []).tempo
57 if val > 63:
58 val = -128 + val
59 tempo = clamp(tempo + val * 0.5, 30, 300)
60 cbox.do_cmd('/master/set_tempo', None, [tempo])
62 def on_buttonN_press(self, button):
63 cbox.do_cmd("/master/stop", None, [])
64 song = cbox.Document.get_song()
65 song.loop_single_pattern(lambda: song.load_drum_pattern('pat%d' % (button + 1)))
66 cbox.do_cmd("/master/seek_ppqn", None, [0])
67 cbox.do_cmd("/master/play", None, [])
68 self.cur_pattern = button
70 def on_button8_press(self):
71 self.cur_pattern = None
72 cbox.do_cmd("/master/stop", None, [])
74 def on_mixer_press(self):
75 global quit
76 quit = True
78 def on_xfade_touch(self, cmd, val):
79 if val > 0:
80 print "Do not touch"
82 def handler(self, cmd, val):
83 if cmd in self.handlers:
84 self.handlers[cmd](cmd, val)
85 return
87 def poll(self):
88 self.nocturn.poll(self.handler)
90 def update(self):
91 scene = cbox.Document.get_scene()
92 cmds = nocturn.NocturnCommands()
93 master = cbox.GetThings("/master/status", ['playing'], [])
94 for i in range(7):
95 cmds.setModeButtonLight(i, self.cur_pattern == i)
96 gain = scene.status().instruments[instr_name][1].get_things('/output/1/status', ['gain']).gain
97 cmds.setEncoderMode(0, 0)
98 cmds.setEncoderValue(0, clamp(int(gain * 2 + 64), 0, 127))
100 cmds.setModeButtonLight(7, self.cur_pattern is None)
101 self.nocturn.execute(cmds)
103 nb = NovaBox()
104 while not quit:
105 nb.poll()
106 nb.update()
107 nb.nocturn.reset()