5

Has anyone been able to loop a MIDI file without problems on IOS9 Beta? As soon as I try to loop by setting numberOfLoops to 0 in MusicTrackLoopInfo, it locks up the app by sending random MIDI to the player. I've reported it, but am wondering if anyone has found a work around. The same code works perfectly under all other iOS versions.

MusicTrackLoopInfo loopInfo;
loopInfo.loopDuration = loopLength;
loopInfo.numberOfLoops = 0;
Aron Nelson
  • 838
  • 1
  • 9
  • 17
  • Just received an email from Apple regarding the bug report I opened for this issue - they've indicated that this bug has been fixed in the iOS 9.2 beta. I've downloaded the beta and confirmed both in the simulator and on a device (with 9.2 beta) that this issue has been fixed. I'm not sure when this will be released to the public, but nice to know that a fix is on the way. – Derek Lee Oct 28 '15 at 07:02

3 Answers3

3

OK I just heard iOS9 will ship with this bug in it. Terrible.

Here is a work around.

Don't set numberOfLoops at all, OR set numberOfLoops = 1; // means loop once Now make a variable (i.e. myVariableToKeepTrackOfAddedCopies) that keeps track of the number of times you will actually perform the following:

In your MIDIReadProc at some point BEFORE the track has finished playing, do the following:

// Copy the track to itself - effectively doubling the length
MusicTrack theTrack=nil;
MusicTrackGetProperty(theTrack, kSequenceTrackProperty_TrackLength,         &trackLen, &trackLenLen);
trackLen = 4.0; //<-- this is your real track length
MusicTrackCopyInsert(theTrack, 0, trackLen, theTrack, 0);
myVariableToKeepTrackOfAddedCopies++;

So now your track is twice as long before it ends and the track will continue. This will work the same as looping except you are taking up more memory since you are making the track length longer after each iteration.

When you stop the sequence/track, cut the track back to the original size.

MusicTrackCut(theTrack, 4.0, 4.0 +     (4.0*myVariableToKeepTrackOfAddedCopies));
MusicTrackGetProperty(theTrack, kSequenceTrackProperty_TrackLength,     &trackLen, &trackLenLen);

Irritating, but it works. I just verified on iOS9 beta 5. Hope it helps.

Aron Nelson
  • 838
  • 1
  • 9
  • 17
  • Obviously this is not a good solution because the track keeps getting longer. The answer is to set the music player to the start but I can't figure out how to set the start precisely at the required time. If anyone has any idea how you could set the start time at the precise moment, that would be great! – Aron Nelson Aug 25 '15 at 23:30
  • Hi Aron, I am struggling with the same issue but unfortunately your proposed solution does not fit my technical case. In case it is helpful, I've been following this Apple Forum Thread: https://forums.developer.apple.com/message/26360#26360 As well this Core Audio thread: http://prod.lists.apple.com/archives/coreaudio-api/2015/Aug/msg00030.html – Derek Lee Sep 21 '15 at 04:15
  • From what little I know, Apple does know about the problem and has said confirmed that iOS9 ships with the bug. I have no idea when they will actually fix the problem. I submitted a sample project a long time ago but the fix never made it to the GM. I'm waiting as well. Why doesn't the code work for you? – Aron Nelson Sep 22 '15 at 16:53
  • Hi @aron - So as a stopgap I ended up taking your suggested approach, though because of the way I'm using several MIDI tracks and changing them in real-time it took some finagling to get it to work properly. I ended up using a MIDI user event (and MusicSequenceUserCallback) since I could just configure that to only fire one time (instead of the MIIDIReadProc). I also ended up having to offload this work to a background thread because I would encounter an fatal EXC_BAD_ACCESS for some reason after a certain # of times in the AudioToolbox. Thanks for your help with this! – Derek Lee Oct 28 '15 at 07:07
  • I believe this will be fixed in next iOS version after 9.1. – Aron Nelson Nov 14 '15 at 10:05
  • Hi @aron - My apologies as I meant to follow up earlier. Apple notified me when the iOS 9.2 beta became available about a week or so ago and I was able to test and confirm that this has been fixed in the 9.2 beta. I don't know when Apple will release 9.2 officially, but I was able to confirm that it has been resolved. – Derek Lee Nov 14 '15 at 14:03
  • That's great Derek!!!! I was meaning to try it but I'm right in the middle of something and didn't want to put the beta on my iPad. Thanks for checking! – Aron Nelson Nov 15 '15 at 06:08
  • My pleasure! I also tried to update the Apple Developer Forum post with the information at the time but they had already "archived" it and I couldn't leave any additional information on it. Was really bummed about that because I know there were other devs that were referencing that post for updates there's no way to update it unfortunately. Thanks for the reminder! – Derek Lee Nov 15 '15 at 13:42
  • Derek, I created another thread - do a search for MIDI looping in the developer forum. In any case I just updated it with your info. Thanks! – Aron Nelson Nov 16 '15 at 07:04
1

This is fixed as of iOS release 9.2

CJ Hanson
  • 823
  • 8
  • 17
0

Oddly enough, the tempo track does not seem to have this problem. The following code does not lock up for me:

MusicTrack tempoTrack;
OSSTATUS = MusicSequenceGetTempoTrack(self.sequence, &tempoTrack);
SafeMusicTrackClear(tempoTrack); //calls into MusicTrackClear 

MusicTrackNewExtendedTempoEvent(tempoTrack, 0, self.tempo * self.tempoMultiplier);

MIDIMetaEvent timeSignatureMetaEvent;
timeSignatureMetaEvent.metaEventType = 0x58;
timeSignatureMetaEvent.dataLength = 4;
timeSignatureMetaEvent.data[0] = 1;
timeSignatureMetaEvent.data[1] = 4;
timeSignatureMetaEvent.data[2] = 0x18;
timeSignatureMetaEvent.data[3] = 0x08;

MusicTrackNewMetaEvent(tempoTrack, 0, &timeSignatureMetaEvent);

MusicTrackLoopInfo loopInfo;
loopInfo.loopDuration = 0.25f;
loopInfo.numberOfLoops = 0;

MusicTrackSetProperty(tempoTrack, kSequenceTrackProperty_LoopInfo, &loopInfo, sizeof(loopInfo));

Unfortunately, it does not seem that the tempo track can actually play notes.


UPDATE:

After a few hours of digging around and trying to figure out a better solution to the problem, I settled on manually looping by sending a user event at the end of my sequence.

My sequence is created in a method...

-(void) loadPacketsForLoopingSequence {
    SafeMusicTrackClear(loopingTrack); //calls into MusicTrackClear

    // calculate timestampToPlaySequenceAt -- the starting point of the current sequence iteration, probably in the past, based on MusicPlayerGetTime and the length of the sequence -- here

    // calculate timestampToPlayNextSequenceAt -- the starting point of the next sequence iteration, based on MusicPlayerGetTime and the length of the sequence -- here

    // a single iteration of the notes get added to loopingTrack here, starting at timestampToPlaySequenceAt

    MusicEventUserData event;
    event.length = 1;
    event.data[0] = 0xab; //arbitrary designation

    // -0.5 to make sure we still have time to do the next step in the callback
    MusicTrackNewUserEvent(loopingTrack, timestampToPlayNextSequenceAt - 0.5, &event);
}

...which is called again in the callback:

void sequenceCallback(void* inClientData,
                      MusicSequence inSequence,
                      MusicTrack inTrack,
                      MusicTimeStamp inEventTime,
                      const MusicEventUserData* inEventData,
                      MusicTimeStamp inStartSliceBeat,
                      MusicTimeStamp inEndSliceBeat) {
    CSMidiMusicPlayer* musicPlayer = (CSMidiMusicPlayer*)inClientData;
    [musicPlayer loadPacketsForLoopingSequence];
}

The callback has to be registered during sequence init using MusicSequenceSetUserCallback.

The -0.5 kludge could probably be eliminated altogether by examining the parameters in sequenceCallback and modifying loadPacketsForLoopingSequence to accept a parameter, but I haven't gotten that far yet.

I like this solution because it stays in MIDI time and doesn't modify the MIDI file in unexpected, stateful ways. (New notes basically get streamed in when you get close enough to a loop marker.)

Archagon
  • 2,470
  • 2
  • 25
  • 38