summaryrefslogtreecommitdiff
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
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)
-rw-r--r--contact_mic.ck45
-rw-r--r--lfo.ck88
-rw-r--r--lib.ck4
-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
-rw-r--r--midi_recorder.ck96
-rw-r--r--settings.nktrl_setbin1184 -> 1184 bytes
10 files changed, 363 insertions, 216 deletions
diff --git a/contact_mic.ck b/contact_mic.ck
index ce27ed6..667a89f 100644
--- a/contact_mic.ck
+++ b/contact_mic.ck
@@ -34,38 +34,17 @@ adc.chan(1) => Bus.out_right;
/*
* Mic/effect configuration via MIDI
*/
-/* FIXME: custom nanoKONTROL events */
-if (me.args() > 0)
- me.exit();
-/* fixed to Scene 1 */
-0 => int on_channel;
-
-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() >>>;
-
-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 == 14) {
- value*0.4 + 0.1 => del.gain;
- } else if (msg.data2 == 2) {
- value*10::ms => del.delay;
- } else if (msg.data2 == 3) {
- value*10 + 1 => pregain.gain;
- } else if (msg.data2 == 15) {
- value*4 + 1 => amp.gain;
- }
- }
+"primary" => NanoEvent.new @=> NanoEvent @nanoev;
+
+while (nanoev => now) {
+ if ("feedbackDistKnob" => nanoev.isControl) {
+ nanoev.getFloat(0.1, 0.5) => del.gain;
+ } else if ("feedbackPitchSlider" => nanoev.isControl) {
+ nanoev.getDur(10::ms) => del.delay;
+ } else if ("feedbackPregainSlider" => nanoev.isControl) {
+ nanoev.getFloat(1, 10) => pregain.gain;
+ } else if ("feedbackGainKnob" => nanoev.isControl) {
+ nanoev.getFloat(1, 5) => amp.gain;
}
-} \ No newline at end of file
+}
diff --git a/lfo.ck b/lfo.ck
index 090102a..210f834 100644
--- a/lfo.ck
+++ b/lfo.ck
@@ -66,63 +66,43 @@ change_osc(int new_osc)
/*
* LFO configuration via MIDI
*/
-/* FIXME: custom nanoKONTROL events */
if (me.args() > 1)
me.exit();
-/* first param: scene number */
-0 => int on_channel;
-if (me.args() > 0)
- (me.arg(0) => Std.atoi)-1 => on_channel;
-if (on_channel < 0 || on_channel > 3)
- me.exit();
-
-MidiIn min;
+NanoEvent nanoev;
-/* always open MIDI Through port, actual connection is done by Jack */
-if (!min.open(0))
- me.exit();
-<<< "MIDI device:", min.num(), " -> ", min.name() >>>;
-
-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 == 22) {
- value => rev.gain;
- } else if (msg.data2 == 13) {
- 100 + value*900 => base.next;
- /* base freq slider is sample rate for SampOsc */
- value*2 => (lfo[2] $ SampOsc).rate;
- } else if (msg.data2 == 12) {
- value*100 => lfo_gain.gain;
- } else if (msg.data2 == 21) {
- /* setting lfo_freq does not influence SampOsc! */
- value*20 => lfo_freq.next => (lfo[2] $ SampOsc).freq;
- } else if (msg.data2 == 31) {
- if (value $ int)
- 0 => change_lfo;
- } else if (msg.data2 == 41) {
- if (value $ int)
- 1 => change_lfo;
- } else if (msg.data2 == 30) {
- if (value $ int)
- 2 => change_lfo;
- } else if (msg.data2 == 40) {
- if (value $ int)
- 1 => change_osc;
- else
- 0 => change_osc;
- } else if (msg.data2 == 9) {
- value*second => rev.delay;
- }
- /*else if (msg.data2 == 9)
- value $ int => lfo.harmonics;*/
- }
+/* first param: scene name */
+"primary" @=> nanoev.wantScene;
+if (me.args() > 0)
+ me.arg(0) @=> nanoev.wantScene;
+
+while (nanoev => now) {
+ if ("lfoVolumeKnob" => nanoev.isControl) {
+ nanoev.getFloat() => rev.gain;
+ } else if ("lfoPitchSlider" => nanoev.isControl) {
+ nanoev.getFloat(100, 1000) => base.next;
+ /* base freq slider is sample rate for SampOsc */
+ nanoev.getFloat(2) => (lfo[2] $ SampOsc).rate;
+ } else if ("lfoDepthSlider" => nanoev.isControl) {
+ nanoev.getFloat(100) => lfo_gain.gain;
+ } else if ("lfoFreqKnob" => nanoev.isControl) {
+ /* setting lfo_freq does not influence SampOsc! */
+ nanoev.getFloat(20) => lfo_freq.next => (lfo[2] $ SampOsc).freq;
+ } else if ("lfoSinOscButton" => nanoev.isControl) {
+ if (nanoev.getBool())
+ 0 => change_lfo;
+ } else if ("lfoPulseOscButton" => nanoev.isControl) {
+ if (nanoev.getBool())
+ 1 => change_lfo;
+ } else if ("lfoSampOscButton" => nanoev.isControl) {
+ if (nanoev.getBool())
+ 2 => change_lfo;
+ } else if ("lfoWaveToggle" => nanoev.isControl) {
+ if (nanoev.getBool())
+ 1 => change_osc;
+ else
+ 0 => change_osc;
+ } else if ("lfoEchoSlider" => nanoev.isControl) {
+ nanoev.getDur(second) => rev.delay;
}
}
diff --git a/lib.ck b/lib.ck
index 22a7f34..8da23c9 100644
--- a/lib.ck
+++ b/lib.ck
@@ -1,9 +1,9 @@
/* Includes */
Machine.add("lib/SampOsc.ck");
Machine.add("lib/Clipper.ck");
-Machine.add("lib/MIDI.ck");
Machine.add("lib/Element.ck");
-Machine.add("lib/Queue.ck");
+Machine.add("lib/List.ck");
+Machine.add("lib/NanoEvent.ck");
Machine.add("lib/Bus.ck");
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);
- }
-}
diff --git a/midi_recorder.ck b/midi_recorder.ck
index 25dfb09..7b4482b 100644
--- a/midi_recorder.ck
+++ b/midi_recorder.ck
@@ -13,11 +13,7 @@ class RecEvent {
return obj;
}
}
-Queue buffer;
-
-/* FIXME: custom nanoKONTROL events */
-if (me.args() > 0)
- me.exit();
+List buffer;
MidiOut mout;
MidiIn min;
@@ -27,20 +23,34 @@ if (!min.open(0))
me.exit();
if (!mout.open(0))
me.exit();
-
<<< "MIDI device:", min.num(), " -> ", min.name() >>>;
-1 => int on_channel; /* Scene 2 */
+false => int looping;
-false => int recording;
-time start;
+fun void
+do_recording()
+{
+ buffer.flush();
+ now => time start;
-false => int looping;
+ while (min => now) {
+ while (MidiMsg msg => min.recv) {
+ <<< "REC Channel:", recev.msg.data1 & 0x0F,
+ "Command:", recev.msg.data1 & 0xF0,
+ "Controller:", recev.msg.data2,
+ "Value:", (recev.msg.data3 $ float)/127 >>>;
+
+ RecEvent.new(now - start, msg) => buffer.put;
+ now => start;
+ }
+ }
+}
+Shred @recording_shred;
fun void
do_playback(int looping)
{
- if (buffer.peek() == null)
+ if (buffer.getHead() == null)
return;
while (true) {
@@ -59,43 +69,39 @@ do_playback(int looping)
if (!looping)
break;
}
-
- <<< "PLAY FIN", 1 >>>;
}
Shred @playback_shred;
-while (min => now) {
- while (MidiMsg msg => min.recv) {
- msg.data1 & 0x0F => int channel;
- msg.data1 & 0xF0 => int cmd;
- (msg.data3 $ float)/127 => float value;
- //<<< "Channel:", channel, "Command:", cmd, "Controller:", msg.data2, "Value:", value >>>;
-
- channel == on_channel && cmd == 0xB0 => int is_cmd;
-
- if (is_cmd && msg.data2 == 44) {
- if (value $ int => recording) {
- now => start;
- buffer.flush();
- }
- } else if (is_cmd && msg.data2 == 45) {
- if (value $ int) {
- if (playback_shred != null)
- playback_shred.exit();
- spork ~ do_playback(looping) @=> playback_shred;
- }
- } else if (is_cmd && msg.data2 == 46) {
- if (value $ int && playback_shred != null) {
+/*
+ * Recorder configuration via MIDI
+ */
+"secondary" => NanoEvent.new @=> NanoEvent @nanoev;
+
+while (nanoev => now) {
+ if ("recordToggle" => nanoev.isControl) {
+ if (nanoev.getBool()) {
+ if (recording_shred != null)
+ recording_shred.exit();
+ spork ~ do_recording() @=> recording_shred;
+ } else if (recording_shred != null) {
+ recording_shred.exit();
+ null @=> recording_shred;
+
+ /* remove recordToggle event from buffer queue */
+ buffer.pop();
+ }
+ } else if ("playButton" => nanoev.isControl) {
+ if (nanoev.getBool()) {
+ if (playback_shred != null)
playback_shred.exit();
- null @=> playback_shred;
- }
- } else if (is_cmd && msg.data2 == 49) {
- value $ int => looping;
- } else if (recording) {
- <<< "REC Channel:", channel, "Command:", cmd, "Controller:", msg.data2, "Value:", value >>>;
-
- RecEvent.new(now - start, msg) => buffer.push;
- now => start;
+ spork ~ do_playback(looping) @=> playback_shred;
}
+ } else if ("stopButton" => nanoev.isControl) {
+ if (nanoev.getBool() && playback_shred != null) {
+ playback_shred.exit();
+ null @=> playback_shred;
+ }
+ } else if ("loopToggle" => nanoev.isControl) {
+ nanoev.getBool() => looping;
}
-} \ No newline at end of file
+}
diff --git a/settings.nktrl_set b/settings.nktrl_set
index 94500bf..d8dc839 100644
--- a/settings.nktrl_set
+++ b/settings.nktrl_set
Binary files differ