Question

I have a MusicPlayer that holds a MusicSequence containing 3 MusicTracks. I have set up an AUGraph with 3 AUSampler Nodes plugged into a multichannel mixer, which in turn is connected to an output node.

I am using a SoundFont, and would like my 3 different MusicTracks to play on 3 different musical instruments, as is described here. However, the code I've got doesn't work - instead, it plays only one of the parts.

I create the AUGraph as follows:

NewAUGraph (&_processingGraph);
AUNode samplerNode, samplerNodeTwo, samplerNodeThree, ioNode, mixerNode;

AudioComponentDescription cd = {};
cd.componentManufacturer     = kAudioUnitManufacturer_Apple;

//----------------------------------------
// Add 3 Sampler unit nodes to the graph
//----------------------------------------
cd.componentType = kAudioUnitType_MusicDevice;
cd.componentSubType = kAudioUnitSubType_Sampler;

AUGraphAddNode (self.processingGraph, &cd, &samplerNode);
AUGraphAddNode (self.processingGraph, &cd, &samplerNodeTwo);
AUGraphAddNode (self.processingGraph, &cd, &samplerNodeThree);

//-----------------------------------
// 2. Add a Mixer unit node to the graph
//-----------------------------------
cd.componentType          = kAudioUnitType_Mixer;
cd.componentSubType       = kAudioUnitSubType_MultiChannelMixer;

AUGraphAddNode (self.processingGraph, &cd, &mixerNode);

//--------------------------------------
// 3. Add the Output unit node to the graph
//--------------------------------------
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_RemoteIO;  // Output to speakers

AUGraphAddNode (self.processingGraph, &cd, &ioNode);

//---------------
// Open the graph
//---------------
AUGraphOpen (self.processingGraph);

//-----------------------------------------------------------
// Obtain the mixer unit instance from its corresponding node
//-----------------------------------------------------------
AUGraphNodeInfo (
                             self.processingGraph,
                             mixerNode,
                             NULL,
                             &mixerUnit
                             );

//--------------------------------
// Set the bus count for the mixer
//--------------------------------
UInt32 numBuses = 3;
AudioUnitSetProperty(mixerUnit,
                              kAudioUnitProperty_ElementCount,
                              kAudioUnitScope_Input,
                              0,
                              &numBuses,
                              sizeof(numBuses));



//------------------
// Connect the nodes
//------------------

AUGraphConnectNodeInput (self.processingGraph, samplerNode, 0, mixerNode, 0);
AUGraphConnectNodeInput (self.processingGraph, samplerNodeTwo, 0, mixerNode, 1);
AUGraphConnectNodeInput (self.processingGraph, samplerNodeThree, 0, mixerNode, 2);

// Connect the mixer unit to the output unit
AUGraphConnectNodeInput (self.processingGraph, mixerNode, 0, ioNode, 0);

// Obtain references to all of the audio units from their nodes
AUGraphNodeInfo (self.processingGraph, samplerNode, 0, &_samplerUnit);
AUGraphNodeInfo (self.processingGraph, samplerNodeTwo, 0, &_samplerUnitTwo);
AUGraphNodeInfo (self.processingGraph, samplerNodeThree, 0, &_samplerUnitThree);
AUGraphNodeInfo (self.processingGraph, ioNode, 0, &_ioUnit);

I then load the 3 instruments from the SoundFont (IDs 0, 1 and 2 in the SoundFont) as follows, passing in the 'bankURL' of the SoundFont:

// Load the first instrument
AUSamplerBankPresetData bpdata;
bpdata.bankURL  = (__bridge CFURLRef) bankURL;
bpdata.bankMSB  = kAUSampler_DefaultMelodicBankMSB;
bpdata.bankLSB  = kAUSampler_DefaultBankLSB;
bpdata.presetID = (UInt8) 0;

AudioUnitSetProperty(self.samplerUnit,
                              kAUSamplerProperty_LoadPresetFromBank,
                              kAudioUnitScope_Global,
                              0,
                              &bpdata,
                              sizeof(bpdata));

// Load the second instrument
AUSamplerBankPresetData bpdataTwo;
bpdataTwo.bankURL  = (__bridge CFURLRef) bankURL;
bpdataTwo.bankMSB  = kAUSampler_DefaultMelodicBankMSB;
bpdataTwo.bankLSB  = kAUSampler_DefaultBankLSB;
bpdataTwo.presetID = (UInt8) 1;

AudioUnitSetProperty(self.samplerUnitTwo,
                              kAUSamplerProperty_LoadPresetFromBank,
                              kAudioUnitScope_Global,
                              0,
                              &bpdataTwo,
                              sizeof(bpdataTwo));

// Load the third instrument
AUSamplerBankPresetData bpdataThree;
bpdataThree.bankURL  = (__bridge CFURLRef) bankURL;
bpdataThree.bankMSB  = kAUSampler_DefaultMelodicBankMSB;
bpdataThree.bankLSB  = kAUSampler_DefaultBankLSB;
bpdataThree.presetID = (UInt8) 2;

AudioUnitSetProperty(self.samplerUnitThree,
                              kAUSamplerProperty_LoadPresetFromBank,
                              kAudioUnitScope_Global,
                              0,
                              &bpdataThree,
                              sizeof(bpdataThree));

Finally, I set the AUSampler nodes to be used by each MusicTrack as follows:

//-------------------------------------------------
// Set the AUSampler nodes to be used by each track
//-------------------------------------------------
MusicTrack track, trackTwo, trackThree;
MusicSequenceGetIndTrack(testSequence, 0, &track);
MusicSequenceGetIndTrack(testSequence, 1, &trackTwo);
MusicSequenceGetIndTrack(testSequence, 2, &trackThree);

AUNode samplerNode, samplerNodeTwo, samplerNodeThree;
AUGraphGetIndNode (self.processingGraph, 0, &samplerNode);
AUGraphGetIndNode (self.processingGraph, 1, &samplerNodeTwo);
AUGraphGetIndNode (self.processingGraph, 2, &samplerNodeThree);

MusicTrackSetDestNode(track, samplerNode);
MusicTrackSetDestNode(trackTwo, samplerNodeTwo);
MusicTrackSetDestNode(trackThree, samplerNodeThree);

However, when I then play the MusicPlayer, I only hear a single part playing. The problem is arising in trying to use different instruments - when I use a single instrument with the standard MusicPlayer setup (instead of editing the AUGraph as I do above), it works fine.

Does anyone have any idea what I'm doing wrong?

Was it helpful?

Solution

I've found the solution. Before loading the instruments from the SoundFont, the following line is needed:

MusicSequenceSetAUGraph(testSequence, self.processingGraph);

As long as the point at which this line is run comes before the instruments are loaded from the SoundFont and before the various MusicTracks are assigned AUSampler nodes, it seems to work - all parts are played on different instruments, as desired. This answer to a related question helped me figure this out.

OTHER TIPS

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];
......
MusicPlayerStart(p);

This works in my project. BTW, thanks for sharing your codes. Really helped :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top