Java Code for Delta: Midi Effect/Looper

On December 15, 2011, in Uncategorized, by enemyin1

Here’s the java code for jDelta: a sequencing object for the MAX MSP software environment that I wrote a few years ago. JDelta stores note pitch, note duration, velocity and note delta times. It plays back each note according to its delta time with respect to preceding notes in a sequence. The neat thing is that probability of each stored note event, its transposition, velocity and its length can all be manipulated via the PD arrays (notePD, etc.). So the stored sequence is a seed from which variable sequences can be generated on the fly.

This code is designed to be used in MAX MSP 5 (should be compatible with 6 but I haven’t tried it). Currently, the clock source is MaxClock. Similarly, there are other expressions here peculiar to code written for the max environment such as the ‘DeclarInlets’ statements which determine the number of inputs and outputs from the graphical max object. There are a lot of methods but their purpose has been documented as as far as I’m able. An example of a piece recorded using JDelta as a looping device is for my Ring Modulated Piano improvisation here.

Anyway, feel free to tinker around. I think a better programmer than me ought to be able to adapt this to work much more smoothly and maybe to load sequences from midi files (!).

An example of the MAX interface I use with jDelta is given with the link to RM piano.

***************************************************************************************************************************

 

 

 

 

import com.cycling74.max.*;

 

import java.util.HashMap;

 

import java.util.Random;

 

import java.io.*;

 

 

 

 

 

 

 

 

 

public class jDelta extends MaxObject

 

{

 

 

 

private HashMap <Integer, Integer> indexTable;

 

//To pair pitches with indices//

 

private HashMap <Integer, Double> timeTable;

 

//to pair pitches with relevant onset times//

 

 

 

 

 

 

 

private int min;

 

private int max;

 

private int index;

 

// Index for array assigns. Incremented with note on’s//

 

private int count;

 

// Count outputs note data, cycling through arrays//

 

private Random generator;

 

// instance variable representing random number generator//

 

private double onTime;

 

// holds time of note on//

 

 

 

private double masterDuration;

 

//Field to hold target duration value from external source

 

 

 

private boolean wrap;

 

//If wrap ‘true’ then sequence length is maintained at a constant duration

 

private boolean Sticky;

 

// keeps Max at last value to allow update at same sequence length//

 

 

 

private double lastDelta;

 

// holds value that is added to final delta time to increase length between end and beginning of sequence//

 

 

 

private double offTime;

 

// holds time of note ff//

 

private double noteDuration;

 

// holds noteDuration//

 

private double deltaTime;

 

// holds time beteween note on’s//

 

private double r;

 

// holds next random value//

 

 

 

private int [] pBank;

 

private int [] velBank;

 

private double [] ndBank;

 

private double [] deltaBank;

 

 

 

// Arrays storing pitch, velocity and note duration//

 

 

 

private int arraySize;

 

// Pass Banksize argument to arraySize for determining step time//

 

 

 

private int [] transBank;

 

private int [] velChangeBank;

 

 

 

// Arrays storing tranpose and velocity change data //

 

 

 

 

 

private double [] notePD;

 

 

 

// Stores doubles representing probability of note event//

 

 

 

private double [] transPD;

 

 

 

//Stores doubles representing probability of transpose event//

 

 

 

private double [] velChangePD;

 

 

 

//Stores doubles representing probability of velocity change event//

 

 

 

//

 

private double [] tempoBank;

 

// Array to hold tempo values//

 

 

 

private double [] tempoPD;

 

 

 

// array to hold probability of tempo changes//

 

 

 

 

 

private MaxClock clock;

 

 

 

private PrintWriter pw;

 

 

 

 

 

 

 

private int [] pitchBuffer1;

 

private int [] velBuffer1;

 

private double [] ndBuffer1;

 

private double [] deltaBuffer1;

 

 

 

private int [] pitchBuffer2;

 

private int [] velBuffer2;

 

private double [] ndBuffer2;

 

private double [] deltaBuffer2;

 

 

 

private int [] pitchBuffer3;

 

private int [] velBuffer3;

 

private double [] ndBuffer3;

 

private double [] deltaBuffer3;

 

 

 

private int [] pitchBuffer4;

 

private int [] velBuffer4;

 

private double [] ndBuffer4;

 

private double [] deltaBuffer4;

 

 

 

// Buffers for on the fly storage//

 

 

 

 

 

private static final String[] INLET_ASSIST = new String[]{

 

“inlet 1 help”

 

};

 

private static final String[] OUTLET_ASSIST = new String[]{

 

“outlet 1 help”

 

};

 

 

 

// variables for zip method//

 

 

 

// Minimum index of array to which zip applies//

 

int zipMin;

 

// Maximum index of array to which zip applies//

 

int zipMax;

 

// Number of steps by which indexed values are moved//

 

int zipAdd;

 

 

 

 

 

/** Creates a new instance of jDelta */

 

public jDelta(int bankSize)

 

 

 

{

 

 

 

 

 

declareInlets(new int[]{DataTypes.ALL, DataTypes.ALL,  DataTypes.ALL });

 

declareOutlets(new int[]{DataTypes.ALL, DataTypes.ALL, DataTypes.ALL, DataTypes.ALL, DataTypes.ALL,

 

DataTypes.ALL, DataTypes.ALL,  DataTypes.ALL, DataTypes.ALL, DataTypes.ALL, DataTypes.ALL });

 

 

 

setInletAssist(INLET_ASSIST);

 

setOutletAssist(OUTLET_ASSIST);

 

 

 

this.Sticky = false;

 

this.wrap = false;

 

this.masterDuration = 2000.0;

 

//initialise masterDuration to something!//

 

this.indexTable = new HashMap<Integer, Integer> ();

 

this.timeTable = new HashMap<Integer, Double> ();

 

this.min = 1;

 

this.count = 1;

 

this.onTime = 0.0;

 

this.index = 0;

 

this.lastDelta = 0.0;

 

this.arraySize = bankSize;

 

 

 

 

this.clock = new MaxClock(new Callback(this, “Play”));

 

this.pBank = new int [  bankSize ] ;

 

this.velBank = new int [  bankSize] ;

 

this.ndBank = new double [  bankSize] ;

 

this.deltaBank = new double  [ bankSize];

 

 

 

this.transBank = new int [ bankSize];

 

this.velChangeBank = new int [ bankSize];

 

 

 

this.tempoBank = new double [bankSize];

 

//set all tempo values to 1 by default//

 

for (int i = 0; i < bankSize; i++)

 

{

 

tempoBank[i] = 1.0;

 

}

 

this.tempoPD = new double [bankSize];

 

 

 

this.notePD = new double [ bankSize];

 

this.transPD = new double [ bankSize];

 

this.velChangePD = new double [ bankSize];

 

 

 

 

 

this.generator = new Random();

 

 

 

this.pBank[0] = 60;

 

this.velBank[0] = 0;

 

this.ndBank[0] = 0.0;

 

this.deltaBank[0] = 0.0;

 

 

 

 

 

 

 

pitchBuffer1 = new int [bankSize];

 

velBuffer1 = new int [bankSize];;

 

ndBuffer1 = new double [bankSize];

 

deltaBuffer1 = new double [bankSize];

 

 

 

pitchBuffer2 = new int [bankSize];

 

velBuffer2 = new int [bankSize];;

 

ndBuffer2 = new double [bankSize];

 

deltaBuffer2 = new double [bankSize];

 

 

 

pitchBuffer3 = new int [bankSize];

 

velBuffer3 = new int [bankSize];;

 

ndBuffer3 = new double [bankSize];

 

deltaBuffer3 = new double [bankSize];

 

 

 

pitchBuffer4 = new int [bankSize];

 

velBuffer4 = new int [bankSize];;

 

ndBuffer4 = new double [bankSize];

 

deltaBuffer4 = new double [bankSize];

 

 

 

 

 

}

 

 

 

 

 

 

 

public void Reset()

 

{

 

index = 0;

 

count = 1;

 

}

 

 

 

 

 

public void giveMin(int intMin)

 

{

 

min = intMin;

 

count = intMin;

 

outlet(4, count);

 

}

 

 

 

public void giveMax(int intMax)

 

{

 

max = intMax;

 

outlet(4, max);

 

}

 

 

 

public void getWrapValue(int anInt)

 

{

 

//      Determines whether wrap applies. Inputting 1 turns on wrap

 

 

 

if (anInt == 1)

 

{

 

wrap = true;

 

outlet(10, 1);

 

 

 

;}

 

else

 

{

 

wrap = false;

 

outlet(10, 0);

 

}

 

}

 

 

 

 

 

public void Sticky(int anInt)

 

{

 

//      Determines whether wrap applies. Inputting 1 turns on wrap

 

 

 

if (anInt == 1)

 

{

 

Sticky = true;

 

outlet(10, 1);

 

 

 

;}

 

else

 

{

 

Sticky = false;

 

outlet(10, 0);

 

}

 

}

 

 

 

 

 

public double giveFinalDelta(double aDouble)

 

{

 

lastDelta = aDouble;

 

return lastDelta;

 

}

 

 

 

public void setFinalDelta(double aDouble)

 

{

 

lastDelta = aDouble;

 

}

 

 

 

 

 

public void getDuration()

 

{

 

 

 

double durationSum = 0.0;

 

for(int i = min; i <= max ;i++)

 

{

 

durationSum = durationSum + deltaBank[i];

 

}

 

 

 

outlet (8, min);

 

outlet(9, max);

 

outlet(6, durationSum);

 

 

 

 

}

 

 

 

public double returnDuration()

 

{

 

double durationSum = 0.0;

 

for(int i = 0; i <= max ;i++)

 

{

 

durationSum = durationSum + deltaBank[i];

 

}

 

 

 

return durationSum;

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

public double getRunningDur()

 

{

 

double durationSum = 0.0;

 

for(int i = min; i <= (index-1) ;i++)

 

{

 

durationSum = durationSum + deltaBank[i];

 

}

 

outlet(11, durationSum);

 

return durationSum;

 

 

 

}

 

// Get running total of durations prior to the duration to be entered into deltaBank//

 

 

 

 

 

public void getMasterDuration(double aDouble)

 

{

 

 

 

masterDuration = aDouble;

 

outlet(6, masterDuration);

 

}

 

 

 

 

 

public void Start()

 

{

 

 

 

clock.delay(0.0);

 

 

 

}

 

 

 

public void Stop()

 

{

 

clock.unset();

 

count = 1;

 

 

 

}

 

 

 

 

 

// Determines pitch, velocity, note duration and delta time for a given array index//

 

public void list (int [] aList)

 

{

 

if (getInlet() == 0)

 

{

 

if ( aList[1] != 0)

 

// If a note on…//

 

{

 

 

 

deltaTime = (clock.getTime() – onTime);

 

//                        Calculate deltaTime by substracting last onTime from current time. On first note onTime is

 

//initialised to zero

 

onTime = clock.getTime();

 

// update onTime//

 

 

if(wrap == false)

 

{

 

 

 

 

deltaBank[index] = deltaTime;

 

//Sends delta to index associated with previous note – prior to incrementing index

 

if (index < (pBank.length – 1))

 

{index = index + 1;}

 

else

 

{index = 0;}

 

// note on increments index if less than array length and zeros it otherwise//

 

outlet(4, index);

 

if(Sticky == false)

 

{max = index;}

 

outlet(9, max);

 

 

pBank[index] =  aList[0];

 

velBank[index] =  aList[1];

 

// Assigns pitch and velocity//

 

 

 

this.indexTable.put( aList[0], index);

 

this.timeTable.put( aList[0], onTime);

 

//associates current index and current time with the input pitch//

 

 

 

}

 

 

 

else

 

//If Wrap is enabled…

 

{

 

 

if (! (!((getRunningDur() + deltaTime) < masterDuration)&& !(index == 0)))

 

// If the delta time plus sequence length is less than desired duration or index = 0 it can be safely added to deltaBank//

 

{

 

 

 

deltaBank[index] = deltaTime;

 

//Sends delta to index associated with previous note – prior to incrementing index

 

if (index < (pBank.length – 1))

 

{index = index + 1;}

 

else

 

{index = 0;}

 

outlet(4, index);

 

max = index;

 

outlet(9, max);

 

// note on increments index if less than array length and zeros it otherwise//

 

pBank[index] =  aList[0];

 

velBank[index] =  aList[1];

 

// Assigns pitch and velocity//

 

 

 

this.indexTable.put( aList[0], index);

 

this.timeTable.put( aList[0], onTime);

 

//associates current index and current time with the input pitch//

 

 

 

}

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

}

 

 

 

else

 

// If a note off…//

 

{

 

 

 

offTime = clock.getTime();

 

//records time of note off//

 

noteDuration = (offTime – this.timeTable.get( aList[0]));

 

//                        calculates note duration by subtracting time of note on for the same pitch, retrieved from the

 

//hashtable

 

ndBank[this.indexTable.get( aList[0])] =  noteDuration;

 

 

 

this.timeTable.remove( aList[0]);

 

this.indexTable.remove( aList[0]);

 

//removes mappings//

 

 

 

 

 

}

 

}

 

 

 

if (getInlet() == 1)

 

{

 

 

 

if(aList[0] == 3)

 

{

 

 

 

for (int i = 1; i < aList.length; i++)

 

{transBank[i] = aList[i];}

 

 

 

}

 

 

 

if(aList[0] == 4)

 

{

 

for (int i = 1; i < aList.length; i++)

 

velChangeBank[i] = aList[i];

 

}

 

 

 

}

 

 

 

}

 

 

 

public void Export () throws IOException

 

{

 

 

 

String fileName =  MaxSystem.saveAsDialog(“Save As”, “Filename”);

 

DataOutputStream os = new DataOutputStream(new FileOutputStream(fileName));

 

for(int i = 0; i < pBank.length; i++)

 

{

 

os.writeInt(pBank[i]);

 

}

 

 

 

for(int i = 0; i < velBank.length; i++)

 

{

 

os.writeInt(velBank[i]);

 

}

 

 

 

for(int i = 0; i < ndBank.length; i++)

 

{

 

os.writeDouble(ndBank[i]);

 

}

 

 

 

for(int i = 0; i < pBank.length; i++)

 

{

 

os.writeDouble(deltaBank[i]);

 

}

 

 

 

os.writeInt(min);

 

os.writeInt(max);

 

os.writeInt(index);

 

 

 

os.close();

 

}

 

 

 

//Import with dialog//

 

 

 

public void Import() throws IOException

 

{

 

String fileName = MaxSystem.openDialog(“Open File”);

 

outlet(10, fileName);

 

DataInputStream is = new DataInputStream(new FileInputStream(fileName));

 

for(int i = 0; i < pBank.length; i++)

 

{

 

pBank[i] = is.readInt();

 

}

 

 

 

for(int i = 0; i < velBank.length; i++)

 

{

 

velBank[i] = is.readInt();

 

}

 

 

 

for(int i = 0; i < ndBank.length; i++)

 

{

 

ndBank[i] = is.readDouble();

 

}

 

 

 

for(int i = 0; i < deltaBank.length; i++)

 

{

 

deltaBank[i] = is.readDouble();

 

}

 

min = is.readInt();

 

max = is.readInt();

 

index = is.readInt();

 

 

 

is.close();

 

 

 

if(wrap)

 

{

 

// If wrap is enabled reduce number of sequence steps (max) to fit within duration. Final duration added on play//

 

while((returnDuration() > masterDuration) && max > 1)

 

{

 

max = max – 1;

 

}

 

 

 

}

 

 

 

outlet(4, index);

 

outlet(8, min);

 

outlet(9, max);

 

 

 

}

 

 

 

 

 

//Import with prepend to fileName//

 

 

 

public void Insert(String fileName) throws IOException

 

{

 

 

fileName =  “/Applications/Max5/patches/delta clips/” + fileName;

File f = new File(fileName);

 

outlet(10, fileName);

 

DataInputStream is = new DataInputStream(new FileInputStream(f));

 

 

 

 

 

for(int i = 0; i < pBank.length; i++)

 

{

 

pBank[i] = is.readInt();

 

}

 

 

 

for(int i = 0; i < velBank.length; i++)

 

{

 

velBank[i] = is.readInt();

 

}

 

 

 

for(int i = 0; i < ndBank.length; i++)

 

{

 

ndBank[i] = is.readDouble();

 

}

 

 

 

for(int i = 0; i < deltaBank.length; i++)

 

{

 

deltaBank[i] = is.readDouble();

 

}

 

 

 

min = is.readInt();

 

max = is.readInt();

 

index = is.readInt();

 

 

 

is.close();

 

 

 

if(wrap)

 

{

 

// If wrap is enabled reduce number of sequence steps (max) to fit within duration. Final duration added on play//

 

while((returnDuration() > masterDuration) && max > 1)

 

{

 

max = max – 1;

 

}

 

 

 

}

 

 

 

outlet(4, index);

 

outlet(8, min);

 

outlet(9, max);

 

 

 

}

 

 

 

 

 

public void write()throws FileNotFoundException

 

{

 

String fileName;

 

fileName = MaxSystem.saveAsDialog(“Save As”, “Filename”);

 

pw = new PrintWriter(fileName);

 

 

 

for(int i = 0; i < pBank.length; i++)

 

{

 

pw.print(pBank[i]);

 

}

 

 

 

for(int i = 0; i < velBank.length; i++)

 

{

 

pw.print(velBank[i]);

 

}

 

 

 

for(int i = 0; i < velBank.length; i++)

 

{

 

pw.print(ndBank[i]);

 

}

 

 

 

for(int i = 0; i < deltaBank.length; i++)

 

{

 

pw.print(deltaBank[i]);

 

}

 

pw.close();

 

 

 

}

 

 

 

 

 

 

 

public void list(double [] list)

 

{

 

if (getInlet() == 1)

 

{

 

if(list[0] == 0.0)

 

{

 

 

 

for (int i = 1; i < list.length ; i++)

 

{notePD[i] = list[i]/100.0;}

 

}

 

 

 

if(list[0] == 1.0)

 

{

 

 

 

for (int i = 1; i <list.length; i++)

 

{transPD[i] = list[i]/100.0;}

 

}

 

 

 

if(list[0] == 2.0)

 

{

 

 

 

for (int i = 1; i <list.length; i++)

 

{velChangePD[i] = list[i]/100.0;}

 

}

 

 

 

 

 

if(list[0] == 3.0)

 

{

 

 

 

for (int i = 1; i <list.length; i++)

 

{tempoBank[i] = list[i]/100.0;}

 

}

 

 

 

if(list[0] == 4.0)

 

{

 

 

 

for (int i = 1; i <list.length; i++)

 

{tempoPD[i] = list[i]/100.0;}

 

}

 

 

 

 

 

 

 

}

 

 

 

}

 

 

 

public void beatOut(int i, int p, int v, double nd)

 

{

 

 

 

if ( r < notePD[i])

 

{

 

outlet(0,p);

 

outlet(1,v);

 

outlet(2, nd);

 

}

 

 

 

outlet(5, count);

 

}

 

 

 

public void Play()

 

{

 

 

 

double delayTime = 200.0;

 

if (wrap == false)

 

{this.deltaBank[max] = (this.ndBank[max] +  lastDelta);}

 

//Final delay is defaulted as equal to final note duration when lastDelta = 0.//

 

else

 

setFinalDelta(masterDuration – getRunningDur());

 

{this.deltaBank[max] = lastDelta;}

 

getDuration();

 

// If Wrap is on, then the last delta is set at the difference between the running dur prior to max and the desired duration. Just need to ensure differnce always pos//

 

 

 

this.r = generator.nextDouble();

 

outlet(3, r);

 

 

 

int p =  pBank [count];

 

int v = velBank [count];

 

double nd = ndBank [count]  ;

 

// assign array values to local variables

 

 

 

if(r < transPD[count])

 

{p = pBank[count] + transBank[count];}

 

if(r < velChangePD[count])

 

{v = velBank[count] + velChangeBank[count];}

 

 

 

beatOut(count, p, v, nd);

 

 

 

if( r < tempoPD[count])

 

{

 

delayTime = (deltaBank[count]*tempoBank[count]);

 

}

 

else

 

{

 

delayTime = deltaBank[count];

 

}

 

 

 

 

 

clock.delay(delayTime);

 

 

 

if (count < max)

 

{

 

r = generator.nextDouble();

 

count = count + 1;

 

}

 

else

 

{

 

count = min;

 

}

 

}

 

 

 

protected void notifyDeleted()

 

{

 

clock.release();

 

post(“Feck”);

 

}

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Tagged with:
 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Do NOT fill this !