ios – mixing midi files, each with own sound font

I’m looking for a way to mix 2 or more midi files, each with their own sound font files. I’ve found the following code for one file and tried to do multiple music players but i guess that shouldn’t be the right approach. Also i get some weird pop sound every second.

So is there any other way, maybe without the musicplayer and musicsequence methods, using only au units?

  • AVPlayer not synchronized
  • Audio Unit: Use sound output as input source
  • How to record an audio file in .mp3 format?
  • AVAudioPlayer initialization error
  • Swift 3 LPCM Audio Recorder | Error: kAudioFileInvalidPacketOffsetError
  • How to save mp3 file in NSMusicDirectory in IOS?
  • Here’s the code i found in another thread:

    -(void) playMusic:(NSString*) name
    NSString *presetURLPath = [[NSBundle mainBundle] pathForResource:@"GortsMiniPianoJ1" ofType:@"SF2"];
    NSURL * presetURL = [NSURL fileURLWithPath:presetURLPath]; 
    [self loadFromDLSOrSoundFont: (NSURL *)presetURL withPatch: (int)3];
    NSString *midiFilePath = [[NSBundle mainBundle] pathForResource:name ofType:@"mid"];
    NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
    if (NewMusicSequence(&musicSequence) != noErr) 
        [NSException raise:@"play" format:@"Can't create MusicSequence"];  
    if(MusicSequenceFileLoad(musicSequence, (CFURLRef)midiFileURL, 0, 0 != noErr)) 
        [NSException raise:@"play" format:@"Can't load MusicSequence"];
    MusicPlayerSetSequence(musicPlayer, musicSequence);
    MusicSequenceSetAUGraph(musicSequence, _processingGraph);

    3 Solutions Collect From Internet About “ios – mixing midi files, each with own sound font”

    Multiple MusicPlayer instances sounds a bit awkward (to keep in sync) and I have doubts that multiple AUGraphs would work. I haven’t tried it myself.

    One approach using a sole instance of MusicPlayer would be to load the tracks from each midi file into a ‘master’ sequence. You can assign an AUSampler node (with unique soundFont) to each track (MusicTrackSetDestNode) and connect them all through an AUMixer. You would need to manage the tempos from each midi file (using MusicTrackNewExtendedTempoEvent) to assign file tempos to the relevant tracks. Muting tracks + track volume is easily handled at the track or mixer level.

    I guess much depends on the nature of the midi files you are working with. Fwiw – I did lots of research on midi file playback on iOS and there is nothing out there that is as easy to use as MusicPlayer. However, the Bass lib might be worth looking at if MusicPlayer turns out not to work for you.

    Specifying the number of mixer inputs:

    // set the bus count
    UInt32 numBuses = BUS_COUNT; // a constant defined elsewhere
    result = AudioUnitSetProperty(mixerUnit, 
    if (noErr != result) {[self printErrorMessage: @"Error setting Bus Count" withStatus: result]; return;}

    I had exactly same issue as you. All tracks play with the first sound font instrument. I followed your solution but it not work at first. Finally, I resolve the problem. As your mentioned, the sequence of calling functions really maters. Yes, it is. Actually, the sequence calling should be like this:

    MusicSequenceSetAUGraph(s, _processingGraph);
    MusicTrackSetDestNode(track[i], samplerNodes[i]);
    [self loadFromDLSOrSoundFont];

    This works in my project.

    So i tried setting the nodes for each track, but that didn’t change anything. The soundfont gets only set for the first samplerUnit.
    Here’s how i set up the graph:

    AudioComponentDescription MixerUnitDescription;
    MixerUnitDescription.componentType          = kAudioUnitType_Mixer;
    MixerUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;
    MixerUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
    MixerUnitDescription.componentFlags         = 0;
    MixerUnitDescription.componentFlagsMask     = 0;
    AudioComponentDescription cd = {};
    cd.componentManufacturer     = kAudioUnitManufacturer_Apple;
    cd.componentType = kAudioUnitType_MusicDevice; // type - music device
    cd.componentSubType = kAudioUnitSubType_Sampler; // sub type - sampler to convert our MIDI
    result = NewAUGraph (&_processingGraph);
    result = AUGraphAddNode(self.processingGraph, &MixerUnitDescription, &mixerNode);
    result = AUGraphAddNode (self.processingGraph, &cd, &samplerNode);
    result = AUGraphAddNode (self.processingGraph, &cd, &samplerNode2);
    cd.componentType = kAudioUnitType_Output;  // Output
    cd.componentSubType = kAudioUnitSubType_RemoteIO;  // Output to speakers
    result = AUGraphAddNode (self.processingGraph, &cd, &ioNode);
    result = AUGraphOpen (self.processingGraph);
    result = AUGraphConnectNodeInput (self.processingGraph, samplerNode, 0, mixerNode, 0);
    result = AUGraphConnectNodeInput (self.processingGraph, samplerNode2, 0, mixerNode, 1);
    result = AUGraphConnectNodeInput (self.processingGraph, mixerNode, 0, ioNode, 0);
    result = AUGraphNodeInfo (self.processingGraph, samplerNode, 0, &_samplerUnit);
    result = AUGraphNodeInfo (self.processingGraph, samplerNode2, 0, &_samplerUnit2);
    result = AUGraphNodeInfo (self.processingGraph, ioNode, 0, &_ioUnit);

    This is the example method from Apple Developer pages which i modified to assign a soundfont to a specific samplerUnit:

    -(OSStatus) loadFromDLSOrSoundFont: (NSURL *)bankURL withPatch: (int)presetNumber withAudioUnit:(AudioUnit)auUnit{
    OSStatus result = noErr;
    // fill out a bank preset data structure
    AUSamplerBankPresetData bpdata;
    bpdata.bankURL  = (__bridge CFURLRef) bankURL;
    bpdata.bankMSB  = kAUSampler_DefaultMelodicBankMSB;
    bpdata.bankLSB  = kAUSampler_DefaultBankLSB;
    bpdata.presetID = (UInt8) presetNumber;
    // set the kAUSamplerProperty_LoadPresetFromBank property
    result = AudioUnitSetProperty(auUnit,
    // check for errors
    NSCAssert (result == noErr,
           @"Unable to set the preset property on the Sampler. Error code:%d '%.4s'",
           (int) result,
           (const char *)&result);
    return result;

    Then i did this twice to get each track into musicSequence:

    if(MusicSequenceFileLoad(tmpSequence, (__bridge CFURLRef)midiFileURL, 0, 0 != noErr)) 
        [NSException raise:@"play" format:@"Can't load MusicSequence"];
    MusicSequenceGetIndTrack(tmpSequence, 0, &tmpTrack);
    MusicSequenceNewTrack(musicSequence, &track);
    MusicTimeStamp trackLen = 0;
    UInt32 trackLenLen = sizeof(trackLen);
    MusicTrackGetProperty(tmpTrack, kSequenceTrackProperty_TrackLength, &trackLen, &trackLenLen);
    MusicTrackCopyInsert(tmpTrack, 0, trackLenLen, track, 0);

    And finally:

    MusicTrackSetDestNode(track, samplerNode);
    MusicTrackSetDestNode(track2, samplerNode2);

    But this won’t assign the the soundfont to the samplerUnit2:

    [self loadFromDLSOrSoundFont: (NSURL *)presetURL2 withPatch: (int)0 withAudioUnit:self.samplerUnit2];

    Assigning to samplerUnit works fine. Any ideas what i’m missing here?