summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2012-04-30 22:23:26 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2012-04-30 22:23:26 +0200
commit76dbc41ed584cdec8571c42b8fbfacf7eada07e4 (patch)
tree9566ddd7629476cae6348d96c3470b9c988de33d /lib
parent4ce722cbf97591bc84d0c6477823391cc23706c6 (diff)
downloaddigitale-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.ck2
-rw-r--r--lib/List.ck86
-rw-r--r--lib/NanoEvent.ck159
-rw-r--r--lib/Oscope.ck55
-rw-r--r--lib/Queue.ck44
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);
- }
-}