Superparticular samchillian
Jump to navigation
Jump to search
//ChucK code for Superparticular Samchillian //Samchillian idea by Leon Gruenbaum //superparticular-ratio implementation by Jacob Barton //paste these lines into a new document in miniAudicle, or save text as a .ck to run in command-line //change these to match your input/output device 0 => int inDeviceNum; 1 => int outDeviceNum; class MicroRobinMidiIO { MidiIn min; MidiOut mouse; MidiMsg inmsg, outmsg; 0 => int ctr; //int rr[128][3]; don't think we need this now. // index = note number? // column 0 = channel sent to // column 1 = note number sent int chans[14]; // list of channels used float holds[16]; // pitches of on notes, zero if off. [0,1,2,3,4,5,6,7,8,10,11,12,13,14] @=> chans; //exclude channel 10 (drums) & 16 (send channel) //microtuning stuff // PitchBend // input: pitch (midi note number float) & velocity of desired note // action: sends appropriate pitchbend message // (assuming pitchbend range = +/- 2 semitones) // output: note number required for correct frequency to be realized // sends pitchbend, assuming +/- wholestep pitchbend range // returns note number required for correct frequency fun int PitchBend(float pitch, int velocity) { //send pitchbend 224 + chans[ctr] => outmsg.data1; 0 => outmsg.data2; Math.round((pitch % 1.0) * 32.0 + 64.0) $ int => outmsg.data3; mouse.send(outmsg); return Math.floor(pitch) $ int; } // StartRelay // input: number of MIDI device, MidiTransform to be used // creates a loop ~ should be sporked fun void StartRelay(int deviceNum, MidiTransform mt) { if( !min.open(inDeviceNum)) me.exit(); if( !mouse.open(outDeviceNum)) me.exit(); // print out device that was opened <<< min.num(), " -> ", min.name() >>>; <<< mouse.num(), " -> ", mouse.name() >>>; while ( true) { min => now; while( min.recv(inmsg)) { if( inmsg.data1 % 16 == 0) // only receive on channel 1 { <<< "r ", inmsg.data1 / 16, inmsg.data1 % 16, inmsg.data2, inmsg.data3>>>; if( inmsg.data1 / 16 == 9) { mt.NoteOn(inmsg.data2, inmsg.data3); } if( inmsg.data1 / 16 == 8) { mt.NoteOff(inmsg.data2, inmsg.data3); } if( inmsg.data1 / 16 == 12) { //prog change apply to channels 1-16 //works! inmsg.data1 - (inmsg.data1 % 16) => int base; for(0=>int i; i<15; i++) { base + chans[i] => outmsg.data1; inmsg.data2 => outmsg.data2; i++; if( i == 15) { 0 => outmsg.data3; mouse.send(outmsg); break; } base + chans[i] => outmsg.data3; mouse.send(outmsg); inmsg.data2 => outmsg.data1; i++; if( i == 15) { 0 => outmsg.data2 => outmsg.data3; mouse.send(outmsg); break; } base + chans[i] => outmsg.data2; inmsg.data2 => outmsg.data3; mouse.send(outmsg); } } if( inmsg.data1 / 16 == 11) { //apply any controller data to channels 1-16 inmsg.data1 - (inmsg.data1 % 16) => int base; inmsg.data2 => outmsg.data2; inmsg.data3 => outmsg.data3; for(0=>int i; i<16; i++) { base + i => outmsg.data1; mouse.send(outmsg); } } } } } } // NoteOn // input: pitch in midi note-number extended, velocity // action: sends a MIDI pitchbend + note-on message to mouse on the current channel // keeping track of holds fun void NoteOn(float nn, int velocity) { IncrementCtr(); nn => holds[chans[ctr%14]]; PitchBend(nn, velocity) => outmsg.data2; // note on, right channel 144 + chans[ctr%14] => outmsg.data1; velocity => outmsg.data3; mouse.send(outmsg); //<<< "s ", outmsg.data1 / 16, outmsg.data1 % 16, outmsg.data2, outmsg.data3>>>; } // increments mod-14 counter, skipping over channels with // notes already on them, if possible. fun void IncrementCtr() { ctr; 0 => int i; for( i; i<14; i++) { if ( holds[chans[(ctr+i) % 14]] == 0.0) { (ctr + i) % 14 => ctr; return; } } (ctr + 1) % 14 => ctr; } // NoteOff // input: pitch & note-off velocity // action: finds the pitch & offs it. fun void NoteOff(float nn, int velocity) { 0 => int c; for(c; c<16; c++) { if(holds[c] == nn) // we found the pitch! { 128 + c => outmsg.data1; Math.floor(nn) $ int => outmsg.data2; velocity => outmsg.data3; mouse.send(outmsg); //<<< "s ", outmsg.data1 / 16, outmsg.data1 % 16, outmsg.data2, outmsg.data3>>>; 0.0 => holds[c]; return; } } <<<"MISS", nn>>>; // we couldn't find the pitch! // don't do anything. } fun void ControlChange(int channel, int prognum, int val) { 128 + channel => outmsg.data1; prognum => outmsg.data2; val => outmsg.data3; } } class MidiTransform { // superclass for MIDI transformers to be used by MicroRobinMidiIO MicroRobinMidiIO myIO; fun void LinkToIO(MicroRobinMidiIO io) { io @=> myIO; } fun void NoteOn( int nn, int vel) { return; } fun void NoteOff( int nn, int vel) { return; } } class SuperparticularSamchillian extends MidiTransform { 57. => float resentFreq; 57. => float prevFreq; 62 => int keyboardCenterNN; float prevFreqsByKey[128]; //set pans funky //for(0=>int f; f<16; f++) //{ // myIO.ControlChange(f, 9, f*8); //} fun void NoteOn( int nn, int vel) { if(nn == keyboardCenterNN) { myIO.NoteOn( Std.ftom(prevFreq), vel); } else { if(nn < keyboardCenterNN) { // superparticular! 1. + 1./(keyboardCenterNN - nn) /=> prevFreq; myIO.NoteOn( Std.ftom(prevFreq), vel); } else { if(nn > keyboardCenterNN) { 1. + 1./(nn - keyboardCenterNN) *=> prevFreq; myIO.NoteOn( Std.ftom(prevFreq), vel); } } } Std.ftom(prevFreq) => prevFreqsByKey[nn]; } fun void NoteOff( int nn, int vel) { myIO.NoteOff( prevFreqsByKey[nn], vel); } } MicroRobinMidiIO mrmio; SuperparticularSamchillian easy; easy.LinkToIO(mrmio); spork ~ mrmio.StartRelay( 1, easy); 1::second => now; KBHit kb; while(true) { kb => now; while( kb.more() ) { kb.getchar() => int c; <<< "ascii:", c>>>; easy.NoteOn(c, 88); } }