Assignment 9.3

Post date: Jul 31, 2010 6:41:7 AM

In the interest of creating larger, more complex projects as opposed to more frequent but simpler ones, I more or less combined 9.2 and 9.3 into a techno synth lead / drum machine / loop machine. The top 2 rows of keys act as a piano-type keyboard to control the synth, the bottom keys are a drum machine, and keys o and p control the loop ('o' starts and stops the loop because it is a circle, the standard symbol for record, and 'p' plays / pauses the loop). it can stack up to 3 tracks on top of each other (2 recorded and the one you are playing) although due to limitations in the minim library, the drum machine cannot be looped back. I wanted this to be able to play chords so I created a seperate sineWave instance for each key (although the audio does start clipping with the bigger chords, so if you want to rock out some jazz on this thing for whatever reason, just set the volume lower). At any rate, enjoy the product of about a day's worth of poking around in the minim documentation and subsequent frustrated debugging. I included an swf video of me hacking through the synth lead and drum part of "kids" by MGMT (great song).

Code:

import ddf.minim.*;

import ddf.minim.signals.*;

Minim minim; //sound library base class

AudioOutput out; //output channel for sinewaves

SineWave[] sine; //array of SineWave oscillator subclasses

AudioRecorder loopRecorder; //recorder objects for 2 loop tracks

AudioRecorder loopRecorder2;

AudioPlayer loopPlayer; //player objects for loop tracks

AudioPlayer loopPlayer2;

byte timesRecorded; //simple counter that goes up every time a track is recorded

AudioSample kick; //various audio samples for the drum machine

AudioSample snare;

AudioSample cowbell;

AudioSample hihat;

AudioSample crash;

AudioSample ride;

void setup()

{

size(512, 200, P2D);

minim = new Minim(this);

// get a line out from Minim, default bufferSize is 1024, default sample rate is 44100, bit depth is 16

out = minim.getLineOut(Minim.MONO);

//set loopRecorders to record "out" to file "recording(2).wav", with buffering true

loopRecorder = minim.createRecorder(out, "recording.wav", true);

loopRecorder2 = minim.createRecorder(out, "recording2.wav", true);

//allocate sinewaves

sine = new SineWave[26];

for(int i = 0; i < 26; i++) {

// create a sine wave Oscillator, set to respective, at 0.5 amplitude, sample rate from line out

sine[i] = new SineWave(0, 0.5, out.sampleRate());

// set the portamento speed on the oscillator to 200 milliseconds

sine[i].portamento(30);

// add the oscillator to the line out

out.addSignal(sine[i]);

}

//load samples from files into 256 byte buffers

kick = minim.loadSample("bassdrum.wav", 256);

snare = minim.loadSample("snare.wav", 256);

hihat = minim.loadSample("hihat.wav", 256);

crash = minim.loadSample("crash.wav", 256);

ride = minim.loadSample("ride.wav", 256);

cowbell = minim.loadSample("cowbell.wav", 256);

}

void draw()

{

make background green if playing, red if recording, black otherwise

if (timesRecorded > 0) {

if (loopRecorder.isRecording() || loopRecorder2.isRecording())

background(255, 0, 0);

else if (loopPlayer.isPlaying())

background(0, 255, 0);

else

background(0);

} else {

if (loopRecorder.isRecording())

background(255, 0, 0);

else

background(0);

}

stroke(255);

// draw the waveforms

for(int i = 0; i < out.bufferSize() - 1; i++)

{

float x1 = map(i, 0, out.bufferSize(), 0, width);

float x2 = map(i+1, 0, out.bufferSize(), 0, width);

line(x1, 50 + out.left.get(i)*50, x2, 50 + out.left.get(i+1)*50);

line(x1, 150 + out.right.get(i)*50, x2, 150 + out.right.get(i+1)*50);

}

// if loop starts getting out of sync while recording, end and save recording for playback

if (timesRecorded > 0) {

if (!loopPlayer.isPlaying() && loopRecorder2.isRecording()) {

loopRecorder2.endRecord();

loopRecorder2.save();

timesRecorded++;

}

}

}

void keyPressed() {

if (!(key-97 > 26) && !(key-97 < 0)) { //if 'key' == ASCII code of any of the letters of the alphabet (lowercase)

if (keysToNotes[int(key)-97] != 0) { //and the key corresponds to a note

sine[int(key)-97].setFreq(keysToNotes[int(key)-97]); //set the frequency to that note, based on the key's respective frequency in the keysToNotes array

}

}

if ( key == 'z' ) kick.trigger(); //if the key corresponds to a drum piece, play the clip

if ( key == 'c' ) snare.trigger();

if ( key == 'b' ) hihat.trigger();

if ( key == 'n' ) crash.trigger();

if ( key == 'x' ) cowbell.trigger(); //needs more cowbell

if ( key == 'm') cowbell.trigger(); //there it is

if ( key == 'v' ) ride.trigger();

if ( key == 'o' ) {

if (loopRecorder.isRecording() == true || loopRecorder2.isRecording() == true) {

if (timesRecorded > 0) {

loopRecorder2.endRecord(); //if recording and have recorded previous loops, save as recording2

loopRecorder2.save();

loopPlayer.pause();

} else {

loopRecorder.endRecord(); //else save as recording

loopRecorder.save();

}

timesRecorded++; //increment counter

if (timesRecorded > 0) loopPlayer = minim.loadFile("../recording.wav", 2048); //reload player files

if (timesRecorded > 1) loopPlayer = minim.loadFile("../recording2.wav", 2048);

} else {

if (timesRecorded > 0) { //if not currently recording, start recording

loopRecorder2.beginRecord();

loopPlayer = minim.loadFile("../recording.wav", 2048);

loopPlayer.play();

} else {

loopRecorder.beginRecord();

}

}

}

if ( key == 'p' && timesRecorded > 0) { //if pressed key p and have recorded a track

if (loopPlayer.isPlaying()) {

loopPlayer.pause(); //pause it if it's playing

if (timesRecorded > 1) loopPlayer2.pause();

} else {

loopPlayer = minim.loadFile("../recording.wav", 2048);

loopPlayer.loop(); //but load and play the track if it's not playing

}

if (timesRecorded > 1) {

loopPlayer2 = minim.loadFile("../recording2.wav", 2048); //and play recording2 if it exists

loopPlayer2.loop();

}

}

}

void keyReleased() { //if key is released

if (!(key-97 > 26) && !(key-97 < 0)) { //set the frequency for the sinewave class corresponding to that key to 0

if (keysToNotes[int(key)-97] != 0) {

sine[int(key)-97].setFreq(0);

}

}

}

void stop()

{

out.close();

minim.stop();

super.stop();

}

PITCHES FILE:

/*************************************************

* Public Constants

*************************************************/

public static int NOTE_B0 =31; //these need to be public static ints

public static int NOTE_C1 =33; //because apparently lame Java doesn't

public static int NOTE_CS1 =35; //have a preprocessor!! Obj-C is so much better

public static int NOTE_D1 =37;

public static int NOTE_DS1 =39;

public static int NOTE_E1 =41;

public static int NOTE_F1 =44;

public static int NOTE_FS1 =46;

public static int NOTE_G1 =49;

public static int NOTE_GS1 =52;

public static int NOTE_A1 =55;

public static int NOTE_AS1 =58;

public static int NOTE_B1 =62;

public static int NOTE_C2 =65;

public static int NOTE_CS2 =69;

public static int NOTE_D2 =73;

public static int NOTE_DS2 =78;

public static int NOTE_E2 =82;

public static int NOTE_F2 =87;

public static int NOTE_FS2 =93;

public static int NOTE_G2 =98;

public static int NOTE_GS2 =104;

public static int NOTE_A2 =110;

public static int NOTE_AS2 =117;

public static int NOTE_B2 =123;

public static int NOTE_C3 =131;

public static int NOTE_CS3 =139;

public static int NOTE_D3 =147;

public static int NOTE_DS3 =156;

public static int NOTE_E3 =165;

public static int NOTE_F3 =175;

public static int NOTE_FS3 =185;

public static int NOTE_G3 =196;

public static int NOTE_GS3 =208;

public static int NOTE_A3 =220;

public static int NOTE_AS3 =233;

public static int NOTE_B3 =247;

public static int NOTE_C4 =262;

public static int NOTE_CS4 =277;

public static int NOTE_D4 =294;

public static int NOTE_DS4 =311;

public static int NOTE_E4 =330;

public static int NOTE_F4 =349;

public static int NOTE_FS4 =370;

public static int NOTE_G4 =392;

public static int NOTE_GS4 =415;

public static int NOTE_A4 =440;

public static int NOTE_AS4 =466;

public static int NOTE_B4 =494;

public static int NOTE_C5 =523;

public static int NOTE_CS5 =554;

public static int NOTE_D5 =587;

public static int NOTE_DS5 =622;

public static int NOTE_E5 =659;

public static int NOTE_F5 =698;

public static int NOTE_FS5 =740;

public static int NOTE_G5 =784;

public static int NOTE_GS5 =831;

public static int NOTE_A5 =880;

public static int NOTE_AS5 =932;

public static int NOTE_B5 =988;

public static int NOTE_C6 =1047;

public static int NOTE_CS6 =1109;

public static int NOTE_D6 =1175;

public static int NOTE_DS6 =1245;

public static int NOTE_E6 =1319;

public static int NOTE_F6 =1397;

public static int NOTE_FS6 =1480;

public static int NOTE_G6 =1568;

public static int NOTE_GS6 =1661;

public static int NOTE_A6 =1760;

public static int NOTE_AS6 =1865;

public static int NOTE_B6 =1976;

public static int NOTE_C7 =2093;

public static int NOTE_CS7 =2217;

public static int NOTE_D7 =2349;

public static int NOTE_DS7 =2489;

public static int NOTE_E7 =2637;

public static int NOTE_F7 =2794;

public static int NOTE_FS7 =2960;

public static int NOTE_G7 =3136;

public static int NOTE_GS7 =3322;

public static int NOTE_A7 =3520;

public static int NOTE_AS7 =3729;

public static int NOTE_B7 =3951;

public static int NOTE_C8 =4186;

public static int NOTE_CS8 =4435;

public static int NOTE_D8 =4699;

public static int NOTE_DS8 =4978;

//table for key to note conversion

int[] keysToNotes = {NOTE_C5, 0, 0, NOTE_E5, NOTE_DS5, NOTE_F5, NOTE_G5,

NOTE_A5, 0, NOTE_B5, NOTE_C6, 0, 0, 0, 0, 0, NOTE_B4, 0,

NOTE_D5, NOTE_FS5, NOTE_AS5, 0, NOTE_CS5, 0, NOTE_GS5, 0};