├── .gitignore ├── CHANGELOG.md ├── Modality.quark ├── Modality ├── Classes │ ├── Additions │ │ ├── SystemOverwrites │ │ │ ├── extDict.sc │ │ │ ├── extHIDAPI.sc │ │ │ ├── extHIDprint.sc │ │ │ ├── extMIDIEndPoint.sc │ │ │ └── extObject.sc │ │ ├── extAsNamedList.sc │ │ ├── extCollDups.sc │ │ ├── extMIDISetSources.sc │ │ ├── plusDictionary.sc │ │ └── plusObject.sc │ ├── GUI │ │ ├── MHexPad.sc │ │ ├── MKtlElementGUI.sc │ │ ├── MKtlGUI.sc │ │ ├── MPadView.sc │ │ ├── MViews.sc │ │ └── OSCMon.sc │ ├── HID │ │ ├── HIDExplorer.sc │ │ └── HIDMKtlDevice.sc │ ├── MIDI │ │ ├── MChanVoicer.sc │ │ ├── MIDIAnalysis.sc │ │ ├── MIDIControl14bit.sc │ │ ├── MIDIExplorer.sc │ │ ├── MIDIMKtlDevice.sc │ │ ├── MIDIMonitor.sc │ │ ├── MIDISim.sc │ │ └── MPush.sc │ ├── MKtl │ │ ├── CompMKtl.sc │ │ ├── MKtl.sc │ │ ├── MKtlDesc.sc │ │ ├── MKtlDevice.sc │ │ ├── MKtlElement.sc │ │ ├── MKtlElementGroup.sc │ │ ├── MKtlLookup.sc │ │ ├── PKtl.sc │ │ ├── PagedMKtl.sc │ │ ├── extElemGroup.sc │ │ ├── extLastUpdate.sc │ │ ├── extMKtl_checkAllCtls.sc │ │ └── extReload.sc │ ├── OSC │ │ └── OSCMKtlDevice.sc │ └── Tools │ │ ├── IntegerClip.sc │ │ ├── ItemsSpec.sc │ │ ├── MNamedList.sc │ │ ├── OSCMessageAndArgsSizeMatcher.sc │ │ ├── OSCMessageInversePatternDispatcher.sc │ │ ├── PassSpec.sc │ │ ├── Piano.sc │ │ ├── PullSet.sc │ │ ├── RelSet.sc │ │ ├── Relative2Absolute.sc │ │ └── SoftSet.sc ├── DeviceExamples │ ├── 0coast-PatternSequencer.scd │ ├── QuNexus_AB.scd │ ├── Uc4_RelSet_example.scd │ ├── cmc_fd_example.scd │ ├── faderfox_uc4_ndefmixer.scd │ ├── launchcontrolXL_jitlib.scd │ ├── linnstrument_8chan.scd │ └── roli.scd ├── HelpSource │ ├── Classes │ │ ├── CompMKtl.schelp │ │ ├── CompassView.schelp │ │ ├── HIDExplorer.schelp │ │ ├── HIDMKtlDevice.schelp │ │ ├── ItemsSpec.schelp │ │ ├── MAbstractElement.schelp │ │ ├── MChanVoicer.schelp │ │ ├── MHexPad.schelp │ │ ├── MIDIAnalysis.schelp │ │ ├── MIDIExplorer.schelp │ │ ├── MIDIMKtlDevice.schelp │ │ ├── MIDIMonitor.schelp │ │ ├── MIDISim.schelp │ │ ├── MKtl.schelp │ │ ├── MKtlDesc.schelp │ │ ├── MKtlDevice.schelp │ │ ├── MKtlElement.schelp │ │ ├── MKtlElementCollective.schelp │ │ ├── MKtlElementGroup.schelp │ │ ├── MKtlGUI.schelp │ │ ├── MKtlLookup.schelp │ │ ├── MNamedList.schelp │ │ ├── MPadView.schelp │ │ ├── MPush.schelp │ │ ├── OSCMKtlDevice.schelp │ │ ├── OSCMon.schelp │ │ ├── PKtl.schelp │ │ ├── PagedMKtl.schelp │ │ ├── PassSpec.schelp │ │ ├── Piano.schelp │ │ ├── RelSet.schelp │ │ └── SoftSet.schelp │ ├── Overviews │ │ └── Modality.schelp │ ├── Reference │ │ ├── MKtl_description_files.schelp │ │ ├── Naming_conventions_in_element_descriptions.schelp │ │ ├── SupportedDevices.schelp │ │ └── media │ │ │ ├── 3DConnection.desc.png │ │ │ ├── ICONIControlPro.desc.png │ │ │ ├── LPD8.desc.png │ │ │ ├── LaunchControlXL.desc.png │ │ │ ├── Launchpad.desc.png │ │ │ ├── MPD18.desc.png │ │ │ ├── RunNDrive.desc.png │ │ │ ├── Teenage_OP1.desc.png │ │ │ ├── Thrustmaster.desc.png │ │ │ ├── g13.desc.png │ │ │ ├── impact_gamepad.desc.png │ │ │ ├── keyboard.desc.png │ │ │ ├── keypad.desc.png │ │ │ ├── mouse.desc.png │ │ │ ├── nanoKONTROL2.desc.png │ │ │ └── softStep2.desc.png │ └── Tutorials │ │ ├── Coding_for_Modal_Flexibility.schelp │ │ ├── Connecting_OSC_devices.schelp │ │ ├── Connecting_external_MIDI_devices.schelp │ │ ├── Connecting_generic_devices.schelp │ │ ├── Connecting_multiple_identical_devices.schelp │ │ ├── Connecting_multiport_MIDI_devices.schelp │ │ ├── Creating_Custom_Elements_and_Groups.schelp │ │ ├── Creating_Named_Groups.schelp │ │ ├── Creating_Presets_for_MKtl_Values.schelp │ │ ├── Fix_Missing_hutDirectory_osx.schelp │ │ ├── How_to_adapt_a_description_file.schelp │ │ ├── How_to_create_a_description_file.schelp │ │ ├── How_to_create_a_description_file_for_HID.schelp │ │ ├── How_to_create_a_description_file_for_MIDI.schelp │ │ ├── How_to_create_a_description_file_for_OSC.schelp │ │ ├── ModalityTutorial.schelp │ │ ├── Substituting_MKtls.schelp │ │ ├── Using_Hardware_Pages.schelp │ │ └── Using_groupType_in_MKtlDescs.schelp ├── MKtlDescriptions │ ├── 16n.desc.scd │ ├── _descFile_testCode.scd │ ├── ableton-push-2.desc.scd │ ├── ableton-push.desc.scd │ ├── akai-apc40.desc.scd │ ├── akai-apcmini.desc.scd │ ├── akai-lpd8.desc.scd │ ├── akai-lpk25.desc.scd │ ├── akai-midimix.desc.scd │ ├── akai-mpd18.desc.scd │ ├── akai-mpkmini.desc.scd │ ├── akai-mpkmini2.desc.scd │ ├── alien-and-heath_xone2.desc.scd │ ├── allen-and-heath_xone2.desc.scd │ ├── arturia-minilab.desc.scd │ ├── beatstep │ │ ├── Relatively#1Global.beatstep │ │ ├── arturia-beatstep-rel-16.desc.scd │ │ └── arturia-beatstep.desc.scd │ ├── behringer-bcf2000.desc.scd │ ├── behringer-bcr2000.desc.scd │ ├── behringer-motor_49.desc.scd │ ├── behringer-motor_61.desc.scd │ ├── behringer-x-touch-compact.desc.scd │ ├── behringer-x-touch-mini.desc.scd │ ├── captain-midi.desc.scd │ ├── cinematix-wheel.desc.scd │ ├── cthrumusic-axis49.desc.scd │ ├── decampo-joybox.desc.scd │ ├── doepfer-pocketfader.desc.scd │ ├── elektron-digitakt.desc.scd │ ├── eowave-ribbon.desc.scd │ ├── evolution-ucontrol-uc33.desc.scd │ ├── faderfox-pc12.desc.scd │ ├── faderfox-pc4.desc.scd │ ├── faderfox_ec4 │ │ ├── SE01-allGroupsCCAh.syx │ │ ├── faderfox_ec4.desc.scd │ │ ├── faderfox_ec4.parentDesc.scd │ │ └── faderfox_ec4_basic.desc.scd │ ├── faderfox_uc4 │ │ ├── faderfox-uc4.desc.scd │ │ ├── faderfox-uc4.parentDesc.scd │ │ └── faderfox-uc4_pg.desc.scd │ ├── generic-mouse.desc.scd │ ├── gyrosc_ga.desc.scd │ ├── icon-icontrols-101.desc.scd │ ├── icon-icontrols-pro.desc.scd │ ├── icon-istage.desc.scd │ ├── icontrols │ │ ├── icon-icontrols-102-hid.desc.scd │ │ ├── icon-icontrols-102.desc.scd │ │ ├── icon-icontrols-200-hid.desc.scd │ │ ├── icon-icontrols-200.desc.scd │ │ ├── icon-icontrols-hid.desc.scd │ │ └── icon-icontrols.desc.scd │ ├── intech-grid.desc.scd │ ├── jesstech-dual-analog-rumble.desc.scd │ ├── jesstech-dual-analog.desc.scd │ ├── keith-mcmillen-quneo_preset1.desc.scd │ ├── korg-microkey.desc.scd │ ├── korg-nanokey.desc.scd │ ├── korg-nanokey2.desc.scd │ ├── korg-nanokontrol.desc.scd │ ├── korg-nanokontrol2.desc.scd │ ├── korg-nanopad2.desc.scd │ ├── linnstrument │ │ └── linnstrument_8chan.desc.scd │ ├── livid-guitar-wing.desc.scd │ ├── logitech-extreme-3d-pro.desc.scd │ ├── m-audio-oxygen49.desc.scd │ ├── m-audio-triggerfinger.desc.scd │ ├── macally-ishock.desc.scd │ ├── makenoise-0coast.desc.scd │ ├── music_thing_8mu.desc.scd │ ├── native-instruments-traktor-kontrol-z1.desc.scd │ ├── native-instruments-traktor-kontrol-z2.desc.scd │ ├── nintendo-switch-pro.desc.scd │ ├── nordDrum2.desc.scd │ ├── novation-impulse-49.desc.scd │ ├── novation-launchcontrol-xl.desc.scd │ ├── novation-launchcontrol.desc.scd │ ├── novation-launchpad.desc.scd │ ├── novation-launchpadminimk1.desc.scd │ ├── organelle.desc.scd │ ├── qunexus │ │ ├── keith-mcmillen-qunexus.parentDesc.scd │ │ ├── keith-mcmillen-qunexus_port1_AB.desc.scd │ │ ├── keith-mcmillen-qunexus_port1_C.desc_off.scd │ │ ├── keith-mcmillen-qunexus_port1_D.desc_off.scd │ │ ├── keith-mcmillen-qunexus_port2.desc.scd │ │ └── keith-mcmillen-qunexus_port3.desc.scd │ ├── rme-audio-totalmix.desc.scd │ ├── saitek-cyborg-command-pad-unit.desc.scd │ ├── saitek-cyborg-x.desc.scd │ ├── saitek-impact-gamepad.desc.scd │ ├── sensestage-minibee1-xpee.desc.scd │ ├── sensestage-minibee1-xpree.desc.scd │ ├── sensestage-minibee1.desc.scd │ ├── shanwan-wirelessgamepad.desc.scd │ ├── shanwan_ps3.desc.scd │ ├── shbobo-shnth.desc.scd │ ├── snyderphonics-manta.desc.scd │ ├── snyderphonics-manta_mc.desc.scd │ ├── steinberg-cmc-fd.desc.scd │ ├── steinberg-cmc-qc │ │ ├── steinberg-cmc-qc.parentDesc.scd │ │ └── steinberg-cmc-qc_port1.desc.scd │ ├── steinberg_cmc_pd │ │ ├── steinberg-cmc-pd.parentDesc.scd │ │ ├── steinberg-cmc-pd_port1.desc.scd │ │ └── steinberg-cmc-pd_port2.desc.scd │ ├── tascam-us-2400.desc.scd │ ├── teenage-engineering-op-1.desc.scd │ ├── thrustmaster-dual-analog-3.2.desc.scd │ ├── thrustmaster-dual-analog-4.desc.scd │ ├── thrustmaster-megaworldectronics.desc.scd │ ├── thrustmaster-run-n-drive-wireless.desc.scd │ ├── thrustmaster-run-n-drive.desc.scd │ ├── touche.desc.scd │ ├── touchosc-simple1.desc.scd │ ├── touchosc-simple1_2.desc.scd │ ├── vmeter-vmeter.desc.scd │ └── x-io-x-osc.desc.scd ├── Tests │ ├── 0_BigTests_MKtlDescToMKtl.scd │ ├── 1_MKtlDesc_tests.scd │ ├── 2_MKtlElement_tests.scd │ ├── 3_MKtl_tests.scd │ ├── 5_TestMidiElementCompleteness.scd │ ├── Linux_ManyMIDISources.scd │ ├── NoteGroup_DeviceTests.scd │ ├── NoteGroup_GuiTests.scd │ └── Polyphony_and_closing.scd ├── discussion │ ├── desc file info.md │ ├── desc file info_discussion.txt │ └── renderPageMarkdown.scd └── unused_shaky_descs │ ├── DanceMat.desc_OFF.scd │ ├── PuzzleSphere. desc_OFF.scd │ ├── numark-orbit.desc.scd │ └── sherman-filterbank-2.desc.scd └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | Modality/MKtlDescriptions/_allDescs.cache.scd 3 | -------------------------------------------------------------------------------- /Modality.quark: -------------------------------------------------------------------------------- 1 | ( 2 |   name: "Modality Toolkit", 3 |   summary: "A common interface to MIDI, OSC and HID for highly personalised electronic instruments", 4 |   version: "0.2.1", 5 |   schelp: "Modality", 6 |   license: "GPL", 7 |   copyright: "Modality team, http://modality.github.io" 8 | ) -------------------------------------------------------------------------------- /Modality/Classes/Additions/SystemOverwrites/extHIDAPI.sc: -------------------------------------------------------------------------------- 1 | + HID { 2 | *findAvailable { 3 | var rawDevList; 4 | // start eventloop if not yet running - this needs to happen here, 5 | // otherwise it is not called when not using modality; 6 | // we are overriding an essential class method here! 7 | if ( running.not ) { this.initializeHID }; 8 | rawDevList = HID.prbuildDeviceList; 9 | 10 | // if (rawDevList.isNil) { 11 | if (rawDevList == 0) { 12 | "HID: no devices found.".postln; 13 | ^this 14 | }; 15 | // simple sorting by vendorID, productID, on to usagePage etc. 16 | // sorts multiple entries for different pages of the same device together. 17 | // unless physical HIDs are added or removed, always keeps same order of HIDs, 18 | // which guarantees same ID number when connected devices are the same. 19 | rawDevList.sort { |a, b| a.asString 1).keys(Array) 9 | } 10 | } -------------------------------------------------------------------------------- /Modality/Classes/Additions/extMIDISetSources.sc: -------------------------------------------------------------------------------- 1 | // for debugging only 2 | + MIDIClient { 3 | *prSetSources { |srcs| 4 | sources = srcs; 5 | } 6 | *prSetDestinations { |dests| 7 | destinations = dests 8 | } 9 | } -------------------------------------------------------------------------------- /Modality/Classes/Additions/plusDictionary.sc: -------------------------------------------------------------------------------- 1 | + Dictionary { 2 | 3 | sortedKeysValuesCollect { arg function, sortFunc; 4 | var keys = this.keys(Array); 5 | var res = this.class.new(this.size); 6 | keys.sort(sortFunc); 7 | 8 | keys.do { arg key, i; 9 | res.put( key, function.value(this[key], key, i) ); 10 | }; 11 | ^res; 12 | } 13 | } -------------------------------------------------------------------------------- /Modality/Classes/Additions/plusObject.sc: -------------------------------------------------------------------------------- 1 | + Object { 2 | deepAt { |...args| 3 | ^args.inject(this, {|state,x| 4 | state.at(x) 5 | }) 6 | } 7 | 8 | // expands when indexOrKey is a collection, 9 | // accepts nil and 'all' as wildcards, 10 | // returns nil on dead ends. 11 | deepAt2 { |first ... rest| 12 | var res; 13 | if (first.isNil or: { first == \all }) { 14 | if (rest.isEmpty) { 15 | ^this; 16 | } { 17 | ^this.collect(_.deepAt2(*rest)); 18 | } 19 | }; 20 | if (first.isCollection) { 21 | ^first.collect { |idx| 22 | this.deepAt2(idx, *rest) }; 23 | }; 24 | // first is a single key or index now 25 | if (this.respondsTo(\at)) { 26 | res = this.at(first); 27 | if (rest.isEmpty) { 28 | ^res 29 | } { 30 | ^res.deepAt2(*rest) 31 | }; 32 | }; 33 | ^nil 34 | } 35 | } 36 | 37 | + Dictionary { 38 | // get this dict's keys and those of all parents 39 | allKeys { |species| 40 | var dict = this, ancestry = []; 41 | while { dict.notNil } { 42 | ancestry = ancestry.add(dict); 43 | dict = dict.parent; 44 | }; 45 | ^ancestry.collect { |dict| dict.keys(species) } 46 | } 47 | 48 | fillFromParents { 49 | this.allKeys.do { |keys| 50 | keys.do { |key| 51 | this.put(key, this.at(key)) 52 | }; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Modality/Classes/GUI/MViews.sc: -------------------------------------------------------------------------------- 1 | MButtonView : Button { 2 | var action; 4 | var <>upAction; 5 | // var <>moveAction; 6 | 7 | mode_ { |modeName| 8 | mode = modeName; 9 | } 10 | upValue_ { |newValue = 0| 11 | this.value_( newValue ); 12 | } 13 | } 14 | 15 | MSliderView : Slider { 16 | var action; 18 | var <>upAction; 19 | // var <>moveAction; 20 | mode_ { |modeName| 21 | mode = modeName; 22 | } 23 | upValue_ { |newValue = 0| 24 | this.value_( newValue ); 25 | } 26 | } 27 | 28 | MKnobView : Knob { 29 | var action; 31 | var <>upAction; 32 | // var <>moveAction; 33 | mode_ { |modeName| 34 | mode = modeName; 35 | } 36 | upValue_ { |newValue = 0| 37 | this.value_( newValue ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Modality/Classes/MIDI/MChanVoicer.sc: -------------------------------------------------------------------------------- 1 | MChanVoicer { 2 | classvar startFunc, <>endFunc, rollback = \last; 6 | 7 | *initClass { 8 | rollbackFuncs = ( 9 | \last: { |mcv| mcv.heldNotes.last }, 10 | \first: { |mcv| mcv.heldNotes.first }, 11 | \lowest: { |mcv| mcv.heldNotes.minItem }, 12 | \highest: { |mcv| mcv.heldNotes.maxItem } 13 | ); 14 | } 15 | 16 | *new { |chan = 0, srcID, noteEl, velEl, bendEl, pressEl, offVelEl| 17 | ^super.newCopyArgs(chan, noteEl, velEl, bendEl, pressEl, offVelEl).init; 18 | } 19 | 20 | init { 21 | noteEl = noteEl ?? { MKtlElement([\note, chan].join($_).asSymbol, (spec: \midiNum)) }; 22 | velEl = velEl ?? { MKtlElement([\vel, chan].join($_).asSymbol, (spec: \midiVel)) }; 23 | bendEl = bendEl ?? { MKtlElement([\bend, chan].join($_).asSymbol, (spec: \midiBend)) }; 24 | pressEl = pressEl ?? { MKtlElement([\press, chan].join($_).asSymbol, (spec: \midiNum)) }; 25 | offVelEl = offVelEl ?? { MKtlElement([\vel, chan].join($_).asSymbol, (spec: \midiVel)) }; 26 | heldNotes = List[]; 27 | } 28 | 29 | noteOn { |note, vel| 30 | if (heldNotes.isEmpty) { this.startNote(note, vel) }; 31 | heldNotes.remove(note); 32 | heldNotes.add(note); 33 | noteEl.deviceValueAction_(note); 34 | velEl.deviceValueAction_(vel); 35 | } 36 | 37 | noteOff { |note, vel| 38 | var prevNote; 39 | heldNotes.remove(note); 40 | prevNote = rollbackFuncs[rollback].value(this); 41 | if (prevNote.notNil) { 42 | if (prevNote != noteEl.deviceValue) { 43 | noteEl.deviceValueAction_(prevNote); 44 | } 45 | } { 46 | velEl.deviceValueAction_(0); 47 | offVelEl.deviceValueAction_(vel); 48 | this.endNote(note, vel); 49 | } 50 | } 51 | 52 | bend { |bendVal| bendEl.deviceValueAction_(bendVal) } 53 | press { |pressVal| pressEl.deviceValueAction_(pressVal) } 54 | 55 | startNote { |note, vel| startFunc.value(this, note, vel) } 56 | endNote { |note, vel| endFunc.value(this, note, vel) } 57 | 58 | } -------------------------------------------------------------------------------- /Modality/Classes/MIDI/MIDIAnalysis.sc: -------------------------------------------------------------------------------- 1 | MIDIAnalysis { 2 | 3 | classvar elements, respTypes, elemDictByType, results; 4 | 5 | *analyse { |rawCapture| 6 | respTypes = []; 7 | elements = rawCapture.clump(2).collect { 8 | |pair| pair[1].put(\rawElName, pair[0]) 9 | }; 10 | 11 | elemDictByType = (); 12 | 13 | "*** MIDICapture analysis: ***".postln; 14 | MIDIMKtl.allMsgTypes.collect { |type| 15 | var matchingEls = elements.select { |el| el[\midiMsgType] == type }; 16 | [ type, matchingEls.size].postln; 17 | if (matchingEls.size > 0) { 18 | respTypes = respTypes.add(type); 19 | elemDictByType.put(type, matchingEls) 20 | }; 21 | }; 22 | 23 | results = [ 24 | rawElCount: elements.size, 25 | respTypes: respTypes]; 26 | 27 | if (respTypes.includesAny([\noteOn, \noteOff])) { 28 | this.analyseNoteEls; 29 | }; 30 | if (respTypes.includes(\touch)) { 31 | this.analyseTouch; 32 | 33 | }; 34 | if (respTypes.includes(\cc)) { 35 | this.analyseCC; 36 | }; 37 | if (respTypes.includes(\bend)) { 38 | this.analyseBend; 39 | }; 40 | 41 | ^results 42 | } 43 | 44 | *analyseNoteEls { 45 | var noteOnEls =elemDictByType[\noteOn]; 46 | var noteOffEls =elemDictByType[\noteOff]; 47 | var noteOnChan, noteOffChan; 48 | var noteOnNotes, noteOffNotes; 49 | "noteOnEls, noteOffEls:".postln; 50 | 51 | noteOnEls.postcs; 52 | noteOffEls.postcs; 53 | 54 | noteOnChan = this.compressInfo(noteOnEls, \midiChan); 55 | noteOffChan = this.compressInfo(noteOffEls, \midiChan); 56 | "".postln; 57 | 58 | if (noteOnChan.isKindOf(SimpleNumber)) { 59 | "noteOn uses single channel on notes: ".post; 60 | noteOnNotes = this.compressInfo(noteOnEls, \midiNote); 61 | noteOnNotes.postln; 62 | }; 63 | 64 | if (noteOffChan.isKindOf(SimpleNumber)) { 65 | "noteOff uses single channel on notes: ".post; 66 | noteOffNotes = this.compressInfo(noteOffEls, \midiNote); 67 | noteOffNotes.postln; 68 | }; 69 | results = results ++ [ 70 | noteOn: (channel: noteOnChan), 71 | noteOff: (channel: noteOffChan) 72 | ]; 73 | 74 | } 75 | 76 | *compressInfo { |dict, key| 77 | ^dict.collectAs(_[key], Array).asSet.asArray.sort.unbubble; 78 | } 79 | 80 | *checkForMultiple { |devDesc, typeToFilterBy, dictKeyToCompress| 81 | var touchEls = devDesc.select { |el, i| (i.odd and: { el[\midiMsgType] == typeToFilterBy }) }; 82 | var touchChan = this.compressInfo(touchEls, dictKeyToCompress); 83 | ^(channel: touchChan, numEls: touchEls.size); 84 | } 85 | } -------------------------------------------------------------------------------- /Modality/Classes/MIDI/MIDIControl14bit.sc: -------------------------------------------------------------------------------- 1 | MIDIControl14BitHelperLobyte{ 2 | var > 7 ); 60 | this.control( chan, ctlNum+32, val % 128 ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Modality/Classes/MIDI/MIDISim.sc: -------------------------------------------------------------------------------- 1 | 2 | MIDISim { 3 | classvar methodNames = #[ 'action', 'noteOn', 'noteOff', 'polytouch', 'control', 'program', 'touch', 'bend', 'sysex', 'sysexPacket', 'sysrt', 'smpte', 'invalid', 'noteOnList', 'noteOffList', 'polyList', 'controlList', 'programList', 'touchList', 'bendList' ]; 4 | 5 | *value { |ev| 6 | var list = ev[\numbers].collect (_.value); 7 | var msgType = ev[\midiMsgType]; 8 | if (msgType.isKindOf(Symbol).not 9 | or: { MIDIIn.respondsTo(msgType).not }) { 10 | "MIDISim: % is not a midiMsgType.\n".postf(msgType); 11 | ^this 12 | }; 13 | "// MIDISim sends: %, %.\n".postf(msgType, list); 14 | this.perform(msgType, *list); 15 | ^ev 16 | } 17 | 18 | // per chan and midinum 19 | *status { arg src, status, a, b, c; 20 | MIDIIn.doAction(src, status, a, b, c); 21 | } 22 | *noteOn { arg src, chan, num, vel; 23 | MIDIIn.doNoteOnAction(src, chan, num, vel); 24 | } 25 | *noteOff { arg src, chan, num, vel; 26 | MIDIIn.doNoteOffAction(src, chan, num, vel); 27 | } 28 | *polytouch { arg src, chan, num, val; 29 | MIDIIn.doPolyTouchAction(src, chan, num, val); 30 | } 31 | *control { arg src, chan, num, val; 32 | MIDIIn.doControlAction(src, chan, num, val); 33 | } 34 | // per channel 35 | *program { arg src, chan, val; 36 | MIDIIn.doProgramAction(src, chan, val); 37 | } 38 | *touch { arg src, chan, val; 39 | MIDIIn.doTouchAction(src, chan, val); 40 | } 41 | *bend { arg src, chan, val; 42 | MIDIIn.doBendAction(src, chan, val); 43 | } 44 | 45 | // special ones 46 | // *doSysexAction { arg src, packet; 47 | // sysexPacket = sysexPacket ++ packet; 48 | // if (packet.last == -9, { 49 | // MIDIIn.sysex.value(src, sysexPacket); 50 | // sysexPacket = nil 51 | // }); 52 | // } 53 | // *doInvalidSysexAction { arg src, packet; 54 | // MIDIIn.invalid.value(src, packet); 55 | // } 56 | // 57 | // *doSysrtAction { arg src, index, val; 58 | // sysrt.value(src, index, val); 59 | // } 60 | // 61 | // *doSMPTEaction { arg src, frameRate, timecode; 62 | // smpte.value(src, frameRate, timecode); 63 | // } 64 | } 65 | -------------------------------------------------------------------------------- /Modality/Classes/MKtl/CompMKtl.sc: -------------------------------------------------------------------------------- 1 | // /* 2 | 3 | CompMKtl { 4 | classvar MKtl(mktlName).elementGroup 31 | }, Array)); 32 | MKtlElement.addGroupsAsParent = false; 33 | // elementGroup.canFlatten.postln; 34 | } 35 | 36 | flattenElementGroup { 37 | if (elementGroup.canFlatten) { 38 | elementGroup.flatten 39 | } 40 | } 41 | 42 | elementAt { |...args| 43 | ^elementGroup.deepAt(*args) 44 | } 45 | 46 | elAt { |...args| 47 | ^elementGroup.deepAt2(*args) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Modality/Classes/MKtl/PKtl.sc: -------------------------------------------------------------------------------- 1 | // pattern wrapper for MKtls 2 | PKtl : Pattern { 3 | var pageNames; 5 | var elem }; 30 | this.elements =flattenedElements; 31 | MKtlElement.addGroupsAsParent = false; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Modality/Classes/MKtl/extLastUpdate.sc: -------------------------------------------------------------------------------- 1 | /* 2 | MKtl.all.collect(_.timeSinceLast); 3 | */ 4 | 5 | 6 | + MKtl { 7 | timeSinceLast { ^elementGroup.timeSinceLast } 8 | lastUpdatedElement { ^elementGroup.lastUpdatedElement } 9 | lastUpdateTime { ^elementGroup.lastUpdateTime } 10 | } 11 | 12 | + MKtlElementGroup { 13 | timeSinceLast { 14 | ^Main.elapsedTime - this.lastUpdateTime 15 | } 16 | lastUpdatedElement { 17 | ^elements.maxItem(_.lastUpdateTime) 18 | } 19 | lastUpdateTime { 20 | ^elements.maxItem(_.lastUpdateTime).lastUpdateTime 21 | } 22 | } -------------------------------------------------------------------------------- /Modality/Classes/MKtl/extMKtl_checkAllCtls.sc: -------------------------------------------------------------------------------- 1 | + MKtl { 2 | checkAllCtls { 3 | if(device.notNil) { device.checkAllCtls } 4 | } 5 | } 6 | 7 | + MIDIMKtlDevice { 8 | checkAllCtls { 9 | var allElKeys = this.midiKeyToElemDict.keys; 10 | var keysToRemove = allElKeys.copy; 11 | var missingKeys = Set.new; 12 | "MIDIMKtlDevice:checkAllCtls - are all % MKtlElements accessible?\n" 13 | .postf(allElKeys.size); 14 | "Please wiggle every control element once until DONE.".postln; 15 | 16 | this.midiRawAction = { |... msg| 17 | var lookupKey = msg[0].switch( 18 | 'control', { this.makeCCKey(msg[2], msg[3]); }, 19 | 'noteOn', { this.makeNoteOnKey(msg[2], msg[3]); }, 20 | 'noteOff', { this.makeNoteOffKey(msg[2], msg[3]); }, 21 | 'polyTouch', { this.makePolyTouchKey(msg[2], msg[3]); }, 22 | 23 | 'bend', { this.makeBendKey(msg[2], msg[3]); }, 24 | 'touch', { this.makeTouchKey(msg[2], msg[3]); }, 25 | 'program', { this.makeProgramKey(msg[2], msg[3]); }, 26 | 27 | ); 28 | if (lookupKey.isNil) { 29 | "unsupported type in message % !\n.".postf(msg); 30 | } { 31 | if (allElKeys.includes(lookupKey).not and: 32 | { missingKeys.includes(lookupKey).not }) { 33 | "missing: %.\n".postf(lookupKey); 34 | missingKeys.add(lookupKey); 35 | 36 | }; 37 | if (keysToRemove.includes(lookupKey)) { 38 | keysToRemove.remove(lookupKey); 39 | "% down, % to go.\n".postf(lookupKey, keysToRemove.size); 40 | 41 | if (keysToRemove.size == 0) { 42 | if (missingKeys.size > 0) { 43 | "!!! All MKtlElements were activated," 44 | "but were missing: %.\n".postf(missingKeys); 45 | } { 46 | "All MKtlElements were activated - DONE!".postln; 47 | }; 48 | } 49 | }; 50 | } 51 | 52 | }; 53 | } 54 | } 55 | 56 | + OSCMKtlDevice { 57 | checkAllCtls {"not done yet.".inform } 58 | } 59 | 60 | + HIDMKtlDevice { 61 | checkAllCtls {"not done yet.".inform } 62 | } 63 | -------------------------------------------------------------------------------- /Modality/Classes/MKtl/extReload.sc: -------------------------------------------------------------------------------- 1 | /* 2 | x = MKtl(\x, "*trol2"); 3 | x.desc.openFile; // add post message to desc file ... 4 | x.desc.reload; // msg should get posted ... and did. 5 | x.reload; // ... and here too. 6 | */ 7 | 8 | + MKtl { 9 | reload { |lookAgain = false| 10 | desc.reload; 11 | this.rebuild(lookAgain: lookAgain); 12 | } 13 | } 14 | + MKtlDesc { 15 | reload { 16 | // path is known, should still work 17 | var newDesc = path.load; 18 | if (MKtlDesc.isValidDescDict(newDesc).not) { 19 | warn("% : reloaded desc is not a valid dict." 20 | "at path: \n%".format(thisMethod, path)); 21 | ^this 22 | }; 23 | this.fullDesc_(newDesc); 24 | ^this 25 | } 26 | } -------------------------------------------------------------------------------- /Modality/Classes/Tools/IntegerClip.sc: -------------------------------------------------------------------------------- 1 | /* 2 | clip to an integer range and ensure integer type 3 | */ 4 | IntegerClip { 5 | var 64 ){ val = 64-val; }; 65 | this.delta_( val ); 66 | } 67 | 68 | delta_{ |dl| 69 | r2a.delta_( dl ); 70 | } 71 | 72 | value{ 73 | ^spec.unmap( r2a.value ); 74 | } 75 | 76 | value_{ |inval| 77 | r2a.value_( spec.map( inval ) ); 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /Modality/DeviceExamples/0coast-PatternSequencer.scd: -------------------------------------------------------------------------------- 1 | MKtl(\nocoast, "makenoise-0coast"); 2 | MKtl(\nocoast).openDeviceVia("USB MIDI Device", true, 1); 3 | 4 | 5 | // test 6 | MKtl(\nocoast).elAt(\key, 50, \on).deviceValue_(127); 7 | 8 | MKtl(\nocoast).elAt(\key, 50, \off).deviceValue_(0); 9 | 10 | MKtl(\nocoast).elAt(\key, 55, \on).deviceValue_(127); 11 | MKtl(\nocoast).elAt(\key, 55, \off).deviceValue_(0); 12 | 13 | 14 | 15 | // turn off arpeggiator 16 | MKtl(\nocoast).elAt(\ctl, \arpeggiator).deviceValue_(0) 17 | 18 | 19 | MKtl(\nocoast).elAt(\key, 55, \on).deviceValue_(127); 20 | MKtl(\nocoast).elAt(\key, 55, \off).deviceValue_(0); 21 | 22 | 23 | // portamento? 24 | MKtl(\nocoast).elAt(\ctl, \portamento).deviceValue_(1) 25 | 26 | 27 | 28 | 29 | ( 30 | MKtl(\nocoast).elAt(\ctl, \chanA).deviceValue_(0); 31 | MKtl(\nocoast).elAt(\ctl, \cvA).deviceValue_(0); 32 | MKtl(\nocoast).elAt(\ctl, \gateA).deviceValue_(0); 33 | 34 | // make midiB independant gate/cv: 35 | MKtl(\nocoast).elAt(\ctl, \chanB).deviceValue_(1); 36 | MKtl(\nocoast).elAt(\ctl, \cvB).deviceValue_(0); 37 | MKtl(\nocoast).elAt(\ctl, \gateB).deviceValue_(0); 38 | 39 | 40 | 41 | ) 42 | 43 | MKtl(\nocoast).elAt(\ctl, \gateB).deviceValue_(4); 44 | 45 | 46 | // midiA 47 | (\type: \midi, \midiout: MKtl(\nocoast).device.midiOut, \chan: 0, \midinote: rrand(50, 100), \dur: 0.1).play 48 | 49 | 50 | // midiB 51 | (\type: \midi, \midiout: MKtl(\nocoast).device.midiOut, \chan: 1, \midinote: rrand(10, 40), \dur: 0.1).play 52 | 53 | 54 | 55 | ////// just a pattern... 56 | Pdef(\player).set(\mydur, 0.125); 57 | ( 58 | Pdef(\player).quant = 4; 59 | Pdef(\player, Pbind( 60 | \type, \midi, 61 | \midicmd, \noteOn, 62 | \midiout, MKtl(\nocoast).device.midiOut, 63 | \chan, MKtl(\nocoast).elAt(\key).elemDesc.midiChan, 64 | \ctranspose, 2, 65 | \note, Pslide([0, 2, 3, 5]-3, inf, 3, 1, 0), 66 | \octave, Pseq([3], inf), 67 | \dur, Pkey(\mydur), 68 | \sustain, {~dur/4} 69 | )) 70 | ) 71 | Pdef(\player).play 72 | Pdef(\player).stop 73 | 74 | Pdef(\player).gui 75 | 76 | 77 | /// just another pattern on midiB 78 | ( 79 | Pdef(\playerB).quant = [4, 1]; 80 | Pdef(\playerB, Pbind( 81 | \type, \midi, 82 | \midicmd, \noteOn, 83 | \midiout, MKtl(\nocoast).device.midiOut, 84 | \chan, 1, 85 | // \degree, Pseq([0, 4, 4, 12], inf), 86 | \degree, Pseq((0!16) ++ (1!16), inf), 87 | // \octave, Pseq([2, 4] * [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2], inf), 88 | \octave, Pseq([2, 44], inf) * Pseq([1, 1, 0, 1], inf), 89 | \dur, Pfunc({2 * Pdef(\player).get(\mydur)}), 90 | \sustain, Pkey(\dur) * Pseq([Pseries(0.01, 0.01, 32)], inf), 91 | )).play 92 | ) 93 | 94 | Pdef(\playerB).gui 95 | 96 | -------------------------------------------------------------------------------- /Modality/DeviceExamples/QuNexus_AB.scd: -------------------------------------------------------------------------------- 1 | // QuNexus has four modes: 2 | // Preset A uses just noteOn/Off and channel bend 3 | // Preset B adds channel pressure 4 | // -> desc "*qunexus*AB" covers both A and B; 5 | 6 | // Preset C allocates notes to 10 rotating midi chans, 7 | // and sends poly bend and pressure mod (cc1) on those chans 8 | 9 | // Preset D is for drums (uses chan 10) and clips 10 | 11 | 12 | // code example for global note responder + voicer: 13 | MKtl.find(\midi); 14 | z = MKtl('qnxs0', "*qunexus*AB"); 15 | 16 | // a debug post for all elements: 17 | z.elAt.action = { |el| [el.name, el.value].postln }; 18 | z.elAt.action = nil; 19 | 20 | // make an NPVoicer, use with \default synthdef: 21 | s.boot; s.latency = nil; 22 | v = NPVoicer(Ndef(\piano)).play; 23 | v.prime(\default); 24 | 25 | // make a global noteOn function, 26 | MFdef(\qn_on).add(\debug, { |el| el.postln }); 27 | 28 | // and add it to all on elements 29 | z.elAt(\pk, \all, \on).do(_.action_( MFdef(\qn_on))); 30 | 31 | // add a noteOn 32 | MFdef(\qn_on).add(\note1, { |el, grp| 33 | var note = el.elemDesc[\midiNum]; 34 | var vel = el.value; 35 | v.put(note, [\freq, note.midicps, \amp, vel.squared ].postln); 36 | }); 37 | 38 | MFdef(\qn_off).add(\debug, { |el| el.postln }); 39 | MFdef(\qn_off).add(\note1, { |el| v.release(el.elemDesc[\midiNum].postln) }); 40 | 41 | z.elAt(\pk, \all, \off).do(_.action_( MFdef(\qn_off))); 42 | 43 | z.elAt(\bend).action = nil; 44 | 45 | z.elAt(\bend).action = { |el| 46 | // bend only covers part of the 14bit range, 47 | // so scale observed min/max to +- semitones: 48 | var bendVal = el.deviceValue.linlin(6784 + 128, 9472, -2, 2); 49 | // [\bend, el.deviceValue, bendVal].postcs; 50 | v.proxy.objects.do { |obj, noteKey| 51 | // [obj, noteKey].post; 52 | obj.set(*[\freq, (noteKey + bendVal)].midicps); 53 | } 54 | }; 55 | 56 | z.elAt(\monotouch).action = { |el| 57 | var touchVal = el.value.postln; 58 | v.proxy.objects.do { |obj, noteKey| 59 | [\touch, noteKey].post; 60 | obj.set(*[\amp, touchVal.squared].postln); 61 | } 62 | }; 63 | 64 | 65 | MIDIFunc.trace; 66 | MIDIFunc.trace(false); 67 | 68 | /********* not working yet: 69 | 70 | * Preset C, poly bend and pressure with channel-based polyphony; 71 | should be done with: 72 | 73 | MChanVoicer.openHelpFile 74 | 75 | * Preset D is for drums (uses chan 10) and clips, 76 | to be done when someone wants to use it. 77 | 78 | */ 79 | 80 | -------------------------------------------------------------------------------- /Modality/DeviceExamples/Uc4_RelSet_example.scd: -------------------------------------------------------------------------------- 1 | // Test relative setting on UC4: 2 | 3 | // on the device, select grp1, 4 | // then go into edit mode: hold \shift, press \edit_setup 5 | // select enc1 on knob 1, change ctlmode to Ccr2, 6 | // hold until running bars go away -> all encoders are now relative 7 | 8 | // then test here: 9 | m = MKtl(\uc4, "*uc4"); 10 | m.trace; 11 | m.gui; 12 | 13 | // add a spec to uc4, for relative use: 14 | // ccr2 is up 58 ... 63 down / 65 .. 70 up, 15 | // so use this for good resolution on gui elements 16 | m.addSpec(\knRel, [56, 72, \lin, 1, 64]); 17 | m.elAt(\kn, \1).do(_.deviceSpec_(m.getSpec(\knRel))); 18 | 19 | ~ckeys = [\a, \b,\c, \d, \e, \f]; 20 | ~ckeys.do (Spec.add(_, \amp)); 21 | m.specs; 22 | // a silly sound 23 | Ndef(\a, { 24 | (VarSaw.ar((1..6) * 48.midicps, 0, 0.1) 25 | * ~ckeys.collect(_.kr(0)).lag(0.1)).mean; 26 | }).play; 27 | 28 | 29 | Ndef(\a).gui(6); 30 | 31 | // make the action fot the encoders: 32 | // - moving knob above 0 will raise level in rel steps, 33 | // - below middle will lower it 34 | 35 | ~ckeys = Ndef(\a).controlKeys; 36 | m.elAt(\kn, \1).action = { |el| 37 | RelSet( 38 | Ndef(\a), 39 | ~ckeys[el.indexInGroup].postln, 40 | ((el.value - 0.5) * 0.06).postln, 41 | \amp 42 | ) 43 | }; 44 | 45 | // now try that it works by moving knobs 46 | // ... 47 | // then set Ndef to random values 48 | Ndef(\a).set(* ~ckeys.collect ([_, 1.0.linrand]).flat); 49 | 50 | // and move knobs again, should continue smoothly 51 | // from where they are now. 52 | -------------------------------------------------------------------------------- /Modality/DeviceExamples/cmc_fd_example.scd: -------------------------------------------------------------------------------- 1 | // 2 | /* example for the Steinberg CMD-FD hi-res faderbox. 3 | 4 | * use 4 faders of FD to set values of four sliders in a grid of 16x4: 5 | 6 | */ 7 | 8 | // make a dict for this example 9 | ~fdex = ~fdex ? (); 10 | 11 | // make a window with 4 x 16 faders to control 12 | ~fdex.w = Window(); ~fdex.w.addFlowLayout(2@2, 1@1); 13 | ~fdex.faders = {{ |i| EZSlider(~fdex.w, Rect(0,0,23,95), i, layout: \vert) }!16 } ! 4; 14 | ~fdex.w.front; 15 | ~fdex.faders.shape; 16 | 17 | // create vars and methods to select a group of 4 highlighted faders 18 | // init values 19 | ~fdex.selected = ~fdex.faders[0].keep(4); 20 | ~fdex.slrow = 0; ~fdex.slcol = 0; 21 | 22 | 23 | ~fdex.selectFaders = { |dict, row, col| 24 | defer { 25 | // unhighlight prev selected 26 | ~fdex.selected.do(_.setColors(background: Color.clear)); 27 | 28 | // set new indices 29 | ~fdex.slrow = row ? ~fdex.slrow; 30 | ~fdex.slcol = col ? ~fdex.slcol; 31 | 32 | //// select new ones 33 | ~fdex.selected = ~fdex.faders 34 | .wrapAt(~fdex.slrow ) 35 | .wrapAt(~fdex.slcol + (0..3)); 36 | 37 | "selected: row % col %.\n".postf(~fdex.slrow, ~fdex.slcol); 38 | 39 | // highlight selected faders 40 | ~fdex.selected.do(_.setColors(background: Color.green(0.85))); 41 | } 42 | }; 43 | /* test method: 44 | ~fdex.selectFaders(0, 4); 45 | ~fdex.selectFaders(3, -3); 46 | */ 47 | 48 | // now the cmc faderbox: 49 | ~fdex.mk = MKtl('fd', "*-fd"); 50 | // ~fdex.mk.gui; 51 | // this is how to get the index of the fader to use as offset 52 | ~fdex.mk.elAt('fader').action = { |el| el.indexInGroup.postln }; 53 | 54 | // set the group action of the fader to change a slider value 55 | ~fdex.mk.elAt('fader').action = { |el| 56 | var sliderToMove = ~fdex.selected[el.indexInGroup]; 57 | defer { sliderToMove.value = el.value }; 58 | }; 59 | 60 | // set buttons to step up and down columns = channels 61 | ~fdex.mk.elAt('bt', 'chanL', 'on').action = { 62 | ~fdex.selectFaders(~fdex.slrow, ~fdex.slcol - 1 % 16); 63 | }; 64 | ~fdex.mk.elAt('bt', 'chanR', 'on').action = { 65 | ~fdex.selectFaders(~fdex.slrow, ~fdex.slcol + 1 % 16); 66 | }; 67 | // ... and rows = banks: 68 | ~fdex.mk.elAt('bt', 'bankL', 'on').action = { 69 | ~fdex.selectFaders(~fdex.slrow - 1 % 4, ~fdex.slcol); 70 | }; 71 | ~fdex.mk.elAt('bt', 'bankR', 'on').action = { 72 | ~fdex.selectFaders(~fdex.slrow + 1 % 4, ~fdex.slcol); 73 | }; 74 | -------------------------------------------------------------------------------- /Modality/DeviceExamples/faderfox_uc4_ndefmixer.scd: -------------------------------------------------------------------------------- 1 | s.boot; s.latency = 0.03; 2 | 3 | // test setup: 4 | // make 11 proxies named a..k with 11 params named a..k 5 | ( 6 | ~names = "abcdefghijk".collectAs(_.asSymbol, Array); 7 | ~names.do { |n| 8 | Spec.add(n, [100, 1000, \exp]); 9 | Ndef(n, { 10 | var freqs = ~names.collect(_.kr); 11 | Splay.ar(Ringz.ar(Dust.ar(5 ! freqs.size), freqs, 0.2)); 12 | }); 13 | ~names.do { |par| Ndef(n).set(par, exprand(100, 1000)) }; 14 | }; 15 | Ndef(\a).play(vol: 0.1) 16 | ) 17 | 18 | // make a dict for all faderfox stuff 19 | ( 20 | // make a faderfox mktl; 21 | ~ff.free; ~ff = MKtl(\uc4, "*uc4_pg"); 22 | ~ff.addHalo(\gui, ~ff.gui); 23 | ~ff.addHalo(\mx, NdefMixer(s, 16)); 24 | 25 | // sliders from first 2 pages softSet ndef volume 26 | ~ff.addNamed(\vols, ~ff.elAt([\pgSl1, \pgSl2], \sl).flat); 27 | ~ff.elAt(\vols).do { |el, i| 28 | el.action = { |el| 29 | var ndef = ~ff.getHalo(\mx).arGuis[i].object; 30 | if (ndef.notNil) { 31 | ndef.softVol_( 32 | el.value.squared, 33 | lastVal: el.prevValue.squared 34 | ); 35 | }; 36 | }; 37 | }; 38 | 39 | // xfader is master vol, always sets 40 | ~ff.addNamed(\masta, ~ff.elAt(\xfader)); 41 | ~ff.elAt(\masta).action = { |el| 42 | s.volume.volume = el.value.linlin(0.0, 1.0, -90, 6); 43 | }; 44 | 45 | // buttons from first 2 pages play/stop 46 | ~ff.addNamed(\playBts, ~ff.elAt([\pgSl1, \pgSl2], \bt, \all, \on).flat); 47 | ~ff.elAt(\playBts).do { |bt, i| 48 | bt.action = { 49 | var pbut = ~ff.getHalo(\mx).arGuis[i].monitorGui.playBut; 50 | defer { pbut.valueAction_(1 - pbut.value) }; 51 | }; 52 | }; 53 | 54 | // encoder hats from first two pages send ndef to editor 55 | ~ff.addNamed(\editHats, ~ff.elAt([\pgKn1, \pgKn2], \pbt, \all, \on).flat); 56 | ~ff.elAt(\editHats).do { |bt, i| 57 | bt.action = { 58 | var mongui = ~ff.getHalo(\mx).arGuis[i].postln; 59 | var obj = mongui.object; 60 | obj !? { defer { mongui.edBut.doAction } }; 61 | }; 62 | }; 63 | 64 | // encoders from first two pages do relative sets on editgui params 65 | ~ff.addNamed(\editKns, ~ff.elAt([\pgKn1, \pgKn2], \kn).flat); 66 | ~ff.elAt(\editKns).do { |kn, i| 67 | kn.action = { 68 | var mx = ~ff.getHalo(\mx); 69 | var param, obj = mx.editGui.object; 70 | obj !? { 71 | param = mx.editGui.paramGui.prevState.editKeys[i]; 72 | param !? { RelSet(obj, param, kn.value - 0.5 * 0.25) }; 73 | }; 74 | }; 75 | }; 76 | ) 77 | -------------------------------------------------------------------------------- /Modality/DeviceExamples/launchcontrolXL_jitlib.scd: -------------------------------------------------------------------------------- 1 | // use launchcontrol XL for JITLib: 2 | // control e.g. first 8 ndefs in a proxymixer/NdefMixer. 3 | // 4 | // * 3 knobs control top 3 params of each proxy 5 | // * 8 sliders do softVol on the proxies 6 | // * upper buttons do play/stop 7 | // * lower buttons send to editGui 8 | 9 | ( 10 | q = q ? (); // store things by name 11 | // make the Mktl and gui 12 | m = MKtl(\lxl, "*launchcontrol-xl"); 13 | m.gui; 14 | 15 | // 8 names for the proxies or Ndefs 16 | q.names = "abcdefgh".as(Array).collect(_.asSymbol); 17 | 18 | Spec.add(\vsWidth, [0.5, 0.99, -3]); 19 | q[\sndFunc] = { |freq = 100, detune = 2, vsWidth = 0.5| 20 | VarSaw.ar(freq + [detune.neg, detune], 0, vsWidth) * 0.2; 21 | }; 22 | 23 | 24 | // Ndef / NdefMixer variant 25 | q.pxmix = NdefMixer(s.boot); 26 | // create 8 ndefs for 8 names 27 | q.names.do { |key| Ndef(key, q[\sndFunc]) }; 28 | // get proxies by name 29 | q.pxes = q.names.collect { |key| Ndef(key) }; 30 | 31 | 32 | // ... or ProxySpace variant: 33 | p = ProxySpace.push(s.boot); 34 | q.pxmix = ProxyMixer(p); 35 | q.names.collect { |key| p.put(key, q[\sndFunc]) }; 36 | // get proxies by name 37 | q.pxes = q.names.collect(p[_]); 38 | 39 | 40 | // function to get proxies from mixer - works for both: 41 | q.getpxes = { q.pxmix.arGuis.collect(_.object) }; 42 | 43 | // get a single proxy by index 44 | q.getpxAt = { |q, i| q.getpxes[i] } 45 | // test 46 | q.getpxAt(7); 47 | 48 | 49 | 50 | // Now the LCXL mapping: On page 0 of LC XL: 51 | // 8 sliders softVol first 8 ndef volumes 52 | // 8 upper buttons play/stop 53 | // 8 lower buttons pause/resume 54 | // 8x 3 buttons control first 3 ndef params 55 | 56 | // 8 sliders to first 8 ndefs 57 | m.elAt(0, \sl).do { |sl, i| 58 | sl.action = { |sl| 59 | var ndef = q.getpxAt(i); 60 | if (ndef.notNil) { 61 | ndef.softVol_(sl.value.squared, pause: true, lastVal: sl.prevValue.squared) 62 | }; 63 | }; 64 | }; 65 | 66 | // 8 upper buttons toggle playing on/off on noteOn: 67 | m.elAt(0, \bt, \fcs, \all, \on).do { |bt, i| 68 | bt.action = { |sl| 69 | var ndef = q.getpxAt(i); 70 | if (ndef.notNil) { 71 | if (ndef.monitor.isPlaying) { ndef.stop } { ndef.play } 72 | }; 73 | }; 74 | }; 75 | 76 | // 8 lower buttons send proxy to editGui 77 | m.elAt(0, \bt, \ctl, \all, \on).do { |bt, i| 78 | bt.action = { |sl| 79 | var ndef = q.getpxAt(i); 80 | if (ndef.notNil) { 81 | q.pxmix.arGuis[0].edBut.valueAction_(1); 82 | }; 83 | }; 84 | }; 85 | 86 | // 8x 3 buttons softSet first 3 ndef params 87 | m.elAt(0, \kn).do { |kngrp3, parIndex| 88 | kngrp3.do { |kn, chanIndex| 89 | kn.action = { |kn| 90 | var ndef, param, spec; 91 | ndef = q.getpxAt(chanIndex); 92 | if (ndef.notNil) { 93 | param = ndef.controlKeys[parIndex]; 94 | ndef.softSet(param, kn.value, mapped: false, lastVal: kn.prevValue); 95 | }; 96 | }; 97 | } 98 | }; 99 | ) 100 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/CompassView.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: CompassView 2 | summary:: a view for compass elements 3 | categories:: GUI 4 | related:: Overviews/Modality, Classes/MKtl, Classes/MKtlGUI 5 | 6 | DESCRIPTION:: 7 | A view designed to show compass elements as they are on gamepads, gaming josticks etc. 8 | Not currently used, intended for combined element guis. 9 | 10 | First code examples: 11 | 12 | code:: 13 | w = Window("Compass").front; 14 | z = CompassView(w.view, Rect( 200, 100,100,100)); 15 | z.action = { |comp| comp.dump }; 16 | z.nameView.string = "test"; 17 | z.value = 0; 18 | z.value = rrand(1, 8); 19 | z.valueAction = 0; 20 | z.valueAction = rrand(1, 8).postln; 21 | 22 | 23 | // use a Saitek Gamepad to show connecting to a CompassView 24 | MKtl.find; 25 | m = MKtl('imp', "saitek-impact-gamepad"); 26 | 27 | // compass has 8 directions (N, NE .. NW) + a rest value. 28 | // this HID reports a bogus mapping of deviceValue to normalized value, 29 | // so direction north and rest position are the same with value: 30 | m.elAt(\compass).action = { |el| 31 | "val: % devVal: %\n".postf(*[el.value, el.deviceValue, ].round(0.001)) 32 | }; 33 | 34 | // the deviceValues are better: rest: -1/7, N: 0, NE: 1/7, E: 2/7 .. NW: 7/7. 35 | // a conversion function for the directions: 36 | m.elAt(\compass).action = { |el| 37 | var intVal = (el.deviceValue * 7 + 1).round.asInteger; 38 | var dirs = [\rest, \N, \NE, \E, \SE, \S, \SW, \W, \NW]; 39 | [\compass, intVal, dirs[intVal], el.value.asFraction].postln; 40 | }; 41 | 42 | :: 43 | 44 | CLASSMETHODS:: 45 | 46 | METHOD:: new 47 | 48 | 49 | INSTANCEMETHODS:: 50 | 51 | METHOD:: nameView 52 | the name view 53 | 54 | METHOD:: skin 55 | the gui skin used 56 | 57 | METHOD:: zone 58 | the compositeView in which the buttons live 59 | 60 | METHOD:: value 61 | get and set the value of the compassview (0 - 8) 62 | 63 | METHOD:: buttons 64 | the buttons showing the directions 65 | 66 | private:: init 67 | 68 | METHOD:: action 69 | get and set the action to perform when compassview is activated 70 | 71 | METHOD:: valueAction 72 | set value and do the action 73 | 74 | METHOD:: buttonReset 75 | reset all buttons to 0 76 | 77 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/HIDExplorer.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: HIDExplorer 2 | summary:: explore an HID device for use with the Modality toolkit 3 | categories:: Libraries>Modality 4 | related:: Overviews/Modality, Classes/MKtl, Reference/MKtl_description_files, Classes/MIDIExplorer 5 | 6 | DESCRIPTION:: 7 | This class helps you to create a description file for an HID device. 8 | 9 | code:: 10 | // open an HID device 11 | MKtl( 'x', "*drive" ); 12 | // create a desc file doc from what HID reports 13 | MKtl( 'x' ).createDescriptionFile; 14 | 15 | // or by observation: 16 | HIDExplorer.start(MKtl( 'x' ).device.source); 17 | // ... now move all controllers thru their full range ... 18 | 19 | // then do: 20 | HIDExplorer.stop(MKtl( 'x' ).device.source); 21 | MKtl( 'x' ).device.openDoc; 22 | :: 23 | 24 | 25 | The file will have a dictionary that can be saved as a description file. 26 | It contains a device idInfo, the protocol (here, \hid), and the elements 27 | in a flat array consisting of lines like this: 28 | 29 | code:: 30 | // --------- input elements ---------- 31 | '<_b1_>': ('hidUsage': 1, 'hidUsagePage': 9, 'type': '', 'ioType': 'in' ), 32 | :: 33 | 34 | Here you give a proper name to: code::'_b1_':: to e.g. code::'bt1'::, based on the number that is written on the button - this is the name that will be used to refer to the element. 35 | 36 | Then you also need to edit the type: code::'':: to code::'button'::, and similar for other element types - see other HID device desc files for examples. 37 | 38 | Note that often elements can be grouped in meaningful order, e.g. gamepads have 12 or so number buttons, which can be put into one array with shared properties - see e.g. 39 | code:: 40 | MKtlDesc("*drive").openFile; 41 | :: 42 | 43 | The other items in each line are lookup info for the MKtl, so you should not change them. 44 | 45 | Check link::Reference/MKtl_description_files:: for the format of the description files. 46 | 47 | 48 | CLASSMETHODS:: 49 | 50 | private:: compileFromDevice, openDocFromDevice, compileFromObservation 51 | 52 | METHOD:: start, stop 53 | start abd stop observing an HID device 54 | 55 | METHOD:: openDoc 56 | open a document of the currently observed data, optionally including parameter specs. 57 | 58 | METHOD:: verbose 59 | get and set flag whether to post info 60 | METHOD:: trace 61 | get and set flag whether to post tracing info, default flag is true 62 | 63 | 64 | 65 | private:: allMsgTypes, detectDuplicateElements, exploreFunction, observeDict, observedSrcDev, prepareObserve, resps, results, specMap, stringFor, updateRange 66 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/ItemsSpec.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: ItemsSpec 2 | summary:: A class to map an Array of items to a value range between 0 and 1. 3 | categories:: Control, Spec, Libraries/Modality 4 | related:: Overviews/Modality, Classes/MKtl, Classes/MKtlDesc, Tutorials/How_to_create_a_description_file_for_OSC 5 | 6 | DESCRIPTION:: 7 | 8 | ItemsSpec maps an item from an array of possible items onto a value range between 0 and 1. 9 | 10 | Simple example: 11 | 12 | code:: 13 | ~itspec = ItemsSpec.new( ["off","amber","red"] ); 14 | 15 | // mapping 16 | ~itspec.map( 0 ); 17 | ~itspec.map( 0.5 ); 18 | ~itspec.map( 1 ); 19 | 20 | ~itspec.map( 0.33); // still off 21 | ~itspec.map( 0.34 ); // already amber 22 | 23 | // unmapping 24 | ~itspec.unmap( "off" ); 25 | ~itspec.unmap( "amber" ); 26 | ~itspec.unmap( "red" ); 27 | 28 | ~itspec.unmap( "orange" ); // item not there -> nil 29 | 30 | ~itspec.default; 31 | ~itspec.items; 32 | ~itspec.spec; 33 | 34 | 35 | // check that ranges are equal size: 21 values, 7 each 36 | ~itspec.map( (0, 0.05..1) ); 37 | // -> anything in [0, 0.3333] -> "off", [0.3334, 0.6666] -> amber, [0.6667, 1] -> red 38 | 39 | ["off","amber","red"].collect { |it| ~itspec.unmap(it) }; 40 | 41 | // change the warp to bend ranges 42 | ~itspec.warp = 4; 43 | ~itspec.map( (0, 0.05..1) ); // off has wider part of the range now (15, 4, 2) 44 | 45 | :: 46 | 47 | CLASSMETHODS:: 48 | 49 | 50 | METHOD:: new 51 | Create a new ItemsSpec. 52 | 53 | ARGUMENT:: items 54 | An Array of items to map onto. 55 | 56 | ARGUMENT:: warp 57 | An optional warp like \lin, \cos, \sin, or float number. 58 | 59 | ARGUMENT:: default 60 | An optional default value (float between 0 and 1) 61 | 62 | INSTANCEMETHODS:: 63 | 64 | private:: init 65 | 66 | 67 | METHOD:: map 68 | Map a SimpleNumber onto the items. 69 | 70 | ARGUMENT:: inval 71 | A SimpleNumber 72 | 73 | returns:: a String 74 | 75 | METHOD:: unmap 76 | Unmap an items contained in items to its number value 77 | 78 | ARGUMENT:: inval 79 | an item 80 | 81 | returns:: A SimpleNumber or nil if the item was not found. 82 | 83 | METHOD:: default 84 | The default value of an ItemsSpec is 0 85 | 86 | METHOD:: items 87 | The array of items. 88 | 89 | METHOD:: spec 90 | A ControlSpec that handles the conversion of 0-1 range to number of indices for the ItemsSpec. 91 | 92 | 93 | METHOD:: asSpec 94 | Convenience method. 95 | 96 | returns:: the ItemsSpec 97 | 98 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/MHexPad.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: MHexPad 2 | summary:: extends MPadView for Hexagonal and round pads 3 | categories:: GUI 4 | related:: Classes/MKtl, Classes/MKtlGUI, Overviews/MOdality 5 | 6 | DESCRIPTION:: 7 | code::MHexPad:: and code::MRoundPad:: allow more different shapes and behaviors than its superclass, MPadView. 8 | 9 | 10 | First code examples: 11 | 12 | code:: 13 | // make a window with some MPadViews 14 | 15 | w = Window("manta").front; 16 | h = 6.collect { |j| 17 | 8.collect { |i| 18 | var h = MHexPad(w, Rect(i * 45 + (j % 2 * 22), j * 40, 45, 45)).mode_(\noteOnLed); 19 | h.action = { |el| "pad %, % vel % \n".postf(j, i, el.value.round(0.01)) }; 20 | h.upDoesAction = true; 21 | h.label = "p_%_%".format(j, i); 22 | h.moveAction = { |el| "pad %, % move % \n".postf(j, i, el.moveValue.round(0.01)) }; 23 | }}; 24 | 25 | h.flat.do(_.ledVal_(2)); 26 | h.flat.do(_.ledVal_(1)); 27 | h.flat.do(_.ledVal_(0)); 28 | 29 | h.flat.do(_.vShiftLabel_(10)); 30 | 31 | 32 | Task { 33 | 100.do { |i| 34 | defer { h.flat.do(_.angle_(i * 0.1)) }; 35 | 0.1.wait; 36 | } 37 | }.play; 38 | 39 | h[0][0].ledColors.put(0, Color.black); h[0][0].refresh; 40 | h[0][0].ledVal = 3.rand; 41 | h[0][0].label = ""; 42 | 43 | h.flat.do { |m| m.hexColor_(Color.rand).refresh }; 44 | 45 | Task { 46 | 100.do { |i| 47 | defer { h.flat.do { |m| m.angle_(m.angle + 0.1.bilinrand) } }; 48 | 0.1.wait; 49 | } 50 | }.play; 51 | 52 | :: 53 | 54 | 55 | CLASSMETHODS:: 56 | 57 | private:: initClass 58 | 59 | 60 | INSTANCEMETHODS:: 61 | 62 | private:: baseDrawFunc, setColors 63 | 64 | METHOD:: upDoesAction 65 | 66 | argument:: bool 67 | get and set flag whether mouseUp also triggers the pads action or not. 68 | this allows using noteOn with value 1-127, and noteOn with value 0 to signal note end 69 | (which is the standard the Manta uses). 70 | 71 | METHOD:: angle 72 | rotate the pad by an angle 73 | 74 | METHOD:: ledColors 75 | get and set the colors to use for the led display 76 | 77 | METHOD:: ledVal 78 | get and set the value which led color to show. 79 | 80 | METHOD:: hexColor 81 | the color for the background hexagon shape. 82 | 83 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/MIDIAnalysis.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: MIDIAnalysis 2 | summary:: analyse MIDI input for making MKtls 3 | categories:: Libraries>Modality 4 | related:: Overviews/Modality, Classes/MKtl, Classes/MIDIExplorer, Classes/MIDISim 5 | 6 | DESCRIPTION:: 7 | MIDIAnalysis is an unfinished sketch for more detailed analysis of MIDI events as collected by MIDIExplorer or MIDIMonitor. May be moved to MIDIMonitor eventually. 8 | 9 | CLASSMETHODS:: 10 | 11 | METHOD:: analyse 12 | analyse a captured list of midi events. 13 | 14 | METHOD:: analyseNoteEls 15 | analyse the noteOn/off events in the list of midi events. 16 | 17 | private:: compressInfo, checkForMultiple 18 | 19 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/MIDIMonitor.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: MIDIMonitor 2 | summary:: monitor MIDI input 3 | categories:: Utilities 4 | related:: Overviews/Modality, Classes/MKtl, Classes/MIDIExplorer, Classes/MIDIIn, Classes/MIDIClient, Classes/OSCMon, Classes/HIDExplorer 5 | 6 | DESCRIPTION:: 7 | MIDIMonitor records all midi input and sorts it into a msgTree. This can be used to determine which messages a given controller is actually sending, so one can automatically make descriptions of its elements. 8 | 9 | First code examples: 10 | 11 | code:: 12 | MIDIMonitor.start; 13 | // now hit all keys from soft to hard, 14 | // move all sliders and knobs from min to max, 15 | // ..... 16 | MIDIMonitor.trace(false); // turn off posting (verbose) 17 | MIDIMonitor.trace; // or maybe back on 18 | 19 | /// then: 20 | MIDIMonitor.postTree; 21 | MIDIMonitor.msgTree.keys; // the srcIDs 22 | MIDIMonitor.srcAt(0); // get a specific input 23 | MIDIMonitor.msgTypes(0); // get all msgTypes from a specific input 24 | MIDIMonitor.msgTypes(1); // get all msgTypes from a specific input 25 | 26 | // all midi entries from source at index: 27 | // type, chan, [num], valueRange 28 | MIDIMonitor.treeAt(1); 29 | // all noteOn entries - chan, [num, ] valueRange 30 | MIDIMonitor.treeAt(1, \noteOn); 31 | MIDIMonitor.treeAt(1, \noteOn).keys(SortedList); // the chans 32 | // all noteOn entries on one chan, valueRange [min, max] 33 | MIDIMonitor.treeAt(1, \noteOn, 0); 34 | // all keys used 35 | MIDIMonitor.treeAt(1, \noteOn, 0).keys(SortedList); 36 | // valueRange for \noteOn, chan, noteNum 37 | MIDIMonitor.treeAt(1, \noteOn, 0, 57); 38 | 39 | :: 40 | 41 | CLASSMETHODS:: 42 | 43 | 44 | METHOD:: chanNumMsgTypes, chanMsgTypes, allMsgTypes 45 | The midi message types MIDIMonitor can look for 46 | 47 | METHOD:: start 48 | start monitoring 49 | METHOD:: stop 50 | stop monitoring 51 | METHOD:: monitoring 52 | flag whether monitoring is on 53 | 54 | METHOD:: verbose, trace 55 | turn verbose on and off 56 | 57 | METHOD:: msgTree, postTree 58 | monitoring results are collected here and can be pretty-posted 59 | 60 | METHOD:: treeAt 61 | deep lookup method 62 | 63 | METHOD:: sources 64 | METHOD:: srcAt 65 | METHOD:: msgTypes 66 | METHOD:: midiNumsAt 67 | lookup methods 68 | 69 | METHOD:: indexForSrcID 70 | get the index in MIDIIn.sources for a given source uid 71 | 72 | private:: monitorFuncs, init, checkIns, sysIndices 73 | 74 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/MIDISim.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: MIDISim 2 | summary:: a class to simulate input from MIDI controllers 3 | categories:: Modality 4 | related:: Overviews/Modality, Classes/MKtl, Classes/MIDIMKtlDevice, Classes/MIDIIn, Classes/MIDIClient 5 | 6 | DESCRIPTION:: 7 | MIDISim is intended for testing controller setups without requiring the device to be there. It can create any kind of midi message, thus simplifying the development and automatic testing of complex controller setups. It uses an Event which is passed through so it can be used in Patterns as well. 8 | 9 | 10 | code:: 11 | MIDISim.value((midiMsgType: \control, numbers: [0,0,64])); 12 | MIDISim.((midiMsgType: \touch, numbers: [0, 80])); 13 | 14 | :: 15 | 16 | CLASSMETHODS:: 17 | 18 | METHOD:: value 19 | This method creates a MIDIIn event, based on the event given. 20 | 21 | ARGUMENT:: ev 22 | code:: 23 | // control, noteOn, noteOff, polytouch expect 3 values: 24 | // midiChan, midiNum, midiValue 25 | (midiMsgType: \control, numbers: [midiChan, midiNum, midiValue]); 26 | 27 | // channel-only messages like touch, bend, program expect 2 values: 28 | (midiMsgType: \touch, numbers: [midiChan, midiValue]); 29 | :: 30 | 31 | METHOD:: noteOn, noteOff 32 | METHOD:: control, polytouch 33 | METHOD:: touch, bend, program 34 | METHOD:: status 35 | These methods are redirected to MIDIIn methods. 36 | 37 | EXAMPLES:: 38 | code:: 39 | 40 | MIDISim.value((midiMsgType: \control, numbers: [0,0,64])); 41 | MIDISim.((midiMsgType: \noSuchMsg, numbers: [0,0,64])); 42 | 43 | Tdef(\spoof, { 44 | loop { 45 | MIDISim.value(( 46 | \midiMsgType: \control, 47 | // varying ccnums, single control value 48 | \numbers: [0, { rrand(10, 24) }, 64] 49 | )); 50 | 0.5.wait; 51 | }; 52 | }).play; 53 | 54 | // test all the elements of a device 55 | // device has to be phyiscally present! 56 | MKtl.find(\midi); 57 | m = MKtl('lpd', "akai-lpd8"); 58 | m.elAt.action = { |el| el.postln }; 59 | 60 | ( 61 | Tdef(\spoof, { 62 | // play five events for every element 63 | m.elementsDict.do { |elem| 64 | var desc = elem.elemDesc; 65 | var midiChan = desc[\midiChan]; 66 | var midiNum = desc[\midiNum]; 67 | var msgType = desc[\midiMsgType]; 68 | if (msgType == \cc) { msgType == \control }; 69 | 70 | 5.do { 71 | MIDISim.value(( 72 | \midiMsgType: msgType, 73 | \numbers: [midiChan, midiNum, rrand(0, 127)] 74 | ).postcs); 75 | 0.5.wait; 76 | }; 77 | }; 78 | }).play; 79 | ) 80 | 81 | :: 82 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/MKtlElementCollective.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: MKtlElementCollective 2 | summary:: A group for elements with collective get/set communication 3 | categories:: MKtl 4 | related:: Classes/MKtlElementGroup, Tutorials/How_to_create_a_description_file_for_OSC 5 | 6 | DESCRIPTION:: 7 | 8 | sc version: 3.9dev 9 | MKtlElementCollective contains elements whose values the corresponding device sends (or receives) in grouped form: For example, an OSC device which sends all its button states in a single OSC message with a message name and n values. 10 | Very likely one never needs to make an MKtlElementCollective by hand; it is created automatically when a description file defines a group of elements as a collective. 11 | For code examples, see link::Tutorials/How_to_create_a_description_file_for_OSC:: esp. the sections on collective input and output. 12 | 13 | code:: 14 | 15 | // This example of a device creates one. 16 | // touchOSC is a smartphone app that can send accelerometer data: 17 | 18 | t = MKtl(\tosca, "touchosc-simple1"); 19 | t.trace.gui; 20 | // here is the collective: 21 | t.collectivesDict[\accel]; 22 | // here is the OSCFunc that receives the three-value message: 23 | t.device.oscFuncDictionary[\accel].dump; 24 | 25 | // change the address so we can test-send from SC's address: 26 | t.device.source.hostname_("127.0.0.1").port_(57120); 27 | // now send osc values 28 | NetAddr.localAddr.sendMsg('/accxyz', 3.0.rand2, 3.0.rand2, 3.0.rand2); 29 | 30 | // test setting the collective value directly: 31 | t.collectivesDict[\accel].value = [1.0.rand, 1.0.rand, 1.0.rand ]; 32 | 33 | // see the desc file for how the collective was defined: 34 | t.desc.openFile; 35 | :: 36 | 37 | CLASSMETHODS:: 38 | 39 | METHOD:: new 40 | argument:: source 41 | argument:: name 42 | argument:: elemDesc 43 | 44 | 45 | INSTANCEMETHODS:: 46 | 47 | METHOD:: addCollectiveToChildren 48 | 49 | METHOD:: init 50 | argument:: inElemDesc 51 | 52 | METHOD:: removeCollectiveFromChildren 53 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/MKtlGUI.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: MKtlGUI 2 | summary:: gui class for MKtl objects 3 | categories:: Libraries>Modality, External Control 4 | related:: Classes/MKtl 5 | 6 | DESCRIPTION:: 7 | MKtlGUI creates guis for MKtls and all their elements, 8 | using the style info provided in the MKtl's desc file. 9 | 10 | Especially when one does not have the device at hand, 11 | one can continue developing for it using the MKtlGUI 12 | as a functional substitute. 13 | 14 | 15 | First code examples: 16 | 17 | code:: 18 | k = MKtl('nk', "korg-nanokontrol"); 19 | g = MKtlGUI(mktl: k); 20 | 21 | g.traceButton.valueAction_(1); 22 | g.showLabels; 23 | g.showLabels(false); 24 | 25 | // switch page 26 | g.currentPage_(3); 27 | 28 | // put it somewhere 29 | g.moveTo(200, 200); 30 | :: 31 | 32 | CLASSMETHODS:: 33 | 34 | 35 | METHOD:: maxSize 36 | maximum window size 37 | 38 | METHOD:: minViewSize, maxViewSize 39 | max and min sizes for single views on gui 40 | 41 | METHOD:: margin 42 | the margin to use between views 43 | 44 | METHOD:: new 45 | create a new MKtlGUI with 46 | argument:: parent 47 | a parent view - nil will create a window 48 | argument:: bounds 49 | bounds - nil will create bounds based on mktl desc 50 | argument:: mktl 51 | the mktl to show - required! 52 | 53 | 54 | INSTANCEMETHODS:: 55 | 56 | METHOD:: mktl 57 | the mktl it is showing 58 | 59 | METHOD:: parent 60 | its parent view 61 | 62 | METHOD:: labelView, labelButton, traceButton 63 | METHOD:: views, pagesSwitch 64 | various views 65 | 66 | METHOD:: showLabels 67 | set whether to show labels or not 68 | 69 | METHOD:: currentPage 70 | get and set current page to show on gui 71 | 72 | METHOD:: getNumPages 73 | get the number of pages of the MKtl 74 | 75 | private:: init, layoutElementsOnPage, layoutElements, getNumRowsColumns 76 | private:: labelButton, pagesSwitch, gridSize, pageComposites 77 | 78 | private:: skipJack, updateGUI 79 | 80 | METHOD:: moveTo 81 | argument:: h 82 | argument:: v 83 | move gui window to a specific location for setup layout. 84 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/PKtl.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: PKtl 2 | summary:: a pattern for accessing MKtls and their elements 3 | categories:: Undocumented classes 4 | related:: Overviews/Modality, Classes/MKtl, Classes/MKtlElement 5 | 6 | DESCRIPTION:: 7 | PKtl allows using MKtls in patterns. It can access all present MKtls by patterns of names, and the elements they contain by patterns of names or indices. 8 | 9 | First code examples: 10 | 11 | code:: 12 | // make two example MKtls - nanokontrol2s 13 | a = MKtl(\nkA, "*trol2"); a.gui; 14 | a.elAt(\kn).value_((1..8) / 10); 15 | a.elAt(\kn).value; 16 | 17 | b = MKtl(\nkB, "*trol2"); b.gui; 18 | b.elAt(\kn).value = 1!8; 19 | 20 | 21 | // use single element in pattern - move first knob while playing 22 | Pdef(\test, Pbind( 23 | \degree, (Pn(a.elAt(\kn, 0)) * 10).round(1), 24 | \dur, 0.25 25 | )).trace.play 26 | 27 | // same with PKtl 28 | Pdef(\test, Pbind( 29 | \degree, (PKtl(\nkA, \kn, 0) * 10).round(1), 30 | \dur, 0.25 31 | )).trace.play 32 | 33 | // use all knobs as step sequencer 34 | Pdef(\test, Pbind( 35 | \knVal, Pseq(a.elAt(\kn), inf), 36 | \degree, (Pkey(\knVal) * 10).round(1), 37 | \dur, 0.25 38 | )).trace.play 39 | 40 | a.elAt(\sl, 0).value = 1; 41 | // same with PKtl, and uses slider1 to set dur 42 | Pdef(\test, Pbind( 43 | \knVal, PKtl(\nk2, \kn, Pseq((0..7), inf)), 44 | \degree, (Pkey(\knVal) * 10).round(1), 45 | \dur, 0.125 ** PKtl(\nkA, \sl, 0) 46 | )).trace.play 47 | 48 | // alternate between 3 step values from nkA, 3 from nkB 49 | Pdef(\test, Pbind( 50 | \knVal, PKtl( 51 | Pseq([\nkA, \nkB], inf).stutter(3), 52 | \kn, 53 | Pseq((0..7), inf) 54 | ), 55 | \degree, (Pkey(\knVal) * 10).round(0.1), 56 | \dur, 0.125 ** PKtl(\nkA, \sl, 0) 57 | )).trace.play 58 | 59 | // change values on nkB knobs for testing 60 | b.elAt(\kn).value = (0..7).normalize(0.9, 1); 61 | 62 | :: 63 | 64 | CLASSMETHODS:: 65 | 66 | METHOD:: new 67 | create a new PKtl with 68 | argument:: namePat 69 | a pattern for MKtl names 70 | argument:: ... elPats 71 | patterns for element access within MKtls 72 | 73 | INSTANCEMETHODS:: 74 | 75 | METHOD:: namePat, elPats 76 | access pattern data 77 | 78 | private:: embedInStream 79 | 80 | 81 | -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/PassSpec.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: PassSpec 2 | summary:: pass thru value and deviceValue 3 | categories:: Control 4 | related:: Classes/Spec, Classes/ControlSpec 5 | 6 | DESCRIPTION:: 7 | Some control values (strings, symbols, maybe others) should simply be passed thru, rather than converting them with map/nmap functions. PassSpec does just that, so deviceValue and value are always just passed thru as is. 8 | 9 | First code examples: 10 | 11 | code:: 12 | PassSpec.new; // returns PassSpec class, because 13 | PassSpec.map(23); // values are unchanged 14 | PassSpec.unmap(\skidoo); // in both directions 15 | PassSpec.asSpec; // returns PassSpec class 16 | PassSpec.default; // always returns nil 17 | :: 18 | 19 | CLASSMETHODS:: 20 | 21 | METHOD:: new 22 | returns PassSpec class, because when map and unmap are always the same, 23 | there is no need to create instances. 24 | 25 | METHOD:: asSpec 26 | returns PassSpec class 27 | 28 | METHOD:: map, unmap 29 | argument:: inval 30 | returns inval 31 | 32 | METHOD:: default 33 | returns nil, because there is no meaningful default value for a neutral 34 | non-mapping spec class. 35 | 36 | EXAMPLES:: 37 | code:: 38 | // a description desc for a device with a single, 39 | // non-numeric element: 40 | ( 41 | d = ( 42 | deviceName: "testa", 43 | protocol: \osc, 44 | idInfo: "testa", 45 | netAddrInfo: ( ipAddress: "169.254.1.1", srcPort: 9000, recvPort: 8000 ), 46 | 47 | elementsDesc: ( 48 | shared: ( ioType: \in, spec: PassSpec, \type: \unknown), 49 | elements: [( key: 'device', oscPath: "/serialosc/device" )] 50 | ) 51 | ); 52 | // make an MKtl from it 53 | MKtl(\x).free; 54 | MKtl(\x, d); 55 | // look at the element 56 | MKtl(\x).elAt(\device).dump; 57 | ) 58 | 59 | // tests: setting and and getting non-number value works with PassSpec 60 | MKtl(\x).elAt(\device).elemDesc.deviceSpec; // the class PassSpec 61 | 62 | MKtl(\x).elAt(\device).value = "foo"; // string remains unchanged 63 | MKtl(\x).elAt(\device).value.cs; 64 | MKtl(\x).elAt(\device).deviceValue.cs; 65 | 66 | MKtl(\x).elAt(\device).deviceValue = \oof; // symbol too 67 | MKtl(\x).elAt(\device).value.cs 68 | MKtl(\x).elAt(\device).deviceValue.cs 69 | 70 | // TODO: add an MKtlElementGUI that supports non-number elements; 71 | // currently they are not supported, so this line fails: 72 | MKtl(\x).gui; 73 | :: -------------------------------------------------------------------------------- /Modality/HelpSource/Classes/Piano.schelp: -------------------------------------------------------------------------------- 1 | TITLE:: Piano 2 | summary:: layout for piano keyboards 3 | categories:: GUI 4 | related:: Classes/MKtlGUI, Classes/MPadView, Overviews/Modality 5 | 6 | DESCRIPTION:: 7 | 8 | 9 | First code examples: 10 | 11 | code:: 12 | // create layout event for key 33 when leftmost key is 21 13 | Piano.pos(33, 21); 14 | Piano.pos(24, 21); 15 | Piano.pos(21, 21); 16 | Piano.pos(69, 60); 17 | 18 | Piano.layout(48, 72).printAll; ""; 19 | 20 | // example MKtl that has 25 physical keys and 121 reachable ones: 21 | MKtl(\o49, "*oxygen49").gui.showLabels; 22 | 23 | ( 24 | // simple blockish layout 25 | w = Window("PianoLayout", Rect(0, 200, 400, 200)).front; 26 | Piano.layout(48, 72).do { |ev| 27 | var color = Color.perform(ev.color); 28 | UserView(w, Rect(ev.x * 20 + 4, ev.y * 50 + 4, 18, 50)) 29 | .background_(color) 30 | .drawFunc = { 31 | Pen.stringAtPoint(ev.note.asString, 0@25, 32 | color: color.complementary); 33 | } 34 | }; 35 | ) 36 | 37 | // draw black keys on top of whites, use MPadViews 38 | ( 39 | ~win = Window("PianoLayout", Rect(0, 200, 400, 150)).front; 40 | 41 | // sort black before white keys so we draw them on top 42 | ~layouts = Piano.layout(48, 72).sort { |a, b| a.color > b.color }; 43 | 44 | // draw MPadViews as keys 45 | ~keys = ~layouts.collect { |ev| 46 | var color = Color.perform(ev.color); 47 | var height = ev.h * 60; 48 | var rect = Rect(ev.x + (1-ev.w/2) * 24 + 4, 4, 24 * ev.w, height); 49 | var pad = MPadView(~win, rect).mode_(\noteOnOffTouch); 50 | pad.baseColor_(color).label_(ev.note); 51 | pad.vShiftLabel_( ev.h * 15); 52 | pad.action = { |pd| "pad % on: % \n".postf(ev.note, pd.value.round(0.001)) }; 53 | pad.upAction = { |pd| "pad % off: % \n".postf(ev.note, pd.upValue.round(0.001)) }; 54 | pad.moveAction = { |pd| "pad % touch: % \n".postf(ev.note, pd.moveValue.round(0.001)) }; 55 | }; 56 | ) 57 | :: 58 | 59 | CLASSMETHODS:: 60 | private:: initClass 61 | 62 | METHOD:: blackNotes 63 | the indices of the black keys in the octave 64 | 65 | METHOD:: allXs, whiteXs, blackXs 66 | the x positions of white, black and all 12 keys in the octave. 67 | code:: 68 | Piano.allXs; 69 | Piano.whiteIndices; 70 | Piano.blackIndices; 71 | :: 72 | METHOD:: all12 73 | events with layout info for the 12 keys in the octave. 74 | 75 | METHOD:: layout 76 | argument:: from 77 | argument:: to 78 | create a list of layout events for keys from from to to: 79 | Piano.layout(60, 72); 80 | 81 | METHOD:: pos 82 | argument:: note 83 | argument:: start 84 | create info event for single key relative to start (lowest note displayed) 85 | Piano.pos(24, 21); 86 | 87 | 88 | 89 | INSTANCEMETHODS:: 90 | 91 | 92 | EXAMPLES:: 93 | 94 | code:: 95 | _some_example_code_ 96 | :: -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/3DConnection.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/3DConnection.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/ICONIControlPro.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/ICONIControlPro.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/LPD8.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/LPD8.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/LaunchControlXL.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/LaunchControlXL.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/Launchpad.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/Launchpad.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/MPD18.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/MPD18.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/RunNDrive.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/RunNDrive.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/Teenage_OP1.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/Teenage_OP1.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/Thrustmaster.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/Thrustmaster.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/g13.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/g13.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/impact_gamepad.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/impact_gamepad.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/keyboard.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/keyboard.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/keypad.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/keypad.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/mouse.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/mouse.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/nanoKONTROL2.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/nanoKONTROL2.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Reference/media/softStep2.desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/HelpSource/Reference/media/softStep2.desc.png -------------------------------------------------------------------------------- /Modality/HelpSource/Tutorials/Connecting_multiport_MIDI_devices.schelp: -------------------------------------------------------------------------------- 1 | title:: Connecting multiport MIDI devices 2 | summary:: Using midi devices with multiple ports 3 | categories:: Libraries>Modality 4 | related:: Overviews/Modality, Classes/MKtl, Reference/MKtl_description_files, Classes/MIDIExplorer 5 | 6 | Modality can handle MIDI devices with multiple ports. 7 | Most USB-connected MIDI devices have a single in and out port, which in Modality becomes a single MKtl, 8 | as in 9 | code:: 10 | MKtl(\nk2, "*nanokontrol2"); 11 | :: 12 | 13 | Most MIDI devices with multiple ports have the same number of in and out ports; 14 | Modality groups that into one MKtl for in 0, out 0, one for in 1, out 1, etc., 15 | each of which has a separate desc file, describing its elements. 16 | 17 | code:: 18 | // 3 desc files for 3 ports of qunexus keyboard: 19 | MKtlDesc.loadDescs("*qunexus*"); 20 | // and you make them by matching the desc name: 21 | MKtl(\qun1, "*qunexus_port1_AB"); 22 | MKtl(\qun2, "*qunexus_port2"); 23 | MKtl(\qun3, "*qunexus_port3"); 24 | :: 25 | 26 | When you have that device, code::MKtl.find:: will posts all 3 ports, 27 | and their matching desc files: 28 | 29 | code:: 30 | MKtl.find(\midi); 31 | 32 | // will post directly usable code for each port: 33 | 34 | 35 | :: 36 | 37 | You can combine these separate MKtls into a single link::Classes/CompMKtl:: if you like, 38 | or just use them separately. 39 | 40 | To add descriptions for a new device to Modality, simply create a desc file for each port, by following the instructions in link::Tutorials/How_to_adapt_a_description_file:: or in 41 | link::Tutorials/How_to_create_a_description_file_for_MIDI::. 42 | 43 | -------------------------------------------------------------------------------- /Modality/HelpSource/Tutorials/Fix_Missing_hutDirectory_osx.schelp: -------------------------------------------------------------------------------- 1 | title:: Fix Missing hutDirectory on OSX 2 | summary:: Instructions for adding a missing HID info directory to SC - OSX 3 | categories:: Libraries>Modality, Control 4 | related:: Overviews/Modality, Classes/MKtl, Classes/HID, Classes/HIDFunc 5 | 6 | In the OSX version of SC up to release 3.7.0, the directory containing 7 | semantic information on HID devices is missing. This glitch makes HID 8 | harder to use; semantic HIDFunc and HIDdef methods will not work properly: 9 | code:: 10 | HIDdef.usage(\x, { }, \X); // X-axis of mouse 11 | :: 12 | So it is worth repairing until newer SC releases fix this. 13 | 14 | code:: 15 | // * check if hutDirectory is missing: 16 | ~hutDirMissing = HIDUsage.hutDirectory.pathMatch.postcs.isEmpty; 17 | 18 | // if ~hutDirMissing is true, goto the sensestage/hidapi project page 19 | "https://github.com/sensestage/hidapi".openOS; 20 | // * download hidapi master from there 21 | // * then open the directory where the missing dir should go: 22 | HIDUsage.hutDirectory.dirname.dirname.openOS; 23 | // * make a new folder there called "HID_Support", 24 | // * and copy the folder /hidapi-master/hut/ there. 25 | 26 | // * now check that hutDirectory is in place and you see it here: 27 | HIDUsage.hutDirectory.pathMatch.cs; 28 | // you should see 29 | -> [ "/Applications/SuperCollider3.7/SuperCollider3.7.app/Contents/Resources/SCClassLibrary/Common/Control/HID_support/hut/" ] 30 | 31 | // you should get other lookup values than 'undefined' now: 32 | HIDUsage.getUsageDescription(9, 6).cs; 33 | // -> [ 'Button', 'b6' ] 34 | 35 | :: 36 | -------------------------------------------------------------------------------- /Modality/HelpSource/Tutorials/How_to_adapt_a_description_file.schelp: -------------------------------------------------------------------------------- 1 | title:: How to adapt a description file 2 | summary:: For similar devices, description files can be copied and adapted. 3 | categories:: Libraries>Modality 4 | related:: Overviews/Modality, Classes/MKtl, Reference/MKtl_description_files, Classes/MIDIExplorer 5 | 6 | When one wants to add a new controller to Modality, it is often similar to one that is already supported, especially if it conforms to a type pattern like the faderbox, the gamepad, etc etc. So it makes sense to check whether there is a similar desc file already, and copy and modify that for the new device. 7 | Also, when one wants to customize the hardware settings on a controller, it makes sense to have a personalized desc file for this setup. The best place to keep such additional desc files is here: 8 | code:: MKtlDesc.userFolder.openOS; :: 9 | The filename should be "", and must end in code::".desc.scd"::. 10 | After adding a new desc file, please do 11 | code:: 12 | MKtlDesc.writeCache; 13 | :: 14 | to make sure Modality finds it. 15 | 16 | You are highly welcome to contribute your desc files to modality! 17 | Especially when they are generally useful (i.e. not for a one-off selfmade or customized device that only you have), you can save it to the defaultFolder, and submit a pull request at github, or mail it to the modality list. 18 | 19 | code:: 20 | MKtlDesc.defaultFolder.openOS; 21 | :: 22 | 23 | Some good templates to copy are: 24 | code:: 25 | // for faderboxes: 26 | MKtlDesc("korg-nanokontrol2").openFile; 27 | // for gamepads, simple: 28 | MKtlDesc("saitek-impact-gamepad").openFile; 29 | // luxury gamepads: 30 | MKtlDesc("thrustmaster-run-n-drive").openFile; 31 | // for launchpads: 32 | MKtlDesc("novation-launchpad").openFile; 33 | :: 34 | 35 | ... To Do: add example here when there is a good one ... 36 | -------------------------------------------------------------------------------- /Modality/HelpSource/Tutorials/Substituting_MKtls.schelp: -------------------------------------------------------------------------------- 1 | title:: Substituting MKtls 2 | summary:: One process played from multiple interfaces 3 | categories:: Libraries>Modality, External Control 4 | related:: Overviews/Modality, Classes/MKtl, Tutorials/Coding_for_Modal_Flexibility 5 | 6 | Modality makes it easy to play the same process with different interfaces, which can be useful for many reasons: Playing may feel different, broken or lost interfaces can be substituted very quickly, etc etc. 7 | 8 | This example show how to write your performance code so you can substitute very quickly when needed or desired. 9 | 10 | code:: 11 | s.boot; 12 | ( 13 | // a simple sound 14 | Ndef(\blippy, { 15 | var snd = Blip.ar( 16 | \freq.kr(440).lag(0.1), 17 | \numharm.kr(100) 18 | ); 19 | Pan2.ar(snd, \pan.kr(0).lag(0.1), \amp.kr(0.5).lag(0.1)) 20 | }).play(vol: 0.25); 21 | 22 | // add a global spec for numharm param (the others exist as defaults) 23 | Spec.add(\numharm, [1, 50, \exp]); 24 | Ndef(\blippy).gui.parent.alwaysOnTop_(true); 25 | ) 26 | 27 | // make two MKtls: 28 | ( 29 | // a gamepad 30 | MKtl(\gp, "*drive"); 31 | MKtl(\gp).gui.parent.alwaysOnTop_(true); 32 | 33 | // and a nanokontrol2 34 | MKtl('nk2', "korg-nanokontrol2"); 35 | MKtl(\nk2).gui.parent.alwaysOnTop_(true); 36 | ) 37 | 38 | // prepare the gamepad: 39 | // joystick axes for param control, 40 | // two buttons for start/stopping sound 41 | ( 42 | var k = MKtl(\gp); 43 | k.addNamed(\amp1, k.elAt(\joy, \r, \y)); 44 | k.addNamed(\pan1, k.elAt(\joy, \r, \x)); 45 | k.addNamed(\param1, k.elAt(\joy, \l, \x)); 46 | k.addNamed(\param2, k.elAt(\joy, \l, \y)); 47 | 48 | // while at it, prapare buttons for start and stop 49 | k.addNamed(\start1, k.elAt(\bt, \5)); 50 | k.addNamed(\stop1, k.elAt(\bt, \7)); 51 | ) 52 | 53 | // prepare the nanokontrol2: 54 | // 2 sliders and 2 knobs for param control, 55 | // and two buttons for start/stopping sound 56 | ( 57 | var k = MKtl(\nk2); 58 | k.addNamed(\amp1, k.elAt(\sl, 0)); 59 | k.addNamed(\pan1, k.elAt(\kn, 0)); 60 | k.addNamed(\param1, k.elAt(\sl, 1)); 61 | k.addNamed(\param2, k.elAt(\kn, 1)); 62 | 63 | // prepare buttons for start and stop 64 | k.addNamed(\start1, k.elAt(\bt, \S, 0)); 65 | k.addNamed(\stop1, k.elAt(\bt, \R, 0)); 66 | ) 67 | 68 | Ndef(\blippy).setUni(\numharm, 0.25) 69 | 70 | // now choose one of them, and set its actions 71 | // the same code can now be used for two very different MKtls! 72 | ( 73 | k = MKtl(\gp); // this one? 74 | k = MKtl(\nk2); // or run same code with that one! 75 | k.trace; 76 | 77 | k.elAt(\amp1).action_({ |el| Ndef(\blippy).setUni(\amp, el.value) }); 78 | k.elAt(\pan1).action_({ |el| Ndef(\blippy).setUni(\pan, el.value) }); 79 | k.elAt(\param1).action_({ |el| Ndef(\blippy).setUni(\freq, el.value) }); 80 | k.elAt(\param2).action_({ |el| Ndef(\blippy).setUni(\numharm, el.value) }); 81 | 82 | // and for start and stop: 83 | k.elAt(\start1).action_({ |elem| 84 | // only start on button down 85 | if(elem.value > 0) { Ndef(\blippy).play }; 86 | }); 87 | k.elAt(\stop1).action_({ |elem| 88 | if(elem.value > 0) { Ndef(\blippy).stop }; 89 | }); 90 | ) 91 | :: 92 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/16n.desc.scd: -------------------------------------------------------------------------------- 1 | ///////// Example for an external MIDI device /////// 2 | /* 3 | This is the default slider values for the 16n faderbank device. 4 | */ 5 | 6 | ( 7 | deviceName: "16n", 8 | protocol: \midi, 9 | deviceType: \faderbox, 10 | elementTypes: [\fader], 11 | status: ( 12 | linux: "unknown", 13 | osx: "tested and working. 2021-04-30", 14 | win: "unknown"), 15 | 16 | idInfo: "16n", 17 | 18 | deviceInfo: ( 19 | vendorURI: "https://16n-faderbank.github.io/", 20 | manualURI: "https://github.com/16n-faderbank/16n/wiki/", 21 | description: "Sixteen MIDI faders and CV outputs with i2c", 22 | features: [ 23 | "i2c", 24 | "MIDI trs and USB (depending on build)"], 25 | notes: "Available as a kit to build a customised 16 chan midi controller.", 26 | longName: "16n", 27 | ), 28 | 29 | elementsDesc: ( 30 | key: \sl, 31 | shared: (elementType: \slider, midiMsgType: \control, 32 | midiChan: 0, spec: \midiCC, \ioType: \inout), 33 | elements: ( 34 | (1..16).collect { |num, i| (key: num.asSymbol, midiNum: 31 + num) } 35 | ) 36 | ) 37 | ); 38 | 39 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/_descFile_testCode.scd: -------------------------------------------------------------------------------- 1 | /************** defaultTestCode for desc files **************/ 2 | 3 | // test this desc file: 4 | // get name from file when code is within a desc file 5 | // ~thisdescname = thisProcess.nowExecutingPath.basename.split($.).first.postcs; 6 | // or set desc full or partial file name by hand: 7 | ~thisdescname = "descNameHere"; 8 | 9 | ~descs = MKtlDesc.loadDescs(~thisdescname); ~descs.size.postln; ~thisdesc = ~descs.first; 10 | // check web info for it 11 | ~thisdesc.web; 12 | 13 | // close old ~mktl, open new one, make gui: 14 | ~mktl.free; ~mktl = MKtl(\test, ~thisdescname); 15 | try { ~mgui.close }; ~mgui = ~mktl.gui.parent.alwaysOnTop_(true); 16 | 17 | ~mktl.trace; 18 | // -> wiggle all elements now on gui and/or device 19 | 20 | // give all elements an action: 21 | ~mktl.trace(false); 22 | ~mktl.elAt.action = { |el| 23 | "TEST: % has val %.\n".postf(el.name.cs, el.value.round(0.0001)); 24 | }; 25 | // -> AGAIN, now wiggle all elements now on gui and/or device 26 | 27 | 28 | // is the number of elements and hierarchy plausible? 29 | ~mktl.elementsDict.size; 30 | ~mktl.postElements; 31 | 32 | // can all elements can be set to random values? 33 | // (should post and appear on gui): 34 | ~mktl.elementsDict.do(_.valueAction_(1.0.rand)); ""; 35 | 36 | // do all outputElelements change on device and gui? 37 | ~mktl.outputElements.size; // how many are there? 38 | ~mktl.outputElements.do(_.valueAction_(1)); ""; // all on 39 | ~mktl.outputElements.do(_.valueAction_(0)); ""; // all off 40 | 41 | // when done, update test status! 42 | ~mktl.desc.openFile; 43 | 44 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/akai-lpk25.desc.scd: -------------------------------------------------------------------------------- 1 | /**** 2 | 3 | Akai LPK25 has one button that sends info: the sustain 4 | the other buttons change octaves up and down -> which notes are sent out; 5 | both up and down can be pressed multiple times to go further down or up. 6 | 7 | With program pressed and then selecting a different program with the right keys, you change the MIDI channel. 8 | 9 | And then there is an arpeggiator which allows to tap a tempo and then repeats note outputs. 10 | 11 | ****/ 12 | 13 | ( 14 | deviceName: "LPK25", 15 | protocol: 'midi', 16 | deviceType: 'midiKeyboard', 17 | elementTypes: [ \pianoKey ], 18 | status: ( 19 | linux: "tested and working. 24-05-2017 by nescivi", 20 | osx: "unknown", 21 | win: "unknown"), 22 | 23 | idInfo: "LPK25", 24 | 25 | hardwarePages: [0,1, 2, 3], 26 | 27 | testCode: { 28 | a = MKtl('lpk25', "akai-lpk25"); 29 | a.elAt(\prog,0,\pkey,64).action = { |el ... groups| 30 | [el.name, el.value, "groups:", groups.collect(_.name)].postln; 31 | }; 32 | }, 33 | 34 | deviceInfo: ( 35 | vendorURI: 'http://www.akaipro.com/product/lpk25', 36 | manualURI: 'http://www.akaipro.com/product/lpk25#downloads', 37 | // description: , 38 | features: [ 39 | "Velocity-sensitive mini-midiKeyboard", 40 | "Arpeggiator" 41 | ], 42 | longName: "Akai LPK25" 43 | // notes: , 44 | // hasScribble: false 45 | ), 46 | elementsDesc: ( 47 | key: \prog, 48 | shared: ( ioType: \in ), 49 | elements: (0..3).collect{ |page| 50 | ( 51 | shared: ('midiChan': page, page: page ), 52 | elements: [ 53 | ( 54 | key: \pkey, 55 | shared: ('elementType': 'pianoKey', groupType: \noteOnOff, spec: \midiVel), 56 | elements: (0..120).collect { |midinum,i| 57 | var pos = Piano.pos(midinum % 48, 0); 58 | ( 59 | key: i.asSymbol, 60 | shared: ( 61 | midiNum: midinum, 62 | style: ( 63 | row: (4 - (i div: 48 * 2)) + (pos.y * 0.9) + 1, 64 | column: pos.x + 1, 65 | color: pos.color, 66 | height: 1.2 67 | )), 68 | ); 69 | } 70 | ), 71 | ( 72 | key: 'sustain', 'midiNum': 64, 'midiMsgType': 'cc', 'elementType': 'button', 'spec': 'midiCC', 73 | style: ( row: 0, column: 0, height: 1) 74 | ) 75 | ] 76 | ); 77 | } 78 | ) 79 | ); 80 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/akai-mpd18.desc.scd: -------------------------------------------------------------------------------- 1 | /* MPD18 drumpad 2 | 3 | - TODO: 4 | 5 | 6 | one slider, sends on chan 1, ccnum, 1, [0, 127, \lin, 1, 0] 7 | 8 | channel aftertouch,sends on chan 0, [0, 127, \lin, 1, 0] 9 | 10 | 16 pads x 3 banks, pad 1, ... pad 16 11 | 12 | /****** TESTING ******/ 13 | 14 | MKtl.find; 15 | m = MKtl('mpd', "*mpd18"); // Akai MPD18 16 | m.gui; 17 | // slider 18 | m.elAt(\sl).addAction({ " slider YO!".postln; }); 19 | // touch on any pad 20 | m.elAt(\touch).addAction({ |el| [el.name, el.value.round(0.001)].postln }); 21 | 22 | // pad1 noteOn direct indexing 23 | m.elAt(\pad,0, 0).addAction({ |...args| "pad1 sc1 on YO !".postln; }); 24 | 25 | m.elAt.action = { |el| [el.name, el.value.round(0.001)].postln }; 26 | 27 | 28 | /****** END TESTs ******/ 29 | */ 30 | ( 31 | deviceName: "Akai MPD18", 32 | protocol: \midi, 33 | deviceType: \drumpad, 34 | elementTypes: [\pad, \slider], 35 | status: ( 36 | linux: "unknown", 37 | osx: "tested and working, 2016_03_13, adc", 38 | win: "unknown"), 39 | 40 | idInfo: "Akai MPD18", 41 | 42 | deviceInfo: ( 43 | vendorURI: "http://www.akaipro.com/product/mpd18", 44 | // manualURI: "", 45 | // description: "", 46 | features: [ 47 | "16 velocity and pressure-sensitive MPC pads", 48 | "1 fader" 49 | ], 50 | longName: "AKAI MPD18" 51 | 52 | ), 53 | elementsDesc: ( 54 | elements: [ 55 | ( 56 | key: \sl, 57 | midiMsgType: \cc, elementType: \slider, midiChan: 0, midiNum: 1, spec: \midiCC, 58 | style: (row: 1, column: 4, height: 3) 59 | ), 60 | ( 61 | key: \pad, 62 | shared: (elementType: \pad, midiChan: 0, spec: \midiVel, midiMsgType: \noteOn), 63 | elements: (36..83).clump(16).collect { |padNums, page| 64 | ( 65 | shared: (page: page, groupType: \noteOnOffTouch), 66 | elements: padNums.collect { |num, i| 67 | ( 68 | key: (i+1).asSymbol, 69 | shared: ( 70 | midiNum: num, 71 | style: (row: 3 - (i div: 4), column: (i % 4) 72 | ) 73 | ) 74 | ) 75 | } 76 | ) 77 | } 78 | ) 79 | ] 80 | ) 81 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/akai-mpkmini2.desc.scd: -------------------------------------------------------------------------------- 1 | // contributed by gil fuser 2 | 3 | /* 4 | * todo: 5 | * test on osx 6 | * add gui layout 7 | 8 | MKtl(\x, "*mpk*2").gui; 9 | MKtl(\x).trace; 10 | */ 11 | 12 | ( 13 | deviceName: "MPKmini2", 14 | protocol: 'midi', 15 | deviceType: 'midiKeyboard', 16 | elementTypes: [\knob, \key, \pad, \bend], 17 | status: ( 18 | linux: "tested and working", 19 | osx: "tested desc only, looks ok, no gui info yet. 2016-03-17, adc", 20 | win: "tested and working"), 21 | 22 | idInfo: "MPKmini2", 23 | 24 | // hardwarePages: [1, 2, 3, 4], 25 | 26 | deviceInfo: ( 27 | vendorURI: 'http://www.akaipro.com/product/mpk-mini-mkii', 28 | manualURI: 'http://6be54c364949b623a3c0-4409a68c214f3a9eeca8d0265e9266c0.r0.cf2.rackcdn.com/988/documents/MPK%20mini%20-%20User%20Guide%20-%20v1.0.pdf', 29 | features: [\pianoKey, \pad, \knob, \bender], 30 | notes: 31 | "Uses default presets.\n" 32 | "Every key was mapped, with the exception of ```Prog Change```.\n" 33 | "Accidental ```Prog-Change``` activation on the device needs to be reverted by ```Bank A/B```.\n" 34 | "For more flexibility use the [MPK MiniMKII editor](http://www.akaipro.com/product/mpk-mini-mkii#downloads).", 35 | longName: "AKAI MPKmini mk2" 36 | ), 37 | elementsDesc: ( 38 | elements: [ 39 | 40 | // ------- bend ------------ 41 | ( key: \bend, 42 | \midiChan: 0, \midiMsgType: \bend, \elementType: \bender, \spec: \midiBend 43 | ), 44 | // ------ pad ------------- 45 | ( 46 | key: \pad, 47 | shared: (\midiMsgType: \noteOnOff, \midiChan: 9, \spec: \midiCC, \elementType: \pad), 48 | elements: ((36..38)++(40)++(42..53)++(55)++(57)++(59..75)++(82)).collect { |i| 49 | (key: (i).asSymbol, \midiNum: i) 50 | } 51 | ), 52 | // ------ pad into button ------------- 53 | ( 54 | key: \bt, 55 | shared: (\midiMsgType: \cc, \elementType: \button, \spec: \midiCC ), 56 | elements: [ 57 | 58 | ( 59 | shared: (\midiChan: 0), 60 | elements: (28..35).collect { |i| 61 | (key: (i).asSymbol, \midiNum: i) 62 | } 63 | ), 64 | ( 65 | shared: (\midiChan: 9), 66 | elements: (0..16).collect { |i| 67 | (key: (i).asSymbol, \midiNum: i) 68 | } 69 | ), 70 | 71 | 72 | 73 | ] 74 | ), 75 | // ------ knob ------------- 76 | ( 77 | key: \kn, 78 | shared: (\midiMsgType: \cc, \elementType: \knob, 79 | \midiChan: 0, \spec: \midiCC), 80 | elements: ((1..8)++(16..27)).collect { |i, item| 81 | item = item + 1; 82 | (key: (i).asSymbol, \midiNum: i) 83 | } 84 | ), 85 | // ------ piano key ------------- 86 | ( 87 | key: \key, 88 | shared: (\midiMsgType: \noteOnOff, \elementType: \pianoKey, 89 | \midiChan: 0, \spec: \midiVel), 90 | elements: (0..120).collect { |i| 91 | (key: (i).asSymbol, \midiNum: i) 92 | } 93 | ), 94 | ] 95 | ) 96 | 97 | ); 98 | 99 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/alien-and-heath_xone2.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "XONE:K2", 3 | protocol: 'midi', 4 | deviceType: 'mixer', 5 | elementTypes: [ \fader, \button, \knob ], 6 | status: ( 7 | linux: "tested by bgola on 08.11.2023", 8 | osx: "tested by bgola on 08.11.2023", 9 | win: "unknown"), 10 | 11 | idInfo: "XONE:K2", 12 | hardwarePages: [0,1,2], 13 | 14 | deviceInfo: ( 15 | vendorURI: 'https://www.allen-heath.com/hardware/xone-series/xonek2/', 16 | ), 17 | elementsDesc: ( 18 | elements: [ 19 | ( 20 | key: \kn, 21 | shared: (\midiMsgType: 'cc', \midiChan: 0, \spec: \midiCC), 22 | elements: 3.collect{|idx| 23 | ((22*idx)..(22*idx+21)).collect { |midiNum, i| 24 | ( 25 | elementType: if(midiNum%22 < 16 or: {midiNum % 22 > 19}) { \knob } { \slider }, 26 | midiNum: midiNum, 27 | key: "cc_%".format(midiNum).asSymbol, 28 | style: ( 29 | row: if (midiNum % 22 > 19) { 13 } { (i/4).floor * 2 }, 30 | column: if (midiNum % 22 > 19) { (midiNum%22) - 19 } { i%4 }, 31 | height: 1, width: 1), 32 | page: idx 33 | ) 34 | } 35 | }.flatten, 36 | ), 37 | ( 38 | key: \bt, 39 | shared: (midiChan: 0, elementType: \pad, groupType: \noteOnOffTouch), 40 | elements: 3.collect{|idx| 41 | ((12..15) + (4 * idx)).collect {|midiNum, i| 42 | ( 43 | midiNum: midiNum, 44 | style: (row:(i/4).floor + 14, column: (i%4)), 45 | page: idx 46 | ) 47 | } ++ 48 | ((55..24) + (32 * idx)).collect {|midiNum, i| 49 | ( 50 | midiNum: midiNum, 51 | style: ( 52 | //row:(i/4).floor * 2 + 1, 53 | row: if (i < (5*4)) { (i/4).floor * 2 + 1 } { (i/4).floor + 5 }, 54 | column: ( ((i%4) - 3).abs ) 55 | ), 56 | page: idx 57 | ) 58 | } 59 | }.flatten; 60 | ), 61 | ] 62 | ) 63 | ); 64 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/allen-and-heath_xone2.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "XONE:K2", 3 | protocol: 'midi', 4 | deviceType: 'mixer', 5 | elementTypes: [ \fader, \button, \knob ], 6 | status: ( 7 | linux: "tested by bgola on 08.11.2023", 8 | osx: "tested by bgola on 08.11.2023", 9 | win: "unknown"), 10 | 11 | idInfo: "XONE:K2", 12 | hardwarePages: [0,1,2], 13 | 14 | deviceInfo: ( 15 | vendorURI: 'https://www.allen-heath.com/hardware/xone-series/xonek2/', 16 | ), 17 | elementsDesc: ( 18 | elements: [ 19 | ( 20 | key: \kn, 21 | shared: (\midiMsgType: 'cc', \midiChan: 0, \spec: \midiCC), 22 | elements: 3.collect{|idx| 23 | ((22*idx)..(22*idx+21)).collect { |midiNum, i| 24 | ( 25 | elementType: if(midiNum%22 < 16 or: {midiNum % 22 > 19}) { \knob } { \slider }, 26 | midiNum: midiNum, 27 | key: "cc_%".format(midiNum).asSymbol, 28 | style: ( 29 | row: if (midiNum % 22 > 19) { 13 } { (i/4).floor * 2 }, 30 | column: if (midiNum % 22 > 19) { (midiNum%22) - 19 } { i%4 }, 31 | height: 1, width: 1), 32 | page: idx 33 | ) 34 | } 35 | }.flatten, 36 | ), 37 | ( 38 | key: \bt, 39 | shared: (midiChan: 0, elementType: \pad, groupType: \noteOnOffTouch), 40 | elements: 3.collect{|idx| 41 | ((12..15) + (4 * idx)).collect {|midiNum, i| 42 | ( 43 | midiNum: midiNum, 44 | style: (row:(i/4).floor + 14, column: (i%4)), 45 | page: idx 46 | ) 47 | } ++ 48 | ((55..24) + (32 * idx)).collect {|midiNum, i| 49 | ( 50 | midiNum: midiNum, 51 | style: ( 52 | //row:(i/4).floor * 2 + 1, 53 | row: if (i < (5*4)) { (i/4).floor * 2 + 1 } { (i/4).floor + 5 }, 54 | column: ( ((i%4) - 3).abs ) 55 | ), 56 | page: idx 57 | ) 58 | } 59 | }.flatten; 60 | ), 61 | ] 62 | ) 63 | ); 64 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/behringer-motor_61.desc.scd: -------------------------------------------------------------------------------- 1 | /* behringer-motor_61.desc.scd 2 | The Behringer motor 61 has the exact same midi implementation 3 | as the motor 49, so rather than a hard-tomaintain copied file, 4 | we load its desc file here, and make the few changes needed 5 | in the loaded dict. 6 | */ 7 | 8 | var descDict = "behringer-motor_49.desc.scd".loadRelative[0]; 9 | descDict.idInfo.deviceName = "MOTÖR61 Keyboard"; 10 | descDict.deviceInfo.longName = "Behringer MOTÖR61 Keyboard"; 11 | descDict.deviceInfo.features.do(_.replace("49", "61")); 12 | 13 | descDict; 14 | 15 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/cinematix-wheel.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | CinematixWheel has a special midi scheme, which this desc file shows how to use. 3 | 4 | It has 16 rotating wheels, with possible 1023 position values. 5 | The encoding is in midi noteOn messages: 6 | the 7 bits of velocity are the crude wheel position value, 7 | the first 3 bits of note number are extra precision of value, 8 | and the last 4 bits of note number are the wheel index. 9 | 10 | // test as MKtl 11 | c = MKtl(\cin, "cinematix-wheel"); 12 | 13 | MKtlDesc.loadDescs.size; 14 | MKtlDesc.writeCache; 15 | g = c.trace.gui; 16 | c.elAt.action = { |el| "action! %, %, %\n".postf(el.source, el.name.cs, el.value.round(0.001)) }; 17 | 18 | c.elAt(0).deviceValueAction_(1023); 19 | 10.do { c.elAt(16.rand).deviceValueAction_(1024.rand); }; 20 | 21 | // the coding given in modality issues #177 seems to be : 22 | // 7 bits of value are crude wheel position value, 23 | // first 3 bits of note are extra precision, 24 | // last 4 bits of note are wheel index. 25 | ( 26 | MIDIdef.noteOn( \cinWheel, { |val, note| 27 | var wheelIndex, value; 28 | wheelIndex = note mod: 16; 29 | value = val << 3 + (note >> 4); 30 | (wheel: wheelIndex, value: value).postln; 31 | c.elAt(wheelIndex).deviceValueAction_(value); 32 | }); 33 | ); 34 | 35 | MIDIClient.init; 36 | // test that coding is correct: 37 | 38 | // access wheels, last 4 bits of note 39 | MIDIIn.noteOn.value(0, 0, 0, 0); // action: '1', 0 40 | MIDIIn.noteOn.value(0, 0, 1, 0); // action: '2', 0 41 | MIDIIn.noteOn.value(0, 0, 15, 0); // action: '16', 0 42 | 43 | // higher 3 bits of note go to low bits of value 44 | MIDIIn.noteOn.value(0, 0, 16, 0); // action: '1', 0.001 45 | // wheel 4, value 2 46 | MIDIIn.noteOn.value(0, 0, 32 + 4, 0); // action: '5', 0.002 47 | MIDIIn.noteOn.value(0, 0, 112 + 15, 16); // action: '16', 0.132 48 | 49 | // velocity sets higher bits of value 50 | MIDIIn.noteOn.value(0, 0, 0, 1); 51 | MIDIIn.noteOn.value(0, 0, 32, 64); 52 | MIDIIn.noteOn.value(0, 0, 127, 127); 53 | MIDIIn.noteOn.value(0, 0, 15, 64); 54 | 55 | // wheel is 0-15, val = 0 - 1023 56 | ~fakeCinIn = { |wheel, val| 57 | var note = (val % 8 * 16) + wheel; 58 | var velval = val div: 8; 59 | MIDIIn.noteOn.value(nil, nil, note, velval); 60 | (note: note, vel: velval).postln; 61 | }; 62 | 63 | ~fakeCinIn.(0, 1016); 64 | ~fakeCinIn.(0, 512); 65 | ~fakeCinIn.(4, 512); 66 | 67 | ~fakeCinIn.(*[16.rand, 1023.rand].postln); 68 | */ 69 | 70 | ( 71 | idInfo: "CinematixWheel", 72 | protocol: \virtual, 73 | specs: ('lin11bit': [0, 1024, \lin, 1]), 74 | elementsDesc: ( 75 | shared: (elementType: \slider, spec: 'lin11bit'), 76 | elements: (1..16).collect { |num| (key: num.asSymbol) } 77 | ), 78 | ); 79 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/cthrumusic-axis49.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | m.free; m = MKtl(\ax, "*axis49"); m.gui; 3 | 4 | 5 | 6 | // gui layout sketch: 7 | // I don't know what the real midi numbers on each pad, 8 | // so this is just for making the pads geometrically 9 | 10 | w = Window("axis49 - selfless layout, 98 keys in columns", 11 | Rect(0,0,600,320) 12 | ).front; 13 | 14 | b = (1..98).clump(7).collect { |col, i| 15 | var width = 40, angle = 0.0; 16 | col.collect { |num, j| 17 | var left = i * width * 0.8; 18 | var rightHalfShift =(i >= 7 and: i.even).binaryValue; 19 | var top = 6 - (j % 7) + (i % 2 * 0.5) + rightHalfShift * width; 20 | 21 | MHexPad(w, Rect(left, top, width, width)) 22 | .angle_(0.0) 23 | .label_(num); 24 | }; 25 | }; 26 | 27 | // 49key doubled layout 28 | // - how to create two hexpads for each doubled key? 29 | // it's a one-off case, so better put it in its own class or extra sketch. 30 | 31 | 32 | 33 | */ 34 | ( 35 | deviceName: "AXIS-49 USB Keyboard", 36 | protocol: 'midi', 37 | deviceType: 'midiKeyboard', 38 | elementTypes: [ \keys ], 39 | status: ( 40 | linux: "unknown", 41 | osx: "unknown", 42 | win: "unknown"), 43 | 44 | idInfo: "AXIS-49 USB Keyboard", 45 | 46 | deviceInfo: ( 47 | vendorURI: 'http://www.c-thru-music.com/cgi/?page=prod_axis-49', 48 | manualURI: 'http://www.c-thru-music.com/cgi/?page=spec-49', 49 | description: "Velocity sensitive MIDI keyboard with Harmonic Table note arrangement.", 50 | // features: [], 51 | // notes: , 52 | longName: "C-THRU MUSIC AXiS-49" 53 | ), 54 | status: ( 55 | win: "unknown", 56 | osx: "tested and working, 15.3.2016, LFsaw.de, gui not done yet.", 57 | linux: "unknown" 58 | ), 59 | elementsDesc: ( 60 | shared: ( 61 | 'midiChan': 0, 62 | 'elementType': 'pad', // or maybe hexPad. 63 | 'spec': 'midiNote', 64 | midiMsgType: \noteOn, 65 | groupType: \noteOnOff 66 | ), 67 | elements: (0..120).collect { 68 | |num, i| 69 | (key: num.asSymbol, midiNum: num, 70 | // at least show them all 71 | shared: (style: (column: i % 12, row: i div: 12)) 72 | ) 73 | } 74 | ) 75 | ); 76 | 77 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/decampo-joybox.desc.scd: -------------------------------------------------------------------------------- 1 | /* Example for a custom-made MIDI hardware device and its elements. 2 | 3 | The joybox is made from a Doepfer pocket electronics kit, 4 | which can send 16 midi controls, 5 | and 8 XY joysticks in a 4x2 layout (see gui). 6 | 7 | // basic example: 8 | // make an Mktl for it - will say that this is virtual only 9 | MKtl(\jbx, "decampo-joybox"); MKtl(\jbx).gui; 10 | 11 | // MKtl has as idInfo : "EXTERNAL", which is not an existing port name. 12 | MKtl(\jbx).desc.fullDesc.at(\idInfo); 13 | 14 | // So we need to tell it which midi hardware interface we connect it to. 15 | 16 | // See which MIDI sources are available: 17 | MIDIClient.sources; 18 | 19 | // figure out which one you want to use, 20 | // and open it via the port name as idInfo: 21 | MKtl(\jbx).openDeviceVia("IAC Driver"); 22 | 23 | */ 24 | 25 | ( 26 | 27 | deviceName: "joybox", 28 | protocol: \midi, 29 | deviceType: \faderbox, 30 | elementTypes: [\fader], 31 | status: ( 32 | linux: "unknown", 33 | osx: "tested and working. 2016-03-16, adc", 34 | win: "unknown"), 35 | 36 | idInfo: "EXTERNAL", 37 | 38 | deviceInfo: ( 39 | vendorURI: "http://www.doepfer.de/pe.htm", 40 | manualURI: "http://www.doepfer.de/pdf/PE_manual.pdf", 41 | description: "Controller box with 8 XY joysticks, made w doepfer pocket-electronics kit.", 42 | features: ["8 XY joysticks"], 43 | longName: "de Campo's JoyBox" 44 | ), 45 | 46 | elementsDesc: ( 47 | key: \joy, 48 | shared: (elementType: \joyAxis, midiMsgType: \control, 49 | spec: \midiCC, midiChan: 0, ioType: \in), 50 | elements: (0..7).collect { |i| 51 | ( 52 | key: (i+1).asSymbol, 53 | elements: [ 54 | (key: \x, midiNum: i * 2, 55 | style: (height: 2, row: i div: 4 * 2, column: i % 4 * 2)), 56 | (key: \y, midiNum: i * 2 + 1, 57 | style: (height: 2, row: i div: 4 * 2, column: i % 4 * 2 + 0.85)) 58 | ] 59 | ); 60 | } 61 | ) 62 | ); 63 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/doepfer-pocketfader.desc.scd: -------------------------------------------------------------------------------- 1 | ///////// Example for an external MIDI device /////// 2 | /* 3 | Doepfer Pocket fader box has 16 sliders. 4 | 5 | // For a usage example, see 6 | "Tutorials/Connecting_external_MIDI_devices".openHelpFile; 7 | 8 | thisProcess.nowExecutingPath.basename.split($.).first.postcs; 9 | 10 | d = MKtlDesc.loadDescs("*pocketfader").first; 11 | m.free; m = MKtl(\pf, "*pocketfader").trace.gui; 12 | 13 | */ 14 | 15 | ( 16 | deviceName: "pocketfader", 17 | protocol: \midi, 18 | deviceType: \faderbox, 19 | elementTypes: [\fader], 20 | status: ( 21 | linux: "unknown", 22 | osx: "tested and working. 2016-03-16, adc", 23 | win: "unknown"), 24 | 25 | idInfo: "EXTERNAL", 26 | 27 | deviceInfo: ( 28 | vendorURI: "http://www.doepfer.de/pe.htm", 29 | manualURI: "http://www.doepfer.de/pdf/PE_manual.pdf", 30 | description: "MIDI faderbox (discontinued)", 31 | features: [ 32 | "hardware midi plugs (no USB)", 33 | "MIDI configurable by DIP switches."], 34 | notes: "Available as a kit to build a customised 16 chan midi controller.", 35 | longName: "Doepfer Pocketfader", 36 | ), 37 | 38 | elementsDesc: ( 39 | key: \sl, 40 | shared: (elementType: \slider, midiMsgType: \control, 41 | midiChan: 0, spec: \midiCC, \ioType: \inout), 42 | elements: ( 43 | (1..16).collect { |num, i| (key: num.asSymbol, midiNum: i) } 44 | ) 45 | ) 46 | ); 47 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/eowave-ribbon.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | needs initialisation messages for different configs, 3 | see github/modality #24 4 | MKtlDesc.loadDescs("eowave*"); 5 | m.free; m = MKtl(\eo, "eowave*").trace.gui; 6 | */ 7 | 8 | ( 9 | deviceName: "The Ribbon", 10 | protocol: \midi, 11 | deviceType: \ribbon, 12 | elementTypes: [\ribbon, \pad], 13 | status: ( 14 | linux: "unknown", 15 | osx: "unknown", 16 | win: "unknown"), 17 | 18 | idInfo: "The Ribbon", 19 | 20 | deviceInfo: ( 21 | vendorURI: "http://www.eowave.com/instruments.php?prod=78", 22 | manualURI: "http://www.eowave.com/downloads/pdf/EowaveRibbon_manual.pdf", 23 | // description: "", 24 | features: [ 25 | "pressure and postition sensitive ribbon", 26 | "capacitive sensing touch field", 27 | ], 28 | longName: "EOWave Ribbon (v.1)" 29 | // notes: "", 30 | 31 | ), 32 | 33 | elementsDesc: ( 34 | shared: (midiChan: 0), 35 | elements: [ 36 | // ribbon 37 | // [ noteOn, val, 127, num, 78, chan, 0, src, -1477523613 ] 38 | // [ noteOff, val, 0, num, 78, chan, 0, src, -1477523613 ] 39 | ( 40 | key: \rib, 41 | shared: (elementType: \pad, groupType: \noteOnOffBut, spec: \midiBut), 42 | elements: (36..82).collect {|num, i| 43 | (key: i, midiNum: num, shared: 44 | (style: (row: 0, column: i * 0.8, height: 1.5, width: 0.9, 45 | showLabel: true, label: i))) 46 | } 47 | ), 48 | ( 49 | key: \bend, 50 | midiMsgType: \bend, elementType: \slider, midiNum: 0, spec: \midiBend, 51 | style: (row: 1.5, column: 3, width: 4, height: 1) 52 | ), 53 | ( 54 | key: \pressure, 55 | midiMsgType: \cc, elementType: \slider, midiNum: 7, spec: \midiCC, 56 | style: (row: 1.5, column: 8, width: 4, height: 1) 57 | ), 58 | // expressionPad 59 | // [ cc, val, 0, num, 1, chan, 0, src, -1477523613 ] 60 | ( 61 | key: \pad, 62 | midiMsgType: \cc, elementType: \pad, midiNum: 1, spec: \midiCC, 63 | style: (row: 1.5, column: 0, width: 2) 64 | ) 65 | ] 66 | ) 67 | ) 68 | 69 | 70 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/evolution-ucontrol-uc33.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "UC-33 USB MIDI Controller", 3 | protocol: \midi, 4 | deviceType: \faderbox, 5 | \elementTypes: [\slider, \knob, \button], 6 | status: ( 7 | linux: "unknown", 8 | osx: "tested desc, looks ok. 2016-03-24, adc. transport buttons can be added when cc numbers are known.", 9 | win: "unknown"), 10 | 11 | idInfo: "UC-33 USB MIDI Controller", 12 | 13 | deviceInfo: ( 14 | // vendorURI: "", 15 | // manualURI: "", 16 | // description: "", 17 | // features: (), 18 | notes: "desc is for preset3 only", 19 | longName: "Evolution U-Control UC33" 20 | ), 21 | 22 | // preset 3 only, transport buttons missing: 23 | elementsDesc: ( 24 | shared: (midiMsgType: \cc, midiChan: 0, ioType: \in), 25 | elements: [ 26 | ( 27 | key: \kn, 28 | shared: (elementType: \knob, spec: \midiCC), 29 | elements: #[ 30 | [45, 46, 47, 5, 80, 81, 82, 83], 31 | [50, 51, 55, 87, 75, 76, 77, 78], 32 | [20, 21, 40, 43, 70, 71, 72, 73] 33 | ].collect { |row| 34 | ( 35 | elements: row.collect {|num, i| (midiNum: num) } 36 | ) 37 | } 38 | ), 39 | ( 40 | key: \sl, 41 | shared: ( elementType: \slider, spec: \midiCC), 42 | elements: #[105, 106, 107, 108, 110, 111, 112, 116, 26] 43 | .collect {|num, i| (midiNum: num) } 44 | ), 45 | ( 46 | key: \bt, 47 | shared: (elementType: \button, mode: \toggle, spec: \midiBut), 48 | elements: #[57, 52, 53, 54, 41, 42, 44, 22, 23, 24].collect {|num, i| 49 | (midiNum: num) 50 | } 51 | ), 52 | // ( 53 | // key: \tr, 54 | // shared: (elementType: \button, mode: \toggle, spec: \midiBut), 55 | // elements: [ 56 | // (key: \stop, midiNum: ?), 57 | // (key: \play, midiNum: ?), 58 | // (key: \fwd, midiNum: ?), 59 | // (key: \rew, midiNum: ?) 60 | // ] 61 | // ) 62 | ] 63 | ) 64 | ) 65 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/faderfox-pc12.desc.scd: -------------------------------------------------------------------------------- 1 | /**** Faderfox PC12 desc file. superpeachman 2020_11_08 2 | 3 | // General Info: 4 | 5 | PC12 as successor of the PC44 is a universal controller for all kinds of Midi controllable hard- and software. 6 | 7 | 8 | ****/ 9 | 10 | ( 11 | deviceName: "PC12", 12 | protocol: \midi, 13 | deviceType: \faderbox, 14 | elementTypes: [\button, \knob], 15 | status: ( 16 | linux: "unknown", 17 | osx: "unknown", 18 | win: "tested and working, 01.12.2020 by superpeachman"), 19 | 20 | idInfo: "Faderfox PC12", 21 | 22 | deviceInfo: ( 23 | vendorURI: "http://faderfox.de/pc12.html", 24 | manualURI: "http://www.faderfox.de/PDF/PC12%20Manual%20V04.pdf", 25 | hardwarePages: (1..31), 26 | longName: "Faderfox PC12", 27 | ), 28 | 29 | elementsDesc: ( 30 | elements: 31.collect { |chan| 31 | ( 32 | key: ("B" ++ chan).asSymbol, 33 | shared: ('midiChan': chan, \page: chan), 34 | elements: [ 35 | ( 36 | key: \kn, 37 | shared: ( 38 | 'midiMsgType': \cc, 39 | 'midiChan': chan, 40 | 'elementType': \knob, 41 | 'spec': \midiCC 42 | 43 | ), 44 | elements: (1..72).clump(12).collect { |list, gr_i| 45 | ( 46 | key: (["A","B","C","D","E","F"].at(gr_i)).asSymbol, 47 | elements: list.collect { |num, i| 48 | ( 49 | // key: i, 50 | midiNum: num, 51 | \style: ( \row: gr_i, \column: i, height: 0) 52 | ) 53 | } 54 | ) 55 | } 56 | ), 57 | ( 58 | key: \bt, 59 | shared: ( 60 | 'midiMsgType': \cc, 61 | 'midiChan': chan, 62 | 'elementType': \button, 63 | 'spec': \midiCC 64 | 65 | ), 66 | elements: (73..84).collect{|num, i| 67 | ( 68 | midiNum: num, 69 | label: i + 1, 70 | style: ( 71 | row: 6, 72 | column: i 73 | ) 74 | ) 75 | } 76 | ), 77 | ( 78 | key: \vkn, 79 | shared: ( 80 | 'midiMsgType': \cc, 81 | 'midiChan': chan, 82 | 'elementType': \knob, 83 | 'spec': \midiCC 84 | 85 | ), 86 | elements: [( 87 | midiNum: 85, 88 | label: "Val Knob", 89 | style: ( 90 | row: 3, 91 | column: 12 92 | ) 93 | )] 94 | 95 | ), 96 | ( 97 | key: \vbt, 98 | shared: ( 99 | 'midiMsgType': \cc, 100 | 'midiChan': chan, 101 | 'elementType': \pad, 102 | 'spec': \midiCC 103 | 104 | ), 105 | elements: [( 106 | midiNum: 86, 107 | label: "Click Knob", 108 | style: ( 109 | row: 2, 110 | column: 12 111 | ) 112 | )] 113 | 114 | ) 115 | ] // elements: 116 | ) 117 | }; 118 | 119 | ) 120 | ) 121 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/faderfox-pc4.desc.scd: -------------------------------------------------------------------------------- 1 | /**** Faderfox PC4 desc file. 2 | 3 | // General Info: 4 | 5 | PC4 is a 24-knob pot controller. 6 | 7 | 8 | ****/ 9 | 10 | ( 11 | deviceName: "PC4", 12 | protocol: \midi, 13 | deviceType: \faderbox, 14 | elementTypes: [\knob], 15 | status: ( 16 | linux: "unknown", 17 | osx: "tested and working", 18 | win: "unknown" 19 | ), 20 | 21 | idInfo: "Faderfox PC4", 22 | 23 | deviceInfo: ( 24 | vendorURI: "http://faderfox.de/pc12.html", 25 | manualURI: "http://www.faderfox.de/PDF/PC4%20Manual%20V03.PDF", 26 | hardwarePages: (1..16), 27 | longName: "Faderfox PC4", 28 | ), 29 | 30 | elementsDesc: ( 31 | elements: 15.collect { |chan| 32 | ( 33 | key: ("B" ++ chan).asSymbol, 34 | shared: ('midiChan': chan, \page: chan), 35 | elements: [ 36 | ( 37 | key: \kn, 38 | shared: ( 39 | 'midiMsgType': \cc, 40 | 'midiChan': chan, 41 | 'elementType': \knob, 42 | 'spec': \midiCC 43 | 44 | ), 45 | elements: (1..24).collect { |knob_num| 46 | ( 47 | key: knob_num.asSymbol, 48 | midiNum: knob_num, 49 | ) 50 | } 51 | ), 52 | ] 53 | ) 54 | }; 55 | ) 56 | ) 57 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/faderfox_ec4/SE01-allGroupsCCAh.syx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModalityTeam/Modality-toolkit/c5181893857e1e3b93295001584e3af8113d64da/Modality/MKtlDescriptions/faderfox_ec4/SE01-allGroupsCCAh.syx -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/faderfox_ec4/faderfox_ec4.desc.scd: -------------------------------------------------------------------------------- 1 | /**** 2 | 3 | Description file for Faderfox EC4. 4 | 5 | For general information, see parent description file 6 | MKtlDesc.findFile(MKtlDesc.at(\faderfox_ec4).fullDesc[\parentDesc], fileExt: MKtlDesc.parentExt).first.openDocument 7 | 8 | This implements 14bit mode for all 16 groups of the Faderfox EC4 as it is set for "Setup 01" in the sysex file "SE01-allGroupsCCAh.syx" next to this document. 9 | Load the SE01 of the sysex file to a setup of your choosing on your EC4 via 10 | https://www.privatepublic.de/faderfox-editor/ec4/ 11 | https://github.com/privatepublic-de/faderfox-editor 12 | and make sure to select it on the EC4. 13 | 14 | Also, it implements hat presses that can be set on the EC4 by "Setup">"Push:Note". 15 | 16 | 17 | 18 | ****/ 19 | 20 | 21 | ( 22 | parentDesc: "faderfox_ec4", 23 | 24 | status: ( 25 | linux: "unknown", 26 | osx: "unknown", 27 | win: "unknown" 28 | ), 29 | 30 | elementsDesc: 31 | // ------ cc ------------- 32 | ( 33 | shared: (ioType: \inout), 34 | 35 | elements: (0..15).collect{|midiChan| 36 | ( 37 | key: "GR%".format((100 + midiChan+1).asString[1..]).asSymbol, 38 | shared: (midiChan: midiChan, page: midiChan), 39 | 40 | 41 | elements: [ 42 | ( 43 | key: \kn, // group name 44 | shared: (elementType: \knob, midiMsgType: \cc14, spec: \midiCC14), 45 | elements: (0..15).collect {|midiNum, i| 46 | ( midiNum: midiNum, 47 | style: (row: i div: 4 , column: (i % 4) * 2, width: 1, height: 1) 48 | ) 49 | } 50 | ), 51 | ( 52 | key: \hat, // group name 53 | shared: (elementType: \button, groupType: \noteOnOffBut), 54 | elements: (0..15).collect {|midiNum, i| 55 | ( midiNum: midiNum, 56 | style: (row: i div: 4 , column: (i % 4) * 2 + 1, width: 1, height: 1) 57 | ) 58 | 59 | } 60 | ) 61 | ] 62 | ) 63 | } 64 | ) 65 | ); 66 | 67 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/faderfox_ec4/faderfox_ec4.parentDesc.scd: -------------------------------------------------------------------------------- 1 | /**** 2 | 3 | Parent description file for Faderfox EC4 with all general information 4 | 5 | ****/ 6 | 7 | 8 | ( 9 | deviceName: "Faderfox EC4", 10 | protocol: 'midi', 11 | deviceType: 'controller', 12 | elementTypes: [ \knob ], 13 | idInfo: "Faderfox EC4", 14 | hardwarePages: (1..16), 15 | 16 | deviceInfo: ( 17 | vendorURI: "http://faderfox.de/ec4.html", 18 | manualURI: "http://www.faderfox.de/PDF/EC4%20Manual%20V02.pdf", 19 | features: [ 20 | "Universal controller for all kinds of midi controllable hard- and software", 21 | "iPad compatible", 22 | "Control surface script for Ableton Live is shipped with the controller", 23 | "USB interface - class compliant / bus powered / no driver necessary (consumption < 500mW)", 24 | "MIDI in and out ports by 3.5mm minijack sockets type B with routing and merge functionality", 25 | "16 gridless push encoders - resolution = 36 pulses per revolution", 26 | "Encoder push buttons can send separate commands", 27 | "4 x 20 character OLED-display to show control values (numeric/bar), names and programming data", 28 | "Names for encoders, groups and setups are editable (4 characters per name)", 29 | "14 bit high resolution encoder mode for sensitive parameters", 30 | "Programmable value ranges with min/max values", 31 | "Data feedback avoid value jumps", 32 | "All encoders fully programmable in the device by channel, type, number, mode, name etc.", 33 | "Different command types like control change (CC), pitch bend, NRPN, program change and notes", 34 | "Advanced programming functions like copy, paste and fill", 35 | "16 independent groups per setup for 16 encoders (256 commands per setup)", 36 | "Learn function for fast assignment to incoming MIDI commands", 37 | "16 setups with backup/restore function contain all controller settings incl. names", 38 | "Very compact design in a black casing (180x105x70 mm, 350 g)", 39 | ], 40 | longName: "FaderFox EC4", 41 | notes: "All controls fully programmable in the device by channel, type, number.\n" 42 | "Absolute and relative controller modes, and 14 bit high resolution encoder mode for sensitive parameters.\n" 43 | "Programmable value ranges with min/max values, and data feedback for encoders and faders avoid value jumps.\n" 44 | "MIDI in and out ports feature routing and merge functionality." 45 | ) 46 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/faderfox_ec4/faderfox_ec4_basic.desc.scd: -------------------------------------------------------------------------------- 1 | /**** 2 | Description file for Faderfox EC4 default setting, 3 | where encoders only send 7bit cc messages. 4 | For 14bit, see and use ec4.desc.scd! 5 | 6 | For general information, see parent description file: 7 | Document.open("faderfox_ec4.parentDesc.scd".resolveRelative); 8 | ****/ 9 | 10 | ( 11 | parentDesc: "faderfox_ec4", 12 | 13 | status: ( 14 | linux: "unknown", 15 | osx: "unknown", 16 | win: "unknown" 17 | ), 18 | 19 | elementsDesc: 20 | // ------ cc ------------- 21 | ( 22 | shared: (ioType: \inout), 23 | 24 | elements: (0..15).collect{|midiChan| 25 | ( 26 | key: "GR%".format((100 + midiChan+1).asString[1..]).asSymbol, 27 | shared: (midiChan: midiChan, page: midiChan), 28 | 29 | elements: [ 30 | ( 31 | key: \kn, // group name 32 | shared: (elementType: \knob, midiMsgType: \cc, spec: \midiCC), 33 | elements: (0..15).collect {|midiNum, i| 34 | ( midiNum: midiNum, 35 | style: (row: i div: 4 , column: (i % 4) * 2, width: 1, height: 1) 36 | ) 37 | } 38 | ), 39 | ( 40 | key: \hat, // group name 41 | shared: (elementType: \button, groupType: \noteOnOffBut), 42 | elements: (0..15).collect {|midiNum, i| 43 | ( midiNum: midiNum, 44 | style: (row: i div: 4 , column: (i % 4) * 2 + 1, width: 1, height: 1) 45 | ) 46 | 47 | } 48 | ) 49 | ] 50 | ) 51 | } 52 | ) 53 | ); 54 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/faderfox_uc4/faderfox-uc4.parentDesc.scd: -------------------------------------------------------------------------------- 1 | /**** Faderfox UC4 desc file. adc 2015_12_24 (merry xmas) 2 | 3 | // General Info: 4 | 5 | The UC4 is a compact, ultraflexible general purpose MIDI controller. 6 | It has 9 sliders, 8 knobs with 8 pushbuttons and 8 normal buttons; 7 | by switching between 8 groups one has 264 controls in one setup, 8 | and 16 presets of 8 groups each allow very flexible uses. 9 | 10 | It has multiple descs for different orderings of pages and elements: 11 | faderfox_uc4: 12 | \xfader 13 | \sl [slpage][column] 14 | \bt [slpage][column] 15 | // and on a different page 16 | \kn [knpage][index], 17 | \pbt [knpage][index] 18 | 19 | faderfox_uc4_pg has the pages in the top layer: 20 | \xfader 21 | slpg1 [\sl, \bt][column] 22 | ... 23 | slpg8 [\sl, \bt][column] 24 | 25 | knpg1 [\kn, \pbt][index] 26 | ... 27 | knpg8 [\kn, \pbt][index] 28 | 29 | ****/ 30 | 31 | ( 32 | deviceName: "Faderfox UC4", 33 | protocol: 'midi', 34 | deviceType: 'faderbox', 35 | elementTypes: [ \slider, \knob, \button, \pushbutton ], 36 | 37 | idInfo: "Faderfox UC4", 38 | 39 | hardwarePages: (1..8), 40 | 41 | deviceInfo: ( 42 | vendorURI: "http://faderfox.de/uc4.html", 43 | manualURI: "http://www.faderfox.de/PDF/UC4%20Manual%20V01.PDF", 44 | features: [ 45 | "Compact design with metal faceplate" 46 | "8 push encoders without detents (resolution about 36 pulses)", 47 | "9 30mm faders, programmable snap function", 48 | "8 Buttons with LED for switchable parameters", 49 | "8 independent groups for encoders and faders/buttons", 50 | "About 264 commands per setup (33 controls x 8 groups)", 51 | "18 setups with backup/restore function contain all controller settings", 52 | ], 53 | longName: "FaderFox UC4", 54 | notes: "All controls (incl. push buttons) fully programmable in the device by channel, type, number.\n" 55 | "Absolute and relative controller modes, and 14 bit high resolution encoder mode for sensitive parameters.\n" 56 | "Programmable value ranges with min/max values, and data feedback for encoders and faders avoid value jumps.\n" 57 | "MIDI in and out ports feature routing and merge functionality." 58 | ) 59 | ); 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/generic-mouse.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | idInfo: "Generic Mouse", 3 | protocol: 'hid', 4 | deviceType: \mouse, 5 | elementTypes: [\mouseAxis, \button, \mouseWheel], 6 | status: ( 7 | linux: "unknown", 8 | osx: "tested and working. 2016-03-24, adc", 9 | win: "unknown"), 10 | 11 | deviceName: "Generic Mouse", 12 | 13 | deviceInfo: ( 14 | vendorURI: "too many to list", 15 | // manualURI: "", 16 | features: [ 17 | "2 mouse axes", 18 | "3 buttons", 19 | "scroll wheel", 20 | ], 21 | longName: "Generic Mouse", 22 | notes: "", 23 | // hasScribble: false 24 | ), 25 | elementsDesc: ( 26 | // --------- input elements ---------- 27 | elements: [ 28 | ( 29 | key: \bt, 30 | shared: (hidUsagePage: 9, elementType: \button, ioType: 'in', spec: \hidBut), 31 | elements: [ 32 | (key: \left, hidUsage: 1), 33 | (key: \right, hidUsage: 2), 34 | (key: \middle, hidUsage: 3) 35 | ] 36 | ), 37 | ( 38 | key: \x, 39 | hidUsage: 48, hidUsagePage: 1, elementType: \mouseAxis, 40 | ioType: 'in', spec: \mouseAxis, valueType: \relative 41 | ), 42 | ( 43 | key: \y, 44 | hidUsage: 49, hidUsagePage: 1, elementType: \mouseAxis, 45 | ioType: 'in', spec: \mouseAxis, valueType: \relative 46 | ), 47 | ( 48 | key: \wheel, 49 | hidUsage: 56, hidUsagePage: 1, elementType: \mouseWheel, 50 | ioType: 'in', spec: \mouseAxis, valueType: \relative 51 | ) 52 | ] 53 | ) 54 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/icon-icontrols-101.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is for the older version of the iCon iControls, 4 | which registers as "iCON iControl V1.01". 5 | 6 | // tests: 7 | MKtlDesc.loadDescs; 8 | m.free; m = MKtl(\ic, "*icontrols-101"); m.trace.gui; 9 | 10 | m.elAt.action = { |el| el.name.postln }; 11 | m.elementsDict.size; 12 | m.postElements; 13 | */ 14 | 15 | ( 16 | deviceName: "iCON iControl V1.01", 17 | 18 | idInfo: (deviceName: "iCON iControl V1.01", srcPortIndex: 0, destPortIndex: 0), 19 | 20 | status: ( 21 | linux: "unknown", 22 | osx: "tested and working, adc, 2016-05-05.", 23 | win: "unknown"), 24 | 25 | protocol: \midi, 26 | deviceType: \faderbox, 27 | elementTypes: [\fader, \knob, \button], 28 | 29 | deviceInfo: ( 30 | vendorURI: "http://icon-global.com/product/icontrols/", 31 | manualURI: "http://support.icon-global.com/hc/en-us/articles/216201337-iControls", 32 | description: "For older version of the iCon iControls registering as \"iCON iControl V1.01\".", 33 | // features: [], 34 | notes: "" 35 | // hasScribble: false 36 | ), 37 | 38 | 39 | 40 | elementsDesc: ( 41 | elements: [ 42 | ( 43 | key: \tr, 44 | shared: (elementType: \button, midiChan: 0, spec: \midiBut), 45 | elements: [ 46 | MKtlDesc.notePair( \rew, 21, shared: ( \style: (row: 0, column: 0, showLabel: true) ) ), 47 | MKtlDesc.notePair( \play, 24, shared: ( \style: (row: 0, column: 1, showLabel: true) ) ), 48 | MKtlDesc.notePair( \fwd, 22, shared: ( \style: (row: 0, column: 2, showLabel: true) ) ), 49 | MKtlDesc.notePair( \cycle,20, shared: ( \style: (row: 1, column: 0, showLabel: true) ) ), 50 | MKtlDesc.notePair( \stop, 23, shared: ( \style: (row: 1, column: 1, showLabel: true) ) ), 51 | MKtlDesc.notePair( \rec, 25, shared: ( \style: (row: 1, column: 2, showLabel: true) ) ) 52 | ] 53 | ), 54 | ( 55 | key: \kn, 56 | shared: (midiMsgType: \cc, elementType: \knob, midiNum: 12, spec: \midiCC), 57 | elements: 58 | (0..8).collect { |i, n| 59 | ( midiChan: i, \style: (row: 0, column: 4+(n*2)) ) 60 | } 61 | ), 62 | 63 | ( 64 | key: \sl, 65 | shared: (midiMsgType: \cc, elementType: \slider, midiNum: 21, spec: \midiCC), 66 | elements: (0..8).collect { |i, n| 67 | ( midiChan: i, \style: (row: 1, column: 4+(i*2))) 68 | } 69 | ), 70 | ( 71 | key: \bt, 72 | shared: (elementType: \button, midiChan: 0, spec: \midiBut), 73 | elements: #[ 26, 27 ].collect { |midiNum, row| 74 | ( 75 | shared: (midiNum: midiNum), 76 | elements: (0..8).collect { |chan, col| 77 | MKtlDesc.notePair(col+1, midiNum, 78 | shared: ( midiChan: chan, 79 | \style: (row: row+2, column: 3 + (col*2), showLabel: false)) 80 | ); 81 | } 82 | ) 83 | } 84 | ) 85 | ] 86 | ) 87 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/icon-icontrols-pro.desc.scd: -------------------------------------------------------------------------------- 1 | // incomplete, but what is there works 2 | 3 | /* 4 | thisProcess.nowExecutingPath.basename.split($.).first.postcs; 5 | d = MKtlDesc.loadDescs("*icontrols-pro").first; 6 | d.elementsDict.size; 7 | // d.web; 8 | 9 | MKtlDesc.loadDescs("*icontrols-pro"); 10 | m.free; m = MKtl(\ic, "*icontrols-pro"); 11 | g = m.gui; 12 | g.views.size; 13 | m.trace; 14 | m.elementsDict.size; 15 | m.postElements; 16 | */ 17 | 18 | ( 19 | deviceName: "iCON iControls_Pro V1.02", 20 | protocol: \midi, 21 | deviceType: \faderbox, 22 | elementTypes: [\fader, \button], 23 | status: ( 24 | linux: "unknown", 25 | osx: "tested desc, looks good. 2016_03_23, adc", 26 | win: "unknown"), 27 | 28 | idInfo: "iCON iControls_Pro V1.02", 29 | 30 | deviceInfo: ( 31 | // vendorURI: "", 32 | // manualURI: "", 33 | // description: "", 34 | // features: [], 35 | // notes: "", 36 | longName: "iCON iControls Pro", 37 | // hasScribble: false 38 | ), 39 | elementsDesc: ( 40 | elements: [ 41 | ( 42 | key: \bt, 43 | shared: (elementType: \pad, ioType: \inout, 44 | midiMsgType: \noteOn, midiChan: 0, spec: \midiBut, mode: \push ), 45 | elements: [ 46 | ( 47 | key: \rec, 48 | elements: (0..7).collect { |num, i| 49 | ( 50 | key: (i+1).asSymbol, 51 | groupType: \noteOnOffBut, 52 | midiNum: num, 53 | style: (row: 0, column: i) 54 | ) 55 | } 56 | ), 57 | ( 58 | key: \solo, 59 | 60 | elements: (8..15).collect { |num, i| 61 | ( 62 | key: (i+1).asSymbol, 63 | groupType: \noteOnOffBut, 64 | midiNum: num, 65 | shared: (style: (row: 1, column: i)) 66 | ) 67 | } 68 | ), 69 | ( 70 | key: \mute, 71 | elements: (16..23).collect { |num, i| 72 | ( 73 | key: (i+1).asSymbol, 74 | groupType: \noteOnOffBut, 75 | midiNum: num, 76 | shared: (style: (row: 2, column: i)) 77 | ) 78 | } 79 | ), 80 | ( 81 | key: \select, 82 | // assuming this is 24-31? was 16-23, copy paste error? 83 | elements: (24..31).collect { |num, i| 84 | ( 85 | key: (i+1).asSymbol, 86 | groupType: \noteOnOffBut, 87 | midiNum: num, 88 | shared: (style: (row: 3, column: i)) 89 | ) 90 | } 91 | ) 92 | ] 93 | ), 94 | ( 95 | key: \kn, 96 | shared: (midiMsgType: \cc, elementType: \encoder, 97 | ioType: \inout, midiChan: 0, spec: \midiCC, mode: \mackie ), 98 | elements: (16..23).collect { |num, i| (midiNum: num) } 99 | ), 100 | ( 101 | key: \sl, 102 | shared: (midiMsgType: \bend, elementType: \slider, 103 | ioType: \inout, spec: \midiBend), 104 | elements: 9.collect { |i| (midiChan: i) } 105 | ) 106 | ] 107 | ) 108 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/icontrols/icon-icontrols-102-hid.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | This is for the HID part of the older version of the 3 | iCon iControls, which registers as "iCON iControl V1.02". 4 | 5 | It registers on two midi in ports and 1 out, 6 | and the joystick registers as an HID device (a mouse); 7 | this file only only for the joystick. 8 | while its HID descriptor says it has all 6 generic 9 | mouse elements, it actually only has 3: 10 | mouse x and y, and bt1 = click on the joy hat. 11 | 12 | // tests: 13 | MKtlDesc.loadDescs; 14 | m.free; m = MKtl(\ic, "*icontrols-102-hid"); 15 | m.trace; m.gui; 16 | 17 | m.elAt.action = { |el| [el.name, el.deviceValue].postln }; 18 | m.elementsDict.size; 19 | m.postElements; 20 | 21 | [0.47244095802307, 0.52755904197693].asFraction(300) 22 | 23 | */ 24 | 25 | ( 26 | deviceName: "iCON iControl V1.02_iCON", 27 | protocol: 'hid', 28 | deviceType: \joystick, 29 | status: ( 30 | linux: "unknown", 31 | osx: "tested and working. 2016_04_08, adc", 32 | win: "unknown"), 33 | elementTypes: [\joyAxis], 34 | 35 | idInfo: "iCON iControl V1.02_iCON", 36 | 37 | deviceInfo: ( 38 | vendorURI: "http://icon-global.com/product/icontrols/", 39 | manualURI: "http://support.icon-global.com/hc/en-us/articles/216201337-iControls", 40 | // description: "", 41 | // features: [], 42 | notes: "This desc only implements the joystick of the device.", 43 | longName: "iCON iControls (HID)" 44 | ), 45 | //nescivi: (icontrols V2.00) spec is a bit tricky to get right, but this comes close 46 | // adc: (V1.02) on osx this seems to be very close to [60/127, 67/127]. 47 | specs: ( \mouseAxisIcon: [ 0.47244095802307, 0.52755904197693, \lin, 0, 0.5].asSpec ), 48 | elementsDesc: ( 49 | shared: ( hidUsagePage: 1, elementType: 'mouseAxis', ioType: 'in', spec: 'mouseAxisIcon', mode: \center ), 50 | elements: [ 51 | (key: 'x', hidUsage: 48), 52 | (key: 'y', hidUsage: 49), 53 | (key: \hatBt, elementType: \button, spec: \hidBut, hidUsagePage: 9, hidUsage: 1), 54 | ] 55 | ) 56 | ) 57 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/icontrols/icon-icontrols-102.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is for the older version of the iCon iControls, 4 | which registers as "iCON iControl V1.02". 5 | 6 | // tests: 7 | MKtlDesc.loadDescs; 8 | m.free; m = MKtl(\ic, "*icontrols-102"); m.trace.gui; 9 | 10 | m.elAt.action = { |el| el.name.postln }; 11 | m.elementsDict.size; 12 | m.postElements; 13 | */ 14 | 15 | ( 16 | deviceName: "iCON iControl V1.02", 17 | 18 | idInfo: (deviceName: "iCON iControl V1.02", srcPortIndex: 0, destPortIndex: 0), 19 | 20 | status: ( 21 | linux: "unknown", 22 | osx: "tested and working, adc, 2016-05-05.", 23 | win: "unknown"), 24 | 25 | protocol: \midi, 26 | deviceType: \faderbox, 27 | elementTypes: [\fader, \knob, \button], 28 | 29 | deviceInfo: ( 30 | vendorURI: "http://icon-global.com/product/icontrols/", 31 | manualURI: "http://support.icon-global.com/hc/en-us/articles/216201337-iControls", 32 | // description: "", 33 | // features: [], 34 | notes: "" 35 | // hasScribble: false 36 | ), 37 | 38 | 39 | 40 | elementsDesc: ( 41 | elements: [ 42 | ( 43 | key: \bt, 44 | shared: (elementType: \pad, midiChan: 0, spec: \midiBut), 45 | elements: #[ 46 | [ 16, 17, 18, 19, 20, 21, 22, 23, 48 ], 47 | [ 8, 9, 10, 11, 12, 13, 14, 15, 49 ] 48 | ].collect { |xs, row| 49 | ( 50 | elements: xs.collect { |note, col| 51 | ( 52 | key: col, midiNum: note, groupType: \noteOnOffBut, 53 | shared: ( 54 | groupType: \noteOnOffBut, 55 | \style: (row: row+2, column: 3 + (col*2)) 56 | ) 57 | ); 58 | } 59 | ) 60 | } 61 | ), 62 | ( 63 | key: \sl, 64 | shared: ( 65 | midiMsgType: \cc, elementType: \slider, 66 | midiNum: 13, spec: \midiCC), 67 | elements: (0..8).collect { |i, n| ( 68 | midiChan: i, 69 | \style: (row: 1, column: 4+(i*2)) 70 | ) } 71 | ), 72 | ( 73 | key: \kn, 74 | shared: (midiMsgType: \cc, elementType: \knob, 75 | midiChan: 0, spec: \midiCC), 76 | elements: ((16..23)++[12]).collect { |i, n| ( 77 | midiNum: i, 78 | \style: (row: 0, column: 4+(n*2)) 79 | ) } 80 | ), 81 | ( 82 | key: \tr, 83 | shared: (elementType: \pad, midiChan: 0, groupType: \noteOnOffBut, spec: \midiBut), 84 | elements: [ 85 | ( key: \rew, midiNum: 91, shared: ( \style: (row: 0, column: 0) ) ), 86 | ( key: \play, midiNum: 94, shared: ( \style: (row: 0, column: 1) ) ), 87 | ( key: \fwd, midiNum: 92, shared: ( \style: (row: 0, column: 2) ) ), 88 | ( key: \cycle, midiNum: 86, shared: ( \style: (row: 1, column: 0) ) ), 89 | ( key: \stop, midiNum: 93, shared: ( \style: (row: 1, column: 1) ) ), 90 | ( key: \rec, midiNum: 95, shared: ( \style: (row: 1, column: 2) ) ) 91 | ] 92 | ) 93 | ] 94 | ) 95 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/icontrols/icon-icontrols-200-hid.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | This file for the HID part of the V2.00 version of the iCon iControls, 3 | copied from "iCON iControl V1.02" for testing whether 4 | the behavior of the two generations is identical. 5 | 6 | // tests: 7 | MKtlDesc.loadDescs; 8 | m.free; m = MKtl(\ic, "*icontrols-200-hid"); 9 | m.trace; m.gui; 10 | 11 | m.elAt.action = { |el| [el.name, el.deviceValue].postln }; 12 | m.elementsDict.size; 13 | m.postElements; 14 | 15 | */ 16 | 17 | ( 18 | deviceName: "iCON iControls V2.00_iCON", 19 | protocol: 'hid', 20 | deviceType: \joystick, 21 | status: ( 22 | linux: "unknown", 23 | osx: "tested desc, looks good. needs test with current icontrols V2.00. 2016_04_08, adc", 24 | win: "unknown"), 25 | elementTypes: [\joyAxis], 26 | 27 | idInfo: "iCON iControls V2.00_iCON", 28 | 29 | deviceInfo: ( 30 | vendorURI: "http://icon-global.com/product/icontrols/", 31 | manualURI: "http://support.icon-global.com/hc/en-us/articles/216201337-iControls", 32 | // description: "", 33 | // features: [], 34 | notes: "This desc only implements the joystick of the device.", 35 | longName: "iCON iControls (HID)" 36 | ), 37 | //nescivi: (icontrols V2.00) spec is a bit tricky to get right, but this comes close 38 | // adc: (V1.02) on osx this seems to be very close to [60/127, 67/127]. 39 | specs: ( \mouseAxisIcon: [ 0.47244095802307, 0.52755904197693, \lin, 0, 0.5].asSpec ), 40 | elementsDesc: ( 41 | shared: ( hidUsagePage: 1, elementType: 'mouseAxis', ioType: 'in', spec: 'mouseAxisIcon', mode: \center ), 42 | elements: [ 43 | (key: 'x', hidUsage: 48), 44 | (key: 'y', hidUsage: 49), 45 | (key: \hatBt, elementType: \button, spec: \hidBut, hidUsagePage: 9, hidUsage: 1), 46 | ] 47 | ) 48 | ) 49 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/icontrols/icon-icontrols-hid.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "iCON iControls V2.00_iCON", 3 | protocol: 'hid', 4 | deviceType: \joystick, 5 | status: ( 6 | linux: "tested and working, 2016_03_20 by nescivi", 7 | osx: "tested desc, looks good. 2016_03_23, adc", 8 | win: "unknown"), 9 | elementTypes: [\joyAxis], 10 | 11 | idInfo: "iCON iControls V2.00_iCON", 12 | 13 | deviceInfo: ( 14 | vendorURI: "http://icon-global.com/product/icontrols/", 15 | manualURI: "http://support.icon-global.com/hc/en-us/articles/216201337-iControls", 16 | // description: "", 17 | // features: [], 18 | notes: "This desc only implements the joystick of the device.", 19 | longName: "iCON iControls (HID)" 20 | ), 21 | //nescivi: spec is a bit tricky to get right, but this comes close 22 | specs: ( \mouseAxisIcon: [ 0.46062994003296, 0.53937005996704, \lin, 0, 0.5].asSpec ), 23 | elementsDesc: ( 24 | shared: ( hidUsagePage: 1, elementType: 'mouseAxis', ioType: 'in', spec: 'mouseAxisIcon', mode: \center ), 25 | elements: [ 26 | (key: 'x', hidUsage: 48), 27 | (key: 'y', hidUsage: 49) 28 | ] 29 | ) 30 | ) 31 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/intech-grid.desc.scd: -------------------------------------------------------------------------------- 1 | 2 | /**** desc file for intech EN16 4x4 grid of encoders. 3 | 4 | MKtlDesc.loadDescs("intech-grid"); 5 | 6 | k.free; k = MKtl('grid', "*grid"); k.gui; 7 | k.elAt(\enc).action = { |el| [el.name, el.deviceValue, el.value.round(0.001)].postln }; 8 | k.elAt(\bt).action = { |el| [el.name, el.deviceValue, el.value.round(0.001)].postln }; 9 | 10 | Questions / To Do: 11 | Is there a way to set it to relative mode? 12 | https://docs.intech.studio/guides/introduction 13 | 14 | Would be useful to set encoders to values from SC - how? 15 | naively declaring (ioType: \inout) does not work. 16 | some other ccnums? 17 | 18 | ****/ 19 | 20 | ( 21 | deviceName: "Grid", 22 | protocol: 'midi', 23 | deviceType: 'encoder bank', 24 | elementTypes: [ ], 25 | status: ( 26 | linux: "unknown", 27 | osx: "working, 24_09_19, adc", 28 | win: "unknown"), 29 | 30 | idInfo: "Grid", 31 | 32 | // hardwarePages: [1, 2, 3, 4], 33 | 34 | deviceInfo: ( 35 | vendorURI: 'https://intech.studio/shop/en16/', 36 | manualURI: 'https://docs.intech.studio/guides/introduction', 37 | description: "4x4 grid of 16 encoders with pushbuttons.", 38 | features: ["4x4 grid of 16 encoders with pushbuttons."], 39 | notes: "", 40 | hasScribble: false 41 | ), 42 | elementsDesc: ( 43 | shared: (midiChan: 0), 44 | elements: [ 45 | ( 46 | key: \enc, 47 | shared: ('midiMsgType': 'cc', 'elementType': 'encoder', 'spec': 'midiCC'), 48 | elements: (32..47).collect { |midinum, i| 49 | (midiNum: midinum, style: (column: i % 4 * 1.5, row: (i div: 4 * 1.5 + 0.5))) 50 | } 51 | ), 52 | ( 53 | key: \bt, 54 | shared: ('groupType': 'noteOnOff', 'elementType': 'button', 'spec': 'midiBut'), 55 | elements: (32..47).collect { |midinum, i| 56 | (midiNum: midinum, style: (column: i % 4 * 1.5, row: (i div: 4 * 1.5), height: 0.5)) 57 | } 58 | ), 59 | ] 60 | ) 61 | ); 62 | 63 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/jesstech-dual-analog-rumble.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "Dual Analog Rumble Pad_Jess Tech", 3 | protocol: \hid, 4 | deviceType: \gamepad, 5 | elementTypes: [\joyAxis, \button, \hat], 6 | status: ( 7 | linux: "unknown", 8 | osx: "tested desc only, seems ok. needs gui layout. 2016-03-23, adc", 9 | win: "unknown"), 10 | 11 | idInfo: "Dual Analog Rumble Pad_Jess Tech", 12 | 13 | deviceInfo: ( 14 | // vendorURI: 15 | // manual: 16 | // description: 17 | // features: 18 | // notes: 19 | longName: "Jess Tech Dual Analog Rumble Pad" 20 | ), 21 | elementsDesc: ( 22 | elements: [ 23 | ( 24 | key: \bt, 25 | shared: ('hidUsagePage': 9, 'elementType': 'button', 'ioType': 'in',spec: \hidBut ), 26 | elements: [ 27 | (key: \1, 'hidUsage': 5), 28 | (key: \2, 'hidUsage': 6), 29 | (key: \3, 'hidUsage': 7), 30 | (key: \4, 'hidUsage': 8), 31 | (key: \5, 'hidUsage': 9), 32 | (key: \6, 'hidUsage': 10), 33 | (key: \7, 'hidUsage': 11), 34 | (key: \8, 'hidUsage': 12), 35 | (key: \9, 'hidUsage': 16), 36 | ] 37 | ), 38 | ( 39 | key: 'hat', elementType: \hatSwitch, 40 | 'hidUsage': 57, 'hidUsagePage': 1, 'type': 'hatSwitch', 41 | 'ioType': 'in', spec: \cent1, mode: \center 42 | ), 43 | ( 44 | key: \joy, 45 | shared: ('hidUsagePage': 1, 'elementType': 'joyAxis', 46 | 'ioType': 'in', spec: \cent1, mode: \center ), 47 | elements: [ 48 | (key: \l, 49 | elements: [ 50 | (key: \x, 'hidUsage': 48), 51 | (key: \y, 'hidUsage': 49) 52 | ] 53 | ), 54 | (key: \r, 55 | elements: [ 56 | (key: \x, 'hidUsage': 50), 57 | (key: \y, 'hidUsage': 53) 58 | ] 59 | ) 60 | ] 61 | ), 62 | ] 63 | ) 64 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/jesstech-dual-analog.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "Dual Analog Pad_Jess Tech", 3 | protocol: \hid, 4 | deviceType: \gamepad, 5 | elementTypes: [\button, \joyAxis, \hat], 6 | status: ( 7 | linux: "unknown", 8 | osx: "tested desc, looks good. 2016_03_23, adc", 9 | win: "unknown"), 10 | 11 | idInfo: "Dual Analog Pad_Jess Tech", 12 | 13 | deviceInfo: ( 14 | // vendorURI: "", 15 | // manualURI: "", 16 | // description: "", 17 | // features: [], 18 | longName: "Jess Tech Dual Analog Pad" 19 | 20 | ), 21 | 22 | elementsDesc: ( 23 | elements: [ 24 | ( 25 | key: \bt, 26 | shared: ('hidUsagePage': 9, 'elementType': 'button', 'ioType': 'in',spec: \hidBut ), 27 | elements: [ 28 | (key: \1, 'hidUsage': 5), 29 | (key: \2, 'hidUsage': 6), 30 | (key: \3, 'hidUsage': 7), 31 | (key: \4, 'hidUsage': 8), 32 | (key: \5, 'hidUsage': 9), 33 | (key: \6, 'hidUsage': 10), 34 | (key: \7, 'hidUsage': 11), 35 | (key: \8, 'hidUsage': 12), 36 | (key: \9, 'hidUsage': 16), 37 | ] 38 | ), 39 | ( 40 | key: 'hat', elementType: \hatSwitch, 41 | 'hidUsage': 57, 'hidUsagePage': 1, 'type': 'hatSwitch', 42 | 'ioType': 'in', spec: \cent1, mode: \center 43 | ), 44 | ( 45 | key: \joy, 46 | shared: ('hidUsagePage': 1, 'elementType': 'joyAxis', 47 | 'ioType': 'in', spec: \cent1, mode: \center ), 48 | elements: [ 49 | (key: \l, 50 | elements: [ 51 | (key: \x, 'hidUsage': 48), 52 | (key: \y, 'hidUsage': 49) 53 | ] 54 | ), 55 | (key: \r, 56 | elements: [ 57 | (key: \x, 'hidUsage': 50), 58 | (key: \y, 'hidUsage': 53) 59 | ] 60 | ) 61 | ] 62 | ), 63 | ] 64 | ) 65 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/korg-microkey.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | MKtlDesc.loadDescs("*microkey"); m.free; m = MKtl(\nky2, "*microkey"); m.trace.guis; 3 | */ 4 | ( 5 | deviceName: "microKEY", 6 | protocol: 'midi', 7 | deviceType: \midiKeyboard, 8 | elementTypes: [\pianoKey], 9 | status: ( 10 | linux: "unknown", 11 | osx: "tested desc, looks good. gui needs info. 2016_03_23, adc", 12 | win: "unknown"), 13 | 14 | idInfo: "microKEY", 15 | 16 | deviceInfo: ( 17 | vendorURI: "http://www.korg.com/us/products/controllers/microkey/", 18 | // manualURI: "", 19 | // description: "", 20 | features: [ 21 | "Velocity-sensitive mini-midiKeyboard", 22 | "three models: 25-key / 37-key / 61-key" 23 | ], 24 | longName: "KORG microKEY" 25 | // notes: "", 26 | ), 27 | elementsDesc: ( 28 | shared: ('midiChan': 0), 29 | elements: [ 30 | ( 31 | key: \pkey, 32 | shared: ('elementType': 'pianoKey', groupType: \noteOnOff, spec: \midiVel), 33 | elements: (0..120).collect { |midinum, i| 34 | var pos = Piano.pos(midinum % 48, 0); 35 | ( 36 | key: i.asSymbol, 37 | shared: ( 38 | midiNum: midinum, 39 | groupType: \noteOnOff, 40 | style: ( 41 | row: (4 - (i div: 48 * 2)) + (pos.y * 0.9), 42 | column: pos.x, 43 | color: pos.color, 44 | height: 1.2 45 | )) 46 | ); 47 | } 48 | ), 49 | ( 50 | key: 'mod', 51 | 'midiMsgType': 'cc', 'elementType': 'modWheel', 'midiNum': 1, 52 | 'spec': 'midiCC', style: (row: 0, column: 18, height: 2) 53 | ), 54 | ( 55 | key: 'bend', 56 | 'midiMsgType': 'bend', 'elementType': 'bender', 57 | 'spec': 'midiBend', style: (row: 0, column: 19, height: 2) 58 | ) 59 | ] 60 | ) 61 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/korg-nanokey.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | MKtlDesc.loadDescs("*nanokey"); 3 | m.free; m = MKtl(\nky, "*nanokey"); m.trace.gui; 4 | */ 5 | 6 | ( 7 | ( 8 | deviceName: "nanoKEY", 9 | protocol: 'midi', 10 | deviceType: \midiKeyboard, 11 | elementTypes: [\pianoKey], 12 | status: ( 13 | linux: "unknown", 14 | osx: "tested and working. 2015-03-15, adc", 15 | win: "unknown"), 16 | 17 | idInfo: "nanoKEY", 18 | 19 | deviceInfo: ( 20 | // vendorURI: "", 21 | // manualURI: "", 22 | // description: "", 23 | // features: [], 24 | // notes: "", 25 | 26 | longName: "KORG nanoKEY" 27 | ), 28 | elementsDesc: ( 29 | shared: (midiChan: 0), 30 | elements: [ 31 | ( 32 | key: \pkey, 33 | shared: ('elementType': 'pianoKey', groupType: \noteOnOff, spec: \midiVel), 34 | elements: (0..120).collect { |midinum, i| 35 | var pos = Piano.pos(midinum % 48, 0); 36 | (key: i.asSymbol, 37 | shared: ( 38 | midiNum: midinum, 39 | groupType: \noteOnOff, 40 | style: ( 41 | row: (4 - (i div: 48 * 2)) + (pos.y * 0.9), 42 | column: pos.x * 0.8, 43 | color: pos.color, 44 | height: 1.2 45 | )) 46 | ); 47 | } 48 | ), 49 | ( 50 | key: \mod, 51 | 'midiMsgType': 'cc', 'elementType': 'button', 'midiNum': 1,'spec': 'midiBut' 52 | ), 53 | // where do these come from? keys as cc? 54 | ( 55 | key: \cc, 56 | shared: ('midiMsgType': 'cc', 'elementType': 'slider', 'spec': 'midiCC'), 57 | elements: ((14..31) ++ (80..87)).collect { |midinum, i| 58 | ('midiNum': midinum, style: (height: 2, row: 6, column: i)) 59 | } 60 | ), 61 | ( 62 | key: \bend, 63 | 'midiMsgType': 'bend', 'elementType': 'button', 'spec': 'midiBend', 64 | style: (row: 0, column: 12, width: 2) 65 | ) 66 | ] 67 | ) 68 | ) 69 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/korg-nanokey2.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | MKtlDesc.allDescs.clear; 3 | m = MKtl(\nky2, "*nanokey2"); m.gui; 4 | */ 5 | ( 6 | deviceName: "nanoKEY2", 7 | protocol: 'midi', 8 | deviceType: \midiKeyboard, 9 | elementTypes: [\pianoKey, \button], 10 | status: ( 11 | linux: "unknown", 12 | osx: "tested desc only, seems ok. 2016-03-23, adc", 13 | win: "unknown"), 14 | 15 | idInfo: "nanoKEY2", 16 | 17 | deviceInfo: ( 18 | vendorURI: "http://www.korg.com/us/products/controllers/nanokey2/", 19 | // manualURI: "", 20 | // description: "", 21 | // features: [], 22 | // notes: "", 23 | longName: "KORG nanoKEY2" 24 | 25 | ), 26 | elementsDesc: ( 27 | shared: (midiChan: 0), 28 | elements: [ 29 | ( 30 | key: \pkey, 31 | shared: ('elementType': 'pianoKey', groupType: \noteOnOff, spec: \midiVel), 32 | elements: (0..120).collect { |midinum, i| 33 | var pos = Piano.pos(midinum % 48, 0); 34 | (key: i.asSymbol, 35 | shared: ( 36 | midiNum: midinum, 37 | groupType: \noteOnOff, 38 | style: ( 39 | row: (4 - (i div: 48 * 2)) + (pos.y * 0.9), 40 | column: pos.x * 0.8, 41 | color: pos.color, 42 | height: 1.2 43 | )) 44 | ); 45 | } 46 | ), 47 | ( 48 | key: 'mod', 'elementType': 'modWheel', 49 | 'midiMsgType': 'cc' , 'midiNum': 1, 'spec': 'midiCC', 50 | style: (row: 0, column: 18, height: 2) 51 | ), 52 | ( 53 | key: 'bend', 'elementType': 'bender', 'midiMsgType': 'bend', 54 | 'spec': 'midiBend', style: (row: 0, column: 19, height: 2) 55 | ) 56 | ] 57 | ) 58 | ); 59 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/korg-nanopad2.desc.scd: -------------------------------------------------------------------------------- 1 | ///// nanoPAD2 desc file 2 | /* 3 | MKtlDesc.loadDescs("*nanopad2"); 4 | m.free; m = MKtl(\nky2, "*nanopad2"); m.gui; 5 | */ 6 | ( 7 | deviceName: "nanoPAD2", 8 | protocol: \midi, 9 | deviceType: \drumpad, 10 | elementTypes: [\pad, \touchpad, \button], 11 | status: ( 12 | linux: "unknown", 13 | osx: "tested and working. 2016-03-22, dhml & adc", 14 | win: "unknown"), 15 | 16 | idInfo: "nanoPAD2", 17 | 18 | testCode: { 19 | MKtl.find(\midi); 20 | k = MKtl('np', "*nanopad2"); 21 | k.gui; 22 | k.trace; 23 | }, 24 | 25 | deviceInfo: ( 26 | vendorURI: "http://www.korg.com/us/products/controllers/nanopad2/", 27 | // manualURI: "", 28 | // description: "", 29 | // features: [], 30 | // notes: "", 31 | longName: "KORG nanoPAD 2" 32 | 33 | ), 34 | elementsDesc: ( 35 | shared: ('midiChan': 0), 36 | elements: [ 37 | ( 38 | key: \pad, 39 | shared: ( elementType: \pad, \spec: \midiVel, \ioType: \in), 40 | elements: (36..99).clump(16).collect { |padbank, page| 41 | ( 42 | shared: (page: page, groupType: \noteOnOff), 43 | elements: padbank.collect { |num, j| 44 | ( 45 | key: i, midiNum: num, 46 | shared: (style: (row: j div: 8, column: j % 8 + 3)) 47 | ) 48 | } 49 | ) 50 | } 51 | ), 52 | ( 53 | key: 'touch', 54 | shared: ('midiMsgType': 'cc', 'elementType': 'slider', style: (height: 2), 'spec': 'midiCC'), 55 | elements: [ 56 | (key: \x, 'midiNum': 1, style: (height: 2, column: 0)), 57 | (key: \y, 'midiNum': 2, style: (height: 2, column: 1)), 58 | (key: \on, 'midiNum': 16, style: (height: 2, column: 2)) 59 | ] 60 | ) 61 | ] 62 | ) 63 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/logitech-extreme-3d-pro.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "Logitech Extreme 3D_Logitech", 3 | protocol: 'hid', 4 | deviceType: \joystick, 5 | elementTypes: [\joystick, \button], 6 | status: ( 7 | linux: "unknown", 8 | osx: "tested desc, looks good. 2016_03_23, adc", 9 | win: "unknown"), 10 | 11 | ///// orig: 12 | // idInfo: "Logitech Extreme 3D_Logitech", 13 | ///// new 2023 model on macOS needs this one: 14 | idInfo: "Extreme 3D pro_Logitech", 15 | 16 | deviceInfo: ( 17 | vendorURI: "http://gaming.logitech.com/en-us/product/extreme-3d-pro-joystick", 18 | manualURI: "http://support.logitech.com/product/extreme-3d-pro", 19 | longName: "Logitech Extreme 3D", 20 | // description: "", 21 | // features: [], 22 | // notes: "", 23 | 24 | hasScribble: true 25 | ), 26 | 27 | elementsDesc: ( 28 | elements: [ 29 | ( 30 | key: \bt, 31 | shared: ('hidUsagePage': 9, 'elementType': 'button', 32 | 'ioType': 'in', 'spec': \hidBut ), 33 | elements: [ 34 | // trigger 35 | (key: '1', 'hidUsage': 1), 36 | // side thumb 37 | (key: '2', 'hidUsage': 2), 38 | // top of joystick 39 | (key: '3', 'hidUsage': 3), 40 | (key: '4', 'hidUsage': 4), 41 | (key: '5', 'hidUsage': 5), 42 | (key: '6', 'hidUsage': 6), 43 | 44 | // on base plate 45 | (key: '7', 'hidUsage': 7), 46 | (key: '8', 'hidUsage': 8), 47 | (key: '9', 'hidUsage': 9), 48 | (key: '10', 'hidUsage': 10), 49 | (key: '11', 'hidUsage': 11), 50 | (key: '12', 'hidUsage': 12) 51 | ], 52 | ), 53 | ( 54 | key: \joy, 55 | shared: ('hidUsagePage': 1, 'elementType': 'joyAxis', 'ioType': 'in', 'spec': \cent1), 56 | elements: [ 57 | (key: \x, 'hidUsage': 48), 58 | (key: \y, 'hidUsage': 49), 59 | (key: \z, 'hidUsage': 53) 60 | ] 61 | ), 62 | ( 63 | key: \hat, 64 | 'hidUsage': 57, 'hidUsagePage': 1, 'elementType': 'hatSwitch', 65 | 'ioType': 'in', spec: \cent1inv, mode: \center), 66 | ( 67 | key: \slider, 68 | 'hidUsage': 54, 'hidUsagePage': 1, 'elementType': 'slider', 69 | 'ioType': 'in', spec: \cent1inv, mode: \center 70 | ) 71 | ] 72 | ) 73 | ); 74 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/m-audio-triggerfinger.desc.scd: -------------------------------------------------------------------------------- 1 | // M-audio Trigger finger - MKtl 2 | 3 | /* 4 | MKtlDesc.loadDescs("*finger"); m.free; m = MKtl('op1', "*finger"); m.trace.gui; 5 | */ 6 | 7 | ( 8 | deviceName: "USB Trigger Finger", 9 | protocol: \midi, 10 | deviceType: \drumpad, 11 | elementTypes: [\pad, \button, \slider], 12 | status: ( 13 | linux: "unknown", 14 | osx: "tested desc, looks good. needs gui layout. 2016_03_23, adc", 15 | win: "unknown"), 16 | 17 | idInfo: "USB Trigger Finger", 18 | 19 | deviceInfo: ( 20 | // vendorURI: "", 21 | // manualURI: "", 22 | // description: "", 23 | features: [\pad, \slider, \knob], 24 | // notes: "", 25 | longName: "M-AUDIO Trigger Finger" 26 | ), 27 | elementsDesc: ( 28 | shared: (\midiChan: 0, ioType: \in), 29 | elements: [ 30 | ( 31 | key: \kn, 32 | shared: ( elementType: \knob, \midiMsgType: \cc, 33 | spec: \midiCC), 34 | elements: ( #[10, 91, 12, 93, 5, 73, 84, 72].collect { |num, i| 35 | (key: (i + 1).asSymbol, \midiNum: num, 36 | style: (row: 0.5 - (i div: 4 * 0.8), 37 | column: (i % 4 * 0.9) + 3 + (i div: 4 * 0.5) 38 | ) 39 | ) 40 | }) 41 | ), 42 | ( 43 | key: \sl, 44 | shared: ( elementType: \slider, \midiMsgType: \cc, 45 | spec: \midiCC), 46 | elements: #[7, 1, 71, 74].collect { |num, i| 47 | (key: (i + 1).asSymbol, \midiNum: num, 48 | style: (row: 3, column: i * 0.75, width: 0.8) 49 | ) 50 | } 51 | ), 52 | ( 53 | key: \pad, 54 | shared: ( elementType: \pad, \groupType: \noteOnOff, 55 | spec: \midiVel), 56 | elements: ((0..15) + 35).collect { |num, i| 57 | (key: (i + 1).asSymbol, \midiNum: num, 58 | shared: (style: ( 59 | column: i % 4 + 3, 60 | row: 5 - (i div: 4) 61 | )) 62 | ) 63 | } 64 | ), 65 | ] 66 | ) 67 | ) 68 | 69 | // pads * 16 70 | // [35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51] 71 | 72 | // sliders * 4 73 | // [7, 1, 71, 74] 74 | 75 | // knobs * 8 76 | // [10, 91, 12, 93, 5, 73, 84, 72] 77 | 78 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/makenoise-0coast.desc.scd: -------------------------------------------------------------------------------- 1 | /* 0-coast 2 | 3 | // basic example: 4 | // make an Mktl for it - will say that this is virtual only 5 | MKtl(\nocoast, "makenoise-0coast"); MKtl(\nocoast).gui; 6 | 7 | // MKtl has as idInfo : "EXTERNAL", which is not an existing port name. 8 | MKtl(\nocoast).desc.fullDesc.at(\idInfo); 9 | 10 | // So we need to tell it which midi hardware interface we connect it to. 11 | 12 | // See which MIDI sources are available: 13 | MIDIClient.sources; 14 | 15 | // figure out which one you want to use, 16 | // and open it via the port name as idInfo: 17 | MKtl(\nocoast).openDeviceVia("IAC Driver"); 18 | 19 | */ 20 | 21 | ( 22 | 23 | deviceName: "0-Coast", 24 | protocol: \midi, 25 | deviceType: \synth, 26 | elementTypes: [\knobs], 27 | status: ( 28 | linux: "unknown", 29 | osx: "unknown", 30 | win: "unknown"), 31 | 32 | idInfo: "EXTERNAL", 33 | 34 | deviceInfo: ( 35 | vendorURI: "http://makenoisemusic.com/synthesizers/ohcoast", 36 | manualURI: "http://makenoisemusic.com/manuals/0-coast_manual.pdf", 37 | description: "The 0-COAST is a single voice patchable synthesizer. It’s name reflects the fact that it utilizes techniques from both the Moog and Buchla paradigms (aka East Coast, and West Coast, due to their locations), but is loyal to neither and thus implements no coast synthesis.", 38 | features: ["synth stuff"], 39 | longName: "Make Noise 0-coast" 40 | ), 41 | 42 | elementsDesc: ( 43 | elements: [ 44 | ( 45 | key: \key, 46 | shared: (\elementType: \pad, 47 | \midiChan: 0, \spec: \midiVel, \ioType: \out), 48 | elements: (0..124).collect { |num, i| ( 49 | midiNum: num, 50 | groupType: \noteOnOff, 51 | style: (row: i div: 24, column: i % 24) 52 | )} 53 | ), 54 | ( 55 | key: \ctl, 56 | shared: (\elementType: \pad, 57 | \midiChan: 0, \midiMsgType: \cc, \spec: \midiCC, \ioType: \out), 58 | elements: [ 59 | (key: \tempoInDiv, midiNum: 116), 60 | (key: \portamento, midiNum: 5), 61 | (key: \arpeggiator, midiNum: 117), // on/off: 0/1 62 | (key: \latch, midiNum: 119), // on/off: 0/1 63 | (key: \midiClock, midiNum: 114), // on/off: 0/1 64 | (key: \chanA, midiNum: 102), // val determines channel (0..15, 16>all) 65 | (key: \chanB, midiNum: 103), // val determines channel (0..15, 16>all) 66 | (key: \cvA, midiNum: 104), // 0: note, 1: vel, 2: mod, 3:LFO 67 | (key: \cvB, midiNum: 105), // 0: note, 1: vel, 2: mod, 3:LFO 68 | (key: \gateA, midiNum: 106), // 0: note, 1: vel, 2: mod, 3:LFO 69 | (key: \gateB, midiNum: 107), // 0: note, 1: vel, 2: mod, 3:LFO 70 | (key: \pBendMin, midiNum: 108), 71 | (key: \pBendMax, midiNum: 109), 72 | (key: \aTouchMin, midiNum: 110), 73 | (key: \aTouchMax, midiNum: 111), 74 | (key: \velMin, midiNum: 112), 75 | (key: \velMax, midiNum: 113), 76 | 77 | ] 78 | 79 | ) 80 | ] 81 | ) 82 | ); 83 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/music_thing_8mu.desc.scd: -------------------------------------------------------------------------------- 1 | /*** 2 | 3 | Example: 4 | k = MKtl('8mu', "*music_thing*"); 5 | k.elementAt(\sl, 0).action = { \yo.postln; }; 6 | k.elementAt(\sl, 1).action = { 1.postcs; }; 7 | 8 | 9 | ****/ 10 | 11 | ( 12 | deviceName: "Music Thing 8mu", 13 | protocol: 'midi', 14 | deviceType: \faderbox, 15 | elementTypes: [\fader, \knob, \button], 16 | status: ( 17 | linux: "tested by bgola 15.02.2024", 18 | osx: "unknown", 19 | win: "unknown"), 20 | 21 | idInfo: "Music Thing m0 Plus", 22 | 23 | deviceInfo: ( 24 | vendorURI: 'https://www.musicthing.co.uk/8mu_page/', 25 | manualURI: 'https://www.musicthing.co.uk/8mu.html', 26 | description: "8mu is a pocket-sized MIDI controller, slightly smaller than a credit card.", 27 | ), 28 | elementsDesc: ( 29 | elements: [ 30 | // Buttons 31 | ( 32 | key: \bt, 33 | shared: (midiChan: 0, groupType: \noteOnOffBut, elementType: 'pad', midiNum: 1, spec: \midiBut), 34 | elements: 4.collect { |buttonIdx| 35 | ( 36 | midiNum: 36 + (buttonIdx*12), 37 | style: (\row: 0, \column: buttonIdx, \height: 1) 38 | ) 39 | } 40 | ), 41 | // sliders 42 | ( 43 | key: \sl, 44 | shared: (midiChan: 0, elementType: \slider, midiMsgType: \cc, spec: \midiCC), 45 | elements: 8.collect { |sliderIdx| 46 | ( 47 | midiNum: 34 + sliderIdx, 48 | style: (row: 1, column: sliderIdx, showLabel: true) 49 | ) 50 | } 51 | ), 52 | 53 | // Acc 54 | ( 55 | key: \acc, 56 | shared: ('midiChan': 0, 'midiMsgType': 'cc', 'elementType': 'slider', 'spec': 'midiCC'), 57 | elements: [ 58 | ( key: \front_up, 'midiNum': 42, style: (column: 0, showLabel: true)), 59 | ( key: \back_up, 'midiNum': 43, style: (column: 1, showLabel: true)), 60 | ( key: \right_up, 'midiNum': 44, style: (column: 2, showLabel: true)), 61 | ( key: \left_up, 'midiNum': 45, style: (column: 3, showLabel: true)), 62 | ( key: \cw, 'midiNum': 46, style: (column: 4, showLabel: true)), 63 | ( key: \ccw, 'midiNum': 47, style: (column: 5, showLabel: true)), 64 | ( key: \upside_down, 'midiNum': 48, style: (column: 6, showLabel: true)), 65 | ( key: \upside_up, 'midiNum': 49, style: (column: 7, showLabel: true)), 66 | ] 67 | ), 68 | ] 69 | ) 70 | ); 71 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/nintendo-switch-pro.desc.scd: -------------------------------------------------------------------------------- 1 | /**** 2 | ToDo 3 | - update when/if 4 | CompassView and StickView classes are integrated. 5 | 6 | MKtl(\swpro).free; 7 | m = MKtl(\swpro, "*switch-pro"); 8 | m.gui; 9 | 10 | // conversion from arrows to 8 directions + up/release 11 | m.elAt(\arrows).action = { |el| 12 | var intVal = (el.deviceValue * 7 + 1).round.asInteger; 13 | var dirs = [\rest, \N, \NE, \E, \SE, \S, \SW, \W, \NW, \up]; 14 | [\arrows, intVal, dirs.clipAt(intVal), el.value.asFraction].postcs; 15 | }; 16 | 17 | ***/ 18 | ( 19 | idInfo: "Pro Controller_Unknown", 20 | 21 | protocol: 'hid', 22 | deviceName: "Nintendo Pro Controller", 23 | deviceType: 'joystick', 24 | elementTypes: [\joystick, \button], 25 | status: ( 26 | linux: "unknown", 27 | osx: "Seems to Work - Sam Pluta 05/13/20", 28 | win: "unknown" 29 | ), 30 | 31 | deviceInfo: ( 32 | vendorURI: "https://www.nintendo.com/", 33 | manualURI: "", 34 | longName: "Nintendo Switch Pro", 35 | // description: "", 36 | // features: [], 37 | // notes: "", 38 | 39 | hasScribble: false 40 | ), 41 | 42 | elementsDesc: ( 43 | elements: [ 44 | ( 45 | key: \bt, 46 | shared: ('hidUsagePage': 9, 'elementType': 'button', 47 | 'ioType': 'in', 'spec': \hidBut ), 48 | elements: [ 49 | (key: '1', 'hidUsage': 1, 'style': (row: 2.5, column: 4)), 50 | (key: '2', 'hidUsage': 2, 'style': (row: 2, column: 4.8)), 51 | (key: '3', 'hidUsage': 3, 'style': (row: 2, column: 3.2)), 52 | (key: '4', 'hidUsage': 4, 'style': (row: 1.5, column: 4)), 53 | (key: '5', 'hidUsage': 5, 'style': (row: 1, column: 0)), 54 | (key: '6', 'hidUsage': 6, 'style': (row: 1, column: 6)), 55 | (key: '7', 'hidUsage': 7, 'style': (row: 0, column: 0)), 56 | (key: '8', 'hidUsage': 8, 'style': (row: 0, column: 6)), 57 | (key: '9', 'hidUsage': 9, 'style': (row: 0, column: 2)), 58 | (key: '10', 'hidUsage': 10, 'style': (row: 0, column: 2.8)), 59 | (key: '11', 'hidUsage': 11, 'style': (row: 3.2, column: 1.2)), 60 | (key: '12', 'hidUsage': 12, 'style': (row: 3.2, column: 5.2)), 61 | (key: '13', 'hidUsage': 13, 'style': (row: 0.8, column: 2.8)), 62 | (key: '14', 'hidUsage': 14, 'style': (row: 0.8, column: 2)) 63 | ], 64 | ), 65 | ( 66 | key: \joy, 67 | shared: ('hidUsagePage': 1, 'elementType': 'joyAxis', 'ioType': 'in', 'spec': \cent1), 68 | elements: [ 69 | (key: \x1, 'hidUsage': 48, 'style': (row: 4, column: 0.2, height: 1, width: 2)), 70 | (key: \y1, 'hidUsage': 49, spec:[1.0,0.0], 'style': (row: 3, column: 2, height: 2, width: 1)), 71 | (key: \x2, 'hidUsage': 51, 'style': (row: 4, column: 4.2, height: 1, width: 2)), 72 | (key: \y2, 'hidUsage': 52, spec:[1.0,0.0], 'style': (row: 3, column: 6, height: 2, width: 1)) 73 | ] 74 | ), 75 | (key: \arrows, 76 | elements: [(hidUsage: 57, 'hidUsagePage': 1, spec:[0.0,8/7,\lin,1/7], 'ioType': 'in', 'elementType': 'slider', 'style': (row: 5, column: 2.5, height: 1, width: 2))] 77 | ) 78 | ] 79 | ) 80 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/novation-launchcontrol.desc.scd: -------------------------------------------------------------------------------- 1 | /**** 2 | Novation Launch Control 3 | 4 | minimal code examples for testing, such as: 5 | 6 | k = MKtl('nlc', "novation-launchcontrol"); 7 | k.elementAt(0, \kn, 0).action = { \yo.postln; }; 8 | k.elementAt(0, \kn, 1).action = { 1.postcs; }; 9 | k.gui; 10 | 11 | 12 | 13 | 14 | ****/ 15 | 16 | ( 17 | deviceName: "Launch Control", 18 | protocol: \midi, 19 | deviceType: \mixer, 20 | elementTypes: [\button, \knob], 21 | status: ( 22 | linux: "unknown", 23 | osx: "tested and working, 15.3.2016 by LFSaw.de", 24 | win: "unknown"), 25 | 26 | idInfo: "Launch Control", 27 | 28 | hardwarePages: ( 29 | factory: [1,2,3,4,5,6,7,8], 30 | user: [1,2,3,4,5,6,7,8] 31 | ), 32 | 33 | deviceInfo: ( 34 | // vendorURI: "http://global.novationmusic.com/launch/launch-control", 35 | // manualURI: "http://global.novationmusic.com/support/product-downloads?product=Launch+Control", 36 | description: "A rotary box emulating the interface of a mixing desk", 37 | features: [ 38 | "16 rotary pots, each with 300 degree motion", 39 | "8 multi-colour backlit buttons", 40 | "8 “user”-pages + 8 “factory”-pages", 41 | ], 42 | // notes: "", 43 | longName: "novation launchcontrol" 44 | ), 45 | elementsDesc: ( 46 | 47 | elements: 16.collect { |chan| 48 | ( 49 | key: ("pg" ++ chan).asSymbol, 50 | shared: ('midiChan': chan, \page: chan), 51 | elements: [ 52 | ( 53 | key: \kn, 54 | shared: ('midiMsgType': 'cc', 'elementType': 'knob', 55 | 'spec': 'midiCC', \ioType: \inout), 56 | elements: [ 57 | ( 58 | key: 'sndA', 59 | elements: (21..28).collect { |num| ('midiNum': num) } 60 | ), 61 | ( 62 | key: 'sndB', 63 | elements: (41..48).collect { |num| ('midiNum': num) } 64 | ) 65 | ] 66 | ), 67 | ( 68 | key: \bt, 69 | shared: ('elementType': 'pad', 'spec': 'midiNote', \ioType: \inout), 70 | elements: ((9..12) ++ (25..28)).collect{|num, i| 71 | ( 72 | key: (i+1).asSymbol, 73 | groupType: \noteOnOffBut, 74 | midiNum: num, 75 | style: ( row: 2, column: i ) 76 | ) 77 | } 78 | ), 79 | ( 80 | key: \nav, 81 | shared: ('elementType': 'button', 'spec': 'midiCC', midiMsgType: \cc, \ioType: \inout), 82 | elements: [ 83 | [ \up, 114, 1, 8 ], 84 | [ \down, 115, 1, 9 ], 85 | [ \left, 116, 2, 8 ], 86 | [ \right, 117, 2, 9 ], 87 | ].collect{|list| 88 | var key, num, row, col; 89 | #key, num, row, col = list; 90 | 91 | ( 92 | 'key': key, 93 | 'midiNum': num, 94 | 'style': (row: row, column: col) 95 | ) 96 | 97 | } 98 | 99 | ) 100 | ] 101 | ) 102 | } 103 | ) 104 | 105 | ); 106 | 107 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/novation-launchpad.desc.scd: -------------------------------------------------------------------------------- 1 | // Novation Launchpad specs: 2 | // Mostly NoteOn / NoteOff, some cc messages, 3 | // no hardware scenes, flattened out with underscores 4 | 5 | /* 6 | MKtlDesc.loadDescs("*launchpad"); 7 | m.free; m = MKtl(\lp, "*launchpad"); m.gui; 8 | */ 9 | 10 | 11 | ( 12 | deviceName: "Launchpad", 13 | protocol: \midi, 14 | deviceType: \launchpad, 15 | elementTypes: [\button], 16 | status: ( 17 | linux: "unknown", 18 | osx: "now with gui layout. 2019_12_29, adc", 19 | win: "unknown"), 20 | 21 | idInfo: "Launchpad", 22 | 23 | 24 | deviceInfo: ( 25 | vendorURI: "http://us.novationmusic.com/midi-controllers-digital-dj/launchpad", 26 | manualURI: "http://us.novationmusic.com/support/product-downloads?product=Launchpad", 27 | // description: "", 28 | features: [ 29 | "64 multi-colour backlit buttons", 30 | "8 arrow buttons", 31 | "8 other buttons", 32 | ], 33 | // notes: "", 34 | 35 | longName: "novation launchpad" 36 | ), 37 | elementsDesc: ( 38 | shared: (\midiChan: 0), 39 | elements: [ 40 | ( 41 | key: \bt, 42 | shared: (\midiMsgType: \cc, \elementType: \button, 43 | \spec: \midiBut, \mode: \push), 44 | elements: [ 45 | (key: \up, \midiNum: 104), 46 | (key: \down, \midiNum: 105), 47 | (key: \left, \midiNum: 106), 48 | (key: \right, \midiNum: 107), 49 | (key: \session,\midiNum: 108), 50 | (key: \user1, \midiNum: 109), 51 | (key: \user2, \midiNum: 110), 52 | (key: \mixer, \midiNum: 111) 53 | ] 54 | ), 55 | ( 56 | key: \pad, 57 | shared: (\elementType: \pad, \spec: \midiVel, \ioType: \inout), 58 | elements: ((0, 16 .. 112) +.t (0..7)).flat.clump(8).collect {|nums, i| 59 | ( 60 | elements: nums.collect { |num, j| 61 | ( groupType: \noteOnOff, 62 | style: (row: i + 1, column: j) 63 | ) 64 | } 65 | ) 66 | } 67 | ), 68 | ( 69 | key: \arr, 70 | shared: (\elementType: \pad, \spec: \midiVel), 71 | elements: ((0, 16 .. 112) + 8).flat.collect {|num, i| 72 | ( 73 | groupType: \noteOnOff, 74 | \midiNum: num, 75 | style: (row: 10, column: i) 76 | 77 | 78 | ) 79 | } 80 | ) 81 | ] 82 | ) 83 | ) 84 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/organelle.desc.scd: -------------------------------------------------------------------------------- 1 | // organelle desc 2 | /* 3 | d = MKtlDesc.loadDescs("organelle").first; 4 | k.free; k = MKtl(\org, d); k.trace.gui; 5 | */ 6 | 7 | ( 8 | deviceName: "organelle", 9 | protocol: \midi, 10 | deviceType: \organelle, 11 | elementTypes: [\pad, \knob], 12 | 13 | // status: ( 14 | // osx: "tested and working, 2016_03_15, adc", 15 | // linux: "tested and working 2016_03_20, nescivi", 16 | // win: "unknown" 17 | // ), 18 | 19 | idInfo: "organelle", 20 | 21 | deviceInfo: ( 22 | vendorURI: "https://www.critterandguitari.com/pages/organelle", 23 | // manualURI: "https://www.critterandguitari.com/pages/organelle", 24 | description: "ARM-based standalone linux box with midi control", 25 | features: [ 26 | "25 wooden note-buttons", 27 | "4 knobs", 28 | "1 expression pedal input", 29 | "1 alt-button w status LED", 30 | "1 mode-switching knob (e.g. to change PD patches)" 31 | "1 volume knob (analog only, no midi)" 32 | ], 33 | notes: "", 34 | longName: "C&G organelle" 35 | ), 36 | 37 | elementsDesc: ( 38 | 'shared': (\ioType: \inout, midiChan: 0), 39 | 40 | elements: [ 41 | ( 42 | key: \kn, 43 | shared: (\elementType: \knob, \midiMsgType: \cc, 44 | \spec: \midiCC), 45 | elements: (1..4).collect { |num, i| 46 | (key: num.asSymbol, \midiNum: num, 47 | style: (row: 0, column: i * 1.5 + 1)) 48 | } 49 | ), 50 | ( 51 | key: \bt, 52 | shared: (\elementType: \pad, \groupType: \noteOnOffBut, \spec: \midiBut), 53 | elements: (48..71).collect { |num, i| 54 | var pos = Piano.pos(num, 48); 55 | (key: i.asSymbol, midiNum: num, 56 | shared: (style: ( 57 | row: 1 + (pos.y * 0.7), 58 | column: pos.x * 0.7 + 2, 59 | height: 0.7, width: 0.7 60 | ), 61 | 62 | ) 63 | ); 64 | } 65 | ), 66 | ( key: \alt, midiNum: 36, 67 | groupType: \noteOnOffBut, 68 | shared: (key: \alt, groupType: \noteOnOffBut, \elementType: \pad, \spec: \midiBut, 69 | style: (row: 1.6, column: 1.2, height: 0.7, width: 0.7) 70 | ) 71 | ), 72 | ( 73 | key: \pedal, 74 | \elementType: \slider, \midiMsgType: \cc, 75 | \spec: \midiCC, \midiNum: 64, midiChan: 0, 76 | style: (row: 0, column: 0, height: 2) 77 | ) 78 | ] 79 | ) 80 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/qunexus/keith-mcmillen-qunexus.parentDesc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | General QuNexus info: 4 | 5 | QuNexus has 3 input ports, which require three separate desc files. 6 | As the QuNexus also has four preset modes for port 1, there are 7 | seven separate desc files - please choose the one for your usage. 8 | You can load all of them into separate MKtls, andput the different 9 | functionalities there. 10 | 11 | These are the port specifics: 12 | 13 | Port 1, index 0 is the keys and control on the QuNexus itself 14 | the elements on this port have four preset modes, see below 15 | 16 | Port 2 is used for the MIDI Expander input 17 | 18 | Port 3 is for CV1-2 input signals 0 to +5V 19 | * input 1 is cc#112, chan 1, 20 | * input 2 is cc 113, chan 1 21 | 22 | Output ports use the same numbering scheme: 23 | Port 1 is for sending values to the MIDI keys on the QuNexus 24 | 25 | Port 2 is used for the MIDI Expander output 5pin plug 26 | 27 | Port 3 sends CV out on 0 to +5V 28 | * Gate is noteOn/off -> 5V 29 | * CV1 is pitch, i.e. pitch of the noteOn sent 30 | * CV2 is mod on CC#1 31 | * CV3 is pitchbend (on chan 0?) 32 | 33 | 34 | QuNexus has four hardware modes, which concern elements on Port 1: 35 | 36 | Preset A uses just noteOn/Off and channel bend 37 | Preset B adds channel pressure 38 | Preset C allocates notes to 10 rotating midi chans, 39 | and sends poly bend and pressure mod (cc1) on those chans 40 | Preset D is for drums (uses chan 10) and clips 41 | */ 42 | 43 | 44 | ///////// GENERIC INFORMATION on the QuNexus is here, /////// 45 | ///////// SPECIFIC INFORMATION is in the actual descs! /////// 46 | ( 47 | deviceName: "QuNexus", 48 | protocol: \midi, 49 | deviceType: \midiKeyboard, 50 | // elementTypes are in childrenDescs, 51 | status: ( 52 | linux: "unknown", 53 | osx: "in progress", 54 | win: "unknown" 55 | ), 56 | 57 | deviceInfo: ( 58 | 59 | vendorURI: "http://www.keithmcmillen.com/products/qunexus/", 60 | manualURI: "http://www.keithmcmillen.com/downloads#qunexus", 61 | // description: "", 62 | features: [ 63 | "25 LED backlit keys with pressure and tilt sensitivity", 64 | "Pitch bend pad", 65 | "Octave up/down buttons", 66 | "5 function buttons", 67 | ], 68 | // notes: "", 69 | type: [\button, \pianoKey, \bend], 70 | longName: "Keith McMillen QuNexus" 71 | // hasScribble: false 72 | ), 73 | 74 | numPorts: 3, 75 | hardwarePages: [\A, \B, \C, \D] 76 | ) 77 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/qunexus/keith-mcmillen-qunexus_port1_AB.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | // test this desc: 4 | 5 | MKtlDesc.loadDescs("*qunexus_port1_AB"); 6 | m.free; m = MKtl(\qn_ab, "*qunexus_port1_AB"); m.gui; m.trace; 7 | m.elementsDict.size; 8 | m.postElements; 9 | 10 | 11 | General QuNexus info: 12 | The QuNexus has 3 input ports, which require three separate desc files. 13 | Its general info in is "keith-mcmillen-qunexus.parentDesc.scd". 14 | 15 | This file is for Port 1, portIndex 0, and Presets A and B. 16 | 17 | On this port and in these modes, the keys and control on the QuNexus itself 18 | can be played into the computer and set from it. 19 | 20 | Preset A uses just noteOn/Off and channel bend, 21 | Preset B adds channel pressure - so only use Preset B. 22 | */ 23 | 24 | ( 25 | ///////// GENERIC INFORMATION COPIED TO ALL QUNEXUS DESC FILES: /////// 26 | parentDesc: "keith-mcmillen-qunexus", 27 | 28 | ///////// SPECIFIC INFORMATION for this port and config: /////// 29 | 30 | elementTypes: [\pianoKey, \bender, \touch], 31 | status: ( 32 | linux: "unknown", 33 | osx: "tested and working, 2016-03-16, adc", 34 | win: "unknown"), 35 | 36 | 37 | idInfo: (deviceName: "QuNexus", srcPortIndex: 0, destPortIndex: 0), 38 | portName: "Port 1", 39 | modeName: "Preset AB", 40 | 41 | elementsDesc: ( 42 | shared: ('midiChan': 0, preset: \B), 43 | 44 | // Preset B has the same elements as Preset A, and single channel pressure. 45 | elements: [ 46 | ( 47 | key: \k, 48 | shared: ('elementType': 'pad', groupType: \noteOnOff, 'spec': 'midiVel'), 49 | elements: (24..108).collect { |midinote, i| 50 | var xindex = i % 24, yindex = i div: 24; 51 | var octDists = [0, 7] +.x [0, 0.5, 1, 1.5, 2, 3, 3.5, 4, 4.5, 5, 5.5, 6]; 52 | var xpos = octDists[xindex]; 53 | var ypos = (3 - yindex) - (xpos.frac * 0.9) + 0.5 * 1.9; 54 | 55 | ( 56 | key: midinote, midiNum: midinote, 57 | shared: (style: (row: ypos, column: xpos * 0.6, width: 0.7)) 58 | ) 59 | } 60 | ), 61 | ( 62 | key: 'bend', 63 | 'midiMsgType': 'bend', 'elementType': 'bender', 'midiNum': 0,'spec': 'midiBend', 64 | style: (row: 0, column: 5, width: 2) 65 | ), 66 | // Preset B mode only adds single channel pressure from all keys 67 | ( 68 | key: 'monotouch', 69 | 'midiMsgType': 'cc', 'elementType': 'keyTouch', 'midiNum': 1,'spec': 'midiCC', 70 | style: (row: 0, column: 7, width: 2) 71 | ) 72 | ] 73 | ) 74 | ) 75 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/qunexus/keith-mcmillen-qunexus_port1_C.desc_off.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | General QuNexus info: 4 | The QuNexus has 3 input ports, which require three separate desc files. 5 | Its general info in is "keith-mcmillen-qunexus.parentDesc.scd". 6 | 7 | This file is for Port 1, portIndex 0, and Preset C. 8 | 9 | In this mode, the QuNexus allocates notes to 10 rotating midi chans, 10 | and sends poly bend and pressure mod (cc1) on those chans. 11 | this is not directly supported by MOdality yet; 12 | to be adapted from an example for the Roli keyboard soon. 13 | 14 | */ 15 | 16 | ( 17 | ///////// GENERIC INFORMATION COPIED TO ALL QUNEXUS DESC FILES: /////// 18 | 19 | parentDesc: "keith-mcmillen-qunexus", 20 | 21 | ///////// SPECIFIC INFORMATION for this port and config: /////// 22 | 23 | elementTypes: [\pianoKeyChan, \bend, \keyTouch], 24 | status: ( 25 | linux: "not done yet", 26 | osx: "not done yet", 27 | win: "not done yet"), 28 | 29 | idInfo: (deviceName: "QuNexus", srcPortIndex: 0, destPortIndex: 0), 30 | portName: "Port 1", 31 | modeName: "Preset C", 32 | 33 | 34 | description: ( 35 | 36 | // Preset C mode is the most interesting: 37 | // it adds Poly Pitch Bend on up to 10 channels rotating, 38 | // plus pressure on cc1, which also rotates. 39 | // how to make a special MKtl for it? 40 | // see the Roli example ... to be adapted soonish. 41 | // add page: \C to all elements 42 | ) 43 | ); 44 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/qunexus/keith-mcmillen-qunexus_port1_D.desc_off.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | General QuNexus info: 4 | The QuNexus has 3 input ports, which require three separate desc files. 5 | Its general info in is "keith-mcmillen-qunexus.parentDesc.scd". 6 | 7 | This file is for Port 1, Preset D, which is 8 | for drums (uses chan 10) and triggering clips. 9 | /// TO BE DONE! 10 | 11 | */ 12 | 13 | ( 14 | ///////// GENERIC information in parentDesc: /////// 15 | 16 | parentDesc: "keith-mcmillen-qunexus", 17 | 18 | ///////// SPECIFIC INFORMATION for this port and config: /////// 19 | 20 | elementTypes: [], 21 | 22 | idInfo: (deviceName: "QuNexus", srcPortIndex: 0, destPortIndex: 0), 23 | portName: "Port 1", 24 | modeName: "Preset D", 25 | status: ( 26 | linux: "not done yet", 27 | osx: "not done yet", 28 | win: "not done yet"), 29 | 30 | // Preset D mode is for drums and clips - 31 | // please add when someone needs that mode. 32 | // add page: \D to elements when known 33 | description: ( 34 | 35 | ) 36 | ); 37 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/qunexus/keith-mcmillen-qunexus_port2.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | General QuNexus info: 4 | The QuNexus has 3 input ports, which have three separate desc files. 5 | Its general info in is "keith-mcmillen-qunexus.parentDesc.scd". 6 | 7 | This file is for Port 2 = portIndex 1. 8 | This port is for the MIDI Expander in/output for connecting external MIDI hardware. 9 | NOTE: One never needs to make an MKtl from this desc, 10 | it is only for information purposes. 11 | 12 | // For an example how to connect an external device thru a MIDI port, see: 13 | "Connecting external MIDI devices".openHelpFile; 14 | */ 15 | 16 | ( 17 | ///////// GENERIC INFORMATION COPIED TO ALL QUNEXUS DESC FILES: /////// 18 | parentDesc: "keith-mcmillen-qunexus", 19 | 20 | ///////// SPECIFIC INFORMATION for this port and config: /////// 21 | 22 | elementTypes: [], 23 | deviceType: \externalMidiPort, 24 | 25 | status: ( 26 | linux: "unknown", 27 | osx: "tested desc only, without ext. device, looks ok. 2016-03-17, adc", 28 | win: "unknown"), 29 | 30 | idInfo: (deviceName: "QuNexus", srcPortIndex: 1, destPortIndex: 1), 31 | portName: "Port 2", 32 | 33 | // port is for connecting external MIDI, so elementsDesc is empty. 34 | elementsDesc: ( 35 | elements: [] 36 | ) 37 | 38 | ); 39 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/rme-audio-totalmix.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | * start on RME fireface by OSC support: 3 | - implement first message from totalmix osc table, 4 | - add others later 5 | 6 | 7 | // test direct totalmix osc remote control: 8 | f = NetAddr.localAddr.port = 7001; 9 | f.sendMsg("/1/volume1", 1.0.rand); 10 | f.sendMsg("/1/volume8", 1.0.rand); 11 | 12 | // test first message by MKtl: 13 | m = MKtl(\ttm, "rme-audio-totalmix"); 14 | m.elAt(\invol).choose.value_(1.0.rand); 15 | */ 16 | 17 | ( 18 | deviceName: "TotalMix", 19 | deviceType: \mixer, 20 | protocol: \osc, 21 | 22 | idInfo: "TotalMix", 23 | netAddrInfo: (srcPort: 9001, destPort: 7001, ipAddress: "localhost"), 24 | 25 | status: ( 26 | linux: "unknown", 27 | osx: "unknown", 28 | win: "unknown" 29 | ), 30 | deviceInfo: ( 31 | // vendorURI: "http://", 32 | // manualURI: "http://", 33 | // description: "", 34 | // features: [ 35 | // ], 36 | longName: "RME Audio TotalMix" 37 | ), 38 | 39 | elementsDesc: ( 40 | elements: [ 41 | ( 42 | key: \invol, 43 | shared: (elementType: \fader, ioType: \inout, 44 | spec: \unipolar), 45 | elements: (1..8).collect { |i| 46 | (oscPath: "/1/volume%".format(i)); 47 | } 48 | ), 49 | /// more commands to follow here 50 | ] 51 | ) 52 | ) 53 | 54 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/saitek-cyborg-x.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | compass on Saitek Joystick has these deviceValues 3 | 15: center 4 | 1: up 5 | 3: right 6 | 5: down 7 | 7: left. 8 | 9 | how to map that in a spec? see saitek-impact-gamepad.desc 10 | 11 | MKtlDesc.loadDescs; 12 | MKtl(\cybx).free; 13 | MKtl(\cybx, "*cyborg-x").gui; 14 | */ 15 | ( 16 | deviceName: "Cyborg X_Saitek", 17 | deviceType: \joystick, 18 | idInfo: "Cyborg X_Saitek", 19 | protocol: 'hid', 20 | status: ( 21 | linux: "unknown", 22 | osx: "tested and working. 2016-03-17, adc", 23 | win: "unknown"), 24 | 25 | 26 | deviceInfo: ( 27 | // vendorURI: "http://", 28 | // manualURI: "http://", 29 | // description: "", 30 | // features: [ 31 | // ], 32 | longName: "Saitek Cyborg X" 33 | ), 34 | 35 | elementsDesc: ( 36 | shared: ('ioType': 'in'), 37 | elements: [ 38 | ( 39 | key: \trig, 40 | 'hidUsage': 1, 'hidUsagePage': 9, 'elementType': 'trigger', 'spec': \hidBut, 41 | 'style': (row: 3, column: 4) 42 | ), 43 | ( 44 | key: \bt, 45 | shared: ('hidUsagePage': 9, 'elementType': 'button', 'spec': \hidBut ), 46 | elements: [ 47 | // buttons on joyHead 48 | (key: '2', 'hidUsage': 2, style: (row: 1, column: 3)), 49 | (key: '3', 'hidUsage': 3, style: (row: 1, column: 5)), 50 | (key: '4', 'hidUsage': 4, style: (row: 0, column: 3)), 51 | (key: '5', 'hidUsage': 5, style: (row: 0, column: 5)), 52 | 53 | // on base left 54 | (key: '6', 'hidUsage': 6, style: (row: 2.5, column: 2)), 55 | (key: '7', 'hidUsage': 7, style: (row: 3.5, column: 2)), 56 | (key: '8', 'hidUsage': 8, style: (row: 4.5, column: 2)), 57 | (key: '9', 'hidUsage': 9, style: (row: 5.5, column: 2)), 58 | 59 | // on left foot 60 | (key: '10', 'hidUsage': 10, style: (row: 6, column: 0)), 61 | (key: '11', 'hidUsage': 11, style: (row: 6, column: 1)), 62 | // on base, center 63 | (key: '14', 'hidUsage': 14, style: (row: 6, column: 4)) 64 | ], 65 | ), 66 | ( 67 | key: \scroll, 68 | shared: ('hidUsagePage': 9, 'elementType': 'scrollWheel', 'spec': \hidBut), 69 | elements: [ 70 | ( key: \up, 'hidUsage': 12, style: (row: 2, column: 3.5)), 71 | ( key: \down, 'hidUsage': 13, style: (row: 2, column: 4.5)), 72 | ] 73 | ), 74 | ( 75 | key: \joy, 76 | shared: ('hidUsagePage': 1, 'elementType': 'joyAxis', 'spec': \cent1), 77 | elements: [ 78 | (key: \x, 'hidUsage': 48, style: (row: 4, column: 3, height: 2)), 79 | (key: \y, 'hidUsage': 49, style: (row: 4, column: 4, height: 2)), 80 | (key: \rot, 'hidUsage': 53, style: (row: 4, column: 5, height: 2)) 81 | ] 82 | ), 83 | ( 84 | key: \compass, 'hidUsage': 57, 'hidUsagePage': 1, 'elementType': 'compass', 85 | spec: \unipolar, 86 | style: (row: 0.5, column: 4) 87 | ), 88 | ( 89 | key: \lever, 90 | shared: ('hidUsagePage': 1, 'elementType': 'lever', 'spec': \unipolar), 91 | elements: [ 92 | (key: \left, 'hidUsage': 50, style: (row: 4, column: 0, height: 2)), 93 | (key: \right, 'hidUsage': 54, style: (row: 4, column: 1, height: 2)) 94 | ] 95 | ) 96 | ] 97 | ); 98 | ) 99 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/shbobo-shnth.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | idInfo: "SHNTH_SHBOBO", 3 | protocol: 'hid', 4 | deviceName: "SHNTH_SHBOBO", 5 | deviceType: '\shbobo', 6 | elementTypes: [\fader, \button], 7 | status: ( 8 | linux: "tested and working (bgola)", 9 | osx: "unknown", 10 | win: "unknown" 11 | ), 12 | 13 | deviceInfo: ( 14 | vendorURI: "https://www.ciat-lonbarde.net/shbobo/index.html", 15 | description: "The SHNTH, a computer music device by SHBOBO, features the ARM Cortex 32 bit processor. It connects via USB to host computer (windows, linux, or macintosh) which may read its squish data, or program the standalone synthesizer in the computer music language, SHLISP. A switch enables internal batteries; electronic sounds out a stereo-mini jack. It is handtop: four bars on top feel your fingers and two woven antennae below sense palm flesh, complimented by a battery of buttons. There are red lights.", 16 | ), 17 | 18 | elementsDesc: ( 19 | elements: [ 20 | ( 21 | key: \pz, 22 | shared: (hidUsagePage: 1, elementType: \joyAxis, ioType: \in, spec: \cent1inv, mode: \center), 23 | elements: 4.collect {|idx| 24 | (key: idx, hidUsage: 48 + idx, style: (row: 2, column: 2+idx, height: 2)) 25 | } 26 | ), 27 | ( 28 | key: \an, 29 | shared: (hidUsagePage: 1, elementType: \joyAxis, ioType: \in, spec: [1,0]), 30 | elements: 2.collect {|idx| 31 | (key: idx, hidUsage: 52+idx, style: (row:4, column: 3+idx, height: 2)) 32 | } 33 | ), 34 | ( 35 | key: \bt, 36 | shared: (hidUsagePage: 9, elementType: \button, ioType: \in, spec: \hidBut, mode: \push), 37 | elements: 8.collect {|idx| 38 | (key: idx, hidUsage: idx+1, style: (row: if (idx % 2 == 0) { 1 } { 0 }, column:[0,1,2,3,5,4,7,6][idx])) 39 | } 40 | ) 41 | ] 42 | ) 43 | ); 44 | 45 | 46 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/steinberg-cmc-fd.desc.scd: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | // test this desc: 4 | ~filename = thisProcess.nowExecutingPath.basename.split($.).first.postcs; 5 | d = MKtlDesc.loadDescs(~filename).first; 6 | // close old m, open new one, make gui: 7 | m.free; m = MKtl(\fd, "*cmc-fd"); m.gui; 8 | m.trace; 9 | m.elementsDict.size; 10 | m.postElements; 11 | */ 12 | 13 | ( 14 | deviceName: "Steinberg CMC-FD", 15 | protocol: \midi, 16 | deviceType: \controller, 17 | elementTypes: [\fader, \button], 18 | status: ( 19 | linux: "unknown", 20 | osx: "tested, code seems ok, my device broken. 2016-03-15, adc", 21 | win: "unknown"), 22 | 23 | idInfo:"Steinberg CMC-FD", 24 | 25 | deviceInfo: ( 26 | vendorURI: "https://www.steinberg.net/en/products/audio_interfaces/cmc_series/specs_downloads.html", 27 | manualURI: "https://www.steinberg.net/fileadmin/files/PRODUCTS/Controller/CMC/Downloads/CMC_Guide_Book_v1.pdf", 28 | // description: "", 29 | features: ["4 touch faders" ], 30 | longName: "Steinberg CMC-FD", 31 | notes: "discontinued.", 32 | 33 | ), 34 | elementsDesc: ( 35 | shared: ('elementType': 'pad', 36 | 'midiChan': 0, 'spec': 'midiVel'), 37 | elements: [ 38 | // the faders use bend for high res fader values (10 bit ) 39 | ( 40 | key: 'fader', 41 | shared: ('midiMsgType': 'bend', 'elementType': 'slider', 42 | 'midiChan': 0, 'spec': 'midiBend' 43 | ), 44 | elements: (0..3).collect { |chan, i| 45 | ('midiChan': chan,'spec': 'midiBend', 46 | style: (height: 4, row: 0, column: i) 47 | ) 48 | } 49 | ), 50 | 51 | // the buttons 52 | ( 53 | key: \shift, midiNum: 70, groupType: \noteOnOffBut, 54 | shared: (groupType: \noteOnOffBut, style: (row: 6, column: 1.5)) 55 | ), 56 | ( 57 | key: \bt, 58 | shared: ('midiMsgType': 'cc' ), // ??? 59 | elements: [ 60 | (key: 'chanL', midiNum: 48, shared: (style: (row: 7, column: 0))), 61 | (key: 'chanR', midiNum: 49, shared: (style: (row: 7, column: 1))), 62 | (key: 'bankL', midiNum: 46, shared: (style: (row: 7, column: 2))), 63 | (key: 'bankR', midiNum: 47, shared: (style: (row: 7, column: 3))), 64 | ] 65 | ), 66 | 67 | // fader touched or not 68 | ( 69 | key: 'ftouch', 70 | shared: ('midiMsgType': 'cc' ), // ??? 71 | // shared: ('elementType': 'button', 'spec': 'midiVel'), 72 | elements: (104..107).collect { |num, i| 73 | (key: (i+1).asSymbol, midiNum: num, 74 | shared: (style: (row: 3.9, column: i)) 75 | ); 76 | } 77 | ), 78 | // fader touched when shift is down sends channel mute 79 | ( 80 | key: 'fmute', 81 | shared: ('midiMsgType': 'cc' ), // ??? 82 | // shared: ('elementType': 'button', 'midiChan': 0, 'spec': 'midiBut'), 83 | elements: (8..11).collect { |num, i| 84 | ( 85 | key: (i+1).asSymbol, midiNum: num, 86 | shared: (style: (row: 4.8, column: i)) 87 | ); 88 | } 89 | ), 90 | 91 | ] 92 | ); 93 | ) 94 | 95 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/steinberg-cmc-qc/steinberg-cmc-qc.parentDesc.scd: -------------------------------------------------------------------------------- 1 | 2 | ( 3 | deviceName: "Steinberg CMC-QC", 4 | protocol: \midi, 5 | deviceType: \controller, 6 | elementTypes: [\encoder, \button], 7 | numPorts: 3, 8 | 9 | deviceInfo: ( 10 | vendorURI: "http://www.steinberg.net/en/products/controllers/cmc_series/models/cmc_qc.html", 11 | // manualURI: "", 12 | // description: "", 13 | // features: [], 14 | // notes: "", 15 | 16 | ) 17 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/steinberg-cmc-qc/steinberg-cmc-qc_port1.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | To Do: 3 | 4 | * study manual how hardware mode switching works ... 5 | 6 | * element descriptions seem right, 7 | but element responders are not activated correctly ... 8 | no trace, no action, though MIDIFunc trace posts them fine. 9 | 10 | 11 | thisProcess.nowExecutingPath.postcs.load; 12 | 13 | MIDIFunc.trace; 14 | 15 | MKtlDesc.loadDescs("steinberg-cmc*"); 16 | m.free; m = MKtl('qc_1', "*qc_port1"); m.gui; 17 | m.postElements; 18 | m.trace; 19 | 20 | */ 21 | ( 22 | parentDesc: "steinberg-cmc-qc", 23 | 24 | status: ( 25 | linux: "unknown", 26 | osx: "tested, elements. 2016-03-16, adc", 27 | win: "unknown"), 28 | 29 | idInfo: (deviceName: "Steinberg CMC-QC", srcPortIndex: 0, destPortIndex: 0), 30 | 31 | elementsDesc: ( 32 | elements: [ 33 | // active after bt EQ1, 2, 3 was pressed 34 | ( 35 | key: \knRel, 36 | shared: ('midiMsgType': 'control', 'elementType': 'knob', 'midiChan': 0, 'spec': 'midiCC'), 37 | elements: (16..23).collect { |ccnum, i| 38 | ('midiNum': ccnum, style: (row: i div: 4, column: i % 4 )) 39 | } 40 | ), 41 | // // active after bt MIDI was pressed 42 | // ( 43 | // key: \kn, 44 | // shared: ('midiMsgType': 'cc', 'elementType': 'knob', 'midiChan': 0, 'spec': 'midiCC'), 45 | // elements: [74, 71, 10, 7, 73, 72, 91, 93].collect { |ccnum, i| 46 | // ('midiNum': ccnum, style: (row: i div: 4 + 2, column: i % 4 )) 47 | // } 48 | // ), 49 | 50 | 51 | (key: \bt, 52 | shared: ('elementType': 'pad', groupType: \noteOnOffBut, spec: 'midiBut' ), 53 | elements: [ 54 | ['EQ1', 42, (style: (row: 2, column: 0))], 55 | ['EQ2', 50, (style: (row: 2, column: 1))], 56 | ['EQ3', 44, (style: (row: 2, column: 2))], 57 | ['EQ4', 116, (style: (row: 2, column: 3))], 58 | 59 | ['F1', 54, (style: (row: 3, column: 0))], 60 | ['F2', 55, (style: (row: 3, column: 1))], 61 | ['F3', 56, (style: (row: 3, column: 2))], 62 | ['F4', 57, (style: (row: 3, column: 3))], 63 | 64 | ['shift', 70, (style: (row: 4, column: 1.5))], 65 | 66 | ['chanL', 48, (style: (row: 5, column: 0))], 67 | ['chanR', 49, (style: (row: 5, column: 1))], 68 | ['R', 81, (style: (row: 5, column: 2))], 69 | ['W', 78, (style: (row: 5, column: 3))] 70 | ].collect { |list, i| 71 | (key: list[0], midiNum: list[1], shared: list[2]) 72 | } 73 | ), 74 | ] 75 | ) 76 | ); 77 | -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/steinberg_cmc_pd/steinberg-cmc-pd.parentDesc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | According to Manual, 3 | Port 1 is for pd->comp, comp->pd: pads do noteOn/off, noteNum, velocity. 4 | Port 2 is for pd>comp, buttons (shift, curveSetup, 4velmode, browse) 5 | and rot knob; browse can be sent back 6 | Port 3 is apparently unused (i.e. reserved for company-only uses). 7 | 8 | pads and 4velmode also send sysex on port 2; 9 | 4velmode button toggles its light, but sends noteOn/off on touch/release. 10 | (state likely sent in sysex.) 11 | 12 | --- velocity value for pads is always 16! 13 | --- in 4velmode the same note is on buts 1,5,9,13 etc; 14 | and vel changes between the four buttons from 31, 63, 95, 127. 15 | 16 | - shift button is used to switch between note combinations on the pads: 17 | - shift1, pads 1-16: 18 | 1: [35, 36, 38, 40, 39,44,42,46, 41,45,47,48, 51,55,49,56], 19 | 2: (4..19), 20 | 3: (20..35), 21 | 4: (36..51), 22 | 5: (52..67), 23 | 6: (68..83), 24 | 7: (84..99), 25 | 8: (100..115), 26 | // 9: (4..19), 9-15 seem identical to 2-8 27 | // 10: (20..35), 28 | // 11: (36..51), 29 | // 12: (52..67), 30 | // 13: (68..83), 31 | // 14: (84..99), 32 | // 15: (100..115), 33 | // 16: all sysex messages // not document what these are 34 | 35 | */ 36 | ( 37 | deviceName: "Steinberg CMC-PD", 38 | protocol: \midi, 39 | deviceType: \drumpad, 40 | elementTypes: [\pad, \button], 41 | numPorts: 3, 42 | 43 | deviceInfo: ( 44 | vendorURI: "http://www.steinberg.net/en/products/controllers/cmc_series/models/cmc_pd.html", 45 | // manualURI: "", 46 | // description: "", 47 | // features: "", 48 | longName: "Steinberg CMC-PD" 49 | ) 50 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/thrustmaster-megaworldectronics.desc.scd: -------------------------------------------------------------------------------- 1 | ( 2 | deviceName: "USB Joystick_MegaWorldectronics", 3 | protocol: \hid, 4 | deviceType: \joystick, 5 | elementTypes: [\joystick, \button, \hat, \slider], 6 | status: ( 7 | linux: "unknown", 8 | osx: "tested desc, looks good. 2016_03_23, adc", 9 | win: "unknown"), 10 | 11 | idInfo: "USB Joystick_MegaWorldectronics", 12 | 13 | deviceInfo: ( 14 | // vendorURI: "", 15 | // manualURI: "", 16 | // description: "", 17 | // features: [], 18 | // notes: "", 19 | 20 | longName: "ThrustMaster joystick" 21 | ), 22 | elementsDesc: ( 23 | elements: [ 24 | ( 25 | key: \bt, 26 | shared: ('hidUsagePage': 9, 'elementType': 'button', 'ioType': 'in',spec: \hidBut ), 27 | elements: [ 28 | (key: \1, 'hidUsage': 1), 29 | (key: \2, 'hidUsage': 2), 30 | (key: \3, 'hidUsage': 3), 31 | (key: \4, 'hidUsage': 4), 32 | ] 33 | ), 34 | ( 35 | key: 'hat', 36 | 'hidUsage': 57, 'hidUsagePage': 1, 'elementType': 'hatSwitch', 37 | 'ioType': 'in', spec: \cent1, mode: \center), 38 | ( 39 | key: \joy, 40 | shared: ('hidUsagePage': 1, 'elementType': 'joyAxis', 'ioType': 'in', 41 | spec: \cent1, mode: \center ), 42 | elements: [ 43 | (key: \x, 'hidUsage': 48), 44 | (key: \y, 'hidUsage': 49) 45 | ] 46 | ), 47 | ( 48 | key: 'sl', 'hidUsage': 187, 'hidUsagePage': 2, 'elementType': 'slider', 49 | 'ioType': 'in', spec: \cent1, mode: \center ) 50 | ] 51 | ) 52 | ) -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/touche.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This controller comes in two variants: SE (which does not have mahogani) and another version with mahogani, both should work the same with this desc. 4 | 5 | It has 4 "shiftings" of sensitivity (left, right, top, bottom) which are available here, but also an encoder which is used to adjust the sensitivity of the device (not available here). 6 | 7 | ----- Test code ----- 8 | m = MKtl('touchy', "touche"); 9 | 10 | // A Sine wave for each corner of the controller 11 | ( 12 | var minfreq=80, maxfreq=2500; 13 | 14 | // A feedback sine wave for each corner of the controller 15 | Ndef(\fourSines, {|f1, f2, f3, f4, fb1=0.5, fb2=0.5, fb3=0.5, fb4=0.5 amp=0.25| 16 | amp*Splay.ar(SinOscFB.ar([f1,f2,f3,f4], [fb1, fb2, fb3, fb4])) 17 | }).play; 18 | 19 | Ndef(\fourSines)[1] = \filter -> {|in, verb=0.5| 20 | FreeVerb2.ar(in[0], in[1], 1, verb.linlin(0.0,1.0,5.0,0.0001), 0.1) 21 | }; 22 | 23 | Ndef(\fourSines)[2] = \filter -> {|in, pitch=0.5| 24 | PitchShift.ar(in, 0.25, pitch.linlin(0.0,1.0,0.1,4.0), pitch/4, pitch/3) 25 | }; 26 | 27 | m.elAt('sl', 'top').action = {|el| 28 | Ndef(\fourSines).set( 29 | \f1, el.value.linexp(0.0,1.0,minfreq,maxfreq), 30 | \fb4, el.value, 31 | \verb, el.value 32 | ) 33 | }; 34 | 35 | m.elAt('sl', 'bottom').action = {|el| 36 | Ndef(\fourSines).set( 37 | \f2, el.value.linexp(0.0,1.0,minfreq,maxfreq), 38 | \fb3, el.value, 39 | \pitch, el.value, 40 | )}; 41 | 42 | m.elAt('sl', 'left').action = {|el| 43 | Ndef(\fourSines).set( 44 | \f3, el.value.linexp(0.0,1.0,minfreq,maxfreq), 45 | \fb1, el.value, 46 | \wet1, el.value 47 | )}; 48 | 49 | m.elAt('sl', 'right').action = {|el| 50 | Ndef(\fourSines).set( 51 | \f4, el.value.linexp(0.0,1.0,minfreq,maxfreq), 52 | \fb2, el.value, 53 | \wet2, el.value 54 | ) 55 | }; 56 | 57 | ) 58 | */ 59 | 60 | ( 61 | deviceName: "touche", 62 | protocol: 'midi', 63 | deviceType: 'controller', 64 | // elementTypes: [], 65 | status: ( 66 | linux: "tested 22-01-2020 by Mads Kjeldgaard", 67 | osx: "unknown", 68 | win: "unknown" 69 | ), 70 | idInfo: "TOUCHE_SE", 71 | deviceInfo: ( 72 | vendorURI: 'https://www.expressivee.com/touche-se', 73 | manualURI: 'https://www.expressivee.com/media/downloadable/files/ee/TOU002-Touche_SE/touche_se_manual.pdf', 74 | description: "A touch controller with 4 axis and an encoder to adjust sensibility", 75 | //features: [], 76 | //hasScribble: false 77 | ), 78 | elementsDesc: ( 79 | elements: [ 80 | ( 81 | key: \sl, 82 | shared: ('midiChan': 0, 'midiMsgType': 'cc', 'elementType': 'slider', 'spec': 'midiCC'), 83 | elements: 84 | [ 85 | ( key: \bottom, midiNum: 16 , style: (row: 0, column: i, width: 1, height: 8) ), 86 | ( key: \top, midiNum: 17 , style: (row: 0, column: i, width: 1, height: 8) ), 87 | ( key: \left, midiNum: 18 , style: (row: 0, column: i, width: 1, height: 8) ), 88 | ( key: \right, midiNum: 19 , style: (row: 0, column: i, width: 1, height: 8) ), 89 | ] 90 | ) 91 | ] 92 | ) 93 | ); -------------------------------------------------------------------------------- /Modality/MKtlDescriptions/vmeter-vmeter.desc.scd: -------------------------------------------------------------------------------- 1 | 2 | ( 3 | deviceName: "VMeter 1.30 A", 4 | protocol: \midi, 5 | deviceType: \fader, 6 | elementTypes: [\fader], 7 | status: ( 8 | linux: "unknown", 9 | osx: "tested desc, looks good. 2016-03-23, adc", 10 | win: "unknown"), 11 | 12 | idInfo: "VMeter 1.30 A", 13 | 14 | deviceInfo: ( 15 | vendorURI: "http://www.vmeter.net/", 16 | // manualURI: "", 17 | // description: "", 18 | // features: [], 19 | notes: "out of production", 20 | 21 | // hasScribble: false 22 | ), 23 | elementsDesc: ( 24 | elements: [ 25 | (key: \sl, elementType: \slider, ioType: \inout, 26 | 'midiMsgType': 'cc', 'midiChan': 0, 'midiNum': 20,'spec': 'midiCC') 27 | ] 28 | ) 29 | ) -------------------------------------------------------------------------------- /Modality/Tests/0_BigTests_MKtlDescToMKtl.scd: -------------------------------------------------------------------------------- 1 | MKtlDesc.openFolder; 2 | 3 | // test that all descs create working MKtls: 4 | 5 | // load 6 | MKtlDevice.verbose = false; MKtlDesc.loadDescs; MKtlDesc.allDescs.size; 7 | 8 | // make MKtls, keep fails if any 9 | ( 10 | ~failed = List[]; 11 | MKtlDesc.allDescs.sortedKeysValuesDo { |key, desc| 12 | // desc.name.postln; 13 | try { MKtl(desc.name, desc.name.asString) } { 14 | ~failed.add(desc); 15 | "% failed!\n".postf(desc.name); 16 | }; 17 | };""; 18 | ~failed; 19 | ) 20 | 21 | // remove broken ones 22 | ~failed.do { |desc| MKtl.all.removeAt(desc.name); }; 23 | 24 | ( 25 | // test the working ones: 26 | 27 | ["OK, dict, elems, elemsOK, val, name:"].postln; 28 | MKtl.all.sortedKeysValuesDo { |key, mktl| 29 | var postme = mktl.postln; 30 | var numElemsInDict = mktl.elementsDict.size; 31 | var numTopElems = mktl.elementGroup.size; 32 | var allEls = mktl.elementsDict.every(_.isKindOf(MKtlElement)); 33 | var valOk = mktl.elementsDict.every {|el| el.name; el.value.isNumber }; 34 | var ok = allEls and: valOk and: (numElemsInDict > 0) and: (numTopElems > 0); 35 | if (ok.not) { 36 | [ 37 | "BROKEN:", mktl, numElemsInDict.asString.padLeft(3), 38 | numTopElems.asString.padLeft(3), 39 | allEls, valOk].postln; 40 | }; 41 | };""; 42 | ) 43 | 44 | -------------------------------------------------------------------------------- /Modality/Tests/2_MKtlElement_tests.scd: -------------------------------------------------------------------------------- 1 | //// Make MKtls by hand: 2 | 3 | x = MKtlElement(\x, (deviceSpec: [-100, 100], type: \joyX, ioType: \in)); 4 | x.deviceSpec; 5 | 6 | x = MKtlElement(\x, (deviceSpec: \freq, type: \joyX, ioType: \in)); 7 | x.deviceSpec; 8 | x.deviceSpec_([200, 2000, \exp]); 9 | x.deviceSpec; 10 | 11 | x.deviceSpec; 12 | // check 13 | x.value; 14 | x.value = 0.8; x.value; 15 | x.deviceValue; 16 | 17 | x.value = 0.25; x.value; 18 | x.value 19 | x.deviceValue 20 | x.dump 21 | 22 | x.name 23 | x.type 24 | x.ioType 25 | x.elementDescription; 26 | x.elementDescription_((spec: [100, -100], type: \joyY, ioType: \inout)); 27 | x.elementDescription; 28 | x.updateDescription((spec: [5, 105].asSpec, type: \joyXYZ)); 29 | x.deviceSpec; 30 | x.type 31 | x.ioType; 32 | x.deviceSpec 33 | x.deviceSpec_([5, 105] * 2); 34 | 35 | x.value_(0.75).value; 36 | x.deviceValue 37 | // cannot be nil, so it cannot get stuck in bad state 38 | x.deviceValue_(nil); 39 | x.deviceValue 40 | 41 | x.value; 42 | 43 | 44 | 45 | // AN EXAMPLE for making a derived MKtlElement, as in issue #72: 46 | // (math is not correct yet, bt works in principle) 47 | // // issue #72 - Adding custom filter elements to an existing (and instantiated) controller 48 | // m = MKtl.make(\meXYController, \meXYControl); 49 | // m.addVirtualElement(\xyPolar, {| me | 50 | // atan2(me.elements[\x], me.elements[\y]) 51 | // }); 52 | // 53 | 54 | ~post = { |el| [el.name, el.value].postln }; 55 | 56 | x.addAction(~post); 57 | x.value_(1.0.rand); 58 | x.valueAction_(0.6); 59 | 60 | y = MKtlElement(\y, (spec: [-100, 100], type: \joyY, ioType: \in)); 61 | y.addAction(~post); 62 | 63 | Slider2D(nil, Rect(0,0,200,200)).action_({ |sl2d| 64 | x.valueAction_(sl2d.x); y.valueAction_(sl2d.y); 65 | }).front; 66 | 67 | z = MKtlElement(\polar, (spec: [-pi, pi], type: \xyPolar, ioType: '?')); 68 | 69 | ~doZAction = { z.doAction }; 70 | [x, y].do {|el| el.addAction(~doZAction) }; 71 | [x, y].do { |el| el.removeAction(~post) }; 72 | 73 | 74 | // first add a function to calc value 75 | ~getXY = { |polar| 76 | polar.deviceValue_(atan2(x.deviceValue, y.deviceValue)); 77 | }; 78 | z.addAction(~getXY); 79 | z.addAction(~post); 80 | 81 | // polar values/math not ok yet, FIXME later. 82 | ~postPolar = { |polar| "polar atan2 value: % \n".postf(polar.deviceValue); }; 83 | // then one to do something with it 84 | z.removeAction(~post); 85 | z.addAction(~postPolar); 86 | 87 | 88 | // MKtlElements work in patterns 89 | x.value = 0.6; 90 | Pbind(\x, x).trace.play; 91 | x.value = 1.0.rand; 92 | 93 | Pbind(\x, Pfunc({ x.deviceValue })).trace.play; 94 | x.deviceValueAction = exprand(20, 200); 95 | 96 | -------------------------------------------------------------------------------- /Modality/Tests/5_TestMidiElementCompleteness.scd: -------------------------------------------------------------------------------- 1 | // test element completeness for midi: 2 | 3 | // use an existing source device, e.g. on mac, "IAC Driver" 4 | ~midisrc = MIDIClient.sources[0].device; 5 | 6 | // make all midi descs and mktls for them 7 | ~descs = MKtlDesc.loadDescs.select { |d| d.protocol == \midi }; 8 | ~mktls = ~descs.collect { |d| 9 | MKtl(d.name.asSymbol, d).openDeviceVia(~midisrc); 10 | }; 11 | 12 | ~descs.do { |d, i| 13 | var ddsize = d.elementsDict.size; 14 | var mktl = ~mktls[i]; 15 | var mksize = mktl.elementsDict.size; 16 | var res = if (ddsize == mksize) { "OK." } { "wrong!!" }; 17 | "% desc: % dict/mktl: % == % \n".postf(res, d.name, ddsize, mksize); 18 | mktl.free; 19 | };""; 20 | 21 | 22 | // tests with an individual desc 23 | m = MKtl(\x, "*oxy*"); 24 | // use an existing source device, e.g. on mac, "IAC Driver" 25 | x = MIDIClient.sources[0].device; 26 | m.openDeviceVia(x); 27 | m.device.midiKeyToElemDict.size; 28 | m.desc.elementsDict.size; 29 | m.device.midiKeyToElemDict.keys(SortedList).printAll;""; 30 | m.desc.elementsDict.keys(SortedList).printAll;""; 31 | m.desc.openFile; 32 | 33 | 34 | -------------------------------------------------------------------------------- /Modality/Tests/Linux_ManyMIDISources.scd: -------------------------------------------------------------------------------- 1 | // test whether lots of midi ins and outs are sorted correctly. 2 | // example is from linux, where apps auto-register midi IO. 3 | 4 | // give MIDIClient fake sources and destinations: 5 | MIDIClient.prSetSources([ 6 | MIDIEndPoint("System", "Timer", 0), 7 | MIDIEndPoint("System", "Announce", 1), 8 | MIDIEndPoint("Midi Through", "Midi Through Port-0", 917504), 9 | MIDIEndPoint("nanoKONTROL", "nanoKONTROL MIDI 1", 1310720), 10 | MIDIEndPoint("nanoKONTROL2", "nanoKONTROL2 MIDI 1", 1310721), 11 | MIDIEndPoint("nanoKONTROL2", "nanoKONTROL2 MIDI 1", 1310722), 12 | MIDIEndPoint("BCR2000", "BCR2000 MIDI 1", 1572864), 13 | MIDIEndPoint("BCR2000", "BCR2000 MIDI 2", 1572865), 14 | MIDIEndPoint("SuperCollider", "out0", 8388614), 15 | MIDIEndPoint("SuperCollider", "out1", 8388615), 16 | MIDIEndPoint("SuperCollider", "out2", 8388616), 17 | MIDIEndPoint("SuperCollider", "out3", 8388617), 18 | MIDIEndPoint("SuperCollider", "out4", 8388618) 19 | ]).prSetDestinations([ 20 | MIDIEndPoint("Midi Through", "Midi Through Port-0", 917504), 21 | MIDIEndPoint("nanoKONTROL", "nanoKONTROL MIDI 1", 2310720), 22 | MIDIEndPoint("nanoKONTROL2", "nanoKONTROL2 MIDI 1", 2310721), 23 | MIDIEndPoint("nanoKONTROL2", "nanoKONTROL2 MIDI 1", 2310722), 24 | MIDIEndPoint("BCR2000", "BCR2000 MIDI 1", 1572864), 25 | MIDIEndPoint("BCR2000", "BCR2000 MIDI 2", 1572865), 26 | MIDIEndPoint("BCR2000", "BCR2000 MIDI 3", 1572866), 27 | MIDIEndPoint("SuperCollider", "in0", 8388608), 28 | MIDIEndPoint("SuperCollider", "in1", 8388609), 29 | MIDIEndPoint("SuperCollider", "in2", 8388610), 30 | MIDIEndPoint("SuperCollider", "in3", 8388611), 31 | MIDIEndPoint("SuperCollider", "in4", 8388612), 32 | MIDIEndPoint("SuperCollider", "in5", 8388613) 33 | ]); 34 | 35 | // have MKtlLookup organize them 36 | MKtlLookup.addAllMIDI; 37 | MKtlLookup.midiAll.size; 38 | 39 | // this is what we have now: 40 | MKtlLookup.midiAll.sortedKeysValuesDo { |key, val| 41 | key.postcs; 42 | val.postcs; 43 | "---\n".postln; 44 | };""; 45 | 46 | // do we find the in and out of the single nanokontrol? 47 | MKtl(\x, "korg-nanokontrol"); 48 | MKtl(\x).desc.openTestCode; 49 | 50 | // do we find two nk2s, and get asked to provide a multiIndex? 51 | MKtl(\nk2, "korg-nanokontrol2"); 52 | 53 | // if so, take the first one 54 | MKtl(\nk2, "korg-nanokontrol2", multiIndex: 0); 55 | MKtl(\x).desc.openTestCode; 56 | 57 | // do we find the matching ports 1 of the bcr2000 ? 58 | MKtl(\br1, "behringer-bcr2000"); 59 | MKtl(\br1).device.midiOut.dump; // looks ok, have to test with device 60 | 61 | // open SC IO at 0 as MKtl with no elements: 62 | MKtl(\sc0, 'midi_4_supercollider', multiIndex: 0); 63 | 64 | -------------------------------------------------------------------------------- /Modality/Tests/NoteGroup_DeviceTests.scd: -------------------------------------------------------------------------------- 1 | // these devices are already adapted! 2 | // - label just says pad_x, no on/off 3 | 4 | m.free; m = MKtl(\qnx, "*1_AB"); m.trace.gui.showLabels; 5 | 6 | m.free; m = MKtl(\ax, "*axis49"); m.trace.gui.showLabels; 7 | 8 | MKtlDesc.loadDescs; l.free; l = MKtl(\apc, "*apcmini"); 9 | l.elementsDict.size; 10 | l.elementsDict.choose.elemDesc; 11 | l.trace.gui.showLabels; 12 | 13 | 14 | MKtlDesc.loadDescs; l.free; l = MKtl(\ic, "*icontrols"); 15 | l.elementsDict.size; 16 | l.elementsDict.choose.elemDesc; 17 | l.trace.gui.showLabels; 18 | 19 | MKtlDesc.loadDescs; l.free; l = MKtl(\mpk, "*mpkmini"); 20 | l.elementsDict.size; 21 | l.elementsDict.choose.elemDesc; 22 | l.trace.gui.showLabels; 23 | 24 | 25 | MKtlDesc.loadDescs; l.free; l = MKtl(\uc4, "*uc4"); 26 | l.elementsDict.size; 27 | l.elementsDict.choose.elemDesc; 28 | l.trace.gui.showLabels; 29 | 30 | MKtlDesc.loadDescs; l.free; l = MKtl(\lpd8, "*lpd8"); 31 | l.elementsDict.size; 32 | l.elementsDict.choose.elemDesc; 33 | l.trace.gui.showLabels; 34 | 35 | MKtlDesc.loadDescs; l.free; l = MKtl(\mpd18, "*mpd18"); 36 | l.elementsDict.size; 37 | l.elementsDict.choose.elemDesc; 38 | g = l.trace.gui.showLabels; 39 | 40 | MKtlDesc.loadDescs; l.free; l = MKtl(\btst, "*beatstep*16"); 41 | l.elementsDict.size; 42 | l.elementsDict.choose.elemDesc; 43 | g = l.trace.gui.showLabels; 44 | -------------------------------------------------------------------------------- /Modality/Tests/Polyphony_and_closing.scd: -------------------------------------------------------------------------------- 1 | // test controller polyphony when closing devices: 2 | 3 | // connect gamepad, use twice 4 | a = MKtl('a', "*drive").trace; a.gui; 5 | b = MKtl('b', "*drive").trace; b.gui; 6 | 7 | // close a - b should still work: 8 | a.free; 9 | 10 | // connect nanokontrol2, use twice 11 | MKtl(\nk2, "*trol2").trace; 12 | MKtl(\nk2B, "*trol2").trace; 13 | 14 | MKtl(\nk2).free; 15 | -------------------------------------------------------------------------------- /Modality/discussion/desc file info.md: -------------------------------------------------------------------------------- 1 | ## Controller naming conventions 2 | 3 | ## filname conventions 4 | 5 | _.desc.scd 6 | 7 | where 8 | 9 | + `````` – name of vendor with spaces replaced by ```_``` 10 | + ```device_name``` – name of the device with spaces replaced by ```_``` 11 | 12 | ## list of vendors 13 | 14 | Ableton 15 | AKAI 16 | Arturia 17 | Behringer 18 | EOWave 19 | Evolution 20 | Generic 21 | icon 22 | impact 23 | JessTech 24 | Keith McMillen 25 | Korg 26 | Logitech 27 | M-Audio 28 | Native Instruments 29 | Novation 30 | Numark 31 | ShanWan 32 | Sherman 33 | Snyderphonics 34 | Steinberg 35 | Teenage Engineering 36 | Thrustmaster 37 | x-io 38 | 39 | ### device type 40 | 41 | *array of types* 42 | 43 | + pad 44 | + wireless 45 | + ribbon 46 | + keyboard 47 | + gamepad 48 | + joystick 49 | + fader 50 | + mixer 51 | + matrix 52 | + transport 53 | + dj 54 | + rotary 55 | + step sequencer 56 | -------------------------------------------------------------------------------- /Modality/unused_shaky_descs/DanceMat.desc_OFF.scd: -------------------------------------------------------------------------------- 1 | 2 | 3 | [ 4 | \joy_X, ( type: \joyAxis, mode: \center, osx: ( cookie: 16, spec: \cent255),linux: ( slot: [3,0], spec: \cent1 ) ), 5 | \joy_Y, ( type: \joyAxis, mode: \center, osx: ( cookie: 17, spec: \cent255 ), linux: ( slot: [3,1], spec: \cent1inv ) ), 6 | \bt_select, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 12), linux: ( slot: [ 1, 297] ) ), 7 | \bt_start, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 11), linux: ( slot: [ 1, 296] ) ), 8 | \bt_x, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 9), linux: ( slot: [1, 294 ] ) ), 9 | \bt_up, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 5 ), linux: ( slot: [1,290 ] ) ), 10 | \bt_o, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 10 ), linux: ( slot: [1,295 ] ) ), 11 | \bt_left, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 3), linux: ( slot: [1,288 ] ) ), 12 | \bt_right, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 6), linux: ( slot: [1,291 ] ) ), 13 | \bt_triangle, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 7 ), linux: ( slot: [1, 292 ] ) ), 14 | \bt_down, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 4), linux: ( slot: [1,289 ] ) ), 15 | \bt_square, (type: \button, mode: \push, spec: \hidBut, osx: ( cookie: 8), linux: ( slot: [1, 293] ) ) 16 | ]; -------------------------------------------------------------------------------- /Modality/unused_shaky_descs/PuzzleSphere. desc_OFF.scd: -------------------------------------------------------------------------------- 1 | // Realplayer Puzzlesphere 2 | 3 | Spec.add(\cent1, [0, 1, \lin, 0, 0.5].asSpec); 4 | Spec.add(\centAccOSX, [0.0, 4096.0, \lin, 0, 0.5 ].asSpec ); 5 | 6 | [ 7 | // right hand side four labeled buttons 8 | \bt_up, (type: \button, osx: (cookie: 2), linux: (slot: [1,304]), spec: \hidBut, mode: \push), 9 | \bt_down, (type: \button, osx: (cookie: 3), linux: (slot: [1,305]), spec: \hidBut, mode: \push), 10 | \bt_right, (type: \button, osx: (cookie: 4), linux: (slot: [1,306]), spec: \hidBut, mode: \push), 11 | \bt_left, (type: \button, osx: (cookie: 5), linux: (slot: [1,307]), spec: \hidBut, mode: \push), 12 | \bt_red, (type: \button, osx: (cookie: 6), linux: (slot: [1,308]), spec: \hidBut, mode: \push), 13 | \bt_green, (type: \button, osx: (cookie: 7), linux: (slot: [1,309]), spec: \hidBut, mode: \push), 14 | 15 | // joystick axes switches 16 | \acc_X, (type: \accelerometer, osx: (cookie: 10, spec: \centAccOSX), linux: (slot: [3,0], spec: \cent1 ), mode: \center), 17 | \acc_Y, (type: \accelerometer, osx: (cookie: 11, spec: \centAccOSX ), linux: (slot: [3,1], spec: \cent1 ), mode: \center), 18 | \acc_Z, (type: \accelerometer, osx: ( cookie: 12, spec: \centAccOSX), linux: (slot: [3,2], spec: \cent1), mode: \center), 19 | ]; -------------------------------------------------------------------------------- /Modality/unused_shaky_descs/numark-orbit.desc.scd: -------------------------------------------------------------------------------- 1 | /* 2 | * todo: enter gui positions of elements! 3 | 4 | m = MKtl(\orb, "*orbit"); 5 | m.gui; 6 | MKtl.addSpec(\selectNumark, [0, 4]); 7 | 8 | */ 9 | 10 | ( 11 | deviceName: "Numark ORBIT", 12 | protocol: 'midi', 13 | deviceType: \djController, 14 | elementTypes: [\button, \encoder], 15 | status: ( 16 | linux: "unknown", 17 | osx: "unknown", 18 | win: "unknown"), 19 | 20 | idInfo: "Numark ORBIT", 21 | 22 | deviceInfo: ( 23 | vendorURI: "http://www.numark.com/product/orbit", 24 | // manualURI: "", 25 | // description: "", 26 | features: [ 27 | "wireless", 28 | "16 backlit pads", 29 | "4 banks", 30 | "4 knobs per bank", 31 | "2-axis accelerometer", 32 | "control wheel", 33 | ], 34 | // notes: "", 35 | ), 36 | elementsDesc: ( 37 | elements: [ 38 | ( 39 | key: 'pad', 40 | shared: ('elementType': 'pad', 'spec': 'midiVel'), 41 | elements: 4.collect { arg chan; 42 | ( 43 | shared: ('midiChan': chan), 44 | elements: (36..51).collect { arg num, i; 45 | MKtlDesc.notePair( 46 | (i+1).asSymbol, num, 47 | // add style for pad layout.. 48 | (style: (row: i, column: 0)) 49 | ) 50 | 51 | } 52 | ) 53 | } 54 | ), 55 | ( 56 | key: 'sh', 57 | shared: ('type': 'key', 'midiChan': 15,'spec': 'midiVel'), 58 | elements: [ 59 | MKtlDesc.notePair('l', 3, (style: (row: 3, column: 0)) 60 | ), 61 | MKtlDesc.notePair('r', 4, (style: (row: 4, column: 0)) 62 | ), 63 | ] 64 | ), 65 | // ------ cc ------------- 66 | ( 67 | key: 'dial', 68 | shared: ('midiMsgType': 'cc', 'type': 'dial', 69 | 'spec': 'midiCC', 'midiNum': 4), 70 | elements: 4.collect { arg chan; ('midiChan': chan) } 71 | ), 72 | ( 73 | key: 'move', 74 | shared: ('midiMsgType': 'cc', 'type': 'movement', 75 | 'spec': 'midiCC'), 76 | elements: 4.collect { arg chan; 77 | ( 78 | shared: ('midiChan': chan), 79 | elements: [ 80 | (key: 'l', 'midiNum': 9), 81 | (key: 'r', 'midiNum': 10) 82 | ] 83 | ) 84 | } 85 | ), 86 | ( 87 | key: 'k', 88 | 'midiMsgType': 'cc', 'type': 'button', 'midiChan': 15, 89 | 'midiNum': 2,'spec': 'selectNumark'), 90 | ( 91 | key: 'padbank', 92 | 'midiMsgType': 'cc', 'type': 'slider', 'midiChan': 15, 93 | 'midiNum': 1,'spec': 'selectNumark') 94 | ] 95 | ) 96 | ) -------------------------------------------------------------------------------- /Modality/unused_shaky_descs/sherman-filterbank-2.desc.scd: -------------------------------------------------------------------------------- 1 | // Sherman Filterbank 2 Descriptionfile 2 | /* 3 | m = MKtl(\x, "*bank-2"); 4 | m.gui; 5 | */ 6 | ( 7 | deviceName: "Sherman FilterBank 2", 8 | protocol: \midi, 9 | deviceType: \filterbank, 10 | elementTypes: [\button, \knob], 11 | status: ( 12 | linux: "unknown", 13 | osx: "unknown", 14 | win: "unknown"), 15 | 16 | idInfo: "Sherman FilterBank 2", 17 | 18 | deviceInfo: ( 19 | // vendorURI: "", 20 | // manualURI: "", 21 | // description: "", 22 | // features: [], 23 | // notes: "", 24 | 25 | ), 26 | elementsDesc: ( 27 | shared: ('midiChan': 15), 28 | elements: [ 29 | // MIDI coming in from the Sherman Filterbank 30 | ( 31 | key: 'tr', 32 | shared: ('elementType': 'button', 'spec': 'midiBut'), 33 | elements: [ 34 | MKtlDesc.notePair(0, 78, (style: (row: 0, column: 0)) ), 35 | MKtlDesc.notePair(1, 82, (style: (row: 0, column: 1)) ) 36 | ] 37 | ), 38 | // MIDI being sent to the Sherman Filterbank: 39 | ( 40 | key: 'cutFil1', 'midiMsgType': 'bend', 'elementType': 'knob', 41 | 'midiChan': 15, 'spec': 'midiBend', 'ioType': 'out'), 42 | ( 43 | key: 'resFil1', 'midiMsgType': 'touch', 'elementType': 'knob', 44 | 'spec': 'midiTouch', 'ioType': 'out'), 45 | ( 46 | key: 'cutFil2', 'midiMsgType': 'cc', 'elementType': 'knob', 47 | 'midiNum': 1, 'spec': 'midiCC', 'ioType': 'out'), 48 | ( 49 | key: 'resFil2', 'midiMsgType': 'cc', 'elementType': 'knob', 50 | 'midiNum': 2, 'spec': 'midiCC', 'ioType': 'out'), 51 | ( 52 | key: 'fmDepth', 'midiMsgType': 'cc', 'elementType': 'knob', 53 | 'midiNum': 4, 'spec': 'midiCC', 'ioType': 'out'), 54 | ( 55 | key: 'vcaBias', 'midiMsgType': 'cc', 'elementType': 'knob', 56 | 'midiNum': 7, 'spec': 'midiCC', 'ioType': 'out'), 57 | ( 58 | key: 'amDepth', 'midiMsgType': 'cc', 'elementType': 'knob', 59 | 'midiNum': 11, 'spec': 'midiCC', 'ioType': 'out'), 60 | ( 61 | key: 'attADSR', 'midiMsgType': 'cc', 'elementType': 'knob', 62 | 'midiNum': 5, 'spec': 'midiCC', 'ioType': 'out'), 63 | ( 64 | key: 'decADSR', 'midiMsgType': 'cc', 'elementType': 'knob', 65 | 'midiNum': 16, 'spec': 'midiCC', 'ioType': 'out'), 66 | ( 67 | key: 'relADSR', 'midiMsgType': 'cc', 'elementType': 'knob', 68 | 'midiNum': 17, 'spec': 'midiCC', 'ioType': 'out'), 69 | ( 70 | key: 'attAR', 'midiMsgType': 'cc', 'elementType': 'knob', 71 | 'midiNum': 18, 'spec': 'midiCC', 'ioType': 'out'), 72 | ( 73 | key: 'relAR', 'midiMsgType': 'cc', 'elementType': 'knob', 74 | 'midiNum': 19, 'spec': 'midiCC', 'ioType': 'out') 75 | ] 76 | ); 77 | ); 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modality Toolkit 2 | 3 | The *Modality Toolkit* is a library to facilitate accessing (hardware) controllers in SuperCollider. 4 | It is designed and developed by the ModalityTeam, a group of people that see themselves as both developers and (advanced) users of SuperCollider. 5 | 6 | The central idea behind the Modality-toolkit is to simplify creation of individual (electronic) instruments with SuperCollider, using controllers of various kinds. To this end, a common code interface, MKtl, is used for connecting controllers from various sources (and protocols). These are atm. HID and MIDI; OSC, Serialport and GUI-based are planned to be integrated. 7 | 8 | The name *Modality* arose from the idea to scaffold the creation of modal interfaces, i.e. to create interfaces where one physical controller can be used for different purposes and it is possible to *switch its functionality, even at runtime*. 9 | It is our believe that integration of such on-the-fly remapping features helps to create a setup much more flexible, powerful, and interesting to play. 10 | Such a modal interface allows to cope with fast changes of overall direction as it can be necessary when e.g. improvising with musicians playing acoustic instruments. 11 | 12 | For more information, visit the [Modality page](http://modalityteam.github.io/). 13 | 14 | ## Installation 15 | 16 | There are multiple ways to install Modality to your SuperCollider environment: 17 | 18 | + Quarks (recommended for generic installations of SC 3.7+) 19 | + git clone (recommended for active development) 20 | + manual zip-file (recommended for static standalone installations) 21 | 22 | ### Quarks install 23 | 24 | + evaluate ```Quarks.gui``` in SuperCollider 25 | + select and install ```Modality-toolkit``` 26 | 27 | ### git 28 | 29 | + Evaluate ````Platform.userExtensionDir```` to get the path to the SuperCollider extension folder. 30 | + Clone the modality toolkit to that folder via 31 | ``` 32 | git clone https://github.com/ModalityTeam/Modality-toolkit.git Modality 33 | ``` 34 | 35 | ### manual zip-file 36 | 37 | + download the [zip file](https://github.com/ModalityTeam/Modality-toolkit/archive/master.zip) of the current repository head. 38 | + Evaluate ````Platform.userExtensionDir```` to get the path to the SuperCollider extension folder. 39 | + unzip the downloaded file into the extensions folder. 40 | 41 | ## Getting started 42 | 43 | Please read the article on "Modality" in the SuperCollider help system (Here's the [unrendered version](https://github.com/ModalityTeam/Modality-toolkit/blob/master/Modality/HelpSource/Overviews/Modality.schelp) of it if you want to take a peek). 44 | 45 | ## Acknowledgements 46 | Modality and its research meetings have kindly been supported by [BEK](http://www.bek.no/) and [STEIM](http://steim.org/). The Modality toolkit is free software published under the GPL. 47 | 48 | --------------------------------------------------------------------------------