예제 #1
0
    private void StartChuckTimer()
    {
        chuckSubInstance.RunCode(@"
			1 => global float timeStep;
			global Event notifier;
			
			while( true )
			{
				notifier.broadcast();
				timeStep::second => now;
			}
		"        );

        myAdvancerListener.ListenForEvent(chuckSubInstance, "notifier", TimeStepDone);
    }
예제 #2
0
    void StartChuckMetronome(ChuckSubInstance myChuckTempo)
    {
        // instantiate Chuck Pitch Tracking code
        myChuckTempo.RunCode(@"

			100 => global float BEATS_PER_MIN;
			global Event mtrNotifier;
			while(true) 
			{
				mtrNotifier.broadcast();
				(60/BEATS_PER_MIN)::second => now;
			}
		"        );

        myMetronomeNotifier = gameObject.AddComponent <ChuckEventListener>();
        myMetronomeNotifier.ListenForEvent(myChuckTempo, "mtrNotifier", BroadcastBeat);
    }
    // Use this for initialization
    void Start()
    {
        myChuck = GetComponent <ChuckSubInstance>();
        myPos   = 0;

        myChuck.RunCode(@"
			1 => global float timeStep;
			global float pos;
			global Event notifier;

			fun void updatePos() {
				timeStep::second => dur currentTimeStep;
				currentTimeStep / 1000 => dur deltaTime;
				now => time startTime;
				
				pos => float originalPos;
								
				while( now < startTime + currentTimeStep )
				{
					deltaTime / currentTimeStep +=> pos;
					deltaTime => now;
				}
			}
			

			fun void playNote() {
				SinOsc foo => dac;
				0.2::second => now;
				foo =< dac;
			}

			while( true )
			{
				spork ~ playNote();
				spork ~ updatePos();
				notifier.broadcast();
				timeStep::second => now;
			}
		"        );

        myAdvancerSyncer = gameObject.AddComponent <ChuckFloatSyncer>();
        myAdvancerSyncer.SyncFloat(myChuck, "pos");           //current instance of chuck is determining pos value

        myAdvancerListener = gameObject.AddComponent <ChuckEventListener>();
        myAdvancerListener.ListenForEvent(myChuck, "notifier", RotateMyCube);
    }
예제 #4
0
    void Start()
    {
        myChuck = GetComponent <ChuckSubInstance>();

        // broadcast "notifier" every 250 ms
        myChuck.RunCode(@"
            global Event notifier;
            while( true )
            {
                notifier.broadcast();
                250::ms => now;
            }
        ");

        // create a ChuckEventListener on this gameObject
        ChuckEventListener listener = gameObject.AddComponent <ChuckEventListener>();

        // call MyCallback() on the Update() thread after every broadcast from "notifier"
        listener.ListenForEvent(myChuck, "notifier", MyCallback);
    }
예제 #5
0
    void RunChuckClock()
    {
        myChuck = GetComponent <ChuckSubInstance>();

        myChuck.RunCode(@"
			80.0 => global float bpm;
			//global float beatPos;
			global Event beatNotifier;
			
			while( true )
			{
				(15.0/bpm)::second => now; //increment by 16th note interval
				beatNotifier.broadcast();
			}
		"        );

        myTempoSyncer = gameObject.AddComponent <ChuckFloatSyncer>();
        myTempoSyncer.SyncFloat(myChuck, "bpm");
        myNextBeatListener = gameObject.AddComponent <ChuckEventListener>();
        myNextBeatListener.ListenForEvent(myChuck, "beatNotifier", ProcessBeat);
    }
예제 #6
0
    void RunChuckInstrument()
    {
        myChuck.RunCode(@"
			//--
			// name: pitch_track_looper.ck
			// desc: adc looper (via cepstrum and RMS-tresholded pitch-tracking) with various playback tones
			//
			// author: Camille Noufi
			//   date: Nov 2018
			//--

			// See ""Execution(Main)"" section for start of run-time code
			// SETUP modifies pitch tracking and quantization variables
			// HELPER FUNCTIONS do the work

			// *********************************************************************************
			// ******************* CONSTANT SYNCER VARIABLES ****************************
			// *********************************************************************************

			// constant (input) temporal values driving quantization
			global Event beatNotifier, countdownNotifier, recordNotifier, recordOffNotifier, kickNotifier, snareNotifier;
			100 => global float BEATS_PER_MIN; //tempo
			8 => global float BEATS_PER_MEAS; //meter x/4
			4 => float DIVS_PER_BEAT; //4 - 16th note quant, 2 - 8th note quant, etc...

			// *********************************************************************************
			// ******************* SETUP: INSTRUMENTS ******************************************
			// *********************************************************************************

			1 => global int instrument;
			global Event sporkTheLoop;
			
			// connect for synths
			JCRev r => dac;
			0.1 => r.mix;
			
			// DRUM
			me.dir() + ""/kick.wav"" => string drumfile;
			if( me.args() ) me.arg(0) => drumfile; 
			SndBuf kick => dac;
			0 => kick.gain;
			drumfile => kick.read; 

			me.dir() + ""/snare.wav"" => string snarefile;
			if( me.args() ) me.arg(0) => snarefile; 
			SndBuf snare => dac;
			snarefile => snare.read; 

			// *********************************************************************************
			// ******************* SETUP: GAMETRAK ******************************************
			// *********************************************************************************

			// z axis deadzone
			.032 => float DEADZONE;
			// which joystick
			0 => int device;
			// get from command line
			if( me.args() ) me.arg(0) => Std.atoi => device;

			// gametrak class
			class GameTrak
			{
				// timestamps
				time lastTime;
				time currTime;
				
				// previous axis data
				float lastAxis[6];
				// current axis data
				float axis[6];
			}

			// instantiate an instance of class
			GameTrak gt;
			// HID objects
			Hid trak;
			HidMsg msg;
			// open joystick 0, exit on fail
			if( !trak.openJoystick( device ) ) me.exit();
			// print
			<<< ""joystick "" + trak.name() + "" ready"", """" >>>;

			// spork control
			spork ~ gametrak();
			// print
			//spork ~ print();

			fun void print()
			{
				while( true )
				{
					// values
					<<< ""axes:"", gt.axis[0],gt.axis[1],gt.axis[2], gt.axis[3],gt.axis[4],gt.axis[5] >>>;
					100::ms => now;
				}
			}

			// gametrack handling
			fun void gametrak()
			{
				while( true )
				{
					// wait on HidIn as event
					trak => now;
					
					// messages received
					while( trak.recv( msg ) )
					{
						// joystick axis motion
						if( msg.isAxisMotion() )
						{            
							// check which
							if( msg.which >= 0 && msg.which < 6 )
							{
								// check if fresh
								if( now > gt.currTime )
								{
									// time stamp
									gt.currTime => gt.lastTime;
									// set
									now => gt.currTime;
								}
								// save last
								gt.axis[msg.which] => gt.lastAxis[msg.which];
								// the z axes map to [0,1], others map to [-1,1]
								if( msg.which != 2 && msg.which != 5 )
								{ msg.axisPosition => gt.axis[msg.which]; }
								else
								{
									1 - ((msg.axisPosition + 1) / 2) - DEADZONE => gt.axis[msg.which];
									if( gt.axis[msg.which] < 0 ) 0 => gt.axis[msg.which];
								}
							}
						}
						
						// joystick button down
						else if( msg.isButtonDown() )
						{
							<<< ""button"", msg.which, ""down"" >>>;
						}
						
						// joystick button up
						else if( msg.isButtonUp() )
						{
							<<< ""button"", msg.which, ""up"" >>>;
						}
					}
				}
			}



			// *********************************************************************************
			// ******************* SETUP: PITCH DETECTION VARIABLES ****************************
			// *********************************************************************************

			// analysis
			adc => PoleZero dcblock => BPF bpf => FFT fft =^ RMS rms => blackhole;
			FFT fft2 => blackhole;
			// set to block DC
			.99 => dcblock.blockZero;

			// set BPF params
			100 => float fL;
			1200 => float fH;
			Math.sqrt(fL * fH) => float fC;
			fC / (fH - fL) => float Q;
			bpf.set(fC, Q);


			// set FFT params
			1024 => int FFT_SIZE;
			0.5 => float HOP_SIZE;
			FFT_SIZE => fft.size;
			Windowing.hamming(fft.size()) => fft.window;
			// find sample rate
			second / samp => float SRATE;


			// ***************** SETUP: QUANTIZATION AND PLAYBACK VARIABLES ********************

			// sample and duration calculations
			60 => float SEC_PER_MIN;
			(BEATS_PER_MEAS * DIVS_PER_BEAT) $ int => int divsPerMeasure;
			(SEC_PER_MIN * SRATE) / (DIVS_PER_BEAT * BEATS_PER_MIN) => float samplesPerDiv; //samples per smallest note div
			Math.round(samplesPerDiv / (FFT_SIZE * HOP_SIZE)) $ int => int numFramesPerDiv;
			//<<< numFramesPerDiv >>>;
			SEC_PER_MIN / (BEATS_PER_MIN * DIVS_PER_BEAT) => float divDur; //duration in seconds of smallest note div

			//initialize storage arrays:
			//to hold measure frequencies
			float freqArr[divsPerMeasure][numFramesPerDiv]; 
			for (0 => int i; i<freqArr.size(); i++) 
				for (0 => int j; j<freqArr[0].size(); j++)
					0 => freqArr[i][j];
			//to hold midi keynums for all FFT frames
			float keynumArr[divsPerMeasure][numFramesPerDiv];
			for (0 => int i; i<keynumArr.size(); i++) 
				for (0 => int j; j<keynumArr[0].size(); j++)
					0 => keynumArr[i][j];
			// to hold per-note midi keynums
			int midiArr[divsPerMeasure]; 
			for (0 => int i; i<midiArr.size(); i++) 
				0 => midiArr[i];


			// *********************************************************************************
			// ***************************** EXECUTION (MAIN) **********************************
			// *********************************************************************************

			while(true) 
			{
				sporkTheLoop => now;
				spork ~ execute(instrument);
			}

			fun void execute(int type)
			{
				<<< ""Record Instrument"", type >>>;

				// 1 measure click-track countdown
				clickTrackCountdown();

				// record adc input for 1 measure
				recordADC_F0();

				// freq->keynum results
				convertF02KeyNum_AllFrames();

				// get likely keynum for each note
				computeMostLikelyKeyNum(type);

				// play back results in a loop
				playbackLoopGo(type);

			}



			// *********************************************************************************
			// ************************** HELPER FUNCTIONS (ACTUAL PROCESSING) *****************
			// *********************************************************************************


			// ******************************* clickTrackCountdown() *********************************
			fun void clickTrackCountdown()
			{
				countdownNotifier.broadcast();
				for (0 => int i; i < BEATS_PER_MEAS; i++)
				{
					beatNotifier.broadcast();
					spork ~ playClick();
					<<< (i + 1) >>>;
					(DIVS_PER_BEAT * divDur)::second => now;
				}
			}

			// ******************************* recordADC_F0() *********************************
			fun void recordADC_F0()
			{
				recordNotifier.broadcast();
				// for all notes in measure
				0 => float tmpF0;
				for (0=>int i; i < freqArr.size(); i++)
				{
					spork ~ recordNote(i);
					signalBeat(i);
					divDur::second => now;
				}
				recordOffNotifier.broadcast();
			}

			fun void recordNote(int i)
			{
				//for all buffer frames in note
				for (0=>int j; j<freqArr[0].size(); j++)
				{
					extractF0() => freqArr[i][j];
					(FFT_SIZE*HOP_SIZE)::samp => now;
				}
			}

			// ******************************* extractF0() *********************************
			fun float extractF0()
			{
				// if signal is above noise floor, extract the freq
				if (flagAboveRMSThresh())
				{
					return getF0viaSpectrumMax();
					//return getF0viaCepstrum();  
				}
				else
					return 0.0;
			}

			// ********************************** flagAboveRMSThresh() ******************************
			fun int flagAboveRMSThresh() // adjust hardcoded values depending on space/mic setup
			{
				// upchuck: take fft then rms
				rms.upchuck();
				1000 * rms.fval(0) => float thisScaledRMS;
				//<<<thisScaledRMS>>>;

				if (thisScaledRMS >= 0.1)
				{
					return 1;
				}
				else
				{
					return 0;
				}
			}

			// ********************************** getF0viaCepstrum() *******************************
			// cepstrum-based pitch: f0 = scaled_reciprocal(idx(max(IFFT(log(mag^2(FFT(audio_samples)))))))
			fun float getF0viaCepstrum()
			{
				// to hold frame's fft results
				FFT_SIZE/2 => int CEPST_SIZE;
				float logVals[CEPST_SIZE];
				complex c[CEPST_SIZE];
				float cr[CEPST_SIZE];
				
				//take the log of the squared magnitude of the fft (already computed to get rms)
				0 => float this_fval;
				for( 0 => int i; i < fft.fvals().size(); i++ )
				{
					fft.fval(i) => this_fval;
					Math.pow(this_fval,2) => this_fval; //square it
					Math.log(this_fval) => this_fval; //take the log
					this_fval => logVals[i];
				}    
				
				// take re(FFT) of 1024-point frame and put result into cepstrum array
				fft2.transform(logVals);
				fft2.spectrum(c);
				
				for( 0 => int i; i < c.size(); i++ )
				{
					c[i].re => cr[i];
				}  
				
				//find peak of cepstrum
				0 => float max; 0 => float abs_c; 0 => int qi; 
				for( 10 => int i; i < cr.size(); i++ ) // indices of sung freq quefrencies 100-1200hz
				{
					cr[i] => abs_c;
					if( abs_c > max )
					{
						abs_c => max;
						i => qi;
					}
				}
				//<<< quefrency >>>;
				
				// convert to to frequency
				SRATE / qi$float  => float target_freq;
				//<<<target_freq>>>;
				return target_freq;
			}

			// *********************************** getF0viaSpectrumMax *****************************
			fun float getF0viaSpectrumMax()
			{
				0 => float max; int where;
				for (0 => int i; i < fft.fvals().size(); i++)
				{
					// compare
					if (fft.fval(i) > max)
					{
						// save
						fft.fval(i) => max;
						i => where;
					}
				}

				// set freq
				where$float / fft.size() * SRATE => float target_freq;
				return target_freq;
			}

			// ********************************** convertF02KeyNum_AllFrames() ****************************
			fun void convertF02KeyNum_AllFrames()
			{
				for (0 => int i; i < freqArr.size(); i++)
				{
					for (0 => int j; j < freqArr[0].size(); j++)
					{
						freqArr[i][j] => float tmp;
						if (tmp >= fL)
							Math.round(Std.ftom(tmp)) => keynumArr[i][j];
					else
						0.0 => keynumArr[i][j];
						//<<<keynumArr[i][j]>>>;
					}
				}
			}

			// *********************************** computeMostLikelyKeyNum() **********************
			fun void computeMostLikelyKeyNum(int type)
			{
				12 => int octave;
				13 => int ninth;
				
				//for each note in measure
				for (0 => int i; i<keynumArr.size(); i++)
				{ 
					
					// count occurences of each keynum value
					float histogram[128]; 
					0 => float mode; 0 => int this_keynum;
					for (0 => int j; j < keynumArr[0].size(); j++) {
						keynumArr[i][j]$int => this_keynum;
						if (this_keynum<15) 
							0 => this_keynum;
						histogram[this_keynum] + 1 => histogram[this_keynum];
						Math.max(mode, histogram[this_keynum]) => mode;
					}
					
					// select mode - this is the midi pitch for the note
					for (0 => int j; j < histogram.size(); j++) {
						if (histogram[j] == mode) 
						{
							j => midiArr[i];
						}
					}        
				}
				
				// perform drum beat corrections
				if(type == 1 || type == 2) 
				{
					for (1 => int i; i < midiArr.size(); i++) {
						if (midiArr[i-1] != 0) {
							0 => midiArr[i];
						}
					}
				}
				// perform pitch corrections
				else 
				{
					for (1 => int i; i < midiArr.size(); i++) {
						//unison/octave doubling correction
						if (midiArr[i] == (midiArr[i-1] + octave)) {
							midiArr[i-1] => midiArr[i]; 
						}
						// octave doubling correction following interval change
						else if ((midiArr[i-1] != 0) && (midiArr[i] >= (midiArr[i-1] + ninth))) {
							(midiArr[i] - octave) => midiArr[i];
						}
						else if (midiArr[i] <= (midiArr[i-1] - ninth)) {
							(midiArr[i] + octave) => midiArr[i];
						}
						if(midiArr[i]<15)
						{
							0 => midiArr[i];
						}
						//<<<midiArr[i]>>>;
					} 
				}
			}

			// *********************************** playbackLoopGo() **********************
			fun void playbackLoopGo(int type)
			{
				int thisMidiArr[divsPerMeasure]; // to hold midi note keynums
				for (0 => int i; i < midiArr.size(); i++)
					midiArr[i] => thisMidiArr[i];
				while (true)
					playSynthesizedMeasure(type, thisMidiArr);
			}

			fun void playSynthesizedMeasure(int type, int midiArr[])
			{

				divDur::second => dur T;
				// SYNTH INSTRUMENTS
				if (type==5) {
					TriOsc t => ADSR e => r;       
					//0.5 => t.gain;
					(gt.axis[0] + 1.0) / 4.0 => t.gain; //left right
					for (0 => int i; i<midiArr.size(); i++)
					{
						Std.mtof(midiArr[i]) => t.freq;            
						spork ~ play(i, T, e, midiArr, [0.2,0.1,0.9,0.0], [0.0,0.0,0.9,0.0], [0.0,0.0,0.9,0.2]); //ADSR onset, sustain, last
						T => now;
					}
					0.0 => t.gain;
					t =< e =< r;    
				}
				else if (type==4) {
					SinOsc s => ADSR e => r;      
					//1 => s.gain;
					(gt.axis[1] + 1.0) / 2.0 => s.gain; //front and back
					for (0 => int i; i<midiArr.size(); i++)
					{
						Std.mtof(midiArr[i]) => s.freq;
						spork ~ play(i, T, e, midiArr, [0.2,0.1,0.9,0.0], [0.0,0.0,0.9,0.0], [0.0,0.0,0.9,0.3]); //ADSR onset, sustain, last
						T => now;
					}
					0.0 => s.gain; 
					s =< e =< r;    
				}
				else if (type==3) {
					SawOsc w => ADSR e => r;       
					//0.1 => w.gain;
					((gt.axis[2] + 1.0) / 8.0) - 1.0/10.0 => w.gain;
					for (0 => int i; i<midiArr.size(); i++)
					{
						Std.mtof(midiArr[i]) => w.freq;
						spork ~ play(i, T, e, midiArr, [0.2,0.1,0.9,0.0], [0.0,0.0,0.9,0.0], [0.0,0.0,0.9,0.2]); //ADSR onset, sustain, last
						T => now;
					}
					0.0 => w.gain; 
					w =< e =< r;    
				}
				// DRUMS
				else if (type==1) {
					for (0 => int i; i<midiArr.size(); i++)
					{
						if(midiArr[i]>10) 
						{
							kickNotifier.broadcast();
							0 => kick.pos;
							1 => kick.gain;
							1 => kick.rate;
						}
						T => now;
					}
				}
				else if (type==2) {
					for (0 => int i; i<midiArr.size(); i++)
					{
						if(midiArr[i]>10) 
						{
							snareNotifier.broadcast();
							0 => snare.pos;
							1 => snare.gain;
							1 => snare.rate;
						}
						T => now;
					}
				}
			}

			fun void play(int i, dur T, ADSR e, int midiArr[], float on[], float sus[], float last[]) 
			{
				//onset of note
				if ( (i > 0 && (midiArr[i] != midiArr[i-1])) || i==0) {
					e.set( (on[0]*divDur)::second, (on[1]*divDur)::second, on[2], (on[3]*divDur)::second );  //(a,d,s height % of freq,r)                
				}
				//last part of sustained note or last note in measure
				else if ( ((i<midiArr.size()-1) && (midiArr[i+1] != midiArr[i])) || i==(midiArr.size()-1) ) {
					e.set( (last[0]*divDur)::second, (last[1]*divDur)::second, last[2], (last[3]*divDur)::second );  //(a,d,s height % of freq,r)    
				}
				//sustained note
				else {
					e.set( (sus[0]*divDur)::second, (sus[1]*divDur)::second, sus[2], (sus[3]*divDur)::second );  //(a,d,s height % of freq,r)    
				}            
				e.keyOn();// press the key
				T - e.releaseTime() => now; // play/wait until beginning of release
				e.keyOff(); //release the key
				e.releaseTime() => now; // wait until release is done
			}


			// *********************************** signalBeat() **********************
			fun void signalBeat(int i)
			{
				if (i % DIVS_PER_BEAT == 0) 
				{
					spork ~ playClick();
					beatNotifier.broadcast();
					<<< ""recording beat:"", (i / DIVS_PER_BEAT + 1)$int >>>;
				}
			}

			fun void playClick() 
			{
				0 => snare.pos;
				.5 => snare.gain;
				1.5 => snare.rate;
			}

		"        );

        myBeatNotifier = gameObject.AddComponent <ChuckEventListener>();
        myBeatNotifier.ListenForEvent(myChuck, "beatNotifier", SetBeatFlag);

        myCountdownNotifier = gameObject.AddComponent <ChuckEventListener>();
        myCountdownNotifier.ListenForEvent(myChuck, "countdownNotifier", SetCountdownFlag);

        myRecordNotifier = gameObject.AddComponent <ChuckEventListener>();
        myRecordNotifier.ListenForEvent(myChuck, "recordNotifier", SetRecordFlag);

        myRecordOffNotifier = gameObject.AddComponent <ChuckEventListener>();
        myRecordOffNotifier.ListenForEvent(myChuck, "recordOffNotifier", ResetRecordFlag);

        mySnareNotifier = gameObject.AddComponent <ChuckEventListener>();
        mySnareNotifier.ListenForEvent(myChuck, "snareNotifier", SetSnareHitFlag);

        myKickNotifier = gameObject.AddComponent <ChuckEventListener>();
        myKickNotifier.ListenForEvent(myChuck, "kickNotifier", SetKickHitFlag);
    }