I'm starting on a project where I'll be recording and playing back MIDI data. It's a basic sequencer with some MIDI processing features to implement effects like delay, appregios, etc. I'm not trying to re-invent the wheel, as I intend to have it export MIDI files and then I handle the rest of the production process in a DAW like FL Studio. I'm writing it in NodeJS, if this makes any difference.
Anyway, my question is this: I already have code working that receives real-time messages and immediately sends them out, and optionally sends out subsequent messages after a short delay. I know how to do that, and it's working perfectly. But how do I enable timing data to be sent with the event messages? I find little tidbits of information out there about setting a tempo and sending a "start" message, but not much more than that. I want to receive the messages with timing data saying the first note was at 0ms, second was at 250ms, third was at 750ms, etc. Doesn't matter if these are in ms, or MIDI ticks, I'll piece together whatever I have to at the end. Also, I need to eventually be able to send messages out like this as well. Relying on software timers in my code to send out real-time messages isn't the proper solution.
Let me know if you need any clarification as to what I'm trying to do, and thanks for any help.
Well, yes, as you say, there's not much point in re-inventing the wheel.
As far as I know, the midi data stream does NOT include timing data, this is dependant on so many other things. It is pretty much generated by the receiving software, dependant on the settings already established within that software.
There are many pieces of software that will receive incoming data, apply the relevant timing data, and save the result into a SMF (Standard Midi File). Really, your best bet would be to use such a system.
Geoff
Further to the above, if you especially need to do things with the timing data, then you need to investigate where your computer gets it from, and how it is generated. Depending on the system, it may be created by hardware, for example the midi interface (such as an MPU-401), or in software (which I assume would apply to USB type systems). You should not need to generate your own timing information as your system may be generating the data anyway. You would need to generate your own timing data if you're playing a midi file, which by definition will contain it's own timing data (from the moment of 'recording') and you need to match your (real) time with the recorded (file) time to be sure the recorded event are actioned at the correct moment.
Otherwise, any timing data going through the midi link may not be much to do with midi per se, it may just be various midi interfaces talking to each other to co-ordinate their timing.
Just in case there is some confusion here, please clarify where the timing data you are referring to is coming from, and when you send data out, where is the timing data going to and what is it being used for (i.e. what is receiving it, and what is the data being used for). Maybe you are referring to something more specialised than I am thinking of.
geoff
Geoff, thanks for your response. I scanned through the MIDI specs and thought that the references to timecode implied that I can send out some sort of tempo message and a start message, and then any subsequent messages sent to the computer from MIDI controllers would contain timecode as part of their messages. But maybe I'm wrong about all that.
So if I want to write code to record MIDI, just like a DAW does it, I have to assign the timing data myself as the notes come in. Each note comes in, and immediately take the system timestamp and associate it with the note, and then after recording is done, go back and convert it all to timecode with respect to the song's tempo. Right?
And then when I'm sending MIDI data back out, there's no way to send out timing data with it and just send out a whole flurry of notes at a time?
Right now, I'm associating timestamps with the incoming data and using system timers to coordinate sending the notes back out during playback. The timing is off, especially if I play a bunch of notes close together. Granted, my algorithm can probably use some fine-tuning, but I was hoping there was a solution that doesn't rely on system timers.
There may be circumstances where what you describe is correct, but this will depend on the devices at both ends working to the same 'script' as it were.
The sort of setups that I've used work on the basis that the sending device controls the timing on its own, and the receiving device plays the notes as and when received. So if a pile of midi events are received in a lump, they will all try to sound in the same lump, which will probably NOT sound like it was intended to. For most setups, it's a lot easier to have the controlling device keep the timings, and the sounding device merely play the notes as it gets them.
Referring to your comment in the message above, if you want to record incoming midi data just like a DAW does, then you'll need to assign the timings just like a DAW would do as well.
Geoff
Thanks for your help, I'll work on narrowing down where I'm getting the timing delays and see if I can fix it.
I scanned through the MIDI specs and thought that the references to timecode implied that I can send out some sort of tempo message and a start message, and then any subsequent messages sent to the computer from MIDI controllers would contain timecode as part of their messages. But maybe I'm wrong about all that.
MIDI Time Code quarter frame and System realtime messages Timing Clock, Start, Continue, and Stop are intended to synchronize devices, for instance a MIDI sequencer and a video production recorder. See also https://en.wikipedia.org/wiki/MIDI_timecode and https://en.wikipedia.org/wiki/MIDI_beat_clock
You make a message object? by push the data "message" and its timing into an array, performance now is the highresolution timer in Javascript..
Notice there is no PPQ or resolution involved, if you want that you will have to quantize it yourself.
midiMess: [];
browserLoadTIME=0;
rs = document.getElementById("recstart").value;
function startRecord(){
startTIME=performance.now();
}
function onMIDIMessage(message) {
record(message);
}
function record(message) {
clock = performance.now()-startTIME+rs;
track[rectrack].midiMess.push({
time: clock,
data0: message.data[0],
data1: message.data[1],
data2: message.data[2]
});
}
In the URL below is my midisequenser in vanilla Javascript i do have problem with lag during playup that i somehow need to resolve.
Hello Eric maybe you could help me with a javascript problem that i simply do not understand.
I had not realised that javascript do support sending out notes, invoking timer and timing information until i saw this.
output.send( [0x80, 60, 0x40], window.performance.now() + 1000.0 ); // Inlined array creation- note off, middle C,
It seem to say send this note "after 1000 ms" on timer which is performance now.
So having that code with two arrays EvTime and Note that hold your time and note data you end up with something like this.
output.send(Note.shift,, window.performance.now() + waittime );
waittime = EvTime.shift();
But how "do one wrap such a call of code" sequense it until all events shifted? You can not use a loop because javascript do not pause?
I am a bit lost with never programming paradigm so i simply do not understand how to use "and serialize the code" into "iterative?" calls?
I would be very happy if someone could tell me "how" because this latency issue have boggled me for years, and its big doing it with setTimeOut
You can see the latency at work in the URL. 30 seconds on a 5.46 minute song playing up 13000 notes.
Jonas Thörnvall.
Update: Lets just say doing it recursively it goes a little faster then expected, so how do i do the calls with the timings "not setTimeOut".
function STARTPLAY(){
if (keepGoing) {
if (copyEv.length) {
outportarr[outportindex].send(noteMessage.shift(),window.performance.now() + copyEv.shift());
}
}
STARTPLAY();
}
Update: Tried to subtract performance.now with saved oldPerformance "time" and if greater then time "copyEv element" in array, i shift to next event. But i get callstack exceeded in the recursion?
oldPerformance=windows.performance.now;
STARTPLAY();
function STARTPLAY(){
evTiming=copyEv[ 0 ];
if (copyEv.length) {
if(windows.performance.now-oldPerformance>evTiming){
oldPerformance=windows.performance.now;
outportarr[outportindex].send(noteMessage.shift(),window.performance.now() + copyEv.shift());
}
}
STARTPLAY();
}