RE: Editing a looping MusicTrack as it plays
RE: Editing a looping MusicTrack as it plays
- Subject: RE: Editing a looping MusicTrack as it plays
- From: Neil Wallace <email@hidden>
- Date: Sun, 22 Jan 2012 14:07:26 +0000
- Thread-topic: Editing a looping MusicTrack as it plays
Many thanks for the suggestions - thats certainly given me some things to try out...
Aran - The approach you are suggesting seems like it would have a very bad worst case for the time between adding a note to the sequence and it being audible in the sequence (ie it could take a full bar for the note to appear) - although I can try experimenting with how often the data gets copied from the loop sequence into the continuous sequence.
David - The example code was just as small a sample as I could make to demonstrate the problem (for instance in my real app I don't clear the whole track instead I just remove the note at the time step I am editing) - unfortunately I don't think the problem is to do with editing a note that is just about to play as in my actual app this hasn't been a source of problems - the problem only happens when you edit a note when the playhead is around the loop point (the note being edited does not need to be anywhere near the loop point). In fact I can also get the problem to occur by muting and un-muting the track at the loop point.
I will have a bit more of a tinker based on both your suggestions and let you know if I make any headway.
________________________________________
From: Aran Mulholland [email@hidden]
Sent: 21 January 2012 22:27
To: David Hicks
Cc: email@hidden; Neil Wallace
Subject: Re: Editing a looping MusicTrack as it plays
Hey Neil
I have been playing with the same stuff and have been thinking about a
way to get things working correctly. One theory that I have toyed with
is to not use the looping feature at all, to simply have a timeline of
events that continues to march on. I would store my loop in another
midi track and allow the user to edit that track, when a note gets
added or removed I would copy it to the main (playing) track. Just
before the end of every bar I would copy the next bar into the
currently playing track. Using a MusicTrackUserEvent I would know when
to copy the information.
Using MusicTrackNewUserEvent you can add events to a midi track that
will fire a callback (from the render thread). You can also add some
custom information to each event to differentiate between events. I
have been doing some other midi stuff and have wanted to have
notifications of certain events happening without continually querying
the timeline. What I have been doing is creating another pthread and
when the MusicTrackUserEvent fires i trigger a semaphore to wake up
the thread. Then I iterate the track that I am adding
MusicTrackUserEvents to and remove any events that are there and deal
with them appropriately. It gives me a way to process events without
having to process them on the render thread (big no no)
You could use this tech to fire notifications every 16th note for UI
refresh for example, which I assume would be a lot more efficient than
polling for the current time with an NSTimer. Or you could stop
editing of any notes around the loop points by putting them in the
queue as a MusicTrackUserEvent to be added to the main loop at the
correct time.
Aran.
On Sun, Jan 22, 2012 at 3:36 AM, David Hicks <email@hidden> wrote:
> Neil,
>
> I haven’t seen any responses to your query yet, so until someone with more
> experience with looping MusicTracks weighs in, here’s what occurs to me. (I
> have some experience with populating MusicTracks on the fly, but not with
> looping.)
>
> It makes some sense to me that there might be a conflict with playing a MIDI
> note event while it is being removed via MusicTrackClear. (I wouldn’t
> necessarily expect the whole track to go silent, but who knows?) If I were
> in your shoes (not much documentation of looping MusicTracks, no input from
> this list) I would keep track of where I was during playback of the
> MusicTrack, and to avoid Clearing any events at or near the current play
> position.
>
> At a minimum this would mean providing a callback function to the sequence
> (I assume you don’t have one, from your code snippet), see documentation
> below from MusicSequence reference. The callback provides you with
> ‘inStartSliceBeat’ and ‘inEndSliceBeat’, which you can use to determine if
> an event you are about to clear is scheduled for immediate playback. If so,
> don’t clear these now. You could clear any other events you want to without
> hazard, I would think, and add back in their new note-versions as well.
>
> I probably wouldn’t do the Clear and Add during the callback itself. I think
> I would instead add member variables, say mStartSliceBeat and mEndSliceBeat,
> to my class, and these would get set from the callback. Then these member
> variables would be referenced in the main thread at the point where you
> normally do your MusicTrackClear (touchesBegan).
>
> HTH,
>
> David
>
>
> MusicSequenceSetUserCallback
>
> Registers a user callback function with a music sequence.
>
> OSStatus MusicSequenceSetUserCallback (
> MusicSequence inSequence,
> MusicSequenceUserCallback inCallback,
> void *inClientData
> );
>
> Parameters
>
> inSequence
>
> The music sequence that you want to add a user callback function to.
>
> inCallback
>
> A reference to your callback function. Use NULL to remove a registered
> callback function.
>
> inClientData
>
> Your data that the music sequence provides back to your callback function
> when it is invoked.
>
> Return Value
>
> A result code.
>
> Discussion
>
> The music sequence invokes your callback for each user event added to any
> music track owned by the sequence. If there is a callback registered, then
> UserEvents will be chased when MusicPlayerSetTime is called. In that case
> the inStartSliceBeat and inEndSliceBeat will both be the same value and will
> be the beat that the player is chasing to.
>
> Usually, where the sequence data is being scheduled for playback, the
> following applies:
>
> inStartSliceBeat <= inEventTime < inEndSliceBeat
> The only exception to this is if the track that owns the MusicEvent is
> looping. In this case the start beat will still be less than the end beat
> (so your callback can still determine that it is playing, and what beats are
> currently being scheduled), however, the inEventTime will be the original
> time-stamped time of the user event.
>
> Further down the MusicSequence reference we find this:
>
> MusicSequenceUserCallback
>
> typedef void (*MusicSequenceUserCallback) (
> void *inClientData,
> MusicSequence inSequence,
> MusicTrack inTrack,
> MusicTimeStamp inEventTime,
> const MusicEventUserData *inEventData,
> MusicTimeStamp inStartSliceBeat,
> MusicTimeStamp inEndSliceBeat
> );
>
> If you named your callback MyMusicSequenceUserCallback, you would declare it
> like this:
>
> void MyMusicSequenceUserCallback (
> void *inClientData,
> MusicSequence inSequence,
> MusicTrack inTrack,
> MusicTimeStamp inEventTime,
> const MusicEventUserData *inEventData,
> MusicTimeStamp inStartSliceBeat,
> MusicTimeStamp inEndSliceBeat
> );
>
>
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Coreaudio-api mailing list (email@hidden)
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
>
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden