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};