Browse files

add files

  • Loading branch information...
1 parent b7198e9 commit 8b38bbfca8d63b561383c767a605b425d7ad2791 @miguel-negrao miguel-negrao committed Apr 14, 2012
Showing with 7,100 additions and 0 deletions.
  1. +13 −0 AbandonOldHelp/CompassView.scd
  2. +264 −0 AbandonOldHelp/Dispatch.scd
  3. +113 −0 AbandonOldHelp/FuncChain.scd
  4. +33 −0 AbandonOldHelp/LaunchpadOut.scd
  5. +81 −0 AbandonOldHelp/MIDIMKtl.scd
  6. +99 −0 AbandonOldHelp/MKtl.scd
  7. +82 −0 AbandonOldHelp/MKtlElement.scd
  8. +111 −0 AbandonOldHelp/MKtlGui.scd
  9. +15 −0 AbandonOldHelp/Modality.overview.scd
  10. +50 −0 AbandonOldHelp/PKtl.scd
  11. +170 −0 AbandonOldHelp/Sandrode.scd
  12. +504 −0 Dispatch.graffle
  13. BIN Dispatchpdf.pdf
  14. +225 −0 MKtl Cookbook.scd
  15. +138 −0 MKtlGui_sketch.scd
  16. BIN Modality Workshop EventStreams and FPSignals.pdf
  17. BIN Modality Workshop EventStreams and FPSignals.rtfd/1__#$!@%!#__Pasted Graphic 4.pdf
  18. BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 1.pdf
  19. BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 4.pdf
  20. BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 5.pdf
  21. BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 6.pdf
  22. BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 7.tiff
  23. +279 −0 Modality Workshop EventStreams and FPSignals.rtfd/TXT.rtf
  24. +1,929 −0 ModalityOverview.graffle
  25. +3 −0 ModalityOverview.svg
  26. +333 −0 NotesNTests/Modality FRP.scd
  27. +9 −0 NotesNTests/guihints.scd
  28. +38 −0 NotesNTests/hashVariants.rtf
  29. +24 −0 RealPlayer.scd
  30. +142 −0 RunNDrive.scd
  31. +136 −0 Sketch.scd
  32. +73 −0 SketchesNotesEtc/_TODOlity.scd
  33. +29 −0 SketchesNotesEtc/_sketchForIO.scd
  34. +14 −0 SketchesNotesEtc/_sketchForSpecs.scd
  35. +23 −0 SketchesNotesEtc/_theWishlist.scd
  36. +331 −0 SuperCollider/SCClassLibrary/Common/Control/GeneralHID.sc
  37. +288 −0 SuperCollider/SCClassLibrary/Common/Control/HIDDeviceService.sc
  38. +413 −0 SuperCollider/SCClassLibrary/Platform/osx/MXHID.sc
  39. +32 −0 Tests/JIT.gui_tests.scd
  40. +68 −0 TillsWonderfulDocumentAboutGettingToDestinationSupport.scd
  41. +70 −0 blogpost.txt
  42. +14 −0 extDict-testCases.scd
  43. +141 −0 patches from steim/LFSaw_LaunchpadPiece.scd
  44. +56 −0 patches from steim/Miguel's instrument for HotPot @ Steim.scd
  45. +141 −0 presentation/HAPsensestage2_lac.sty
  46. 0 presentation/modality_presentation-2012.log
  47. 0 presentation/modality_presentation-2012.lyx
  48. BIN presentation/modality_presentation-2012.pdf
  49. +348 −0 presentation/modality_presentation-2012.tex
  50. BIN presentation/modality_presentation.pdf
  51. +268 −0 presentation/modality_presentation.tex
View
13 AbandonOldHelp/CompassView.scd
@@ -0,0 +1,13 @@
+/* CompassView -- a view for GamePad compasses.
+
+ // tests
+w = Window("Compass").front;
+z = CompassView(w.view, Rect( 200, 100,100,100));
+z.action = { |comp| comp.dump };
+z.nameView.string = "test";
+z.value = 0;
+z.value = rrand(1, 8);
+z.valueAction = 0;
+z.valueAction = rrand(1, 8).postln;
+
+*/
View
264 AbandonOldHelp/Dispatch.scd
@@ -0,0 +1,264 @@
+/*
+888~-_ ,e, d8 888
+888 \ " d88~\ 888-~88e /~~~8e _d88__ e88~~\ 888-~88e
+888 | 888 C888 888 888b 88b 888 d888 888 888
+888 | 888 Y88b 888 8888 e88~-888 888 8888 888 888
+888 / 888 888D 888 888P C888 888 888 Y888 888 888
+888_-~ 888 \_88P 888-_88" "88_-888 "88_/ "88__/ 888 888
+ 888
+*/
+
+MIDIMKtl.find
+//average
+(
+a = MIDIMKtl('bcr0');
+// MDispatch
+d = MDispatch.new;
+
+d.mapToElem( a, \sl1_1, \myController1 );
+d.mapToElem( a, \sl2_1, \myController1 );
+
+d.createOutput(\average);
+
+d.addToProc( \average, { |dis|
+ var val = [\sl1_1,\sl2_1].collect{ |it|
+ dis.getInput( \myController1, it )
+ }.sum;
+ dis.setOutput( \average, val/2 );
+} );
+
+d.addToOutput(\average,\blah,{ |element| postln("average is "++element.value) })
+)
+
+d.remove;
+d.verbose = true;
+
+
+//same ktl, different controls to different sources
+(
+k = MIDIMKtl('bcr0');
+d = MDispatch.new;
+~sliders = d.map(k, k.elementsOfType(\slider).collect(_.name) , \nksliders);
+~buttons = d.map(k, k.elementsOfType(\button).collect(_.name) , \nkbuttons);
+)
+d.remove
+
+//------- TEMPLATES-----------------------
+
+//paged launchpad
+(
+~paged = MDispatch.make(\paged,MIDIMKtl('lnch0'),\left,\right,32);
+~paged.verbose = true
+)
+~paged.remove
+
+//paged nanoKontrol
+(
+MIDIMKtl('bcr0').reset;
+~paged = MDispatch.make(\paged,MIDIMKtl('bcr0'),\rew,\fwd);
+~paged.verbose = true
+)
+
+
+
+//velocity
+(
+MIDIMKtl('bcr0') .reset;
+~dis = MDispatch.make(\velocity, MIDIMKtl('bcr0') );
+~dis.verbose = true;
+)
+
+~dis.addFuncElem(\knF1,\blah,{Ê|elem| elem.value.postln })
+
+(
+w = Window.new.front;
+~sl = Slider(w, Rect(20, 60, 150, 20));
+~dis.addFuncElem(\knF1,\blah,{Ê|elem| { ~sl.value_(elem.value/10) }.defer; x.set(\freq,elem.value*300) });
+~dis.addFuncElem(\knF2,\blah,{ |elem| x.set(\mod, elem.value) })
+)
+~dis.reset
+x = { |freq= 400, mod = 1| Saw.ar( Lag.kr(freq,1)+30 ) * SinOsc.ar(Lag.kr(mod) ).range(0.0,1.0)* 0.5 }.play
+
+x = { |freq= 400, mod = 1| Saw.ar( Lag.kr(freq,1)+30 ) * SinOsc.ar(Lag.kr(mod) ).range(0.0,1.0)* 0.5 }.play
+Ndef(\y,{ |freq= 400, mod = 1| Saw.ar( Lag.kr(freq,1)+200 ) * SinOsc.ar(Lag.kr(mod,1) ).range(0.0,1.0)* 0.5 }).play
+Ndef(\y,{ |freq= 400, mod = 1| Saw.ar( Lag.kr(freq,1)+200 ) * 0.5 })
+~dis.addFuncElem(\knF1,\blah,{Ê|elem| { ~sl.value_(elem.value/10) }.defer; Ndef(\y).set(\freq,elem.value*300) });
+~dis.addFuncElem(\knF2,\blah,{ |elem| Ndef(\y).set(\mod, elem.value*5) })
+x.free
+x = { |freq= 400, mod = 1| Saw.ar( LeakDC.kr( Lag.kr(freq)) + 100 ) * SinOsc.ar( LeakDC.kr( Lag.kr(mod), 0.990) + 1 ).range(0.0,1.0)* 0.5 }.play
+
+
+
+// paged and then velocity
+(
+MIDIMKtl('bcr0').reset;
+~dis1 = MDispatch.make(\paged, MIDIMKtl('bcr0'),\rew,\fwd,4);
+~dis2 = MDispatch.make(\velocity, ~dis1);
+~dis2.verbose = true
+)
+~dis2.recursiveRemove
+
+
+
+//merge
+//make slider output velocity but keep the rest of controls absolute and merge into just one controller
+(
+k = MIDIMKtl('bcr0');
+k.reset;
+~sliders = MDispatch.make(\velocity,k, k.elementsOfType(\slider).collect(_.name) );
+
+~restOfElems = k.elements.select{ |elem|
+ elem.deviceDescription[\type] != \slider
+}.collect(_.name);
+
+~result = MDispatch.make(\merge,[[~sliders,nil],[k,~restOfElems]]);
+~result.verbose = true;
+~sliders.verbose = true;
+)
+~result.remove // only removes the merge dispatch
+~result.recursiveRemove // removes all dispatchs going down the chain
+
+MIDIMKtl('bcr0').verbose = true;
+
+// trigger
+(
+MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\trigger, MIDIMKtl('bcr0') );
+~disp.verbose = true;
+)
+~disp.remove
+MIDIMKtl('bcr0').verbose = false
+
+
+
+// multiple trig
+(
+MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\multipleClick,MIDIMKtl('bcr0'));
+~disp .verbose = true;
+)
+~disp .insp
+~disp .remove
+
+
+
+// thresh
+(
+MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\threshold, MIDIMKtl('bcr0'), thresh: 0.5 );
+~disp.verbose = true;
+)
+~disp.remove
+
+MIDIMKtl('bcr0').verbose = true;
+
+// thresh up
+(
+MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\thresholdUp, MIDIMKtl('bcr0'), thresh: 0.5 );
+~disp.verbose = true;
+)
+~disp.remove
+
+
+
+// thresh down
+(
+MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\thresholdDown, MIDIMKtl('bcr0'), thresh: 0.5 );
+~disp.verbose = true;
+)
+~disp.remove
+
+
+
+// thresh zones
+(
+MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\threshZones, MIDIMKtl('bcr0'), [0.2,0.5,0.8] );
+~disp.verbose = true;
+)
+~disp.remove
+
+
+9.do{ |i|
+ 3.do{ |j|
+ var r = rrand(300,3000);
+ ~disp.addToOutput(("sl"++i++"_1_"++j).asSymbol, \sound,{ { FreeVerb.ar(0.1*SinOsc.ar(r) * EnvGen.ar(Env.perc, doneAction:2),0.5,0.9) }.play });
+ }
+}
+
+
+// up
+(
+MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\up, MIDIMKtl('bcr0') );
+~disp.verbose = true;
+)
+~disp.remove
+
+// down
+(
+k = MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\down, k);
+~disp.verbose = true;
+)
+
+// inRange
+(
+k = MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\inRange, k, nil, nil, [0.5,1.0]);
+~disp.verbose = true;
+)
+
+// match
+(
+k = MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\match, k, [0.0,1.0]);
+~disp.verbose = true;
+)
+
+// change
+(
+k = MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\change, k, nil, nil, [0.0,1.0]);
+~disp.verbose = true;
+)
+~disp.remove
+
+// changeDirection
+(
+k = MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\changeDirection, k, nil, nil, [0.0,1.0]);
+~disp.verbose = true;
+)
+~disp.remove
+
+// wait
+(
+k = MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\wait, k);
+~disp.verbose = true;
+)
+
+// within - only if values come in within n seconds of each other.
+(
+k = MIDIMKtl('bcr0').reset;
+~trigger = MDispatch.make(\trigger,k);
+~disp = MDispatch.make(\within, ~trigger, 1.3);
+~disp.verbose = true;
+)
+
+
+// delay - only if values come in within n seconds of each other.
+(
+k = MIDIMKtl('bcr0').reset;
+~disp = MDispatch.make(\delay, k, 1);
+~disp.verbose = true;
+)
+
+~disp.remove
+~disp.insp
+
+k.verbose = true
+k = MIDIMKtl('bcr0')
View
113 AbandonOldHelp/FuncChain.scd
@@ -0,0 +1,113 @@
+FuncChain a named FunctionList
+
+FuncChain is an ordered list of functions that
+can be accessed, added and removed by name.
+
+adding with a new name adds to the end of the list;
+adding with an existing name replaces the func at that name.
+one can add before or after an existing name,
+and one can add at the head or end of the list.
+
+ // one can make a FuncChain with pairs of name, func:
+(
+a = FuncChain([
+ \ada, { "ada" },
+ \bob, { "bob" }
+]);
+)
+ // or make an empty one:
+a = FuncChain.new;
+
+ // common behavior for all add methods:
+ // if name exists, func at that name is removed;
+ // new func is put where indicated.
+
+ put(name, func)
+ // if name is present, put func there;
+ // if not, add a at the end.
+a.put(\x, {1});
+a.put(\y, {2});
+a.put(\x, {234});
+a.put(\z, {567});
+
+a.at(\z).postcs
+
+ add(name, func, addAction, otherName)
+ // add allows redirection by flags;
+ // if no flags are given, behaves like put.
+
+a.add(\x, {10});
+a.add(\y, {20});
+a.add(\z, {30});
+a.add(\w, {40});
+
+ addLast(name, func)
+ // if name is used, removes func at name;
+ // adds new func to the end of the list
+a.addLast(\x, {3});
+a.addLast(\y, {3});
+
+ addFirst(name, func)
+ // if name is used, removes func at name;
+ // adds new func to the head of the list
+a.addFirst(\z, {5});
+a.addFirst(\x, {5});
+a.addFirst(\y, {5});
+
+
+ // adding relative to another name
+
+ addBefore(name, func, otherName)
+ // if name is not present, warns and adds to head
+a.addBefore(\abc, { 1234 }, \x);
+a.addBefore(\abc, { 1234 }, \y);
+a.addBefore(\abc, { 1234 }, \z);
+a.addBefore(\abc, { 1234 }, \tralala);
+
+ addAfter(name, func, otherName)
+ // if name is not present, warns and adds to end
+a.addAfter(\abc, { 1234 }, \x);
+a.addAfter(\abc, { 1234 }, \y);
+a.addAfter(\abc, { 1234 }, \z);
+a.addAfter(\abc, { 1234 }, \x);
+a.addAfter(\xyz, { 1234 }, \tralala);
+a.addAfter(\rst, { 1234 }, \tralala);
+
+
+ // returns the func at name, or nil if name is not present
+ removeAt(name)
+a.removeAt(\a)
+a.removeAt(\x); a
+a.removeAt(\y); a
+a.removeAt(\xyz); a
+a.removeAt(\rst); a
+
+ replaceAt(name, func, othername)
+
+a.replaceAt(\rst, { 987 }, \a); a
+a.replaceAt(\x, { 123 }, \z); a
+
+ // internal methods
+removeAtIndex(index) // fails if out of range
+replaceAtIndex(index, name, func)
+putAtIndex(index, name, func)
+
+
+ // questions:
+* semantically, maybe addBefore and addAfter should rather be
+ addBefore(othername, name, func)
+ - or do we need that order for something?
+
+* maybe implement valueAt(name, ... args) to get at just some of the functions?
+
+ // todo:
+* write examples that check .value method
+
+implement moving:
+
+ move( name, addAction, otherName )
+ moveLast(name)
+ moveFirst(name)
+ moveAfter(name)
+ moveBefore(name)
+
View
33 AbandonOldHelp/LaunchpadOut.scd
@@ -0,0 +1,33 @@
+MIDIMKtl.find
+
+a = MIDIMKtl(\lnch0)
+b = LaunchpadOut(a)
+
+20.do{b.setColor({8.rand}!2, \lAmber)}
+
+{|i| b.setPlayColor(i, \red) }!8
+
+b.reset
+
+
+
+MIDIMKtl.destinationDeviceDict
+MIDIMKtl.sourceDeviceDict
+
+a.verbose = true
+
+
+b.midiOut.inspect
+
+
+a.elements
+
+
+
+MIDIClient.destinations.indexOf(MIDIMKtl(\lnch0).source)
+MIDIEndPoint
+
+MIDIMKtl(\lnch0)
+
+.source.inspect
+MIDIClient.destinations.first.inspect
View
81 AbandonOldHelp/MIDIMKtl.scd
@@ -0,0 +1,81 @@
+// MIDIMKtl - an MKtl class for MIDI.
+
+// One can find potential MIDI sources like this:
+ MIDIMKtl.find;
+
+// It'll post a line such as this
+MIDIMKtl('lnch0', 593081660); // Launchpad
+MIDIMKtl('nnkn0', -616253900); // nanoKONTROL
+
+// If you already know the (autogenerated) short name of your device, MIDIMKtl.find is optional to call.
+
+// Instantiate your device with the autogenerated identifier
+a = MIDIMKtl('lnch0');
+b = MIDIMKtl('nnkn0');
+
+// ... or give it another identifier by calling it with its uid
+// (to be found in MIDIMKtl.find)
+// (you can also swap default names with this --for now only once, though-- )
+a = MIDIMKtl('launch', 593081660);
+
+// there is no uid like that, so this fails
+c = MIDIMKtl(\launch, 12345);
+
+// calling the method again with the same key will return the assigned one
+MIDIMKtl(\launch);
+
+b === a
+// -> true
+
+// just any device you have connected:
+MIDIMKtl.find;
+a = MIDIMKtl(MIDIMKtl.deviceDict.keys.asArray.first)
+
+
+// the source
+MIDIMKtl('lnch0').source;
+MIDIMKtl('lnch0').srcID;
+
+a = b;
+
+// it loads descriptions of all its elements from files:
+a.deviceDescription;
+// which can be posted conveniently
+a.postDeviceDescription; "";
+a.elementNames.postcs; "";
+
+// turn on verbose mode:
+// this adds a "verbose" function to the functionChain of all elements.
+a.verbose = true;
+b.verbose = true;
+
+// turn it off again
+a.verbose = false
+
+a.elements
+a.prepareFuncDict
+
+// add a custom function
+
+( // add method to NANOkontrol's leftmost slider
+ a.addFunc(\sl1_1, \yubel, { |who, what, howmuch|
+ "YAYAYAY: ".post; [who, what, howmuch].postln;
+ });
+)
+a.removeFunc(\sl1_1, \yubel)
+
+( // add method to a Launchpad's upper left button
+ a.addFunc(\up, \yubel, { |who, what, howmuch|
+ "YAYAYAY: ".post; [who, what, howmuch].postln;
+ });
+)
+a.removeFunc(\up, \yubel)
+
+
+
+// tests for lookup key conversions:
+
+MIDIMKtl.makeCCKey(1, 20).postcs; // 'c_1_20'
+MIDIMKtl.ccKeyToChanCtl('c_1_20').postcs; // [1, 20]
+MIDIMKtl.makeNoteKey(1, 64).postcs; // 'c_1_20'
+MIDIMKtl.noteKeyToChanNote('n_1_64').postcs; // [1, 20]
View
99 AbandonOldHelp/MKtl.scd
@@ -0,0 +1,99 @@
+//////////// MKtl - a model for a (hardware or gui-based) controller //////////
+
+ // One can find all currently available devices on MIDI, HID;
+ // ... others later: hardware (Serial, OSC, Wii, Keyboard)
+ // ... virtual control devices (GUI, SETO, DataNode)
+MKtl.find;
+
+ // post known devices that have descriptions:
+MKtl.postAllDescriptions;
+
+
+
+// There are two ways of making MKtls:
+
+// 1 // FOR CURRENTLY PRESENT DEVICES:
+ // based on an existing uid,
+ // make a MIDIMKtl or HIDMKtl:
+ // this gets the description for the device on that USB port ID;
+ // replace ??? with your preferred name for that device.
+MIDIMKtl('?', -616253900);
+
+
+// 2 // based on a description name, make a blank MKtl with those specs;
+ // this should work without the device's physical presence.
+ // this gets the description for the device on that USB port ID;
+MKtl.make(\nk1, 'nanoKONTROL');
+MKtl(\nk1).dump;
+MKtl(\nk1).deviceDescription;
+
+MKtl(\nk1).elements.choose.deviceDescription
+
+
+// 2b // the same procedure for HIDKtl:
+
+MKtl.all.clear;
+MKtl.make(\ferr1, 'Run_N_Drive');
+MKtl(\ferr1).dump;
+MKtl(\ferr1).elements[\joyLX].deviceDescription
+MKtl(\ferr1).elements.do {|el| el.deviceDescription.postln };
+
+
+// look only for HIDMKtl devices present:
+HIDMKtl.find;
+
+HIDMKtl(\fer1, 102760448); // osx
+HIDMKtl('fer1', "usb-0000:00:1a.0-1.1/input0" ); // Thrustmaster Run'N' Drive on linux
+
+
+
+ ///////// Some Tests //////////
+
+ // there is no uid like that, so this fails
+MIDIMKtl(\nk1, 12345); //
+
+
+
+// it auto-loads descriptions of all its elements from files:
+ MIDIMKtl(\nk1).deviceDescription;
+
+// which can be posted conveniently
+ MIDIMKtl(\ferr1).postDeviceDescription;
+
+ MIDIMKtl(\nk1).ktlNames.postcs; "";
+
+(
+ MIDIMKtl(\nk1).addFunc(\sl1_1, \yubel, { |who, what, howmuch|
+ "YAYAYAY: ".post; [who, what, howmuch].postln;
+ });
+) //
+
+// turn verbose mode on; this adds a "verbose" function to all functionChains for all elements.
+ MIDIMKtl(\nk1).verbose = true;
+
+// turn it off again
+ MIDIMKtl(\nk1).verbose = false;
+
+ MIDIMKtl(\nk1).openTester;
+
+
+MIDIMKtl('launchpad1', 593081660); // Launchpad
+MIDIMKtl('launchpad1').postDeviceDescription
+
+MIDIMKtl('launchpad1').defaultValueFor(\bu13)
+
+// maybe "debug" (as)?
+MIDIMKtl('launchpad1').verbose = true
+MIDIMKtl('launchpad1').verbose = false
+
+MIDIMKtl(\launchpad1).verbose = true
+
+(
+ MIDIMKtl(\launchpad1).addFunc(\bu0, \yubel, { |who, what, howmuch|
+ "YAYAYAY: ".post; [who, what, howmuch].postln;
+ });
+) //
+MIDIMKtl(\launchpad1).verbose = false
+
+
+MIDIMKtl(\launchpad1).removeFunc(\bu0, \yubel)
View
82 AbandonOldHelp/MKtlElement.scd
@@ -0,0 +1,82 @@
+MKtlElement
+
+
+
+
+MIDIMKtl.find
+
+b = MIDIMKtl('launchpad1', 1258389554); // Launchpad
+b = MIDIMKtl('launchpad1', 593081660); // Launchpad
+
+b.verbose = true
+a = MKtlElement(b, \bu0);
+b.verbose = false
+
+
+a.addFuncAfter(\test, {|... args| args.postln}, nil)
+a.value_(12)
+
+HIDMKtl.find;
+
+HIDMKtl('ferrari', 102760448); // Run'N' Drive
+
+ //trivial case:
+ // make one-dimensional elements, fron devSpecs:
+ // lookup specs etc by name used in devSpec
+
+ // fails if nanoKONTROL not present;
+a = MKtlElement(MKtl(\nk1), \sl1);
+
+x = MKtlElement(MKtl(\ferrari), \joyLX, \joyAxis);
+x.devSpec.spec.postcs;
+x.spec.default;
+x.dump
+
+y = MKtlElement(MKtl(\ferrari), \joyLY, \joyAxis);
+y.dump
+
+h = MKtlElement(MKtl(\ferrari), \joyLHat, \button );
+h.dump
+
+
+
+
+ // simple tests
+x.addFunc(\postX, { |...args| args.postln; });
+x.funcChain.add(\postX, { |...args| args.postln; });
+
+y.addFunc(\postX, { |...args| args.postln; });
+h.addFunc(\postX, { |...args| args.postln; });
+
+x.removeFunc(\postX);
+y.removeFunc(\postX);
+h.removeFunc(\postX);
+
+
+ // composite elements:
+a = MKtlElement(MKtl(\ferrari), \joyL, \thumbstick -> [\joyLX, \joyLY, \joyLHat]);
+
+a.value(\x)
+ // current x-axis value
+
+a.value(\y)
+ // current y-axis value
+
+a.value(\angle)
+ // current angle (calculated)
+
+a.value(\radius)
+ // current radius (calculated)
+
+a.value(\polar)
+ // current angle + radius (calculated)
+
+a.value
+ // current values (only raw, non-calculated ones)
+
+
+
+
+a.addFunc(\polar, {|arg| arg.postln})
+
+a.addFunc(\x, {|arg| arg.postln})
View
111 AbandonOldHelp/MKtlGui.scd
@@ -0,0 +1,111 @@
+MKtlGui - a gui that can draw views
+ for all the elements of an MKtl.
+
+( // make two MKtls for tests
+MKtl.make(\ferr1, 'Run_N_Drive');
+MKtl.make(\nk1, 'nanoKONTROL');
+MKtlGui.init;
+
+MKtlAllGui(12);
+
+m = MKtlGui(MKtl(\ferr1));
+)
+
+( // make the zones for each element by hand:
+
+var zoneDict = (
+ 'bt1r': Rect(250 + 40, 200 - 12, 40, 24),
+ 'bt2r': Rect(275 + 40, 225 - 12, 40, 24),
+ 'bt3r': Rect(300 + 40, 200 - 12, 40, 24),
+ 'bt4r': Rect(275 + 40, 175 - 12, 40, 24),
+
+ 'lfBot7': Rect(110, 25, 40, 20),
+ 'lfTop5': Rect(40, 100, 80, 20),
+
+ 'midL9': Rect(150, 190, 48, 20),
+ 'midR10': Rect(202, 190, 48, 20),
+
+ 'rfBot8': Rect(250, 25, 40, 20),
+ 'rfTop6': Rect(280, 100, 80, 20),
+
+ \joyL: Rect(60, 260, 100, 100),
+ \joyR: Rect(220, 260, 100, 100),
+
+ \compass: Rect.aboutPoint(100@200, 45, 45),
+
+ 'wheel': Rect(5, 125, 40, 150 ),
+ 'throtL': Rect(30, 55, 120, 40),
+ 'throtR': Rect(250, 55, 120, 40)
+);
+ // temp fix for composite elements:
+m.object.elements.put(\joyL, (type: \joyStick));
+m.object.elements.put(\joyR, (type: \joyStick));
+
+m.makeElemViews(zoneDict);
+)
+
+
+
+TODO next: connect all the elements;
+
+// make a template to sketch a gui for a new device:
+
+MKtlGui.postZoneTemplate(MKtl(\ferr1));
+
+MKtlGui.postZoneTemplate(MKtl(\nk1));
+
+
+
+// incomplete sketch for nanoKontrol gui:
+
+m = MKtlGui(MKtl(\nk1));
+
+(
+var left = 10, width = 42;
+ // where should each gui element be?
+var zoneDict = (
+ 'rew': Rect(10, 10, 40, 20),
+ 'play': Rect(50, 10, 40, 20),
+ 'fwd': Rect(90, 10, 40, 20),
+ 'loop': Rect(10, 35, 40, 20),
+ 'stop': Rect(50, 35, 40, 20),
+ 'rec': Rect(90, 35, 40, 20),
+ 'sl1_1': Rect(30, 145, 20, 120),
+ 'sl2_1': Rect(70, 145, 20, 120),
+ 'sl3_1': Rect(110, 145, 20, 120),
+ 'sl4_1': Rect(150, 145, 20, 120),
+ 'sl5_1': Rect(190, 145, 20, 120),
+ 'sl6_1': Rect(230, 145, 20, 120),
+ 'sl7_1': Rect(270, 145, 20, 120),
+ 'sl8_1': Rect(310, 145, 20, 120),
+ 'sl9_1': Rect(350, 145, 20, 120),
+ 'kn1_1': Rect(10, 60, 40, 80),
+ 'kn2_1': Rect(50, 60, 40, 80),
+ 'kn3_1': Rect(90, 60, 40, 80),
+ 'kn4_1': Rect(130, 60, 40, 80),
+ 'kn5_1': Rect(170, 60, 40, 80),
+ 'kn6_1': Rect(210, 60, 40, 80),
+ 'kn7_1': Rect(250, 60, 40, 80),
+ 'kn8_1': Rect(290, 60, 40, 80),
+ 'kn9_1': Rect(330, 60, 40, 80),
+ 'bu1_1': Rect( 10, 170, 20, 20),
+ 'bu2_1': Rect( 50, 170, 20, 20),
+ 'bu3_1': Rect( 90, 170, 20, 20),
+ 'bu4_1': Rect(130, 170, 20, 20),
+ 'bu5_1': Rect(170, 170, 20, 20),
+ 'bu6_1': Rect(210, 170, 20, 20),
+ 'bu7_1': Rect(250, 170, 20, 20),
+ 'bu8_1': Rect(290, 170, 20, 20),
+ 'bu9_1': Rect(330, 170, 20, 20),
+ 'bd1_1': Rect( 10, 200, 20, 20),
+ 'bd2_1': Rect( 50, 200, 20, 20),
+ 'bd3_1': Rect( 90, 200, 20, 20),
+ 'bd4_1': Rect(130, 200, 20, 20),
+ 'bd5_1': Rect(170, 200, 20, 20),
+ 'bd6_1': Rect(210, 200, 20, 20),
+ 'bd7_1': Rect(250, 200, 20, 20),
+ 'bd8_1': Rect(290, 200, 20, 20),
+ 'bd9_1': Rect(330, 200, 20, 20)
+);
+m.makeElemViews(zoneDict);
+)
View
15 AbandonOldHelp/Modality.overview.scd
@@ -0,0 +1,15 @@
+Modality is a toolkit created by a loose collaboration between both developers and (advanced) users of SuperCollider (also called Modality, see below).
+
+One of its basic ideas is to simplify the creation of your very personal instruments with SuperCollider, using controllers of many different kinds. To this end, a common code interface, MKtl, is used for connecting controllers from different sources (and protocols) like HID and MIDI, Serial, OSC, GUI, etc.
+
+A second starting point is that the same physical interfaces (sets of sliders, buttons, motion sensors etc etc) can be used for many different purposes, and a highly modal approach to mapping and on-the-fly remapping can help to make a setup much more flexible, powerful, and interesting to play. For example, when improvising with acoustic musicians, highly modal interfaces allow much faster changes of overall direction.
+
+
+The Modality project:
+... was initiated by Jeff Carey and Bjoernar Habbestad, and collaborators so far have included: Marije Baalman, Alberto de Campo, Wouter Snoei, Till Bovermann, Miguel Negrao, Robert van Heumen, and Hannes Hoelzl.
+
+Documentation to follow at http://modality.bek.no/
+
+Acknowledgements:
+Modality and its research meetings have kindly been supported by BEK in Bergen, Norway, and STEIM, Amsterdam.
+
View
50 AbandonOldHelp/PKtl.scd
@@ -0,0 +1,50 @@
+PKtl access MKtl values in patterns
+
+MKtl.find;
+ // make an MKtl
+MKtl.make(\nk1, 'nanoKONTROL');
+
+z = PKtl(MKtl(\nk1), Pseq( [\sl1_1, \sl1_2], inf ) );
+z = PKtl(MKtl(\nk1), [\sl1_1, \sl1_1] );
+z = PKtl(MKtl(\nk1), \sl1_1, 5 );
+
+Pdef(\mk, Pbind(\amp, z/4, \degree, Pseq([0,3], inf), \dur, 0.25, \pos, [-1,1] ).trace).play;
+Pdef(\mk, Pbind(\amp, z/4, \degree, [0,3], \dur, 0.25, \pos, [-1,1] ).trace).play;
+
+
+n.at(\sl1_1).value_(64);
+n.at(\sl1_1).value_(127.rand);
+
+
+n = MKtl('thrs0');
+z = PKtl(MKtl(\thrs0), Pseq( [\bt1r, \bt2r], inf ) );
+z = PKtl(MKtl(\thrs0), [\bt1r, \bt2r] );
+z = PKtl(MKtl(\thrs0), \bt1r, 5 );
+
+
+ // get a value from its elements
+n.elements.at(\bt1r).value;
+ // shortcut for access
+z.elName = [\bt1r, \bt2r];
+n.at(\bt1r).value;
+
+ // make a PKtl
+z = PKtl(MKtl(\thrs0), Pseq( [\bt1r, \bt2r], inf ) );
+z = PKtl(MKtl(\thrs0), [\bt1r, \bt2r] );
+z = PKtl(MKtl(\thrs0), \bt1r, 5 );
+
+ // use it in a playing pattern
+Pdef(\mk, Pbind(\amp, z/4, \degree, Pseq([0,3], inf), \dur, 0.25, \pos, [-1,1] ).trace).play;
+Pdef(\mk, Pbind(\amp, z/4, \degree, [0,3], \dur, 0.25, \pos, [-1,1] ).trace).play;
+
+ // change mktl's relevant value
+n.at(\sl1_1).value_(64);
+n.at(\sl1_1).value_(127.rand);
+
+ // test polyphony support:
+z.elName = [\sl1_1, \sl1_2];
+
+n.at(\sl1_1).value_(127.rand.postln);
+n.at(\sl1_2).value_(127.rand.postln);
+
+n.setValueAt([\sl1_1, \sl1_2], [10.rand, rrand(60, 70)].postln);
View
170 AbandonOldHelp/Sandrode.scd
@@ -0,0 +1,170 @@
+/* Sandrode - simulates Touch Sandrodes as used in CircuitBending
+ requires the MultiTouchPad quark by Batuhan Bozkurt.
+ Inspired by Peter Blasser's magical Fyrall, Fourses, et al.
+
+Quarks.checkoutAll;
+
+/// wait wait wait ...
+
+Quarks.install("MultiTouchPad");
+
+// download tongsengmod from here:
+https://github.com/downloads/batuhan/tongsengmod/tongsengmod.zip
+
+put code/addToExtensions folder in:
+~Library/Application Support/SuperCollider/Extensions/
+
+// and put it in usr/bin:
+unixCmd("open /usr/bin");
+
+ // then recompile SC3:
+thisProcess.recompile;
+
+
+ // OK, ready!
+
+
+
+// tested with external Magic Trackpad - only sees the internal one, hm.
+
+*/
+
+(
+
+Server.default = s = Server.internal;
+s.boot;
+
+GUI.cocoa;
+q = q ? ();
+q.sandNames = [\a, \b, \c, \d, \e];
+
+ // make 5 sandrodes
+Sandrode(\a, 0.25@0.50, 0.15);
+Sandrode(\b, 0.40@0.25, 0.15);
+Sandrode(\c, 0.60@0.25, 0.15);
+Sandrode(\d, 0.75@0.50, 0.15);
+Sandrode(\e, 0.50@0.75, 0.15);
+
+ // make a MultiTouchPad - uses MultiTouchPad quark.
+MultiTouchPad.gui;
+w = MultiTouchPad.guiWin;
+u = w.view.children.first;
+
+StaticText(w, Rect( 0, 0, 400, 100))
+ .stringColor_(Color.grey(0, 0.5))
+ .string_(
+"// shortcut keys for MultiTouchPad.guiWin:
+ // m to maximise window
+ // x to set window t0 normal size
+ // space to start sandrodes,
+ // . to stop,
+ // d to redraw".postln);
+
+w.view.keyDownAction = Sandrode.mtpKeydownFunc;
+
+
+ // adapt drawing function of MultiTouchPad
+u.drawFunc = Sandrode.mtpDrawFunc;
+
+ // draw the sandrode areas
+Sandrode.drawAll(w.view);
+
+Sandrode.verbose = true;
+Sandrode.verbose = false;
+
+
+MultiTouchPad.start;
+
+ // set the three MultiTouchPad actions:
+MultiTouchPad.touchAction = {|curID, xys|
+ Sandrode.addMtpTouch(curID, xys);
+// [\touch, curID].postln;
+};
+
+MultiTouchPad.untouchAction = {|curID|
+ Sandrode.removeTouch(curID);
+// [\untouch, curID].postln;
+};
+ // this one does the most:
+ // ccalculate how strong the auras influence each other;
+ // based on that, set the modulation weights in the Ndef below:
+
+MultiTouchPad.setAction = {|curID, xys|
+ Sandrode.addMtpTouch(curID, xys);
+ Sandrode.auras.keysValuesDo { |key, aura|
+ var vals = q.sandNames.collect { |k2| (aura[k2] ? 0); };
+ Ndef(\nodes).setn(key, vals.round(0.0001));
+ };
+};
+)
+ // and the sound:
+ // a network of modulating VarSaws.
+ // controlled by aura weights ...
+(
+Spec.add(\octaves, [0, 9]);
+Spec.add(\angle, [0, 1]);
+Spec.add(\delaytime, [0, 0.1, \amp]);
+Spec.add(\spiky, [0.1, 10, \exp]);
+Spec.add(\hairy, [0.0, 2, \amp]);
+Spec.add(\xtalk, [0.0, 1, \amp]);
+
+Ndef(\nodes, { |widefreq=10, hairy = 0.618, angle = 0.4, octaves = 4, delaytime = 0.01, spiky = 1, xtalk = 0.03|
+ var n = Sandrode.all.size;
+ var modIn = LocalIn.ar(5);
+
+ var influxes = [
+ \a.kr(0!n),
+ \b.kr(0!n),
+ \c.kr(0!n),
+ \d.kr(0!n),
+ \e.kr(0!n)
+ ];
+
+ var activity = (influxes.sum.sum * 0.3).clip(0, 1);
+
+ var influxes2 = influxes + xtalk.lag(0.5);
+ var freqs = ([
+ (modIn * influxes2[0]).sum,
+ (modIn * influxes2[1]).sum,
+ (modIn * influxes2[2]).sum,
+ (modIn * influxes2[3]).sum,
+ (modIn * influxes2[4]).sum
+ ] * (octaves.lag(0.5) * 12 * hairy.lag(0.5))).midiratio * widefreq.lag(0.5)
+ * (2 ** [-2, -1, 0, 1, 2].scramble);
+
+ var freqs2 = DelayN.ar(freqs, 0.1, delaytime);
+ // roll off angles and spikyness for high freqs:
+ var angles = (angle * 2 - 1) ** 0.1 + 1 * 0.5;
+ // not working: scale angles to smaller range when freq is high;
+ // freqs2.collect { |freq| freq.explin(100, 5000, 1, 0) * ((angle * 2 - 1) ** 0.33 * 0.5) + 0.5 };
+ var spikyness = spiky ** freqs.explin(100, 5000, 1, 0.25);
+
+ // the oscillators
+ var oscs = VarSaw.ar(freqs2.clip(0, 20000), 0, angles) ** spikyness;
+
+ // influxes.first.poll;
+ // out to feedback
+ LocalOut.ar(oscs);
+ // mix to two chans
+ Splay.ar(oscs) * activity.linexp(0, 1, 0.1, 1);
+}).play;
+NdefGui(Ndef(\nodes), 12);
+)
+
+ // test setting node weights
+Ndef(\nodes).set([\a, \b, \c, \d, \e].choose, ({1.0.rand.squared}!5).round(0.001));
+
+
+(
+Ndef('nodes').set('octaves', 4.7227722772277, 'widefreq', 4.2369058214701, 'hairy', 2.0, 'spiky', 0.27267389657355, 'delaytime', 0.031849818645231, 'xtalk', 0.0, 'angle', 0.53960396039604);
+)
+
+
+ // general Sandrode tests
+
+c = Sandrode(\a, 0.5 @ 0.5, 0.2);
+c.dump;
+
+w = Window.new.front;
+c.drawOn(w.view);
+c.sectArea(Rect(0.5, 0.5, 0.1, 0.1)); // 0.25
View
504 Dispatch.graffle
@@ -0,0 +1,504 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>AutoAdjust</key>
+ <true/>
+ <key>CanvasColor</key>
+ <dict>
+ <key>w</key>
+ <string>1</string>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>CanvasScale</key>
+ <real>1</real>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2011-05-19 21:02:59 +0200</string>
+ <key>Creator</key>
+ <string>Miguel Negrao</string>
+ <key>DisplayScale</key>
+ <string>1 cm = 1 cm</string>
+ <key>GraphDocumentVersion</key>
+ <integer>5</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{245.5, 135}, {57, 14}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>29</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 Dispatch}</string>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{439, 314}, {40, 14}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>28</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 func1}</string>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>27</integer>
+ <key>Points</key>
+ <array>
+ <string>{463, 332.5}</string>
+ <string>{409, 332.5}</string>
+ </array>
+ <key>Rotation</key>
+ <real>180</real>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{370, 288.5}, {50, 14}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>12</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 outputs}</string>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{122.5, 223}, {43, 14}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>ID</key>
+ <integer>11</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 Inputs}</string>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{381, 363.5}, {28, 25}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>8</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{381, 320.5}, {28, 25}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>7</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{130, 390}, {28, 25}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>6</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{130, 351}, {28, 25}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>5</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{130, 298}, {28, 25}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>4</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{130, 255}, {28, 25}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>3</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{208, 222}, {132, 265}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>2</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 FuncChain}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{106, 113.5}, {327, 482}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>1</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ </dict>
+ </array>
+ <key>GridInfo</key>
+ <dict/>
+ <key>GuidesLocked</key>
+ <string>NO</string>
+ <key>GuidesVisible</key>
+ <string>YES</string>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>ImageCounter</key>
+ <integer>1</integer>
+ <key>IsPalette</key>
+ <string>NO</string>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Layer 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict/>
+ <key>LinksVisible</key>
+ <string>NO</string>
+ <key>MagnetsVisible</key>
+ <string>NO</string>
+ <key>MasterSheet</key>
+ <string>Master 1</string>
+ <key>MasterSheets</key>
+ <array>
+ <dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>AutoAdjust</key>
+ <true/>
+ <key>CanvasColor</key>
+ <dict>
+ <key>w</key>
+ <string>1</string>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>CanvasScale</key>
+ <real>1</real>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>DisplayScale</key>
+ <string>1 cm = 1 cm</string>
+ <key>GraphicsList</key>
+ <array/>
+ <key>GridInfo</key>
+ <dict/>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>IsPalette</key>
+ <string>NO</string>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Layer 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict/>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OutlineStyle</key>
+ <string>Basic</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Master 1</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>VPages</key>
+ <integer>1</integer>
+ </dict>
+ </array>
+ <key>ModificationDate</key>
+ <string>2011-05-19 21:05:39 +0200</string>
+ <key>Modifier</key>
+ <string>Miguel Negrao</string>
+ <key>NotesVisible</key>
+ <string>NO</string>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OriginVisible</key>
+ <string>NO</string>
+ <key>OutlineStyle</key>
+ <string>Basic</string>
+ <key>PageBreaks</key>
+ <string>YES</string>
+ <key>PrintInfo</key>
+ <dict>
+ <key>NSBottomMargin</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string>
+ </array>
+ <key>NSLeftMargin</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string>
+ </array>
+ <key>NSPaperSize</key>
+ <array>
+ <string>size</string>
+ <string>{595, 842}</string>
+ </array>
+ <key>NSRightMargin</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string>
+ </array>
+ <key>NSTopMargin</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFklwCG</string>
+ </array>
+ </dict>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Canvas 1</string>
+ <key>SmartAlignmentGuidesActive</key>
+ <string>YES</string>
+ <key>SmartDistanceGuidesActive</key>
+ <string>YES</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>UseEntirePage</key>
+ <true/>
+ <key>VPages</key>
+ <integer>1</integer>
+ <key>WindowInfo</key>
+ <dict>
+ <key>CurrentSheet</key>
+ <string>0</string>
+ <key>DrawerOpen</key>
+ <false/>
+ <key>DrawerTab</key>
+ <string>Outline</string>
+ <key>DrawerWidth</key>
+ <real>209</real>
+ <key>FitInWindow</key>
+ <false/>
+ <key>Frame</key>
+ <string>{{28, 0}, {574, 846}}</string>
+ <key>ShowRuler</key>
+ <false/>
+ <key>ShowStatusBar</key>
+ <true/>
+ <key>VisibleRegion</key>
+ <string>{{0, 0}, {559, 732}}</string>
+ <key>Zoom</key>
+ <string>1</string>
+ </dict>
+</dict>
+</plist>
View
BIN Dispatchpdf.pdf
Binary file not shown.
View
225 MKtl Cookbook.scd
@@ -0,0 +1,225 @@
+/*
+The MKtl Cookbook
+
+In this document, you can find typical usage scenarios for MKtl. They are based on very concrete tasks like connecting a slider of your MIDI interface to a parameter in your synth.
+Their intention is to give unexperienced users a quick starting point (and reference) on how to use MKtl for a given task.
+*/
+
+// == Content ==
+// Connect a slider on a NanoKontrol to the frequency (freq) of my Synth.
+// Connect a slider on a BCF2000 to the frequency (freq) of my Synth. Setting the value of the Synth updates the (motor-)fader's state.
+// Use a button on a launchpad as a toggle-button for muting a Synth
+// Use a specific value of an MDispatch directly in a Synth. (kr method)
+// Create a custom MDispatch
+
+
+
+
+
+
+
+
+
+
+// Connect a slider on a NanoKontrol to the frequency (freq) of my Synth.
+
+MIDIMKtl.find;
+a = MIDIMKtl('nnkn0');
+
+// the synth
+SynthDef(\sliderFreq, {|out = 0, freq = 100, fLag = 0.05, amp = 0.1, preAmp = 0.5, aLag = 0.05|
+ Out.ar(out, SinOsc.ar(freq.lag(fLag), 0, preAmp.lag(aLag)).tanh * amp.lag(aLag)!2)
+}).add;
+x = Synth(\sliderFreq);
+
+
+// select the first slider in the first page
+a.addFuncElem(\sl1_1, \freq, {|elem|
+ x.set(\freq, elem.value.linexp(0, 1, 40, 1000));
+})
+
+
+x.free;
+
+/* Discussion
+The example first searches for all MIDIMktls and assigns the one named 'nnkn0' to the variable "a".
+We add a function to the first slider in the first page. It is called 'freq'. When called, the function is passed the MKtlElement responsible for the first slider. Its value slot contains the current value in the range between 0 and 1.
+By calling linexp on it, we remap the value to a range between 40 and 1000.
+To view all possible element names to which actions can be hooked, call
+
+ a.elementNames
+
+The other parameters of the Synth can be mapped e.g. by these lines:
+
+a.addFuncElem(\sl2_1, \freq, {|elem|
+ x.set(\preAmp, elem.value.linlin(0, 1, 0.5, 15));
+})
+
+a.addFuncElem(\sl3_1, \freq, {|elem|
+ x.set(\amp, elem.value);
+})
+*/
+
+
+// Connect a slider on a BCF2000 to the frequency (freq) of my Synth. Setting the value of the Synth updates the (motor-)fader's state.
+
+
+//---****----- DISPATCH -----*****------
+
+//note: change the key names and controller names to match names existing in your controller
+
+//1 - create a dispatch that inverts the values, i.e. if the incoming value is 0 it becomes 1 and if it's 1 becomes 0.
+
+(
+//change 'nnkn0' to the MKtl shortname of your controller
+MIDIMKtl.find;
+k = MKtl('nnkn0');
+k.reset;
+k.verbose = false;
+
+//connect automatically adds all elements in k and also creates outputs with the same name
+//as the inputs.
+~dis = MDispatch().connect(k);
+//only trigger actions if value is 1
+~dis.addToProc( \trigger, { |dis|
+ var in = ~changedIn;
+ //invert value
+ dis.setOutput(in[\key], 1 - in[\val])
+});
+//set it to true to check if it's working;
+~dis.verbose = false;
+//now do something when the dispatch outputs data
+// change the key to a key that exists in your controller
+~dis.addFuncElem(\sl1_1,\nameOfMyAction,{ |x| ("ping: "++x.value).postln })
+//giving a name to the action allows to later remove that action:
+//d.removeFuncElem(\knG1,\nameOfMyAction)
+)
+
+//match multiple keys - could be slow !
+~dis.addFuncElem('sl*',\nameOfMyAction,{ |x| ("ping: "++x.name++" "++x.value).postln }, match:true)
+//note that since we used the same name for the action, the code above replaced the previous action for knG1
+//also if you modify the code above in the line above and run that line again you will replace the actions
+//without need to remove the old actions.
+
+// remove dispatch actions from the ktl
+~disp.remove
+
+
+//2 - calculate the average of two controls
+
+(
+MIDIMKtl.find;
+k = MKtl('nnkn0');
+//make sure the Ktl doesn't have any functions or dispatchs attached.
+k.reset;
+d = MDispatch.new;
+
+//only listen to the input from these specific knobs:
+d.mapToElem( k, \sl1_1, \myController1 );
+d.mapToElem( k, \sl2_1, \myController1 );
+/*
+myController1 is the name that we are giving to the source, which in this case is the BCR2000 controller. This allows to change the source later but keep the same functionality, that is we could later do d.changeSource(\mycontroller1,'bcr1') and it would listen from a second BCR2000 controller instead of the original one.
+*/
+
+//in the example above the outputs were automatically created using MDispatch:connect
+//with the same name as the inputs. In this case we want to create just one output with a specific name:
+d.createOutput(\average);
+
+d.addToProc( \average, { |dis|
+ var val = [\sl1_1,\sl2_1].collect{ |key|
+ dis.getInput( \myController1, key )
+ }.sum;
+ dis.setOutput( \average, val/2 );
+} );
+
+d.addFuncElem(\average,\blah,{ |element| postln("average is "++element.value) })
+)
+
+//3 - chain dispatchs
+
+/*
+Dispatchs behave exactly as a Ktl, that is they mimc a real device, therefore they can be chained using
+connect or map
+*/
+
+(
+MIDIMKtl.find;
+k = MKtl('nnkn0');
+k.reset;
+//one dispatch for invert
+~dis1 = MDispatch().connect(k);
+~dis1.addToProc( \invert, { |dis|
+ var in = ~changedIn;
+ dis.setOutput(in[\key], 1 - in[\val])
+});
+//one dispatch to quantize
+~dis2 = MDispatch().connect(~dis1);
+~dis2.addToProc( \invert, { |dis|
+ var in = ~changedIn;
+ dis.setOutput(in[\key], in[\val].round(0.2) )
+});
+~dis2.verbose = true
+)
+
+//4 - use templates
+
+/*
+The Modality quark provides some built in dispatchs. They are stored in the DispatchTemplates folder. It's possible to list the available
+templates by doing
+*/
+MDispatch.availableTemplates
+
+(
+MIDIMKtl.find;
+k = MKtl('nnkn0').reset;
+//create a threshZones dispatch, the source is k and [0.2,0.5,0.8] are the threshold points. When the controller passes these points will trigger an action.
+~disp = MDispatch.make(\threshZones, k, [0.2,0.5,0.8] );
+~disp.verbose = true;
+
+~disp.addFuncElem('*', \sound,{
+ { FreeVerb.ar(0.1*SinOsc.ar(rrand(300,3000)) * EnvGen.ar(Env.perc, doneAction:2),0.5,0.9) }.play
+},match:true);
+
+)
+
+
+//5 - chain templates
+
+(
+MIDIMKtl.find;
+k = MKtl('nnkn0').reset;
+~disp1 = MDispatch.make(\up, k );
+~disp2 = MDispatch.make(\velocity, ~disp1 );
+~disp2.verbose = true;
+)
+
+~disp2.remove // only removes the merge dispatch
+~disp2.recursiveRemove // removes all dispatchs going down the chain
+
+
+//6 - split and merge streams
+
+(
+MIDIMKtl.find;
+k = MKtl('nnkn0').reset;
+/*The dispatch template's 'make' function first arguments usually are:
+ source -> the MKtl or MDispatch.
+ elemKeys -> an array with the keys to map from the source. All other elements whose keys are not in this array are ignored.
+ sourceKey -> the name to give to this source (see 2)
+
+ We select only the elements of type slider by providing an array with all such elements and create a velocity dispatch
+*/
+~sliders = MDispatch.make(\velocity,k, k.elementsOfType(\slider).collect(_.name) );
+/*the merge dispatch just takes the input from multiple sources and outputs it without modification.
+ it takes as arguments an array with elements [source,elements]. if elements is nil, all elements are imported from that source.
+ By using the merge dispatch we have a result dispatch that behaves as if it was just one controller although we have now modified
+ the behaviour of some controls.
+*/
+~result = MDispatch.make(\merge,[ [~sliders, nil], [k, k.elementsNotOfType(\slider).collect(_.name) ] ]);
+~result.verbose = true;
+)
+~result.remove // only removes the merge dispatch
+~result.recursiveRemove // removes all dispatchs going down the chain
+
+
+
View
138 MKtlGui_sketch.scd
@@ -0,0 +1,138 @@
+///////// A sketch for how auto-guis for MKtls could work //////////
+
+// the descriptions could contain a location and size (a Rect)
+// of the gui element that is reasonably similar to the physical device.
+// based on its type, gui elements will be drawn in that location.
+
+ // make a virtual MKtl, based on Ferrari GamePad
+MKtl.make(\ferr1, 'Run_N__Drive');
+
+ // post a template for typing in lots of rects:
+(
+"(\n var zoneDict = (".postln;
+MKtl(\ferr1).elements.keys.asArray.sort.do { |k|
+ " '%': Rect(0, 0, 40, 40),\n".postf(k);
+};
+");\n)".postln;""
+)
+
+ // all descriptions
+MKtl(\ferr1).postDeviceDescription;
+
+ // one specific description
+MKtl(\ferr1).elements[\joyLHat].dump
+
+( // collect all the types that occur in a Set
+z = Set[];
+MKtl(\ferr1).elements.do { |el|
+ z.add(el.type);
+};
+z.postcs
+)
+
+ // A GUI sketch for a GamePad:
+
+(
+var zoneDict, skin, buildFuncs, elemGuis;
+
+ // the window
+try { w.close };
+w = Window("Ferrari").background_(Color(1, 0.1, 0.1)).front;
+
+ // the zones for each element
+ // - suggestions for width and height could be based on types
+zoneDict = (
+ 'bt1r': Rect(250 + 40, 200 - 12, 40, 24),
+ 'bt2r': Rect(275 + 40, 225 - 12, 40, 24),
+ 'bt3r': Rect(300 + 40, 200 - 12, 40, 24),
+ 'bt4r': Rect(275 + 40, 175 - 12, 40, 24),
+
+ 'compass': Rect.aboutPoint(100@200, 45, 45),
+
+ 'joyLHat': Rect(140, 320 + 20, 50, 40),
+ 'joyLX': Rect(60, 280 + 20, 120, 40),
+ 'joyLY': Rect(100, 240 + 20, 40, 120),
+
+ 'joyRHat': Rect(300, 320 + 20, 50, 40),
+ 'joyRX': Rect(220, 280 + 20, 120, 40),
+ 'joyRY': Rect(260, 240 + 20, 40, 120),
+
+ 'lfBot7': Rect(110, 25, 40, 20),
+ 'lfTop5': Rect(40, 100, 80, 20),
+
+ 'midL9': Rect(150, 190, 48, 20),
+ 'midR10': Rect(202, 190, 48, 20),
+
+ 'rfBot8': Rect(250, 25, 40, 20),
+ 'rfTop6': Rect(280, 100, 80, 20),
+
+ 'throtL': Rect(30, 55, 120, 40),
+ 'throtR': Rect(250, 55, 120, 40),
+
+ 'wheel': Rect(5, 125, 40, 150 )
+);
+ // a color scheme
+skin = (onColor: Color(0.5, 1, 0.5, 1.0), offColor: Color.grey(0.7), fontColor: Color.black);
+
+
+ // the funcs to make each kind of element.
+ // these could be class methods of MKtlGui.
+
+ // Joysticks should be 2DSliders,
+ // hat with alt-click action maybe; state represented with knobColor.
+ // a student of mine has done nice 2DSliders with Pen ...
+ // Compass should be a new class.
+
+buildFuncs = (
+ joyAxis: { |w, el| EZSlider(w, zoneDict[el.name], el.name,
+ el.spec, { |sl|
+ el.valueAction_(sl.value);
+ [el.name, sl.value, el.prevValue, el.value].postln;
+ }, el.value, layout: \line2, numberWidth: 40);
+ },
+ springFader: { |w, el| buildFuncs[\joyAxis].value(w, el) },
+
+ button: { |w, el| Button(w, zoneDict[el.name.postcs].postln)
+ .states_([[el.name, skin.fontColor, skin.offColor],
+ [el.name, skin.fontColor, skin.onColor]])
+ .action_({ |but|
+ el.valueAction_(but.value);
+ [el.name, but.value, el.prevValue, el.value].postln
+ });
+ },
+ hidHat: { |w, el| buildFuncs[\button].value(w, el) },
+
+ // Compass needs to be a class, ... because value_ on a
+ // pseuod-object dict does not work.
+ compass: { |w, el|
+ var center = zoneDict[el.name].center;
+ var zone = StaticText(w, zoneDict[el.name]).string_(el.name)
+ .background_(Color.grey(0.8, 0.5))
+ .align_(\center);
+ var buttons = 8.collect { |i|
+ var angle = (i + 4 / 8 * -2pi );
+ var butcent = center + Polar(35, angle).asPoint;
+ Button(w, Rect.aboutPoint(butcent, 10, 10))
+ .states_([[(i + 1).asString, skin.fontColor, skin.offColor ],
+ [(i + 1).asString, skin.fontColor, skin.onColor ]])
+ .action_({ |but|
+ buttons.do { |but2, j| if (i != j) { but2.value = 0 } };
+ el.valueAction_(if (but.value > 0, i + 1, 0));
+ [el.name, but.value, el.prevValue, el.value].postln;
+ });
+ };
+ }
+);
+
+elemGuis = ();
+
+MKtl(\ferr1).elements.do { |el|
+ var gui = buildFuncs[el.type].value(w, el);
+ elemGuis.put(el.name, gui);
+ el.addFunc(\gui, { |name, newval| try { [name, newval].postln; gui.value_(newval) } });
+};
+)
+(
+MKtl(\ferr1).elements[\joyLY].valueAction_(255.rand);
+MKtl(\ferr1).elements[\joyRY].valueAction_(255.rand);
+)
View
BIN Modality Workshop EventStreams and FPSignals.pdf
Binary file not shown.
View
BIN Modality Workshop EventStreams and FPSignals.rtfd/1__#$!@%!#__Pasted Graphic 4.pdf
Binary file not shown.
View
BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 1.pdf
Binary file not shown.
View
BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 4.pdf
Binary file not shown.
View
BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 5.pdf
Binary file not shown.
View
BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 6.pdf
Binary file not shown.
View
BIN Modality Workshop EventStreams and FPSignals.rtfd/Pasted Graphic 7.tiff
Binary file not shown.
View
279 Modality Workshop EventStreams and FPSignals.rtfd/TXT.rtf
@@ -0,0 +1,279 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf230
+{\fonttbl\f0\fnil\fcharset0 Monaco;\f1\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;\red191\green0\blue0;\red0\green115\blue0;\red0\green0\blue191;
+\red0\green0\blue0;\red0\green0\blue191;\red0\green0\blue255;\red96\green96\blue96;}
+\deftab560
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f0\fs32 \cf2 /*\
+SuperCollider Symposium- Modality Workshop \
+April 2012\
+London\
+\'a9 Modality Team\
+Creative Commons Licence Attribution-NonCommercial-ShareAlike 3.0\
+http://creativecommons.org/licenses/by-nc-sa/3.0/\
+*/\
+\cf0 \
+\pard\pardeftab560
+\cf0 \
+\cf2 /*Function Reactive Programming*/\cf0 \
+\cf2 /*Introduction*/\cf0 \
+\
+FRP deals with with \cf3 'Event Processing'\cf0 .\
+Events come in while the program is running, most often, at unpredictable times.\
+Under the hood it uses Observers, but it automates the removal of the observer from the observers list.\
+Usually it's done with callbacks or observers. FRP is a different approach.\
+\
+From Wikipedia:\
+Functional reactive programming (FRP) is a programming paradigm for reactive programming using the building blocks of functional programming.\
+\
+The key traits of FRP are:\
+ - Input is viewed as a behavior, or time-varying stream of events.\
+ - Continuous, time-varying values.\
+ - Time-ordered sequences of discrete events.\
+ - Time-varying values can be of higher orders. ????\
+ \
+\cf3 'Continuous, time-varying values'\cf0 \
+\
+You know the value at each moment in time.\
+Encoded with subclasses of FPSignal.\
+\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f1\fs24 \cf0 {{\NeXTGraphic Pasted Graphic 1.pdf \width10240 \height6400
+}�}\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f0\fs32 \cf0 \
+\
+//A Var is an FPSignal\
+x = \cf4 Var\cf0 (0.0);\
+\cf2 //current value\cf0 \
+x.now;\
+\cf2 //change value\cf0 \
+x.value_(3.3);\
+x.now;\
+\pard\pardeftab560
+\cf0 \
+\cf3 'Time-ordered sequences of discrete events'\
+\cf0 \
+Encoded with subclasses of EventStream.\
+\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f1\fs24 \cf0 {{\NeXTGraphic Pasted Graphic 4.pdf \width10340 \height6660
+}�}\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f0\fs32 \cf0 \
+\
+x = EventSource();\
+x.do\{ |x| postln("I have received this: "++x) \};\
+x.fire(1);\
+x.fire(5);\
+\
+You can go from to the other with the functions \cf3 'changes'\cf0 and \cf3 'hold'\cf0 .\
+\
+x = Var(0.0);\
+~eventStream = x.changes;\
+\
+x = EventSource();\
+~signal = x.hold(0.0);\
+\
+If you do \
+\
+x = EventSource();\
+x.hold(0.0).changes that is equivalent to the original EventSource x.\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\
+\cf2 /*Event Streams*/\cf0 \
+\
+- EventStreams emit events at given times. \
+\
+- EventStreams can be chained creating a network.\
+\
+- The start of the network is always an EventSource. \
+\
+- To react to these events you use the 'do' method.\
+\
+\cf5 (\
+x = \cf6 EventSource\cf5 ();\
+x.do\{ \cf7 |x|\cf5 postln(\cf8 "Hello world: "\cf5 ++x) \};\
+x.fire(3)\
+)\cf0 \
+\
+You can think of EventStreams as Collections of elements over time. Therefore the methods of collections will work as you expect.\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f1\fs24 \cf0 {{\NeXTGraphic 1__#$!@%!#__Pasted Graphic 4.pdf \width10340 \height6660
+}�}\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f0\fs32 \cf0 \
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+\cf3 'select(f)'\cf0 only outputs a value if f.(value) is true.\
+\
+x.select(_<0.04)\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f1\fs24 \cf0 {{\NeXTGraphic Pasted Graphic 5.pdf \width10480 \height6740
+}�}\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f0\fs32 \cf0 \
+\
+\
+\
+(\
+x = EventSource();\
+y = x.select(_>3);\
+y.do\{ |v| postln("I got a "++v) \};\
+x.fire(1);//will not let through.\
+x.fire(4);//will let through.\
+)\
+\
+\
+\
+\
+\
+\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+\cf3 'collect(f)'\cf0 outputs a value by applying a value to the funciton f.(value).\
+\
+x.collect(_*100)\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f1\fs24 \cf0 {{\NeXTGraphic Pasted Graphic 6.pdf \width10340 \height6760
+}�}\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f0\fs32 \cf0 \
+\
+(\
+x = EventSource();\
+y = x.collect(_*100);\
+y.do\{ |v| postln("I got a "++v) \};\
+x.fire(1);\
+x.fire(4);\
+)\
+\
+\
+\
+\
+\
+- EventSources "fire" events, which are propagated by the network of EventStreams ( collect, select, etc ), until they reach a do method, at which point they can finally cause side-effects. Since the network is completely functional, no visible side-effects are performed until reaching a do method.\
+\
+\
+\
+\
+\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+\cf3 'fold(initialState,f)'\cf0 - this method can be used for keeping state. It's is the equivalent of 'inject' in sc collections. \
+\
+f must receive two variables, the first is the current state and second is the last value.\
+\
+(\
+x = EventSource();\
+y = x.fold(0.0,\{ |state,v| state + v \});\
+y.do\{ |v| postln("I got a "++v) \};\
+x.fire(1);\
+x.fire(4);\
+x.fire(7);\
+x.fire(12);\
+)\
+\
+(\
+x = EventSource();\
+x.fold([],\{ |state,v| state ++ [v] \}).dopost;\
+y.do\{ |v| postln("I got a "++v) \};\
+x.fire(1);\
+x.fire(4);\
+x.fire(7);\
+x.fire(12);\
+)\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+\cf2 //keep last two values\cf0 \
+(\
+x = EventSource();\
+x.fold([0.0,0.0],\{ |state,v| [state[1],v] \}).dopost;\
+y.do\{ |v| postln("I got a "++v) \};\
+x.fire(1);\
+x.fire(4);\
+x.fire(7);\
+x.fire(12);\
+)\
+\
+\
+\
+\
+\
+\
+\
+\
+flatCollect(f) - This is the most important method of EventSource !! It allows selecting which events to output depending on the some other EventSource.\
+\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f1\fs24 \cf0 {{\NeXTGraphic Pasted Graphic 7.tiff \width7440 \height4380
+}�}\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab560\pardirnatural
+
+\f0\fs32 \cf0 \
+(\
+\cf2 //selector\cf0 \
+x = \cf4 EventSource\cf0 ();\
+\cf2 //two sources\cf0 \
+y = \cf4 EventSource\cf0 ();\
+z = \cf4 EventSource\cf0 ();\
+\cf2 //the result\cf0 \
+w = x.flatCollect\{ \cf7 |v|\cf0 \
+\'a0if(v==0)\{y\}\{z\}\
+\};\
+w.do(\cf4 _\cf0 .postln);\
+)\
+(\
+//will get value from y\
+x.fire(0);\
+y.fire(1);\
+z.fire(4);\
+)\
+(\
+//will get value from z\
+x.fire(1);\
+y.fire(1);\
+z.fire(4);\
+)\
+\
+Why is it called flatCollect ?\
+\
+well, because it is analoguous to [1,2,3].collect(_*[1,2,3]).flatten, which in other languages is called flatMap or bind (It is an instance of the famous Monad). Notice that [1,2,3].collect(_*[1,2,3]) creates an array of arrays which is then flattened to just one array. \
+In the same way doing \
+\
+x = \cf4 EventSource\cf0 ();\
+y = \cf4 EventSource\cf0 ();\
+z = \cf4 EventSource\cf0 ();\
+x.collect\{ \cf7 |v|\cf0 \
+\'a0if(v==0)\{y\}\{z\}\
+\};\
+\
+creates an EventStream of EventStreams. flatCollect will flatten it down again to a EventStream. \
+}
View
1,929 ModalityOverview.graffle
@@ -0,0 +1,1929 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>ApplicationVersion</key>
+ <array>
+ <string>com.omnigroup.OmniGrafflePro</string>
+ <string>138.17.0.133677</string>
+ </array>
+ <key>AutoAdjust</key>
+ <true/>
+ <key>BackgroundGraphic</key>
+ <dict>
+ <key>Bounds</key>
+ <string>{{0, 0}, {1566, 1118}}</string>
+ <key>Class</key>
+ <string>SolidGraphic</string>
+ <key>ID</key>
+ <integer>2</integer>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2011-05-16 11:38:17 +0200</string>
+ <key>Creator</key>
+ <string>LFSaw</string>
+ <key>DisplayScale</key>
+ <string>1.000 cm = 1.000 cm</string>
+ <key>GraphDocumentVersion</key>
+ <integer>6</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{421, 657}, {22, 14}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>w</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>GillSans-Light</string>
+ <key>NSKern</key>
+ <real>0.0</real>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>48</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>GradientColor</key>
+ <dict>
+ <key>w</key>
+ <string>0.666667</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 GillSans-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\fs24 \cf0 \expnd0\expndtw0\kerning0
+\'85}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{556, 569}, {209, 58}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>47</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 GillSans-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 DataNodeMKtl}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{338, 569}, {209, 58}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>46</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 GillSans-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 KEYBoardMKtl}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{121, 569}, {209, 58}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>45</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 GillSans-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 SETOMKtl}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{556, 501}, {209, 58}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>44</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 GillSans-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 WIIIIIMKtl}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{338, 501}, {209, 58}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>43</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 GillSans-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 GUIMKtl}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{120, 501}, {209, 58}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>42</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 GillSans-Light;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\fs24 \cf0 SERIALMKtl}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>