I thought about all this again last night…
The argument that PortMIDI only accepting real-time MIDI messages rather
than time-stamped MIDI events would generate a cumulation of errors does
not hold! It is easy to schedule events according to absolute time
(starting from a defined zero date).
The following is (approximate) code for playing a 2-second C major chord
starting after 1 second.
time_zero = time(); // milliseconds
next_event_date = 1000;
while(time() < (time_zero + next_event_date)) {
$result = ListenMIDI();
if($result == STOP) break;
}
out.WriteShort(0x90, 60, 100)
out.WriteShort(0x90, 64, 100)
out.WriteShort(0x90, 67, 100)
next_event_date = 2000;
while(time() < (time_zero + next_event_date)) {
$result = ListenMIDI();
if($result == STOP) break;
}
out.WriteShort(0x80, 60, 100)
out.WriteShort(0x80, 64, 100)
out.WriteShort(0x80, 67, 100)
Variables "next_event_date" are the time stamps that we used to imbed
into MIDI events. We could even count in microseconds at the same cost. ;-)
ListenMIDI() is trying to capture an incoming event (or a keyboard
stroke) that would instruct the console to modify some settings or stop
playing immediately. (It is already implemented.)
Frankly I don't understand why a complex scheduler was required when it
looks so simple. Maybe there is a hidden problem that I haven't figured out!
If you create a very simple C program, we can check this procedure and
then implement it in the console. The only difficult problem, which
PortMIDI will solve, is the connection to MIDI in/out devices…
I also felt reluctant to use a framework like JUCE because it has a
license that might prevent the reuse of BP3 in other (possibly
commercial) environments.
Bernard
-----------
There is another advantage in creating a "merry-go-round" MIDI event
loop sending events at their proper time instead of streaming them in
advance with time-stamps as we had done with OMS.
In this setup, events can be modified or deleted at any time before they
are played. In a time-stamped stream you cannot modify events once they
have been queued to the MIDI driver.
For instance, the user may program an incoming event (a MIDI NoteOn, a
stroke on the keyboard) to stop playing immediately and delete all
pending events, or keep them on a pause. This will evidently happen in
real-time music production, and even more if several machines are
interacting in a cooperative manner.
So, I suggest that we look for (or design) an algorithm able to schedule
events in real time… Then PortMIDI would be a workable option!
Bernard Bel
--------------
I had a quick look at PortMIDI which is one of the suggested tracks to
solve the problem of real-time MIDI output/input:
https://github.com/rakyll/portmidi
The input (listening to events) is probably easy to implement as
previously done in the ListenMIDI() loop. But the ouput seems more
problematic. Their example for playing a C major chord for 2 seconds is:
// note on events to play C major chord
out.WriteShort(0x90, 60, 100)
out.WriteShort(0x90, 64, 100)
out.WriteShort(0x90, 67, 100)
// notes will be sustained for 2 seconds
time.Sleep(2 * time.Second)
// note off events
out.WriteShort(0x80, 60, 100)
out.WriteShort(0x80, 64, 100)
out.WriteShort(0x80, 67, 100)
This means that it would take MIDI events without a time-stamp, and all
scheduling would be managed by time.Sleep().
Even though time.Sleep() might accept very accurate time intervals, the
cumulation of intervals over thousands of events is likely to generate
imprecision.
In the current layout (as I remember it) we send time-stamped MIDI
events containing "absolute" timings (from a unique "zero" point) which
makes it possible to maintain time accuracy with 1 millisecond whatever
the number of events.
Maybe this has already been discussed (and solved) by developers using
PortMIDI…
Before OMS I was using a loop (written in assembly language) to schedule
events accurately as it was not yet possible to time-stamp them. I am
not sure I still have the code which anyway is totally obsolete since it
was for MacOS 9. Nonetheless, creating that kind of loop would be a
manner of using PortMIDI without resorting to time.Sleep().
I remember using the image of a merry-go-round to visualize this event
loop; but this may not be of great help! ;-)
An important detail is that BP2 (in MacOS 9 + OMS) did send events in
their chronological order even though time-stamping did not make it
compulsory. This feature might turn out useful when implementing a new
driver…
A practical way of approaching this might be to write a very simple C
program whose sole work is to broadcast a couple of MIDI events, then
listen to capture an incoming event.
Bernard Bel
|