Question

I'm playing a MIDI sequence via MusicPlayer which I loaded from a MIDI file and I want to change the sequence to another while playback. When I try this:

 MusicPlayerSetSequence(_player, sequence);
 MusicSequenceSetAUGraph(sequence, _processingGraph);

it stops the playback. So I start it back again and set the time with

MusicPlayerSetTime(_player, currentTime);

so it plays again where the previous sequence stopped, but there is a little delay. I've tried to add the time interval to currentTime, which I got by obtaining the time before stopping and after starting again. But there is still a delay.

I was wondering if there is an alternative to stopping -> changing sequence -> starting again.

Was it helpful?

Solution 2

Because

MusicPlayerSetSequence(_player, sequence);
MusicSequenceSetAUGraph(sequence, _processingGraph);

will still cause the player to stop, it is still possible to hear a little break. So instead of updating the musicSequence, i went ahead and changed the content of the tracks instead, which won't cause any breaks:

MusicTrack currentTrack;
MusicTrack currentTrack2;
MusicSequenceGetIndTrack(musicSequence, 0, &currentTrack);
MusicSequenceGetIndTrack(musicSequence, 1, &currentTrack2);
MusicTrackClear(currentTrack, 0, _trackLen);
MusicTrackClear(currentTrack2, 0, _trackLen);

MusicSequence tmpSequence;
switch (number) {
    case 0:
        tmpSequence = musicSequence1;
        break;
    case 1:
        tmpSequence = musicSequence2;
        break;
    case 2:
        tmpSequence = musicSequence3;
        break;
    case 3:
        tmpSequence = musicSequence4;
        break;

    default:
        tmpSequence = musicSequence1;
        break;
}

MusicTrack tmpTrack;
MusicTrack tmpTrack2;
MusicSequenceGetIndTrack(tmpSequence, 0, &tmpTrack);
MusicSequenceGetIndTrack(tmpSequence, 1, &tmpTrack2);
MusicTimeStamp trackLen = 0;
UInt32 trackLenLenLen = sizeof(trackLen);
MusicTrackGetProperty(tmpTrack, kSequenceTrackProperty_TrackLength, &trackLen, &trackLenLenLen);
_trackLen = trackLen;
MusicTrackCopyInsert(tmpTrack, 0, _trackLen, currentTrack, 0);
MusicTrackCopyInsert(tmpTrack2, 0, _trackLen, currentTrack2, 0);

No disconnection of nodes, no updating the graph, no stopping the player.

OTHER TIPS

You definitely need to manage the AUSamplers if you are adding and removing tracks or switching sequences. It probably is cleaner to dispose of the AUSampler and create a new one for each new track but it is also possible to 'recycle' AUSamplers but that means you will need to keep track of them.

Managing AUSamplers means that when you are no longer using an instance of one (for example if you delete or replace a MusicTrack), you need to disconnect it from the AUMixer instance, remove it from the AUGraph instance, and then update the AUGraph.

There are lots of ways to handle all this. For convenience in keeping track of AUSampler instances' bus number, sound font loaded and some other stuff, I use a subClass of NSObject named SamplerAudioUnitto contain all the needed properties and methods. Same for MusicTracks - I have a Track class - but this may not be needed in your project.

The gist though is that AUSamplers need to be managed for performance and memory. If an instance is no longer being used it should be removed and the AUMixer bus input freed up.

BTW - I check the docs and there is apparently no technical limit to the number of mixer busses - but the number does need to be specified.

// this is not cut and paste code - just an example of managing the AUSampler instance

- (OSStatus)deleteTrack:(Track*) trackObj
{
    OSStatus result = noErr;

    // turn off MP if playing
    BOOL MPstate = [self isPlaying];
    if (MPstate){
        MusicPlayerStop(player);
    }

    //-disconnect node from mixer + update list of mixer buses
    SamplerAudioUnit * samplerObj = trackObj.sampler;
    UInt32 busNumber = samplerObj.busNumber;

    result = AUGraphDisconnectNodeInput(graph, mixerNode, busNumber);
    if (result) {[self printErrorMessage: @"AUGraphDisconnectNodeInput" withStatus: result];}

     [self clearMixerBusState: busNumber]; // routine that keeps track of available busses

    result = MusicSequenceDisposeTrack(sequence, trackObj.track);
    if (result) {[self printErrorMessage: @"MusicSequenceDisposeTrack" withStatus: result];}

    // remove AUSampler node
    result = AUGraphRemoveNode(graph, samplerObj.samplerNode);
    if (result) {[self printErrorMessage: @"AUGraphRemoveNode" withStatus: result];}

    result = AUGraphUpdate(graph, NULL);
    if (result) {[self printErrorMessage: @"AUGraphUpdate" withStatus: result];}
    samplerObj = nil;
    trackObj = nil;

    if (MPstate){
        MusicPlayerStart(player);
    }

    //    CAShow(graph);
    //    CAShow(sequence);

    return result;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top