For a project I am working on I need to pause the midi sequencer and I want all the sounding notes to sustain and thus make those notes endless. The problem I am facing is that sequencer.stop() apparently not only stops the playback, but also sends MIDI note off messages to all sounding notes, terminating all sounding notes (in some literature refered to as flushing).
I have tried to use sequencer.setTempoInBPM(0) and that gets the job done, but has other unwanted side-effects specific to my project.
The most obvious solution then seems overriding Sequencer.stop(), but how do I do that? And how exactly will that overridden method look like?
Edit:
I would like to edit the question in response to the comment of gpasch.
not only stops the playback, but also sends MIDI note off messages to
all sounding notes: what are you talking about?? isnt this the same
thing??
That is true for audio, but that is not true for MIDI. The MIDI protocol doesnt specify any audio data by itself. It only gives instructions to a musical instruments on what to play. The instrument interprets the MIDI messages and makes the final sound.
In order to let an instrument play a sound of one second, this are the actions:
[Sequencer] MIDI Message Out: note on
[Instrument] MIDI Message In: note on
[Instrument] Starts interpreting note on: starts producing sound
[Sequencer] Waits one second
[Sequencer] MIDI Message Out: note off
[Instrument] MIDI Message In: note off
[Instrument] Starts interpreting note off: stops producing sound
So, if this process gets interrupted on step 4, it would create an "endless note". Because the MIDI instrument got instructions to begin playing a certain note, but never got an instruction to stop playing that note. [*]
Looking back to my question. When I call sequencer.stop() in the middle of a note (step 4), instead of having an "endless note", all notes that are being played on that moment that did not yet have got an note off message, stop sounding. The logical explenation for that, is that sequencer.stop() sends a MIDI All Note Off message under the hood. We can be really thankfull for that, because otherwhise sequencer.stop() would be a real mess. In my particular case though I really need the sequencer to not send the note off message. So the question is: can I make a workaround for that?
*
If it is a piano sound with a natural decay of the sound, the sound will eventually die. But with a synth sound the sound will persist till there has been
The way I would do it is. Create a filter which would basically receive everything sent from the sequencer and send it to your midi out. Inside this filter create a condition where if the "pause flag" is true all note offs would be received but not sent.
Create a pause() method which when called first sets your "pause flag" to true and then does sequencer.stop().
Of course you would need some way to keep track of the note offs that have been blocked so that you can actually stop them when you eventually do want to or else they will really stay on for ever.
Related
I'm looking to write a simple MIDI-driven audio sequencer using Javasound.
I have multiple samples (one for each MIDI pitch) that are loaded into memory as a (globally accessible) Map<MidiPitch,AudioInputStream>.
A custom subclass of javax.sound.midi.Receiver responds to incoming MIDI events as follows:
If the event is a note-on, a Clip is obtained and played as follows:
Clip clip = AudioSystem.getClip();
clip.open(lookupAIS(pitch));
clip.start();
The clip is then added to a globally accessible Map<MidiPitch,List<Clip>>, representing started clips, i.e. clips for which start() has been called as above, but for which a note-off event has not yet been received.
If the event is a note-off, the corresponding list of started clips is obtained from the above map. The clip at the head of the list is removed, and stop() and close() are called on it.
The above Receiveris connected to MidiSystem.getSequencer() in the usual way, then the following called:
sequencer.setSequence(MidiSystem.getSequence(new File(myMidFile)))
sequencer.open()
sequencer.start()
Thread.sleep(aLongTime())
sequencer.stop()
sequencer.close()
The above works when the driving MIDI sequence is at a slow tempo, but at higher tempos, notes simply hang (even for sequences containing a very small number of notes).
My understanding is that clip.start() is run within a separate thread behind the scenes by the Javasound API.
Can anyone suggest why this might be happening? Is it perhaps a synchronization issue?
EDIT: By 'hang', I mean that some notes are stuck, despite the fact that log output reports that the corresponding 'stop' method has been called.
EDIT2: It looks as if the hanging first happens when a given note is played for the second time. This happens even if the MIDI sequence is monophonic, i.e. the previous note has stopped.
Your method of loading the Clip for each play is going to be a considerable source of variable latency. Every time you call this, the file is read anew and will not start playing until it the entire file has finished loading.
I recommend pre-loading all the clips and holding them in memory. When the note-on is called, set the clip cursor to zero and then play:
clip[mapIndex].setFramePosition(0);
clip[mapIndex].start();
These clips should have already been opened. I'm putting them in an array and using "mapIndex" as a plausible way of selecting the correct clip that might work with the mapping you've already set up.
You probably won't need to "stop" or "close" the clips until the entire sequence has finished, unless the clips are rather long and are designed to be stopped while in progress, or if they are being played as loops.
This should improve things considerably. I can't say if it will fix everything. The cpu is probably doing some thread multiplexing, and it is plausible that occasionally, in your current code, the clip.close is being called on one thread before the clip has finished loading on the other.
I want to synchronize other sensor data with the video I'm recording, and so I'd like to record "how far am I into the video" when the sensor is triggered. Is there any way to do this? I couldn't find an appropriate method on the MediaRecorder class.
Another solution would be to just get the precise start time of the video recording, but my tests show that the video starts ~1sec after calling mediarecorder.start, but it's not consistent.
You have raised an interesting topic.
If you refer to the documentation in the developer page, the following diagram states the recording is supposed to start when the start() method is called.
Your solution is supposed to be correct albeit there is a lag up to 1 sec. I would do it the same way
I went through the MediaRecorder class methods, the only method that seems to be useful is the callback setOnInfoListener().
Set it and see if you will get some kind of information when the recording starts! I haven't tried it yet though.
I understand the basics of getting a MIDI sequencer up and running and I would like to be able to increase/decrease the pitch of the sequence during playback, but pitch bend is a message that gets sent to the synthesizer, not the sequencer.
I tried setting the sequencer's receiver to be the synthesizer's transmitter, and when I sent pitch-bend short messages, the sequencer stayed the same pitch but then the synthesizer played a second track at the new pitch bend value, creating some pretty awful-sounding music.
Is there a good way of bending pitch during playback like there is for changing tempo?
Another option (which seems like a big kluge) is to have a few versions of the MIDI files in different keys ready to load when called.
You could try to send the messages directly to the synthesizer's receiver by calling its send method, but the synthesizer might not allow its receivers to influence each other.
Ultimately, the messages sent to the synthesizer are stored in the sequencer's track, so to have pitch-bend messages, edit the sequence to add those messages.
The MIDI protocol doesn't define any standards for pitch, except for pitch bend which is generally intended to be used as a temporary effect rather than a way of setting the arrangement's tuning. It sounds like you are implementing a sequencer in Java, in which case I would rather suggest having a global detune preference. Then you would simply render the audio with the default tuning (440Hz) and then apply pitchbend to the bounced arrangement. There are several libraries which can do pitchbend for you, including the excellent DIRAC.
I'm working on an application to synchronize with realtime audio playback. I would like to define a listener with a specific timestep, and use the listener to get updates every time the audio being played has advanced by a timestep. Implementing this functionality must be possible (existence proof being time counter of audio player apps) but the architecture I have in mind might not be possible.
Ideally, I would listen to the audio stream on the SPEAKER or HEADPHONE Target Port of the sound card. The existing LineListener/LineEvent functionality only supports START/STOP/OPEN/CLOSE events, and I can't subclass and augment the Target Port implementation (or SourceDataLine or TargetDataLine).
How can I do this? Do I need to have a Thread constantly polling the getMicrosecondPosition() or getLongFramePosition() methods and fire my own event at the appropriate time? That sounds very inefficient. If I used a Timer to trigger the poll at regular time periods, then I'm better off than just using the system timers between the available START and STOP events that are supported on Port/DataLines. I would call the getMicrosecondPosition() method to synchronize, but I think that is likely to result in occaisional errors due to drift between the system Timer and real time audio. I'd expect to see repeats or skips of the counter (i.e. 1:23:003, 1:23:004, 1:23:006, 1:23:007... or 1:23:003, 1:23:004, 1:23:004, 1:23:005..).
I'm looking at the javazoom mp3 player source to see how they did it but no luck figuring it out so far. Any help/tips will be appreciated.
Hi I have java application which plays midi messages from sequence. I'm doing this using jfugue library.
the problem is when I'm tryingto stop playback with stop button (which call sequencer.stop() and sequencer.close()) the last played note is sound all of rest time, and I can't stop it.
So I'm asking about solution about stopping all audio and MIDI too! sound playback from java application.
Notice:
If you want propose just mute volume, you need to know that I want end-use will be able to press play button again and hear the sound again, so muting volumr will be not a solution, or explain please.
Thank you!
I'm guessing you need to call Player.allNotesOff() before calling sequencer.stop(). Untested, so please let me know if it didn't work.
When MIDI plays music, it uses a combination of NOTE_ON and NOTE_OFF events. It sounds like when you press the stop button, the sequence is stopping after a NOTE_ON event had been sent, but before a NOTE_OFF event was sent. That means the sound will continue to play indefinitely.
Player.allNotesOff() in JFugue makes a call to JavaSound's allNotesOff() method in the Channel class; this call is made for each MIDI Channel (there are 16 channels). It's unfortunate that this solution isn't working. Try calling Player.allNotesOff() after sequencer.stop(), see if that helps.