Dear Roger, et al, Thank you for your suggestions and advice, as well as details on the Allegro project.
I should note that what I'm trying to do is a bit of an experiment; not production code. Basically, I was trying to build a test that synchronizes MIDI playback to audio (w/ poraudio) callbacks. For right now, I am not stopping/pausing, relocating, etc.; just a straight play-through. For simplicity, the SMF and the WAV file I'm synchronizing start at time "0" -- no offsets. My "thesis" was: a) find PA callbacks where the start of the buffer being requested corresponds to an integer tick, or near-integer, tick in the MIDI file, minus the MIDI device latency; I now have a list of buffer-tick alignment points between a PAStream and a SMF; b) when the callback's buffer matches a buffer-tick alignment point, call a "play MIDI range" function; c) the play MIDI range function plays from the current buffer-tick alignment point through the tick before the next buffer-tick alignment point. Using this method -- at least theoretically -- the audio and the MIDI align themselves at convenient, integer times across the sample rate and PPQ (units of both src file formats). Given 44.1 and 480 @ 60BPM, a 256-frame callback will align itself with the SMF at tick 0, tick 286, tick 574, tick 861, tick 1148... and that gets us to about 2.39 seconds into the file. In conclusion, my goal is to make a function with Allegro that looks like this: playMIDIRange(0, 287); playMIDIRange(286,573). Why this method? First, the MIDI never gets too far without synchronizing with the audio. Second, the MIDI is slaved to the audio callback, which is reliable timing source. Those are basically my motivations. In my opinion/experience, it uses the short-term accuracy of Pt_Sleep combined with the long-term accuracy of the Pa_Callback. The reasons I chose this method actually line up closely with the reasons you cited early MIDI implementations relied on ticks. I want to avoid rounding errors by avoiding the floating point. Could I do playMIDIRange in seconds or beats? Probably; however, since this has to do with syncronization, I want control of how the floats get rounded into ticks, and then events. Regarding chasing events -- yes, I am familiar with this. For right now, I was going to ignore this. If I improve this, what I was going to do was: if the next playMIDIRange comes in before the current playMIDIRange is done playing, I'd collect the events not yet played and add them to the first tick of the new playMIDIRange seq. It won't be pretty, but it will ensure that we don't move forward until all events have been sent through the MIDI port. The MIDI playing is happening on its own thread, of course, else I'd be blocking the Pa_Callback. My question is how does Audacity project do this / plan to do this? I have read many of your papers, also Ross Bencina ACMC paper "PortAudio and Media Syncronization", which inspired this method. Furthermore, how would you suggest to do this? It seems like a tough problem to solve equitably, so I'd love to hear your input. Do you think ticks should be added to the track structure? I look forward to your feedback, and anyone else on the list who wants to chime in! Sincerely, Jerzy On 6/16/08, Roger Dannenberg <[EMAIL PROTECTED]> wrote: > Jerzy, > Why do you want to specify time in ticks rather than seconds or beats? I > think of ticks as an implementation detail to allow fractional beats such > that: > - floating point is avoided (this was important on early PCs) > - representation is compact (by limiting precision) > - rounding errors can be avoided with quantized beats in simple ratios > such as triplets > None of this sounds particularly important in modern systems, and the > Allegro/PortSMF > library factors out ticks while midi files are being parsed. > I ask because if there's really a good reason for using ticks, then it > would make sense to put the original ticks/beat number into the sequence or > track structure where it could be used. > If the goal is just to have access to fractional beats or seconds, that's > why time is a double. > > If I want a sequence with the first tick [you mean from tick 1 to tick > 2? -rbd] > > of a MIDI file at 480 PPQ and 60 BPM am I to > > call:seq->cut(0.0020833333, 0.004166666, false)? > Why not call seq->cut(1.0/480, 2.0/480, false)? But then wiring 480 into > the code is obviously not right, hence my question. > > I would tend to write the play code to seek into the track or sequence > (there's an iterator to help do this) rather than cutting out and copying > the stuff I want to play. I think it's cleaner, and there's also the issue > that MIDI is not stateless -- when you play from some random time point, do > you want to first set all the prior program changes and control changes? > Usually, sequencers just spew out everything but note-on and note-off > commands, starting from the beginning of the sequence so that when you start > playing notes, all the control values are correct. You could do something > more sophisticated, but it's quite tricky to build efficient and editable > structures that let you find the last control change for each possible > control. > > Feel free to follow up with more questions or details -- this is a pretty > open-ended question. > > -Roger > > PS This code was extended with a lot of editing operations for Audacity. > It's not in Audacity yet, but we're working on it actively right now, so > you're likely to see some activity on the project as we fix bugs and extend > functions as needed. Parts of this code have been used a lot, but most of > the editing functions like cut() and selection functions have only received > some basic testing. > > > Jerzy Gangi wrote: > > > > > Hi, > > > > First, I'd like to thank everyone involved for developing the > > portmedia libraries. I have found these libraries very useful, and I > > appreciate the effort you all put in to the portmedia project. > > > > I am trying to create a playRange(seq, ticks_from,ticks_to) function, > > similar to the seq_play(seq) function in allegroplay.cpp. The function > > will take in a range of the SMF's ticks, relative to tick 0 at the > > beginning of the file, and play from the beginning of ticks_from all > > of the way through the end of ticks_to. For right now, I am extending > > allegroplay.cpp, which is making this easy to try out. (thanks to > > whoever prepared the xcode project file). I should mention that my > > driver will not be calling playRange at "pretty" music intervals (eg > > playRange(seq,233,475)), else I'd just use cut() to cut the sequence > > inside some range of beats. Therefore, tick, not beat or second, > > precision is necessary for my playRange(). > > > > My first idea was to use Alg_seq's cut(double start, double len, bool > > all) method. If I could cut away the range I don't need from a > > sequence, I could play the resulting range with > > seq_play(the_having_been_just_cut_seq). Right? However, > cut() only > > lets me specify in terms of beats (as a double) or seconds, the unit > > being settable on the object. If I want a sequence with the first tick > > of a MIDI file at 480 PPQ and 60 BPM am I to > > call:seq->cut(0.0020833333, 0.004166666, false)? See? I think I want a > > tick-based function. I tried a few other things, but a stumbling block > > was that get_dur() returns 0 for me. > > > > I did not see very many public functions in Allegro (which I have to > > say is designed pretty nicely!) that use "ticks". I assume these > > classes were designed to favor musical intervals over ticks. This > > would make sense, and what I am trying may be very well against the > > intended usage of the allegro/portsmf classes. > > > > Any advice on if I should subclass / use the fractional beat unit / > > not be using portsmf, etc. is 100% welcomed! I hope everything here is > > understandable; I'm happy to elaborate on anything. > > > > THANK YOU! > > > > Jerzy Gangi > > Computer Music Department, Peabody Conservatory > > Johns Hopkins University > > _______________________________________________ > > media_api mailing list > > media_api@create.ucsb.edu > > http://lists.create.ucsb.edu/mailman/listinfo/media_api > > > > > > > _______________________________________________ media_api mailing list media_api@create.ucsb.edu http://lists.create.ucsb.edu/mailman/listinfo/media_api