melanchall / drywetmidi

.NET library to read, write, process MIDI files and to work with MIDI devices
https://melanchall.github.io/drywetmidi
MIT License
558 stars 76 forks source link

Syncing multiple Playbacks #299

Open noonesimg opened 4 months ago

noonesimg commented 4 months ago

I've got a question about syncing two or more playbacks.

Imagine a midi sequencer, where a note has been changed while playing. The sequencer loops the same 16 steps forever.

As it's impossible to directly change _playbackEvents on-the-go with the current API, I figured I can just recreate the Playback instance and swap the old Playback with the new one:

var playback = pattern.GetPlayback(tempoMap, (FourBitNumber)0);
playback.OutputDevice = device;
playback.Loop = true;
playback.Start();

Console.ReadKey(); 

var newPlayback = pattern.GetPlayback(tempoMap, (FourBitNumber)0);
newPlayback.Loop = true;
newPlayback.Start();
newPlayback.MoveToTime(playback.GetCurrentTime(TimeSpanType.Midi));
newPlayback.OutputDevice = device;

playback.Stop();
playback.Dispose();

Console.ReadKey();

This results in a slight clock drift, which is negligible at first, but when done enough times its hard to ignore. Is there a possible workaround, like syncing multiple Playbacks to the same clock?

P.S. Awesome library!

melanchall commented 4 months ago

Hmm, unfortunately there is no way to eliminate that drift with the current approach. I'll think about API that allows to place multiple playbacks on top of the same MidiClock but can't say any date when it will be implemented :-(

noonesimg commented 4 months ago

That would be a great feature! I'm currently trying to come up with something writing a Playback class. Will send a PR if any success.

Speaking of which, is there a particular reason, so much Playback API is not exposed to the end used and marked as internal and/or sealed?

melanchall commented 4 months ago

Speaking of which, is there a particular reason, so much Playback API is not exposed to the end used and marked as internal and/or sealed?

If some API is private/sealed/internal, I think that API is not for a user and is needed for internal logic only. So I don't hide useful API intentionally. If that happens, it's because I even didn't think it could be useful.

noonesimg commented 4 months ago

I understand,

All I'm saying is there's plenty of useful stuff behind private/internal!

For example, I'm trying write my own playback to change how clocks work and I can't simply inherit vanilla version, because its internal clock is hidden behind private.

No problem, I'll write my own one from scratch. I study the source code and find that you have really useful stuff like PlaybackEvent class with all the utility written around it, but its internal and sealed, so now I have to either wirte my own or maintain a fork of this awesome library instead of building directly ontop of it :)

melanchall commented 4 months ago

I'm trying write my own playback to change how clocks work and I can't simply inherit vanilla version, because its internal clock is hidden behind private.

That's what I want to "fix" :-) But in fact this can be not so easy as we can imagine. I need to try and if no problems will be observed, I'll publish a prerelease version of the library.

noonesimg commented 4 months ago

Great to hear!

I'm kinda succeeded with syncing my own playbacks by keeping track of time inside each playback, having just one master clock, and subscribing/unsubscribing from Ticked on start/stop. Although I haven't got a slightest idea on how to accurately fit it into the existing Playback class :)

p.s. I could definitely benefit from changing visibility of PlaybackEvent and lots of other things in Melanchall.DryWetMidi.Multimedia. And in general, I think, exposing more of that internal API will make this library even better, especially when something is not possible out of the box yet.

If you're up for this, I can open another issue, and prepare a PR when I have a free evening. If not, I'll just stick with a forked version of mine

melanchall commented 4 months ago

It would be great to see your changes to understand better your needs :-)