diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-04-30 22:23:26 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-04-30 22:23:26 +0200 |
commit | 76dbc41ed584cdec8571c42b8fbfacf7eada07e4 (patch) | |
tree | 9566ddd7629476cae6348d96c3470b9c988de33d /lib | |
parent | 4ce722cbf97591bc84d0c6477823391cc23706c6 (diff) | |
download | digitale-debutanten-76dbc41ed584cdec8571c42b8fbfacf7eada07e4.tar.gz |
NanoEvent MIDI abstraction, List instead of Queue class
* had to adapt all MIDI-using shreds
* some (slider/knob) scalings are more sane now (there are helpers for scaling a MIDI message value between two values)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Element.ck | 2 | ||||
-rw-r--r-- | lib/List.ck | 86 | ||||
-rw-r--r-- | lib/NanoEvent.ck | 159 | ||||
-rw-r--r-- | lib/Oscope.ck | 55 | ||||
-rw-r--r-- | lib/Queue.ck | 44 |
5 files changed, 264 insertions, 82 deletions
diff --git a/lib/Element.ck b/lib/Element.ck index 7a81429..3054d40 100644 --- a/lib/Element.ck +++ b/lib/Element.ck @@ -2,6 +2,8 @@ * List element */ public class Element { + Element @prev; Element @next; + Object @payload; } diff --git a/lib/List.ck b/lib/List.ck new file mode 100644 index 0000000..539fe75 --- /dev/null +++ b/lib/List.ck @@ -0,0 +1,86 @@ +/* + * (Double-linked) list data structure with Queue and Stack operations + */ +public class List { + Element head @=> Element @tail; + + /* + * Stack operations + */ + fun void + push(Object @data) + { + Element el; + tail @=> el.prev; + el @=> tail.next @=> tail; + + data @=> el.payload; + } + + fun Object @ + getTail() + { + /* NOTE: null for tail == head */ + return tail.payload; + } + + fun Object @ + pop() + { + tail @=> Element @el; + if (el.prev == null) + /* empty */ + return null; + + el.prev @=> tail; + null @=> tail.next; + + return el.payload; + } + + /* + * Queue operations + */ + fun void + put(Object @data) + { + data => push; + } + + fun Object @ + getHead() + { + if (head.next == null) + /* empty */ + return null; + else + return head.next.payload; + } + + fun Object @ + get() + { + head.next @=> Element @el; + if (el == null) + /* empty */ + return null; + + el.next @=> head.next; + if (el.next == null) + /* but now it's empty! */ + head @=> tail; + else + head @=> el.next.prev; + + return el.payload; + } + + /* + * Common operations + */ + fun void + flush() + { + while (pop() != null); + } +} diff --git a/lib/NanoEvent.ck b/lib/NanoEvent.ck new file mode 100644 index 0000000..fac8580 --- /dev/null +++ b/lib/NanoEvent.ck @@ -0,0 +1,159 @@ +/* + * nanoKONTROL event class + */ +public class NanoEvent extends Event { + /* map channel (0-15) to scene name */ + static string @channelToScene[]; + /* map scene name and control id (0-255) to control name */ + static string @controlToName[][]; + + string wantScene; + + string scene; + string control; + float value; + + fun int + isScene(string s) + { + return scene == s; + } + fun int + isControl(string c) + { + return control == c; + } + + fun float + getFloat() + { + return value; + } + fun float + getFloat(float max) + { + return max*value; + } + fun float + getFloat(float min, float max) + { + return min + (max - min)*value; + } + + fun dur + getDur(dur max) + { + return max*value; + } + fun dur + getDur(dur min, dur max) + { + return min + (max - min)*value; + } + + fun int + getBool() + { + return value $ int; + } + + fun void + __midi_loop(int device) /* pseudo-private */ + { + MidiIn min; + + if (!min.open(device)) { + <<< "Cannot open MIDI device", device >>>; + me.exit(); + } + + while (min => now) { + while (MidiMsg msg => min.recv) { + channelToScene[msg.data1 & 0x0F] @=> scene; + if (scene == null) { + <<< "Unknown channel", msg.data & 0x0F >>>; + msg.data1 & 0x0F => Std.itoa @=> scene; + } + + msg.data1 & 0xF0 => int cmd; + + controlToName[scene][msg.data2] @=> control; + if (control == null) { + <<< "Unknown controller", msg.data2 >>>; + msg.data2 => Std.itoa @=> control; + } + + (msg.data3 $ float)/127 => value; + + if (cmd == 0xB0 && (wantScene == null || scene == wantScene)) + broadcast(); + } + } + } + /* always open MIDI Through port, actual connection is done by Jack */ + spork ~ __midi_loop(0); + + fun static NanoEvent @ + new(string scene) + { + NanoEvent obj; + + scene @=> obj.wantScene; + + return obj; + } + + fun static void + registerScene(int channel, string name) + { + name @=> channelToScene[channel]; + } + + fun static void + registerControl(string sceneName, int id, string controlName) + { + controlName @=> controlToName[sceneName][id]; + } +} +/* static initialization */ +new string[0x0F] @=> NanoEvent.channelToScene; +new string[0][0xFF] @=> NanoEvent.controlToName; + +/* + * global mappings + */ + +NanoEvent.registerScene(0, "primary"); +NanoEvent.registerScene(1, "secondary"); +NanoEvent.registerScene(3, "oscope"); + +NanoEvent.registerControl("primary", 14, "feedbackDistKnob"); +NanoEvent.registerControl("primary", 02, "feedbackPitchSlider"); +NanoEvent.registerControl("primary", 03, "feedbackPregainSlider"); +NanoEvent.registerControl("primary", 15, "feedbackGainKnob"); + +fun void +registerLFO(string scene) +{ + NanoEvent.registerControl(scene, 22, "lfoVolumeKnob"); + NanoEvent.registerControl(scene, 13, "lfoPitchSlider"); + NanoEvent.registerControl(scene, 12, "lfoDepthSlider"); + NanoEvent.registerControl(scene, 21, "lfoFreqKnob"); + NanoEvent.registerControl(scene, 31, "lfoSinOscButton"); + NanoEvent.registerControl(scene, 41, "lfoPulseOscButton"); + NanoEvent.registerControl(scene, 30, "lfoSampOscButton"); + NanoEvent.registerControl(scene, 40, "lfoWaveToggle"); + NanoEvent.registerControl(scene, 09, "lfoEchoSlider"); +} +"primary" => registerLFO; +"secondary" => registerLFO; + +NanoEvent.registerControl("secondary", 44, "recordToggle"); +NanoEvent.registerControl("secondary", 45, "playButton"); +NanoEvent.registerControl("secondary", 46, "stopButton"); +NanoEvent.registerControl("secondary", 49, "loopToggle"); + +NanoEvent.registerControl("oscope", 67, "modeToggle"); +NanoEvent.registerControl("oscope", 76, "fillToggle"); +NanoEvent.registerControl("oscope", 42, "frameSlider"); +NanoEvent.registerControl("oscope", 57, "delayKnob"); diff --git a/lib/Oscope.ck b/lib/Oscope.ck index b18a0b2..8ecec70 100644 --- a/lib/Oscope.ck +++ b/lib/Oscope.ck @@ -75,45 +75,24 @@ for (0 => int i; i < Bus.oscope.cap(); i++) Bus.oscope[i] => dac.chan(4 + i); /* - * jack.scope configuration via MIDI (Channel/Scene 1) + * jack.scope configuration */ -/* FIXME: custom nanoKONTROL events */ -if (me.args() > 0) - me.exit(); +"oscope" => NanoEvent.new @=> NanoEvent @nanoev; -MidiIn min; - -/* always open MIDI Through port, actual connection is done by Jack */ -if (!min.open(0)) - me.exit(); -<<< "MIDI device:", min.num(), " -> ", min.name() >>>; - -3 => int on_channel; /* scene 4 */ - -while (min => now) { - while (MidiMsg msg => min.recv) { - msg.data1 & 0x0F => int channel; - msg.data1 & 0xF0 => int cmd; - (msg.data3 $ float)/127 => float value; - - if (channel == on_channel && cmd == 0xB0) { - <<< "Channel:", channel, "Command:", cmd, "Controller:", msg.data2, "Value:", value >>>; - - if (msg.data2 == 67) { - if (value $ int) - "embed" => Oscope.mode; - else - "signal" => Oscope.mode; - } else if (msg.data2 == 76) { - if (value $ int) - "fill" => Oscope.style; - else - "line" => Oscope.style; - } else if (msg.data2 == 42) { - 512::samp + value*2::second => Oscope.frames; - } else if (msg.data2 == 57) { - 50::ms + value*second => Oscope.delay; - } - } +while (nanoev => now) { + if ("modeToggle" => nanoev.isControl) { + if (nanoev.getBool()) + "embed" => Oscope.mode; + else + "signal" => Oscope.mode; + } else if ("fillToggle" => nanoev.isControl) { + if (nanoev.getBool()) + "fill" => Oscope.style; + else + "line" => Oscope.style; + } else if ("frameSlider" => nanoev.isControl) { + nanoev.getDur(512::samp, 2::second) => Oscope.frames; + } else if ("delayKnob" => nanoev.isControl) { + nanoev.getDur(50::ms, second) => Oscope.delay; } } diff --git a/lib/Queue.ck b/lib/Queue.ck deleted file mode 100644 index 586330c..0000000 --- a/lib/Queue.ck +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Queue data structure - */ -public class Queue { - Element head @=> Element @tail; - - fun void - push(Object @data) - { - new Element @=> tail.next @=> tail; - data @=> tail.payload; - } - - fun Object @ - peek() - { - if (head.next == null) - /* empty */ - return null; - else - return head.next.payload; - } - - fun Object @ - pop() - { - head.next @=> Element @el; - if (el == null) - /* empty */ - return null; - - el.next @=> head.next; - if (el == tail) - /* but now it's empty! */ - head @=> tail; - return el.payload; - } - - fun void - flush() - { - while (pop() != null); - } -} |