AVMIDIPlayer not working for all instruments

Hi,

I test AVMIDIPlayer in order to replace classes written based on AVAudioEngine with callbacks functions sending MIDI events

to test, I use an NSMutableData filled with:

  • the MIDI header
  • a track for time signature
  • a track containing a few midi events.

I then create an instance of the AVMIDIPlayer using the data

Everything works fine for some instrument (00 … 20) or 90 but not for other instruments 60, 70, …

The MiDI header and the time signature track are based on the MIDI.org sample, https://midi.org/standard-midi-files-specification RP-001_v1-0_Standard_MIDI_Files_Specification_96-1-4.pdf

the midi events are:

 UInt8 trkEvents[] = {
        0x00, 0xC0, instrument,           // Tubular bell
        0x00, 0x90, 0x4C, 0xA0,     // Note 4C
        0x81, 0x40, 0x48, 0xB0,     // TS + Note 48
        0x00, 0xFF, 0x2F, 0x00};    // End
    for (UInt8 i=0; i<3; i++) {
        printf("0x%X ", trkEvents[i]);
    }
    printf("\n");
    [_midiTempData appendBytes:trkEvents length:sizeof(trkEvents)];

A template application is used to change the instrument in a NSTextField

I was wondering if specifics are required for some instruments?

The interface header:

#import <AVFoundation/AVFoundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface TestMIDIPlayer : NSObject
@property (retain) NSMutableData *midiTempData;
@property (retain) NSURL *midiTempURL;
@property (retain) AVMIDIPlayer *midiPlayer;
- (void)createTest:(UInt8)instrument;
@end

NS_ASSUME_NONNULL_END

The implementation:



#pragma mark -
typedef struct _MThd {
    char magic[4];                  // = "MThd"
    UInt8 headerSize[4];            // 4 Bytes, MSB first. Always = 00 00 00 06
    UInt8 format[2];                // 16 bit, MSB first.  0; 1; 2  Use 1
    UInt8 trackCount[2];            // 16 bit, MSB first.
    UInt8 division[2];              // 
}MThd;

MThd MThdMake(void);
void MThdPrint(MThd *mthd) ;

typedef struct _MIDITrackHeader {
    char magic[4];          // = "MTrk"
    UInt8 trackLength[4];   // Ignore, because it is occasionally wrong.
} Track;

Track TrackMake(void);
void TrackPrint(Track *track) ;

#pragma mark - C Functions

MThd MThdMake(void) {
    MThd mthd = {
        "MThd",
        {0, 0, 0, 6},
        {0, 1},
        {0, 0},
        {0, 0}
    };
    MThdPrint(&mthd);
    return mthd;
}

void MThdPrint(MThd *mthd) {
    char *ptr = (char *)mthd;
    for (int i=0;i<sizeof(MThd); i++, ptr++)  {
        printf("%X", *ptr);
    }
    printf("\n");
}

Track TrackMake(void) {
    Track track = {
        "MTrk",
        {0, 0, 0, 0}
    };
    TrackPrint(&track);
    return track;
}

void TrackPrint(Track *track) {
    char *ptr = (char *)track;
    for (int i=0;i<sizeof(Track); i++, ptr++)  {
        printf("%X", *ptr);
    }
    printf("\n");
}


@implementation TestMIDIPlayer

- (id)init {
    self = [super init];
    printf("%s %p\n", __FUNCTION__, self);
    if (self) {
        _midiTempData = nil;
        _midiTempURL = [[NSURL alloc]initFileURLWithPath:@"midiTempUrl.mid"];
        _midiPlayer = nil;
        [self createTest:0x0E];
        NSLog(@"_midiTempData:%@", _midiTempData);
    }
    return self;
}

- (void)dealloc {
    [_midiTempData release];
    [_midiTempURL release];
    [_midiPlayer release];
    [super dealloc];
}

- (void)createTest:(UInt8)instrument {
    
    /*  MIDI Header  */
    [_midiTempData release];
    _midiTempData = nil;
    _midiTempData = [[NSMutableData alloc]initWithCapacity:1024];

    MThd mthd = MThdMake();
    MThd *ptrMthd = &mthd;
    ptrMthd->trackCount[1] = 2;
    ptrMthd->division[1] = 0x60;
    MThdPrint(ptrMthd);
    [_midiTempData appendBytes:ptrMthd length:sizeof(MThd)];
    
    /* Track Header
     Time signature */
    Track track = TrackMake();
    Track *ptrTrack = &track;
    ptrTrack->trackLength[3] = 0x14;
    [_midiTempData appendBytes:ptrTrack length:sizeof(track)];
    
    UInt8 trkEventsTS[]=  {
        0x00, 0xFF, 0x58, 0x04, 0x04, 0x04, 0x18, 0x08, // Time  signature 4/4; 18; 08
        0x00, 0xFF, 0x51, 0x03, 0x07, 0xA1, 0x20,       // tempo 0x7A120 = 500000
        0x83, 0x00, 0xFF, 0x2F, 0x00 };                 // End
    [_midiTempData appendBytes:trkEventsTS length:sizeof(trkEventsTS)];
    
    /* Track Header
     Track events */
    ptrTrack->trackLength[3] = 0x0F;
    [_midiTempData appendBytes:ptrTrack length:sizeof(track)];
    
    UInt8 trkEvents[] = {
        0x00, 0xC0, instrument,           // Tubular bell
        0x00, 0x90, 0x4C, 0xA0,     // Note 4C
        0x81, 0x40, 0x48, 0xB0,     // TS + Note 48
        0x00, 0xFF, 0x2F, 0x00};    // End
    for (UInt8 i=0; i<3; i++) {
        printf("0x%X ", trkEvents[i]);
    }
    printf("\n");
    [_midiTempData appendBytes:trkEvents length:sizeof(trkEvents)];

    [_midiTempData writeToURL:_midiTempURL atomically:YES];
    
    dispatch_async(dispatch_get_main_queue(), ^{
        if (!_midiPlayer.isPlaying)
            [self midiPlay];
    });
}


- (void)midiPlay {
    NSError *error = nil;
    _midiPlayer = [[AVMIDIPlayer alloc]initWithData:_midiTempData soundBankURL:nil error:&error];
    if (_midiPlayer) {
        [_midiPlayer prepareToPlay];
        [_midiPlayer play:^{
            printf("Midi Player ended\n");
            [_midiPlayer stop];
            [_midiPlayer release];
            _midiPlayer = nil;
        }];
        
    }
}
@end

Call from AppDelegate

- (IBAction)actionInstrument:(NSTextField*)sender {
    [_testMidiplayer createTest:(UInt8)sender.intValue];
}
AVMIDIPlayer not working for all instruments
 
 
Q