Delay between notes in Java MIDI Track playing incorrectly - java

Following on from my question here I have created a List that allows me to queue up a collection of notes that need to be played, these are then added to the track in order so they can be played in one go.
I add the notes to be played in the track to the temporary list like so:
int pitch = jComboBoxPitch.getSelectedIndex();
int velocity = ((Integer)jSpinnerVelocity.getValue());
collection.add(new MIDIMessage(pitch,velocity));
When the user has added all the notes necessary they press the "Play" button:
private void PlayMIDI()
{
try
{
Sequencer seq = MidiSystem.getSequencer();
seq.open();
Sequence sequence1 = new Sequence(Sequence.PPQ,16);
Track track = sequence1.createTrack();
Iterator itr = collection.iterator();
int i = 0;
while(itr.hasNext())
{
MIDIMessage msg = (MIDIMessage)itr.next();
ShortMessage noteOnMsg = new ShortMessage();
//Signal/Channel/Pitch/Velocity
noteOnMsg.setMessage(ShortMessage.NOTE_ON, 0,msg.GetPitch(),msg.GetVelocity());
ShortMessage noteOffMsg = new ShortMessage();
//Signal/Channel/Pitch/Velocity
noteOffMsg.setMessage(ShortMessage.NOTE_OFF,0,msg.GetPitch(),msg.GetVelocity());
track.add(new MidiEvent(noteOnMsg,msg.GetDelay()));
track.add(new MidiEvent(noteOffMsg,msg.GetDelay() + i));
i++;
}
sequencer.setSequence(sequence);
sequencer.setTempoInBPM(120);
sequencer.setLoopCount(1);
seq.start();
}
catch(Exception e)
{
}
}
As you can see I iterate through the ArrayList and add each noteOn/Off to the track.
After playing the track I've noticed that it only plays two notes regardless of the amount that are in the list.
I've checked both the temporary collection and the track collection and they both seem to have the correct data in them.
Am I adding the events and messages in the wrong way? I just wish to be able to play the notes in the way they are recorded and that they should have a reasonable amount of time inbetween each note.

You seem to be adding your note on message to channel 0 and the note off message to channel 1. I'm guessing that's not really what you want to do -- set them both to be on the first channel (ie, 0).

I managed to fix the problem I was having, after looking at the way I was implementing my messages being added to the track I saw that each message was added too close to each other, therefore when each note played the next note was played almost instantly.
The following code shows how I fixed it:
int i = 0;
while(itr.hasNext())
{
MIDIMessage msg = (MIDIMessage)itr.next();
ShortMessage noteOnMsg = new ShortMessage();
//Signal/Channel/Pitch/Velocity
noteOnMsg.setMessage(ShortMessage.NOTE_ON, 0,msg.GetPitch(),msg.GetVelocity());
ShortMessage noteOffMsg = new ShortMessage();
//Signal/Channel/Pitch/Velocity
noteOffMsg.setMessage(ShortMessage.NOTE_OFF,0,msg.GetPitch(),msg.GetVelocity());
track.add(new MidiEvent(noteOnMsg,i));
i = i+50;
track.add(new MidiEvent(noteOffMsg,i));
i = i+50;
}
By increasing the count of i to 50 it ensures all the notes have a decent amount of space to seperate them in the track, hence the notes being played correctly.

I know this was asked a few months ago, but I'm trying to do something very similar.
I came across the same problem and found that sequencer.setLoopCount(1); was causing my Sequencer to play the notes twice.
By removing this it played the notes only once. Hope this helps.

Related

dl4j lstm not successful

Im trying to copy the exrcise about halfway down the page on this link:
https://d2l.ai/chapter_recurrent-neural-networks/sequence.html
The exercise uses a sine function to create 1000 data points between -1 through 1 and use a recurrent network to approximate the function.
Below is the code I used. I'm going back to study more why this isn't working as it doesn't make much sense to me now when I was easily able to use a feed forward network to approximate this function.
//get data
ArrayList<DataSet> list = new ArrayList();
DataSet dss = DataSetFetch.getDataSet(Constants.DataTypes.math, "sine", 20, 500, 0, 0);
DataSet dsMain = dss.copy();
if (!dss.isEmpty()){
list.add(dss);
}
if (list.isEmpty()){
return;
}
//format dataset
list = DataSetFormatter.formatReccurnent(list, 0);
//get network
int history = 10;
ArrayList<LayerDescription> ldlist = new ArrayList<>();
LayerDescription l = new LayerDescription(1,history, Activation.RELU);
ldlist.add(l);
LayerDescription ll = new LayerDescription(history, 1, Activation.IDENTITY, LossFunctions.LossFunction.MSE);
ldlist.add(ll);
ListenerDescription ld = new ListenerDescription(20, true, false);
MultiLayerNetwork network = Reccurent.getLstm(ldlist, 123, WeightInit.XAVIER, new RmsProp(), ld);
//train network
final List<DataSet> lister = list.get(0).asList();
DataSetIterator iter = new ListDataSetIterator<>(lister, 50);
network.fit(iter, 50);
network.rnnClearPreviousState();
//test network
ArrayList<DataSet> resList = new ArrayList<>();
DataSet result = new DataSet();
INDArray arr = Nd4j.zeros(lister.size()+1);
INDArray holder;
if (list.size() > 1){
//test on training data
System.err.println("oops");
}else{
//test on original or scaled data
for (int i = 0; i < lister.size(); i++) {
holder = network.rnnTimeStep(lister.get(i).getFeatures());
arr.putScalar(i,holder.getFloat(0));
}
}
//add originaldata
resList.add(dsMain);
//result
result.setFeatures(dsMain.getFeatures());
result.setLabels(arr);
resList.add(result);
//display
DisplayData.plot2DScatterGraph(resList);
Can you explain the code I would need for a 1 in 10 hidden and 1 out lstm network to approximate a sine function?
Im not using any normalization as function is already -1:1 and Im using the Y input as the feature and the following Y Input as the label to train the network.
You notice i am building a class that allows for easier construction of nets and I have tried throwing many changes at the problem but I am sick of guessing.
Here are some examples of my results. Blue is data red is result
This is one of those times were you go from wondering why was this not working to how in the hell were my original results were as good as they were.
My failing was not understanding the documentation clearly and also not understanding BPTT.
With feed forward networks each iteration is stored as a row and each input as a column. An example is [dataset.size, network inputs.size]
However with recurrent input its reversed with each row being a an input and each column an iteration in time necessary to activate the state of the lstm chain of events. At minimum my input needed to be [0, networkinputs.size, dataset.size] But could also be [dataset.size, networkinputs.size, statelength.size]
In my previous example I was training the network with data in this format [dataset.size, networkinputs.size, 1]. So from my low resolution understanding the lstm network should never have worked at all but somehow produced at least something.
There may have also been some issue with converting the dataset to a list as I also changed how I feed the network but but I think the bulk of the issue was a data structure issue.
Below are my new results
Hard to tell what is going on without seeing the full code. For a start I don't see an RnnOutputLayer specified. You could take a look this which shows you how to build an RNN in DL4J.
If your RNN setup is correct this could be a tuning issue. You can find more on tuning here. Adam is probably a better choice for an updater than RMSProp. And tanh probably is a good choice for the activation for your output layer since it's range is (-1,1). Other things to check/tweak - learning rate, number of epochs, set up of your data (like are you trying to predict to far out?).

Java MIDI Sequence.getTracks() wrong number

I'm trying to get the number of Tracks of a MIDI sequence:
File file = new File(strSource);
Sequence sequence = MidiSystem.getSequence(file);
int numTracks = sequence.getTracks().length;
... where strSource is the full path+file name of my .mid file.
numTracks is 1, but the .mid file has 16 tracks (as i can see when i open it in another MIDI editor). The file type is 0.
I read somewhere that type-0 files can't have multiple tracks for the same channel. In this case all tracks are forced into a single track. Is that correct? How can I avoid that?
It seems you're right, type-0 files hold multiple tracks in just one.
Here you have some info.
Isn't possible to extract each separate track from a type 0 file.
Check MIDI file type, if an external MIDI editor can detect multiple tracks, it can be a type 1 or type 2 file, even if extension doesn't match.
I looked at the file with a hex tool ...
It actually has only one track.
The other editor creates the multiple Tracks by itself. It seems to search for program change messages and then put the events into new tracks.
I have programmed a small function that converts a Type # 0 sequence to a Type # 1 sequence
/**
* Make multiple tracks from file0 track
* #param in : Sequence with single track
* #return Multiple track sequence
*/
private Sequence extractFile0Tracks (Sequence in) throws InvalidMidiDataException
{
Track inTrack = in.getTracks()[0];
HashMap<Integer, ArrayList<MidiEvent>> msgMap = new HashMap<>();
// Distribute events per channel to ArrayList map
for (int i = 0; i < inTrack.size(); i++)
{
MidiEvent event = inTrack.get(i);
MidiMessage message = event.getMessage();
if (message instanceof ShortMessage)
{
ShortMessage sm = (ShortMessage) message;
int channel = sm.getChannel() + 1;
ArrayList<MidiEvent> msgList = msgMap.computeIfAbsent(channel, k -> new ArrayList<>());
msgList.add(event);
}
}
// Create sequence with multiple tracks
Sequence newSeq = new Sequence(in.getDivisionType(), in.getResolution());
for (ArrayList<MidiEvent> msgList : msgMap.values())
{
Track tr = newSeq.createTrack();
for (MidiEvent m1 : msgList)
tr.add(m1);
}
return newSeq;
}

MIDI Java generates messed sound

I've been working in a project where I need to manipulate each instrument in a MIDI file in java.
Then I decided to get each MIDI Event from each track from the Sequence and send it to a Receiver. After that the thread waits the time each tick lasts then do it again with the next tick.
The problem is: the sound of the instruments gets very messed, as well as their order.
I tried to execute each track alone too, but it's still messed!
The code:
Sequence sequence = MidiSystem.getSequence(new File(source));
Synthesizer synth = MidiSystem.getSynthesizer();
//Gets a MidiMessage and send it to Synthesizer
Receiver rcv = synth.getReceiver();
//Contains all tracks and events from MIDI file
Track[] tracks = sequence.getTracks();
synth.open();
//If there are tracks
if(tracks != null)
{
//Verify the division type of the sequence (PPQ, SMPT)
if(sequence.getDivisionType() == Sequence.PPQ)
{
int ppq = sequence.getResolution();
//Do the math to get the time (in miliseconds) each tick takes
long tickTime = TicksToMiliseconds(BPM,ppq);
//Returns the number of ticks from the longest track
int longestTrackTicks = LongestTrackTicks(tracks);
//Each iteration sends a new message to 'receiver'
for(int tick = 0; tick < maiorTick ; tick++)
{
//Iteration of each track
for(int trackNumber = 0; trackNumber < tracks.length; trackNumber++)
{
//If the number of ticks from a track isn't already finished
//continue
if(tick < tracks[trackNumber].size())
{
MidiEvent ev = tracks[trackNumber].get(tick);
rcv.send(ev.getMessage(),-1);
}
}
Thread.sleep(tickTime);
}
}
}
synth.close();
As ntabee said, Track.get(n) returns the nth event in the track; to get events by time, you have to compare the events' times manually.
Furthermore, Thread.sleep() is not very precise and can wait for a longer time than desired.
These errors will add up.
To change MIDI messages in real time, tell the sequencer to play to your own Receiver, then do whatever you want to the events and pass them on to the 'real' Receiver.
At least, your code looks like ringing on/off something at every tickTime msec. period.
track.get(tick) just returns the tick-th event in the track, not the event(s) at the moment of tick.
If your goal is just playing a sound, Java provides a high level API for it, see e.g. http://www.jsresources.org/examples/midi_playback_and_recording.html
I decided to use the sequencer. I didn't know, but the "start" method runs in a new thread and while it's still running I can mute each instrument I want to, and that is exactly what I wanted.
Thanks for the answers!

Arduino port stuck on full power

I am working on a system that rings a bell when someone decides to ring it or it is timed. This also includes emergency bells, such as when there is an earthquake threat or an armed intruder. On the armed intruder bell it rings for .5 seconds (500ms) and then waits 1.5s (1500ms) and repeats. However, on the fourth cycle the bell gets stuck on and will stay on until I turn the arduino off. I have tried many ways of trying to fix it but it will not work. I will post my code below and could someone have a look and see what is wrong with it?
Thank you!
Java for loop:
for(int i = 0; i < 21; i++) {
out.write("500".getBytes("UTF-8"));
out.flush();
Thread.sleep(2000);
}
Arduino Code:
int Relay = 13;
//The pin that the relay is attached to
//int time;
//Creates temp variable
void setup() {
Serial.begin(9600);
pinMode(Relay, OUTPUT);
}
void loop() {
while(true) {
//Check if data has been sent from the computer:
if (Serial.available()) {
int time;
//Assign serial value to temp
time = Serial.parseInt();
//Output value to relay
delay(1000);
digitalWrite(Relay, HIGH);
delay(time);
digitalWrite(Relay, LOW);
}
}
}
You might try putting a small delay into the if-loop, after checking and before reading from the Serial (just a few ms. Maybe put your 1000ms delay at the beginning). As the data arrive serialized, and you check with a high frequency, it might be, that the program starts reading out data before everything is received.
I don't know how exactly Serial.parseInt() works, but maybe try:
out.write("500\n".getBytes("UTF-8"));
in your Java-loop

Creating Java echo sound effect

So I am trying to manipulate a sound file in java by breaking it up into samples and storing those samples in an array. I then loop through the array changing each sample. I realize that there is already an echo filter in java but I need to design my own by having the sound repeated at various intervals throughout the sound clip with diminishing volume. I currently have a method that controls the volume but am stumped when it comes to getting the sound to repeat starting at a particular delay over the current sound file. This is what I have so far:
public void echoEffect(int delay){
SoundSample[] sampleArray = this.getSamples(); // get array
SoundSample sample = null; // current sample obj
int value = 0; // value at sample
// loop through SoundSample objects
for(int i = 0, index = 0; index < sampleArray.length; i++,index++)
{
sample = sampleArray[index]; // get current obj
value = sample.getValue(); // get the value
sample.setValue(value*2); // set the value
}
}
What I believe I need to do is change the method from void to Sound and return the the delayed sound file. Possibly returning something like value + value[i+delay]*(some fraction to decrease the sound)
New Update rather than post:
Anyway this is what I have so far I just can't seem to get the code to work properly and I know I'm close but I need the method to output an echo effect on a sound file. Here is what I have at the current point:
public void echo(int delay){
SoundSample[] sampleArray = this.getSamples(); // get array
//Sound target = new Sound(sampleArray.length);
SoundSample sampleDelay = null;
SoundSample sample = null; // current sample obj
int value = 0; // value at sample
int index = 0;
double value2 = 0;
// loop through SoundSample objects
while (index < sampleArray.length)
{
sample = sampleArray[index]; // get current obj
value = sample.getValue(); // get the value
sampleDelay = (sampleArray[delay-index]);
value2 = (sampleDelay.getValue()*.6);
sample.setValue(value + (int) value2); // set the value
index++; // increment index
}
}
Let me know what you guys think all this seems to do is shift the amplitude for some reason...
The problem with posting an SSCCE is that this is using some classes that are not regularly in java I believe and therefor I was just looking for someone to help with the logic. I'm trying to loop through the samples of a sound file then set the value at the delay point to be the beginning of the sound but fainter in volume. IDK if I'm explaining this right but I was hoping this would be a simple fix.
Instead of
sampleDelay = (sampleArray[delay-index]);
you want
sampleDelay = (sampleArray[index-delay]);
Are you sure it is not the following?
sampleDelay = (sampleArray[index*delay]);

Categories

Resources