AVAudioUnitのパラメータ詳細（応用編）

本記事は、[AVAudioUnitのパラメータ詳細（基本編）](http://qiita.com/MJeeeey/items/e5dc75b20adb59c7d48f)の続きになります。

AVAudioUnitを用いた場合でもAudio Unitレベルの詳細のパラメータを設定することは可能です。その方法について紹介します。

今回は`AVAudioUnitReverb`を用いた場合を例として説明します。基本編で取り上げたように、AVAudioUnitReverbでは公開されているパラメータは1つだけです。
これでは物足りない！という方のために（笑）、AudioUnitで用意されている全てのパラメータを利用する方法を説明していきます。
（今回取り上げるコードを含むプロジェクトは、最後に紹介するGithubにて取得することができます。）

AVAudioUnitは、AudioUnitをプロパティとして持っています。

~~~objectivec:AVAudioUnit.h
/*! @property audioUnit
    @abstract Reference to the underlying audio unit.
    @discussion
        A reference to the underlying audio unit is provided so that parameters that are not
        exposed by AVAudioUnit subclasses can be modified using the AudioUnit C API.
 
        No operations that may conflict with state maintained by the engine should be performed
        directly on the audio unit. These include changing initialization state, stream formats,
        channel layouts or connections to other audio units.
*/
@property (nonatomic, readonly) AudioUnit audioUnit;
~~~

AudioUnitのプロパティを経由して、AudioUnitSetParameter関数で各パラメータの値を設定する、についてが今回の説明する内容です。

# 準備

まずは変数の宣言です。

~~~objectivec
@interface AudioEngineIO ()
{
	AVAudioEngine *_audioEngine;
	AVAudioFile *_audioFile;
	AVAudioPlayerNode *_audioPlayerNode;
	AVAudioUnitReverb *_audioUnitReverb;
	
	UInt32 _numOfParams;
	AudioUnitParameterID _paramId;
	AudioUnitParameterInfo *_paramInfo;
}
@end
~~~

最初に初期化関数を呼び出します。引数はアプリ内にセットしたオーディオファイルのパスを指定します。

~~~objectivec
- (OSStatus)initAVAudio:(NSString *)strFilePath
{
	OSStatus ret = noErr;
	
	_audioEngine = [[AVAudioEngine alloc] init];
	_audioFile = [[AVAudioFile alloc] initForReading:[NSURL fileURLWithPath:strFilePath] error:nil];
	
	_audioPlayerNode = [[AVAudioPlayerNode alloc] init];
	[_audioEngine attachNode:_audioPlayerNode];
	
	_audioUnitReverb = [[AVAudioUnitReverb alloc] init];
	[_audioEngine attachNode:_audioUnitReverb];
	
	// Nodeの接続
	[_audioEngine connect:_audioPlayerNode to:_audioUnitReverb format:_audioFile.processingFormat];
	[_audioEngine connect:_audioUnitReverb to:_audioEngine.mainMixerNode format:_audioFile.processingFormat];

	// パラメータ取得（詳細編）
	[self initAudioUnitParameter:_audioUnitReverb.audioUnit];

	NSError *error = nil;
	if (![_audioEngine startAndReturnError:&error]) {
		return -1;
	}
	
	return ret;
}
~~~

AudioUnitのプロパティに関する部分を別関数にまとめました。
パラメータをメンバ変数に代入、初期化を行います。

~~~objectivec
- (void)initAudioUnitParameter:(AudioUnit)audioUnit
{
	// AudioUnitGetProperty で取得する paramList のサイズを取得
	UInt32 size = sizeof(UInt32);
	AudioUnitGetPropertyInfo(audioUnit,
							 kAudioUnitProperty_ParameterList,
							 kAudioUnitScope_Global,
							 0,
							 &size,
							 NULL);
	
	UInt32 numOfParams = size / sizeof(AudioUnitParameterID);
	NSLog(@"numOfParams = %d", numOfParams);
	_numOfParams = numOfParams;
	
	// paramList の各IDを取得
	AudioUnitParameterID paramList[numOfParams];
	AudioUnitGetProperty(audioUnit,
						 kAudioUnitProperty_ParameterList,
						 kAudioUnitScope_Global,
						 0,
						 paramList,
						 &size);
	
	_paramInfo = (AudioUnitParameterInfo *)malloc(numOfParams * sizeof(AudioUnitParameterInfo));
	
	for (int i = 0; i < numOfParams; i++) {
		NSLog(@"paramList[%d] = %d", i, (unsigned int)paramList[i]);
		_paramId = paramList[i];
		
		// 各IDのパラメータを取得
		size = sizeof(_paramInfo[i]);
		AudioUnitGetProperty(audioUnit,
							 kAudioUnitProperty_ParameterInfo,
							 kAudioUnitScope_Global,
							 paramList[i],
							 &_paramInfo[i],
							 &size);
		
		NSLog(@"paramInfo.name = %s", _paramInfo[i].name);
		NSLog(@"paramInfo.minValue = %f", _paramInfo[i].minValue);
		NSLog(@"paramInfo.maxValue = %f", _paramInfo[i].maxValue);
		NSLog(@"paramInfo.defaultValue = %f", _paramInfo[i].defaultValue);
		
		// init
		AudioUnitSetParameter(audioUnit,
							  paramList[i],
							  kAudioUnitScope_Global,
							  0,
							  _paramInfo[i].defaultValue,
							  0);
	}
}
~~~

AVAudioUnitReverbの場合、numOfParams = 7 となります。つまり、AudioUnitを経由することで7つのパラメータを調整することができることがわかります。

以上で準備は完了です。

# パラメータの設定

UISliderで以下のsetEffectRate関数を呼び出します。上で取得した numOfParams の数だけUISliderを用意します。

~~~objectivec
/*
 iIndex : AudioUnit Parameter ID
 value : 設定値
*/
- (void)setEffectRate:(NSInteger)iIndex value:(Float32)value
{
	[self setValue:iIndex value:value forParameter:_paramId min:_paramInfo[iIndex].minValue max:_paramInfo[iIndex].maxValue];
}

// private
- (void)setValue:(NSInteger)iIndex value:(Float32)value forParameter:(AudioUnitParameterID)parameter min:(Float32)min max:(Float32)max
{
	if (value < min || value > max) {
		NSLog(@"Invalid value(%f)<%f - %f> for parameter(%d). Ignored.", value, min, max, (unsigned int)parameter);
		return;
	}
	OSStatus rt = AudioUnitSetParameter(_audioUnitReverb.audioUnit,
										parameter,
										kAudioUnitScope_Global,
										0,
										value,
										0);
	if (rt != noErr) {
		NSLog(@"Error Setting parameter(%d)", (unsigned int)parameter);
	}
}
~~~

これで完成したと思います！

## リバーブのパラメータ

以下は、上のinitAudioUnitParameter関数を実行したときに出力されるログです。

~~~
numOfParams = 7
paramList[0] = 0
paramInfo.name = dry/wet mix
paramInfo.minValue = 0.000000
paramInfo.maxValue = 100.000000
paramInfo.defaultValue = 50.000000
paramList[1] = 1
paramInfo.name = gain
paramInfo.minValue = -20.000000
paramInfo.maxValue = 20.000000
paramInfo.defaultValue = 0.000000
paramList[2] = 2
paramInfo.name = min delay time
paramInfo.minValue = 0.000100
paramInfo.maxValue = 1.000000
paramInfo.defaultValue = 0.008000
paramList[3] = 3
paramInfo.name = max delay time
paramInfo.minValue = 0.000100
paramInfo.maxValue = 1.000000
paramInfo.defaultValue = 0.050000
paramList[4] = 4
paramInfo.name = low freq decay time
paramInfo.minValue = 0.001000
paramInfo.maxValue = 20.000000
paramInfo.defaultValue = 1.000000
paramList[5] = 5
paramInfo.name = high freq decay time
paramInfo.minValue = 0.001000
paramInfo.maxValue = 20.000000
paramInfo.defaultValue = 1.000000
paramList[6] = 6
paramInfo.name = randomize reflections
paramInfo.minValue = 1.000000
paramInfo.maxValue = 1000.000000
paramInfo.defaultValue = 1.000000
~~~

「Audio Unitのパラメータ詳細（Effect編）」の[kAudioUnitSubType_Reverb2](http://qiita.com/MJeeeey/items/fc9ebf3547fe40244e2c#kaudiounitsubtype_reverb2)と同じになります。

# Audio Unit Extensions（オーディオ・プラグイン）

iOS 9 から、AudioUnitをアプリ間でプラグインとして共有できるようになりました。`Audio Unit Extensions`や`Audio Units`と呼ばれているようです。
以下が対象のプロパティです。

~~~objectivec:AVAudioUnit.h
/*! @property AUAudioUnit
    @abstract An AUAudioUnit wrapping or underlying the implementation's AudioUnit.
    @discussion
        This provides an AUAudioUnit which either wraps or underlies the implementation's
        AudioUnit, depending on how that audio unit is packaged. Applications can interact with this
        AUAudioUnit to control custom properties, select presets, change parameters, etc.
 
        As with the audioUnit property, no operations that may conflict with state maintained by the
        engine should be performed directly on the audio unit. These include changing initialization
        state, stream formats, channel layouts or connections to other audio units.
*/
@property (nonatomic, readonly) AUAudioUnit *AUAudioUnit NS_AVAILABLE(10_11, 9_0);
~~~

こちらについては、これだけで複数の記事が書けるくらいのボリュームになりますので、、別の機会で詳細に取り上げたいと思っています。

# サンプル

本記事の実装方法については、以下のサンプルプログラムを参照してください。（AudioEngineIO）
https://github.com/JunichiMinamino/AudioObjCSample