mrRay / vvopensource

OSC and MIDI frameworks for OS X and iOS, a framework for managing and rendering to GL textures in OS X, and a functional ISF (interactive shader format) implementation for OS X.
231 stars 33 forks source link

noob - midi clock clarification - CAClockStart(mtcClockRef) + bonus question. #14

Closed johndpope closed 9 years ago

johndpope commented 9 years ago

Hi mrRay thx for your efforts on this project. I've gotten further using this code base than using any other sample code. I'm on tail end of a hobby project I've been working on over holidays and just struggling to get two musicplayers to sync and trying to get my head around the midi clock synchronisation in core audio. Maybe I'm pushing the proverbial up hill. Any way - my question is .....

In the MidiTestApp project - there's some code inside VVMidiNode that handles clocks. There's a few lines of code I've come across which you have commented - but I'm still in the dark about.

// err = CAClockSetProperty(mtcClockRef, kCAClockProperty_SyncSource, sizeof(endpointRef), endpointRef); /* // this should make the clock receive MIDI from the endpoint, but it doesn't work- instead i manually pass MIDI data to the clock (which parses and applies it) if (endpointRef) { err = CAClockSetProperty(mtcClockRef, kCAClockProperty_SyncSource, sizeof(endpointRef), &endpointRef); if (err != noErr) NSLog(@"\t\terr %ld setting sync mode in %s", err, func); }

I can see uncommenting these lines - that the code won't go past this point with BAD_EXEC. I can't see anywhere you are starting the clock.
err = CAClockStart(mtcClockRef); How do you start the clock? Or is it that it's not used? Is all the clock code redundant?

Given the code above should work and doesn't work - for clarity - do you think it would be worthwhile to refactor the ca clock class out into it's own class ? or try to salvage/refactor this class perhaps? https://github.com/fjolnir/sekwenser/blob/master/Source/Sequencer/PSClock.h This psclock class would need work to fit in as the init code / and source / destination is tightly coupled - but some of the other methods could work well.

You say you've gotten around this by 'manually passing MIDI data to the clock' - can you read my elaborate a little? I'm not even sure how that would work.

BONUS question Here's my question - will probably submit to stackoverflow but would appreciate any light you can share on this....

I have a bunch of MusicPlayers from audiotoolbox.

When I play a note - I'm successfully triggering a midi music player to start playing a midisequence (say 8 notes) ...... it's working great for mono(midi) material. 1 note at a time.

If I trigger a subsequent key (while first key press) - I want the note to join in playing it's own midi sequence at exactly the same spot of the 1st key. currentNotes = 2. Here's my actual code that does the magic.

if (currentNotes.count) {
    NSValue *top = [currentNotes objectAtIndex:0]; // the first note playing already.
    MusicPlayer pTop = [top pointerValue];
    MusicTimeStamp outTime;
    MusicPlayerGetTime(pTop,  &outTime);
    MusicPlayerSetTime(p, outTime); //fast forward the 2nd/subsequent note playing to match top
    MusicPlayerStart(p);
}
else {
    // Starts the music playing
    MusicPlayerSetTime(p, 0.0f);
    MusicPlayerStart(p);
}

It sort of works - but there's an ever so slightly out of sync'ness. I was hoping to use the midi clocks to keep the different musicplayers in sync. Or did I miss something. I read on stackoverlfow not to use multiple musicplayers and have separate tracks instead. I would need to be able to enable and silence tracks from note on note off.

johndpope commented 9 years ago

I worked out the line of code VVMIDINode - that you commented out to set the clock to mdi.

if (!running) CAClockStart(mtcClockRef); running = YES;

                //  this should make the clock receive MIDI from the endpoint, but it doesn't work- instead i manually pass MIDI data to the clock (which parses and applies it)
                size = sizeof(endpointRef);
                err = CAClockSetProperty(mtcClockRef, kCAClockProperty_SyncSource, size, &endpointRef); //    N.B. the extra & at end. 

does each VVMIDINode need it's own clock? or would a sharedglobal clock be better?

mrRay commented 9 years ago

“I can see uncommenting these lines - that the code won't go past this point with BAD_EXEC. “

you already figured out why it’s crashing, but that’s not why i commented it out- i commented it out because i just couldn’t figure out how to get clock to respond to the MIDI data i was sending it. i never figured out why, but i know that this wasn’t a crashing issue when it was taken out.

“How do you start the clock? Or is it that it's not used? Is all the clock code redundant?”

i never start the clock at all- it’s slaved to an external source (to the clock data being sent to the VVMIDINode that owns the clock). i just arm the clock and then start feeding it externally-produced data. basically, VVMIDINode owns these clocks because CAClockRef is really good at smoothly interpolating measure position from a stream of received MIDI clock pulses/timecode values.

to be clear, the only thing VVMIDINode wants these clocks to do is keep track of midi clock/MTC it received (the clocks don’t send any data), so my use of the clock is very limited.

“You say you've gotten around this by 'manually passing MIDI data to the clock' - can you read my elaborate a little? I'm not even sure how that would work.”

sure:

“does each VVMIDINode need it's own clock? or would a sharedglobal clock be better?”

i don’t know if it’s strictly necessary, but i gave each node a clock to support different devices sending different clocks to VVMIDI.

hope this helps!