SkinnyTod,
> I'm using the 'MusicPlayer' class (MusicPlayer API) to make a little file > midi player as a learning project. I've got it working but now I want to > visualize the midi events (notes) as they play.
> I thought I could use 'MusicSequenceSetUserCallback' (MusicSequence API) to > call the instance of my main class when events occur.
I have an application with MIDI file playback functionality. It's possible what I do will be applicable to your app.
I remember finding that MusicSequenceSetUserCallback didn't really work for chasing events and showing them in (more or less) real-time. The MusicSequenceSetUserCallback works for UserData added to a MusicSequence, if I recall correctly.
MusicSequenceSetUserCallback @abstract Establish a user callback for a sequence @discussion This call is used to register (or remove if inCallback is NULL) a callback that the MusicSequence will call for ANY UserEvents that are added to any of the tracks of the sequence.
What my app does is create a MusicSequence, using MusicSequenceFileLoadData, and associates an AUGraph with the Sequence; then, in AUGraphGetNodeCount I find the Synth AU: if (desc.componentType == kAudioUnitType_MusicDevice) { result = AUGraphNodeInfo(theGraph, node, 0, &theSynth);
then I add a callback to that AU: result = AudioUnitAddRenderNotify(theSynth, synthRenderCallback, self);
This callback is called on the AU thread, and for my purposes only needs to check the value of the current sample against the value of the previous sample. It updates a member variable of my controller this way: if( This->mCurrentTime < inTimeStamp->mSampleTime ) { fDiff = inTimeStamp->mSampleTime - This->mCurrentTime;
(I then adjust the sample difference based on current tempo relative to the sequence tempo.)
(Your code will probably want to be aware that the Callback gets called twice each cycle, PreRender and PostRender.)
The signature of these callbacks shows the input arg inTimeStamp, referred to above. You can find similar code in MixMash I believe.
OSStatus synthRenderCallback( void * inRefCon, AudioUnitRenderActionFlags * ioActionFlags, const AudioTimeStamp * inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData)
You also need to set an NSTimer that gets called on the main thread while the Sequence is playing and which checks some Class member variable(s) to figure out where the sequence currently is. Based on the current time you chase to the current event in your sequence and do whatever is relevant.
HTH, David |