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 | |
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)
-rw-r--r-- | contact_mic.ck | 45 | ||||
-rw-r--r-- | lfo.ck | 88 | ||||
-rw-r--r-- | lib.ck | 4 | ||||
-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 | ||||
-rw-r--r-- | midi_recorder.ck | 96 | ||||
-rw-r--r-- | settings.nktrl_set | bin | 1184 -> 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 +} @@ -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; } } @@ -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 Binary files differindex 94500bf..d8dc839 100644 --- a/settings.nktrl_set +++ b/settings.nktrl_set |