ProMidi 2.0 problems
in
Contributed Library Questions
•
3 years ago
I am trying to simply play notes generated by image data and am having two major problems.
One problem is that when I try to establish a new Controller anywhere in my code I get the message that
"the type Controller is ambiguous"
This happens no matter where I put the code:
midiOut.sendController(
new Controller(1)
);
this same code in a test sketch performed perfectly.
I ultimately abandoned the Controller, and simply used Program Change :
midiOut.sendProgramChange(
new ProgramChange(1)
);
This gets notes playing 3 notes simultaneously even. However, eventually (even if I reduced the notes to one playing at a time) i get an error message, and the notes eventually stop playing:
Exception in thread "Thread-5" java.lang.ArrayIndexOutOfBoundsException: 0
at java.util.AbstractCollection.toArray(AbstractCollection.java:126)
at promidi.MidiOut$NoteBuffer.run(MidiOut.java:263)
It seems to crash sooner if I play the notes more frequently, but even playing them fairly slowly eventually I get that error.
any ideas would be greatly appreciated... even if there is another library that would work better I would like to hear about that.
Thanks in advance!
here is my code with some of the interface removed:
import controlP5.*;
import promidi.*;
//midi classes
MidiIO midiIO;
MidiOut midiOut;
//translation class
translater[] myTranslaters;
//interface classes
ControlP5 volumeControl;
ControlP5 imageControl;
ControlP5 pitchControl;
ControlP5 tempoControl;
//various volumes
public float main=50.0,reds=100.0,blues=100.0,greens=100.0;
//markers to identify sliders by ID no.
public boolean pitch,tempo;
int num;
// sets up database of images [maxImages] is the number of images to be imported
int maxImages = 4;
PImage[] images=new PImage[maxImages];
void setup(){
//format screen size
size(800,600);
//setup MIDI
//get an instance of MidiIO
midiIO = MidiIO.getInstance(this);
//print a list with all available devices
midiIO.printDevices();
//open a midiout using the first device and the first channel
midiOut = midiIO.getMidiOut(0,0);
//*********
//initialize variables
num=4;
pitch=false;
tempo=false;
//format controllers -
//seperate classes for each type of controller for formatting purposes
volumeControl = new ControlP5(this);
imageControl = new ControlP5(this);
pitchControl = new ControlP5(this);
tempoControl = new ControlP5(this);
//basic formatting of sliders for main volume and each color channel
volumeControl.addSlider("main",0,100,50,width-50,20,10,200);
volumeControl.addSlider("blues",0,100,100,width-90,20,10,200);
volumeControl.addSlider("greens",0,100,100,width-130,20,10,200);
volumeControl.addSlider("reds",0,100,100,width-170,20,10,200);
//make object variables for each slider for formatting purposes
Slider s1=(Slider)volumeControl.controller("main");
Slider s2=(Slider)volumeControl.controller("blues");
Slider s3=(Slider)volumeControl.controller("greens");
Slider s4=(Slider)volumeControl.controller("reds");
//set colors for each slider
s1.setColorValue(0x000000);
...
//begin translating images using Translater class
myTranslaters=new translater[maxImages];
//establish a new Translater object for each image
for(int i=0;i<images.length;i++){
images[i]=loadImage((i+1)+".jpg");
myTranslaters[i]=new translater(i,images[i]);
myTranslaters[i].start();
}
}
void draw(){
background(255);
//trigger Translater objects every frame
for(int i=0; i<myTranslaters.length;i++){
myTranslaters[i].vol=main/100;
myTranslaters[i].volRed=reds/100;
myTranslaters[i].volGreen=greens/100;
myTranslaters[i].volBlue=blues/100;
myTranslaters[i].convert();
}
}
//trigger events using P5 controller IDs to set variables
void controlEvent(ControlEvent theEvent) {
/* events triggered by controllers are automatically forwarded to
the controlEvent method. by checking the id of a controller one can distinguish
which of the controllers has been changed.
*/
// println("got a control event from controller with id "+theEvent.controller().id());
//set i to be the controller ID: IDs are assigned based on the Translater.offsetNo
//volume controllers are numbered as offsetNo, pitch controllers are
//numbered as offsetNo *2, temp-offsetNo*3, play buttons=offsetNo*4, hold buttons
//offsetNo*5
int i=theEvent.controller().id();
//set markers for controllers based on their ID and adjust i to equal the offsetNo
//for the Translater object.
if(i>=maxImages && i<(maxImages*2)){
i=i-images.length;
pitch=true;
}
else if(i>=(maxImages*2) && i<(maxImages*3)){
i=i-(maxImages*2);
tempo=true;
}
else if(i>=(maxImages*3) && i<(maxImages*4)){
i=i-(maxImages*3);
//if play button is already "on" set it to be off
myTranslaters[i].on=!myTranslaters[i].on;
//change labels for the play button each time it is pressed
if(myTranslaters[i].on){
theEvent.controller().setLabel("on");
theEvent.controller().setColorForeground(color(0,200,0));
}
else{
theEvent.controller().setLabel("play");
theEvent.controller().setColorForeground(color(0));
}
}
else if(i>=(maxImages*4)){
i=i-(maxImages*4);
//if hold button is already "on" set it to be off
myTranslaters[i].hold=!myTranslaters[i].hold;
//change labels for the hold button each time it is pressed
if(myTranslaters[i].hold){
theEvent.controller().setLabel("h-on");
theEvent.controller().setColorForeground(color(200,0,0));
}
else{
theEvent.controller().setLabel("hold");
theEvent.controller().setColorForeground(color(0));
}
}
else{
//if i<maxImages from the start then the ID is for a volume controller which matches the offsetNo
println("volume"+i);
myTranslaters[i].myVol=(theEvent.controller().value()/100);
println(myTranslaters[i].myVol);
}
if(i>=0){
if(pitch){
println(myTranslaters[i].offsetNo);
myTranslaters[i].maxPitch=((theEvent.controller().value())/100)*20000;
pitch=false;
}
else if(tempo){
println("tempo"+i);
myTranslaters[i].myTempo=(theEvent.controller().value())+200;
println(myTranslaters[i].myTempo);
tempo=false;
}
}
}
class translater{
Note note1,note2,note3;
//set up image object, audio channel objects(Ess) , sine wave objects(Ess)
PImage shell;
//variables for pitch shifting, and image scaling
float pitch1, pitch2, pitch3, scaler,w,h,scaledX,scaledY;
//variables for tempo
float m=millis();
float newMillis=0;
//variables for sliders (must be public)
public float vol=.5,volRed=1.0,volGreen=1.0,volBlue=1.0, myVol=.5, maxPitch, minR,maxR,minG,maxG,minB, maxB,myTempo=250.0;
//integer variables for image placement and Translater class identification
int i,offsetNo,imageX,imageY,yspace=20;
boolean on,hold;
//accept input numbers, and establish maxPitch
translater(int pos, PImage tempImage){
offsetNo=pos;
shell=tempImage;
//hold and play are off by default... default pitch range (20hz-20khz)
hold=false;
on=false;
maxPitch=126;
midiOut.sendProgramChange(
new ProgramChange(1)
);
}
void start(){
//establish the width and height of the image being convertes
w=shell.width;
h=shell.height;
//set scaler to use to limit all images to a width of 200 pixels
//imageX and imageY use offsetNo to determine placement
//currently limited to 2 images spaced by variable yspace across the y-axis
scaler=(w/200);
imageX=10+((offsetNo%2)*220);
imageY=yspace;
if(offsetNo>1){
imageY=300;
}
//store scaled values in scaledX and scaledY, and display the scaled image in position
scaledX=shell.width/scaler;
scaledY=shell.height/scaler;
image(shell,imageX,imageY,scaledX ,scaledY);
// position, format and display volume, pitch, and tempo sliders, also hold and play buttons
String slideNameA="volume "+(offsetNo+1);
String slideNameB="pitch "+(offsetNo+1);
String slideNameC="tempo "+(offsetNo+1);
int xPos=imageX;
int yPos=imageY+int(scaledY);
int slideWidth=int(scaledX);
//create and display interface objects and assign IDs based on offsetNo dividing IDs by incrementing integers will return ofsetNo
imageControl.addSlider(slideNameA,0,100,50,xPos,yPos+20,slideWidth,10).setId(offsetNo);
imageControl.addBang("play "+(offsetNo+1),xPos,yPos+80,10,10).setId(offsetNo+(images.length*3));
imageControl.addBang("hold "+(offsetNo+1),xPos+(slideWidth-10),yPos+80,10,10).setId(offsetNo+(images.length*4));
pitchControl.addSlider(slideNameB,0,100,50,xPos,yPos+40,slideWidth,10).setId(offsetNo+images.length);
tempoControl.addSlider(slideNameC,0,100,50,xPos,yPos+60,slideWidth,10).setId(offsetNo+(images.length*2));
//create varables to hold controllers for later formatting
Slider v=(Slider)imageControl.controller(slideNameA);
Slider p=(Slider)pitchControl.controller(slideNameB);
Slider t=(Slider)tempoControl.controller(slideNameC);
Bang play=(Bang)imageControl.controller("play "+(offsetNo+1));
Bang holder=(Bang)imageControl.controller("hold "+(offsetNo+1));
imageControl.controller("hold "+(offsetNo+1)).setLabel("hold");
imageControl.controller("play "+(offsetNo+1)).setLabel("play");
//use object variables for Label formatting
controlP5.Label vlabel=v.captionLabel();
vlabel.style().marginLeft = -150;
controlP5.Label plabel=p.captionLabel();
plabel.style().marginLeft = -150;
controlP5.Label tlabel=t.captionLabel();
tlabel.style().marginLeft = -150;
controlP5.Label hlabel=holder.captionLabel();
hlabel.style().marginLeft = -8;
//use object variables to format colors of interface objects
holder.setColorValue(0x000000);
......
}
void convert(){
//this function converts the image to sound
//*****************************************
//establish volume variables for the image as a whole and for each channel from the master control
//println(offsetNo + " "+myVol);
volRed=vol*(volRed*myVol);
//println("red= "+volRed);
volGreen=vol*(volGreen*myVol);
//println("green= "+volGreen);
volBlue=vol*(volBlue*myVol);
//println("blue= "+volBlue);
//formulas to isolate scaled areas of the changing pitch spectrum based on the relative
//positions of each color in the visible spectrum
minR=20;
maxR=maxPitch*.22;
minG=maxPitch*.32;
maxG=maxPitch*.53;
minB=maxPitch*.59;
maxB=maxPitch*.69;
//variables used to calculate time passing for tempo control
m=millis();
m=m-newMillis;
//if all pixels have been converted turn of the play button for that image
if(i>=shell.pixels.length-1){
//println("off");
on=false;
}
//if the play button is on and the appropriate amount of time (determined by tempo
//&& the milli/newMilli comprison) begin the conversion process
//******************************************************************************
if(on && m>myTempo){
println(offsetNo+"!!!!!!!!!!!!!!!"+myTempo);
//load each pixel and use it's color values to determine where within the spectrum
//of each channel a frequency will be pulled from,convert the current pixel to black
//and read the next pixels values
shell.loadPixels();
shell.pixels[i]=color(0);
//println(red(shell.pixels[i+1]));
float pitch1 = ((red(shell.pixels[i+1])/255)*maxR)+minR;
float pitch2 = ((green(shell.pixels[i+1])/255)*(maxG-minG))+minG;
float pitch3 = ((blue(shell.pixels[i+1])/255)*(maxB-minB))+minB;
//println(offsetNo+" "+int(pitch1)+" "+int(pitch2));
note1 =new Note(int(pitch1)+1,100,50);
note2 =new Note(int(pitch2)+1,100,50);
note3 =new Note(int(pitch3)+1,100,50);
midiOut.sendNote(note1);
midiOut.sendNote(note2);
midiOut.sendNote(note3);
//change the image to reflect the new black pixel,then display the image
//increment i so we can convert the next pixel, and reset the time for tempo
shell.updatePixels();
image(shell,imageX,imageY, shell.width/scaler,shell.height/scaler);
i++;
newMillis=millis();
}
//if the play button is not "on" and the tempo time has not yet passed
//display the image without converting a new pixel
else{
image(shell,imageX,imageY, shell.width/scaler,shell.height/scaler);
}
}
}