PS. I haven't checked PortMIDI, but it might handle a message that would
cancel all cued events… Being able to stop playing immediately is quite
important. This is the only reason why I had suggesyed a separate scheduler.
-------
This sounds very encouraging. Maybe start with PortMIDI
(http://portmedia.sourceforge.net/portmidi/) since it has been recently
fixed for Windows, and you have experience with it. Since the
environments (CoreMIDI, MME, ALSA) are different, I suppose that
conditional compilation will be necessary…
I guess that the difficulty in picking up accurate timings from the
system is the reason why Rick's event scheduler was using assembly
language. I had forgotten that time() returns dates with 1 second
resolution!
We do have events in chronological order with increasing time-stamps, so
the implementation won't affect time-setting algorithms.
Indeed, these "free of charge but…" licenses sound a bit unsane !
Bernard
Anthony Kozar wrote on 19/08/2020 22:12:
> Bernard,
>
> I'm sorry that I didn't reply to this thread earlier to soothe your
> late night worries! PortMidi DOES appear to support timestamped
> messages.
>
> The GitHub project you looked at is for a PortMidi binding in the Go
> language. The project for the original C library is here:
>
> http://portmedia.sourceforge.net/portmidi/
>
> The functions for writing MIDI data to a stream are described in the
> documentation as
>
> PmError Pm_WriteShort (PortMidiStream *stream, PmTimestamp when, long
> msg)
> Pm_WriteShort() writes a timestamped non-system-exclusive midi message.
> Messages are delivered in order as received, and timestamps must be
> non-decreasing. (But timestamps are ignored if the stream was opened
> with latency = 0.)
>
> PmError Pm_WriteSysEx (PortMidiStream *stream, PmTimestamp when,
> unsigned char *msg)
> Pm_WriteSysEx() writes a timestamped system-exclusive midi message.
>
> I also looked at the source code which has a little more information
> about the latency parameter. If we set a positive latency of at least
> 1 ms, then the timestamps will be honored and PortMidi will use the
> system's native Midi scheduler if there is one. (CoreMIDI has a very
> accurate scheduler with times measured in nanoseconds!) Only with a
> latency of 0 would the timestamps be ignored. So, I think we can
> either let the system or PortMidi schedule the events or do it ourselves.
>
> I think it will be easier for getting started to send timestamped
> messages and let the native libraries on macOS/Windows/Linux do the
> scheduling if they support that. On today's multitasking OSes it is
> much harder, I think, to guarantee that a program can accurately
> perform timed tasks without resorting to system-specific timer calls
> and possibly a multithreaded architecture. IIRC, functions like
> sleep() are not guaranteed to return in time and the function time()
> has only 1 second precision. For the CoreMIDI driver, I used
> functions from the CoreAudio library to get very precise times from
> the system clock and convert them to milliseconds to use for BP2's
> ClockZero reference time.
>
> That said, I am definitely open to other options for a cross-platform
> MIDI library. I was just aware of PortMidi because it is used by
> Csound and some other software I've used. But there was a very recent
> discussion on the Csound dev list about how PortMidi had not been
> updated in a very long time and was causing some issues with their
> Windows build. The problem appears to have been solved by contacting
> the PortMidi developers and they patched the source code repository
> this month, IIRC. But if there is a better supported, open-source
> library out there, I think we should strongly consider it.
>
> Although I have heard some good "buzz" about JUCE in the past, I
> didn't realize that it is not open source. I think the JUCE licensing
> model would definitely be a deterrent to other developers getting
> involved in BP development or reusing BP in their own projects.
>
> Anthony
>
> P.S. Rick Taube has made some great music software tools over the
> years (primarily Common Music which I used to use). Even though the
> source code for his software was available to everyone, it was not
> always possible for other individuals to redistribute his software
> with modifications because he used tools that required expensive
> redistribution licenses (~$2000-2500 IIRC for Macintosh Common Lisp).
> He would purchase those distribution licenses so that anyone could use
> his software (I assume with grant money). But it was disappointing to
> me that I couldn't distribute Mac OS 9 builds of software made with
> MCL after he (and others) stopped even though I purchased a
> (user-level) license for MCL.
>
>
> On 8/19/20, 2:01 PM, Bernard Bel wrote:
>> 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
>
>
> _______________________________________________
> bolprocessor-devel mailing list
> bol...@li...
> https://lists.sourceforge.net/lists/listinfo/bolprocessor-devel
|