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!
Related
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
Me and a friend of mine are working on a game and basically, we have a hitbox set up where if our character hits it (a spider), the character loses health.
Because we are using a for loop, the inevitable happens, the player loses their health 1 by 1, but so fast the human eye can not see it. Therefore we need a timer, pause, or something for every x amount of time the player is hitting the spider. Here is our code:
if (ResetEntities.spiderObj.intersects(ResetEntities.blueCharacterObj)) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
if (ResetEntities.blueHealth < 0) {
ResetEntities.blueHealth = 0;
Frame.blueHealthLabel.setText("Health: "
+ ResetEntities.blueHealth);
} else {
ResetEntities.getBlueHealth(ResetEntities.blueHealth - 1);
Frame.blueHealthLabel.setText("Health: "
+ ResetEntities.blueHealth);
}
}
}
In this code we have an if statement to check for intersection. I tried to sleep the script, (I realized 10 is incredibly fast, but 1000 lags horribly, so Thread#sleep isn't the way to go I imagine). Then we have a for statement to run 100 times (being the players health). Then we have an if seeing if the health goes below 0, set back to 0, so the player can't get negative health. If it's not below 0, we do the else and subtract 1 health. But as I said this runs too fast to see.
Any help?
The traditional way - and in my opinion best way - to do it is, to make the wounded character invulnerable until a specific point in time: When the character gets wounded, check the game-timeline (or if you dont have one then the current millis-count) then add a specific time eg 1 second (or the character-dependent recovery-time) and remember that timestamp in the character-object. A gametick that wants to wound a character first checks whether the character is still in hidden invulnerability. This is especially good because typically one doesn't really want multiple threads updating the game-state.
Use Timer for your background process' timing. And use javax.swing.SwingUtilities.invokeLater(Runnable) to update your GUI.
The question is unclear, but are you asking how you can stop the health from going down so quick? If so,this is what I would do:
You should have a int value invincibilityTime and set that to 100 once the player is hit, and then every time your game goes through the gameloop it decreases it by one. Put if(invincibilityTime <= 0) before the (ResetEntities.spiderObj.intersects(ResetEntities.blueCharacterObj)) to check if the invicibility period has expired.
I've been working on implementing a system for real-time audio capture and analysis within an existing music software project. The goal of this system is to begin capturing audio when the user presses the record button (or after a specified count-in period), determine the notes the user sings or plays, and notate these notes on a musical staff. The gist of my method is to use one thread to capture chunks of audio data and put them into a queue, and another thread to remove the data from the queue and perform the analysis.
This scheme works well, but I am having trouble quantifying the latency between the onset of audio capture and playback of the MIDI backing instruments. Audio capture begins before the MIDI instruments begin playing back, and the user is presumably going to be synchronizing his or her performance with the MIDI instruments. Therefore, I need to ignore audio data captured before the backing MIDI instruments begin playing and only analyze audio data collected after that point.
Playback of the backing tracks is handled by a body of code that has been in place for quite a while and maintained by someone else, so I would like to avoid refactoring the whole program if possible. Audio capture is controlled with a Timer object and a class that extends TimerTask, instances of which are created in a lumbering (~25k lines) class called Notate. Notate also keeps tabs on the objects that handle playback of the backing tracks, by the way. The Timer’s .scheduleAtFixedRate() method is used to control periods of audio capture, and the TimerTask notifies the capture thread to begin by calling .notify() on the queue (ArrayBlockingQueue).
My strategy for calculating the time gap between the initialization of these two processes has been to subtract the timestamp taken just before capture begins (in milliseconds) from the timestamp taken at the moment playback begins, which I'm defining this as when the .start() method is called on the Java Sequencer object that is in charge of the MIDI backing tracks. I then use the result to determine the number of audio samples that I expect to have been captured during this interval (n) and ignore the first n * 2 bytes in the array of captured audio data (n * 2 because I am capturing 16-bit samples, whereas the data is stored as a byte array… 2 bytes per sample).
However, this method is not giving me accurate results. The calculated offset is always less than I expect it to be, such that there remains a non-trivial (and unfortunately varied) amount of “empty” space in the audio data after beginning analysis at the designated position. This causes the program to attempt to analyze audio data collected when the user had not yet begun to play along with the backing MIDI instruments, effectively adding rests - the absence of musical notes - at the begging of the user’s musical passage and ruining the rhythm values calculated for all subsequent notes.
Below is the code for my audio capture thread, which also determines the latency and corresponding position offset for the array of captured audio data. Can anyone offer insight into why my method for determining latency is not working correctly?
public class CaptureThread extends Thread
{
public void run()
{
//number of bytes to capture before putting data in the queue.
//determined via the sample rate, tempo, and # of "beats" in 1 "measure"
int bytesToCapture = (int) ((SAMPLE_RATE * 2.) / (score.getTempo()
/ score.getMetre()[0] / 60.));
//temporary buffer - will be added to ByteArrayOutputStream upon filling.
byte tempBuffer[] = new byte[target.getBufferSize() / 5];
int limit = (int) (bytesToCapture / tempBuffer.length);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(bytesToCapture);
int bytesRead;
try
{ //Loop until stopCapture is set.
while (!stopCapture)
{ //first, wait for notification from TimerTask
synchronized (thisCapture)
{
thisCapture.wait();
}
if (!processingStarted)
{ //the time at which audio capture begins
startTime = System.currentTimeMillis();
}
//start the TargetDataLine, from which audio data is read
target.start();
//collect 1 captureInterval's worth of data
for (int n = 0; n < limit; n++)
{
bytesRead = target.read(tempBuffer, 0, tempBuffer.length);
if (bytesRead > 0)
{ //Append data to output stream.
outputStream.write(tempBuffer, 0, bytesRead);
}
}
if (!processingStarted)
{
long difference = (midiSynth.getPlaybackStartTime()
+ score.getCountInTime() * 1000 - startTime);
positionOffset = (int) ((difference / 1000.)
* SAMPLE_RATE * 2.);
if (positionOffset % 2 != 0)
{ //1 sample = 2 bytes, so positionOffset must be even
positionOffset += 1;
}
}
if (outputStream.size() > 0)
{ //package data collected in the output stream into a byte array
byte[] capturedAudioData = outputStream.toByteArray();
//add captured data to the queue for processing
processingQueue.add(capturedAudioData);
synchronized (processingQueue)
{
try
{ //notify the analysis thread that data is in the queue
processingQueue.notify();
} catch (Exception e)
{
//handle the error
}
}
outputStream.reset(); //reset the output stream
}
}
} catch (Exception e)
{
//handle error
}
}
}
I am looking into using a Mixer object to synchronize the TargetDataLine which is accepting data from the microphone and the Line that handles playback from the MIDI instruments. Now to find the Line that handles playback... Any ideas?
Google has a good open source app called AudioBufferSize that you are probably familiar with. I modified this app the test one way latency- that is to say, the time between when a user presses a button and the sound is played by the Audio API. Here is the code I added to AudioBufferSize to achieve this. Could you use such an approach to provide the timing delta between the event and when the user perceives it?
final Button latencyButton = (Button) findViewById(R.id.latencyButton);
latencyButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mLatencyStartTime = getCurrentTime();
latencyButton.setEnabled(false);
// Do the latency calculation, play a 440 hz sound for 250 msec
AudioTrack sound = generateTone(440, 250);
sound.setNotificationMarkerPosition(count /2); // Listen for the end of the sample
sound.setPlaybackPositionUpdateListener(new OnPlaybackPositionUpdateListener() {
public void onPeriodicNotification(AudioTrack sound) { }
public void onMarkerReached(AudioTrack sound) {
// The sound has finished playing, so record the time
mLatencyStopTime = getCurrentTime();
diff = mLatencyStopTime - mLatencyStartTime;
// Update the latency result
TextView lat = (TextView)findViewById(R.id.latency);
lat.setText(diff + " ms");
latencyButton.setEnabled(true);
logUI("Latency test result= " + diff + " ms");
}
});
sound.play();
}
});
There is a reference to generateTone which looks likes this:
private AudioTrack generateTone(double freqHz, int durationMs) {
int count = (int)(44100.0 * 2.0 * (durationMs / 1000.0)) & ~1;
short[] samples = new short[count];
for(int i = 0; i < count; i += 2){
short sample = (short)(Math.sin(2 * Math.PI * i / (44100.0 / freqHz)) * 0x7FFF);
samples[i + 0] = sample;
samples[i + 1] = sample;
}
AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
count * (Short.SIZE / 8), AudioTrack.MODE_STATIC);
track.write(samples, 0, count);
return track;
}
Just realized, this question is multi-years old. Sorry, maybe someone will find it useful.
Background: I have a IOIO which I am using to measure the output from an photodiode, this is the converted into a digital output. I need to find the frequency at which the signal changes between 1 and 0. Everything I have tryed so far has hanged my test app, any suggestions?
current code:
if(diode == 1 && frequencyFound == false){
startTime = System.currentTimeMillis();
while((frequencyFound == false)){
if(diode == 0){
while(frequencyFound == false){
if(diode == 1){
double endTime = System.currentTimeMillis();
time = endTime - startTime;
frequency = (long) (1.0 / time);
frequencyFound = true;
}
Thread.sleep(100);
}
}
Thread.sleep(100);
}
}
There are a couple of issues here.
First, Android is a multi-tasking system, and you could find your timing thread put to sleep long enough to miss some signal transitions. Is there no way to be notified of a leading (or trailing) edge transition rather than sampling the input in a loop?
What sort of frequency are you looking at? Will a 100 ms sampling interval be fine enough?
Don't count on Thread.sleep() to sleep for exactly the time you specify. If the interval is too short, the system might decide to return immediately or it might round the sleep time up to a larger amount.
Your timing loop won't record the time to any precision better than 100ms (at best), so your estimate for the frequency will be very poor.
Zapl is right, you MUST run this from a separate thread from your UI thread.
Watching for a single transition will give you a very imprecise estimate of the frequency. Try something like this instead:
// Find frequency to the nearest hz (+/- 10%)
// It's assumed that some other process is responsible for updating the "diode"
// variable. "diode" must be declared volatile.
long duration = 1000; // 1 second
final int interval = 100; // sampling inteval = .1 second
int oldState = diode;
int count = 0;
final long startTime = System.currentTimeMillis();
final long endtime = startTime + duration;
while (System.currentTimeMillis() < endtime) {
// count all transitions, both leading and trailing
if (diode != oldState) {
++count;
oldState = diode;
}
Thread.sleep(interval);
}
// find the actual duration
duration = System.currentTimeMillis() - startTime;
// Compute frequency. The 0.5 term is because we were counting both leading and
// trailing edges.
float frequency = 0.5 * count / (duration/1000);
Two more extreme suggestions for working around some of the timing accuracy concerns Edward raised:
Do the measurement of interval times on the IOIO board under interrupts, where you can presumably accomplish (at least near-) real time operations. Report these time measurements to the Android device.
Skip the ioio board and build something simple to route the signal into the headset connector as a tone that turns on or off. Record audio using the built-in timing guarantees of the audio system, and then analyze the audio buffers (real time no longer required) to determine the intervals from the number of intervening audio samples times in units of the (relatively reliable compared to anything you could do in an android app) audio sample rate. You can also get an analog input easily by using the light sensor to vary the frequency of an audio oscillator.
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.