Good morning, collective wisdom!
I have written an iOS 16-channel General MIDI tone generator using an AUGraph,
and it works great with some DLSs and sound fonts, and sounds terrible with
others. With some DLSs and sound fonts, I can play a sequence and get
essentially the same sound as I hear from other iOS apps (e.g. bs-16i) playing
the same sequence and using the same DLS or sound font; but with DLSs or sound
fonts, I get a much worse sound quality than from the other app, notes that
don't sound, etc.--not even close to acceptable. It's particularly surprising
since the difference in quality exists only for some DLSs and sound fonts--so
what I'm doing works for some, but is not sufficient for others.
Below is the function newAUGraph(), which creates an AUGraph consisting of 16
AUSamplers (one for each channel), each with an output to a mixer, which
combines the 16 channels and routes them to a reverb unit, which then sends the
sounds to the output unit; the AUGraph and the sixteen AUSamplers are stored in
the external (global) variables auGraph and auSampler[16]. Presets are
subsequently loaded into each channel's AUSampler, and notes and other MIDI
messages are sent via MusicDeviceMIDIEvent().
If anybody has time to slog through this and give me a hint as to what I'm
missing, I'll be forever grateful. In any case, thanks for your attention!
All best,
Frank
****************
AUGraph auGraph;
AudioUnit auSampler[16];
#define mustHaveNoError(functionCall) functionCall; if (result != noErr) {NSLog
(@"error %d", (int)result); return result;}
static OSStatus newAUGraph (void)
{
AudioUnit mixerAudioUnit, reverbAudioUnit, outputAudioUnit;
AUNode samplerNode[16], mixerNode, reverbNode, outputNode;
AVAudioSession* avAudioSession = [AVAudioSession sharedInstance];
[avAudioSession setCategory: AVAudioSessionCategoryPlayback error: nil];
[avAudioSession setPreferredHardwareSampleRate:44100.0 error:nil];
[avAudioSession setActive:YES error:nil];
Float64 sampleRate = avAudioSession.currentHardwareSampleRate;
AudioComponentDescription description;
description.componentManufacturer = kAudioUnitManufacturer_Apple;
description.componentFlags = description.componentFlagsMask = 0;
OSStatus result = mustHaveNoError(NewAUGraph(&auGraph));
description.componentType = kAudioUnitType_Output;
description.componentSubType = kAudioUnitSubType_RemoteIO;
result = mustHaveNoError(AUGraphAddNode (auGraph, &description,
&outputNode));
description.componentType = kAudioUnitType_Effect;
description.componentSubType = kAudioUnitSubType_Reverb2;
result = mustHaveNoError(AUGraphAddNode (auGraph, &description,
&reverbNode));
description.componentType = kAudioUnitType_Mixer;
description.componentSubType = kAudioUnitSubType_MultiChannelMixer;
result = mustHaveNoError(AUGraphAddNode (auGraph, &description,
&mixerNode));
result = mustHaveNoError(AUGraphConnectNodeInput(auGraph, reverbNode,
0, outputNode, 0));
result = mustHaveNoError(AUGraphConnectNodeInput(auGraph, mixerNode, 0,
reverbNode, 0));
const UInt32 numberOfChannels = 16;
for (unsigned int i = 0; i < numberOfChannels; i += 1)
{
description.componentType = kAudioUnitType_MusicDevice;
description.componentSubType = kAudioUnitSubType_Sampler;
result = mustHaveNoError(AUGraphAddNode (auGraph, &description,
&samplerNode[i]));
result = mustHaveNoError(AUGraphConnectNodeInput(auGraph,
samplerNode[i], 0, mixerNode, i));
}
result = mustHaveNoError(AUGraphOpen (auGraph));
result = mustHaveNoError(AUGraphNodeInfo(auGraph, mixerNode, NULL,
&mixerAudioUnit));
result = mustHaveNoError(AUGraphNodeInfo(auGraph, reverbNode, NULL,
&reverbAudioUnit));
result = mustHaveNoError(AUGraphNodeInfo(auGraph, outputNode, NULL,
&outputAudioUnit));
UInt32 framesPerSlice;
UInt32 framesPerSlicePropertySize = sizeof(framesPerSlice);
result = mustHaveNoError(AudioUnitGetProperty(outputAudioUnit,
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0,
&framesPerSlice, &framesPerSlicePropertySize));
assert(framesPerSlicePropertySize == sizeof(framesPerSlice));
result = mustHaveNoError(AudioUnitInitialize(outputAudioUnit));
result = mustHaveNoError(AudioUnitSetProperty(outputAudioUnit,
kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate,
sizeof(sampleRate)));
result = mustHaveNoError(AudioUnitSetProperty(outputAudioUnit,
kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRate,
sizeof(sampleRate)));
result = mustHaveNoError(AudioUnitSetProperty(reverbAudioUnit,
kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate,
sizeof(sampleRate)));
result = mustHaveNoError(AudioUnitSetProperty(reverbAudioUnit,
kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRate,
sizeof(sampleRate)));
result = mustHaveNoError(AudioUnitSetProperty(reverbAudioUnit,
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0,
&framesPerSlice, sizeof(framesPerSlice)));
result = mustHaveNoError(AudioUnitSetProperty(mixerAudioUnit,
kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate,
sizeof(sampleRate)));
result = mustHaveNoError(AudioUnitSetProperty(mixerAudioUnit,
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0,
&framesPerSlice, sizeof(framesPerSlice)));
result = mustHaveNoError(AudioUnitSetProperty(mixerAudioUnit,
kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &numberOfChannels,
sizeof(numberOfChannels)));
result = mustHaveNoError(AudioUnitSetParameter(reverbAudioUnit,
kReverb2Param_DryWetMix, kAudioUnitScope_Global, 0, 20, 0));
result = mustHaveNoError(AudioUnitSetParameter(reverbAudioUnit,
kReverb2Param_DecayTimeAt0Hz, kAudioUnitScope_Global, 0, 3, 0));
result = mustHaveNoError(AudioUnitSetParameter(reverbAudioUnit,
kReverb2Param_DecayTimeAtNyquist, kAudioUnitScope_Global, 0, 1, 0));
for (unsigned int i = 0; i < numberOfChannels; i += 1)
{
result = mustHaveNoError(AUGraphNodeInfo(auGraph,
samplerNode[i], NULL, &auSampler[i]));
result = mustHaveNoError(AudioUnitSetProperty(auSampler[i],
kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate,
sizeof(sampleRate)));
result = mustHaveNoError(AudioUnitSetProperty(auSampler[i],
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0,
&framesPerSlice, sizeof(framesPerSlice)));
result = mustHaveNoError(AudioUnitSetProperty(mixerAudioUnit,
kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, i, &sampleRate,
sizeof(sampleRate)));
result = mustHaveNoError(AudioUnitSetProperty(mixerAudioUnit,
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, i,
&framesPerSlice, sizeof(framesPerSlice)));
result = mustHaveNoError(loadOnBoardSynthPatch(0, i));
result = mustHaveNoError(AudioUnitSetParameter(mixerAudioUnit,
kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, i, 1, 0));
}
result = mustHaveNoError(AUGraphInitialize(auGraph));
result = mustHaveNoError(AUGraphStart(auGraph));
CAShow(auGraph);
return result;
}
****************
Frank Weinstock
Professor Emeritus of Piano
College-Conservatory of Music
University of Cincinnati
[email protected]
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list ([email protected])
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/coreaudio-api/archive%40mail-archive.com
This email sent to [email protected]