//******************************************************************************
//
// Simple MIDI Library / SMWavetableSynthCtrl
//
// Wavetableシンセサイザ制御クラス
//
// Copyright (C) 2018-2021 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

//MEMO:
//
//  Sampler 1  ---+
//  Sampler 2  ---+
//     :          +---> Mixer --> RemoteIO
//  Sampler 15 ---+
//  Sampler 16 ---+
//
//  16チャンネル分のサンプラーを作成し、サンプラー単位にWavetable（DLS/SF2ファイル）をロードする。
//  プログラムチェンジ発生時はその時点でWavetableをロードするため、演奏に影響が出る可能性がある。
//  Appleが提供するサンプラーをそのまま利用するため、お手軽に実装できているが、コントロールチェンジの
//  対応状況や最大同時発音数など、仕様や性能が不明である。
//  DLS/SF2ファイルによっては、正常に発音されないことがある。

#import "YNBaseLib.h"
#import "SMWavetableSynthCtrl.h"
#import "SMEvent.h"
#import "SMEventMIDI.h"
#import "SMCommon.h"
#import <AVFoundation/AVFoundation.h>


//******************************************************************************
// コンストラクタ
//******************************************************************************
SMWavetableSynthCtrl::SMWavetableSynthCtrl()
{
	m_pWavetableFilePath = nil;
	m_pAudioSession = nil;
	m_AUGraph = NULL;
	m_isOpened = false;
}

//******************************************************************************
// デストラクタ
//******************************************************************************
SMWavetableSynthCtrl::~SMWavetableSynthCtrl()
{
	[m_pWavetableFilePath release];
	m_pWavetableFilePath = nil;
	_ReleaseAUGraph();
}

//******************************************************************************
// グラフリリース
//******************************************************************************
void SMWavetableSynthCtrl::_ReleaseAUGraph()
{
	Boolean isRunning = false;
	Boolean isInitialized = false;
	Boolean isOpened = false;
	int ch = 0;
	
	if (m_AUGraph != NULL) {
		AUGraphIsRunning(m_AUGraph, &isRunning);
		if (isRunning) {
			AUGraphStop(m_AUGraph);
		}
		AUGraphIsInitialized(m_AUGraph, &isInitialized);
		if (isInitialized) {
			AUGraphUninitialize(m_AUGraph);
		}
		AUGraphIsOpen(m_AUGraph, &isOpened);
		if (isOpened) {
			AUGraphClose(m_AUGraph);
		}
		DisposeAUGraph(m_AUGraph);
		m_AUGraph = NULL;
	}
	
	m_isOpened = false;
	
	for (ch = 0; ch < SM_MAX_CH_NUM; ch++) {
		m_isWavetableLoaded[ch] = false;
	}
	
	return;
}

//******************************************************************************
// 初期化
//******************************************************************************
int SMWavetableSynthCtrl::Initialize()
{
	int result = 0;
	BOOL apiresult = NO;
	NSError* pError = nil;
	
	//オーディオセッション初期化
	m_pAudioSession = [AVAudioSession sharedInstance];
	apiresult = [m_pAudioSession setCategory:AVAudioSessionCategoryPlayback
									   error:&pError];
	if (!apiresult) {
		result = YN_SET_ERR(@"Audio session error.", pError.code, 0);
		goto EXIT;
	}
	
	//オーディオ処理グラフ生成
	result = _CreateAUGraph();
	if (result != 0) goto EXIT;
	
	//コントロール番号サポート情報初期化
	_InitControlNoSupport();
	
EXIT:;
	return result;
}

//******************************************************************************
// デバイス表示名称取得
//******************************************************************************
NSString* SMWavetableSynthCtrl::GetDevDisplayName()
{
	return SM_WAVETABLE_SYNTH_DISPLAY_NAME;
}

//******************************************************************************
// デバイス識別名称取得
//******************************************************************************
NSString* SMWavetableSynthCtrl::GetDevIdName()
{
	return SM_WAVETABLE_SYNTH_DEVID_NAME;
}

//******************************************************************************
// メーカー名取得
//******************************************************************************
NSString* SMWavetableSynthCtrl::GetManufacturerName()
{
	return SM_WAVETABLE_SYNTH_MANUFACTURER_NAME;
}

//******************************************************************************
// Wavetableファイルパス設定
//******************************************************************************
int SMWavetableSynthCtrl::SetWavetableFilePath(NSString* pWavetableFilePath)
{
	int result = 0;
	
	[pWavetableFilePath retain];
	[m_pWavetableFilePath release];
	m_pWavetableFilePath = pWavetableFilePath;
	
	return result;
}

//******************************************************************************
// デバイスオープン
//******************************************************************************
int SMWavetableSynthCtrl::Open()
{
	int result = 0;
	BOOL apiresult = NO;
	OSStatus status = 0;
	NSError* pError = nil;
	
	//オーディオセッション アクティブ化
	apiresult = [m_pAudioSession setActive:YES error:&pError];
	if (!apiresult) {
		result = YN_SET_ERR(@"Audio session error.", pError.code, 0);
		goto EXIT;
	}
	
	//サンプラーユニットセットアップ
	result = _SetupSamplerUnits();
	if (result != 0) goto EXIT;
	
	//グラフ開始
	status = AUGraphStart(m_AUGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	m_isOpened = true;
	
EXIT:;
	return result;
}

//******************************************************************************
// デバイスクローズ
//******************************************************************************
int SMWavetableSynthCtrl::Close()
{
	int result = 0;
	BOOL apiresult = NO;
	OSStatus status = 0;
	NSError* pError = nil;
	
	if (!m_isOpened) goto EXIT;
	
	//演奏停止
	status = AUGraphStop(m_AUGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	//オーディオセッション 非アクティブ化
	apiresult = [m_pAudioSession setActive:NO error:&pError];
	if (!apiresult) {
		result = YN_SET_ERR(@"Audio session error.", pError.code, 0);
		goto EXIT;
	}
	
	m_isOpened = false;
	
EXIT:;
	return result;
}

//******************************************************************************
// オーディオセッションアクティブ設定
//******************************************************************************
int SMWavetableSynthCtrl::SetAudioSessionActive(BOOL isActive)
{
	int result = 0;
	BOOL apiresult = NO;
	OSStatus status = 0;
	NSError* pError = nil;
	
	//アクティブ化
	if (isActive) {
		//すでにアクティブ化されている場合は何もしない
		if (m_isOpened) goto EXIT;
		
		//オーディオセッション アクティブ化
		apiresult = [m_pAudioSession setActive:YES error:&pError];
		if (!apiresult) {
			result = YN_SET_ERR(@"Audio session error.", pError.code, 0);
			goto EXIT;
		}
		
		//演奏開始
		status = AUGraphStart(m_AUGraph);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
		
		m_isOpened = true;
	}
	//非アクティブ化
	else {
		//すでに非アクティブ化されている場合は何もしない
		if (!m_isOpened) goto EXIT;
		
		//演奏停止
		status = AUGraphStop(m_AUGraph);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
		
		//オーディオセッション 非アクティブ
		apiresult = [m_pAudioSession setActive:NO error:&pError];
		if (!apiresult) {
			result = YN_SET_ERR(@"Audio session error.", pError.code, 0);
			goto EXIT;
		}
		
		m_isOpened = false;
	}

EXIT:;
	return result;
}

//******************************************************************************
// MIDIメッセージ送信
//******************************************************************************
int SMWavetableSynthCtrl::SendShortMsg(
		unsigned char* pMsg,
		unsigned int size
	)
{
	int result = 0;
	OSStatus status = 0;
	unsigned char data[3] = {0, 0, 0};
	unsigned char controlNo = 0;
	unsigned char ch = 0;
	unsigned char bankSelectMSB = 0;
	unsigned char bankSelectLSB = 0;
	SMEvent event;
	SMEventMIDI eventMIDI;
	
	if (size > 3) {
		result = YN_SET_ERR(@"Program error.", size, 0);
		goto EXIT;
	}
	
	if (size >= 1) {
		data[0] = pMsg[0];
	}
	if (size >= 2) {
		data[1] = pMsg[1];
	}
	if (size >= 3) {
		data[2] = pMsg[2];
	}
	
	//コントロールチェンジを送信する場合は送信可否を判定する
	if ((data[0] & 0xF0) == 0xB0) {
		//コントロール番号
		controlNo = data[1];
		if (controlNo >= 128) {
			//コントロール番号異常なので送信しない
			goto EXIT;
		}
		else if (m_ControlNoSupport[controlNo] == 0) {
			//サポートしていないコントロール番号なので送信しない
			goto EXIT;
		}
	}
	
	//MIDIイベント解析
	event.SetMIDIData(data[0], &(data[1]) ,size - 1);
	eventMIDI.Attach(&event);
	ch = eventMIDI.GetChNo();
	
	//バンクセレクトを記憶
	if (eventMIDI.GetChMsg() == SMEventMIDI::ControlChange) {
		if (eventMIDI.GetCCNo() == 0) {
			m_BankSelectMSB[ch] = eventMIDI.GetCCValue();
		}
		else if (eventMIDI.GetCCNo() == 32) {
			m_BankSelectLSB[ch] = eventMIDI.GetCCValue();
		}
	}
	
	//プログラムチェンジ
	if (eventMIDI.GetChMsg() == SMEventMIDI::ProgramChange) {
		//MIDIデータ指定のバンクセレクト(MSB/LSB)でWavetable読み込みを試みる
		result = _LoadInstrument(
						ch,
						m_UnitSampler[ch],
						m_BankSelectMSB[ch],
						m_BankSelectLSB[ch],
						eventMIDI.GetProgramNo()
					);
		if (result != 0) {
			//Wavetable読み込み失敗時はデフォルト値でリトライする
			if (ch != SM_WAVETABLE_SYNTH_PERCUSSION_CH) {
				//チャンネル#10以外
				bankSelectMSB = kAUSampler_DefaultMelodicBankMSB; //121
				bankSelectLSB = kAUSampler_DefaultBankLSB; //0
			}
			else {
				//チャンネル#10：ドラム／パーカッション用
				bankSelectMSB = kAUSampler_DefaultPercussionBankMSB; //120
				bankSelectLSB = kAUSampler_DefaultBankLSB; //0
			}
			result = _LoadInstrument(
							ch,
							m_UnitSampler[ch],
							bankSelectMSB,
							bankSelectLSB,
							eventMIDI.GetProgramNo()
						);
			if (result != 0) {
				//ロード失敗はエラーとせず処理を続行する
				result = 0;
			}
		}
	}
	
	//MIDIイベント送信
	status = MusicDeviceMIDIEvent(
					m_UnitSampler[ch],	//ユニット
					data[0],			//ステータス
					data[1],			//データ1
					data[2],			//データ2
					0					//サンプルオフセット
				);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// システムエクスクルーシブ送信
//******************************************************************************
int SMWavetableSynthCtrl::SendLongMsg(
		unsigned char* pMsg,
		unsigned int size
	)
{
	int result = 0;
	OSStatus status = 0;
	int ch = 0;
	
	for (ch = 0; ch < SM_MAX_CH_NUM; ch++) {
		//システムエクスクルーシブ送信
		status = MusicDeviceSysEx(
						m_UnitSampler[0],	//出力ユニット
						pMsg,				//データ位置
						(UInt32)size		//データサイズ
					);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// 全ノートオフ
//******************************************************************************
int SMWavetableSynthCtrl::NoteOffAll()
{
	int result = 0;
	unsigned char i = 0;
	unsigned char msg[3];
	
	//全トラックノートオフ
	for (i = 0; i < SM_MAX_CH_NUM; i++) {
		msg[0] = 0xB0 | i;
		msg[1] = 0x7B;
		msg[2] = 0x00;
		result = SendShortMsg(msg, 3);
		if (result != 0) goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// 全サウンドオフ
//******************************************************************************
int SMWavetableSynthCtrl::SoundOffAll()
{
	int result = 0;
	unsigned char i = 0;
	unsigned char msg[3];
	
	//全トラックサウンドオフ
	for (i = 0; i < SM_MAX_CH_NUM; i++) {
		msg[0] = 0xB0 | i;
		msg[1] = 0x78;
		msg[2] = 0x00;
		result = SendShortMsg(msg, 3);
		if (result != 0) goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// オーディオ処理グラフ生成
//******************************************************************************
int SMWavetableSynthCtrl::_CreateAUGraph()
{
	int result = 0;
	OSStatus status = 0;
	
	_ReleaseAUGraph();
	
	//オーディオ処理グラフ生成
	status = NewAUGraph(&m_AUGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	//ノード生成
	result = _CreateNodes();
	if (result != 0) goto EXIT;
	
	//グラフを開く
	status = AUGraphOpen(m_AUGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	//オーディオユニット取得
	result = _GetAudioUnits();
	if (result != 0) goto EXIT;
	
	//サンプラーユニット初期化
	result = _InitSamplerUnits();
	if (result != 0) goto EXIT;
	
	//マルチチャンネルミキサーユニット初期化
	result = _InitMixerUnit();
	if (result != 0) goto EXIT;
	
	// ノード接続
	result = _ConnectNodes();
	if (result != 0) goto EXIT;
	
	//グラフ初期化
	status = AUGraphInitialize(m_AUGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}

EXIT:;
	return result;
}

//******************************************************************************
// ノード生成
//******************************************************************************
int SMWavetableSynthCtrl::_CreateNodes()
{
	int result = 0;
	OSStatus status = 0;
	int i = 0;
	AudioComponentDescription cd;
	
	//ノード生成：サンプラー
	for (i = 0; i < SM_MAX_CH_NUM; i++) {
		cd.componentType         = kAudioUnitType_MusicDevice;
		cd.componentSubType      = kAudioUnitSubType_Sampler;
		cd.componentManufacturer = kAudioUnitManufacturer_Apple;
		cd.componentFlags        = 0;
		cd.componentFlagsMask    = 0;
		status = AUGraphAddNode(m_AUGraph, &cd, &m_NodeSampler[i]);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
	}
	
	//ノード生成：マルチチャンネルミキサー
	cd.componentType         = kAudioUnitType_Mixer;
	cd.componentSubType      = kAudioUnitSubType_MultiChannelMixer;
	cd.componentManufacturer = kAudioUnitManufacturer_Apple;
	cd.componentFlags = 0;
	cd.componentFlagsMask = 0;
	status = AUGraphAddNode(m_AUGraph, &cd, &m_NodeMixer);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	//ノード生成：出力
	cd.componentType         = kAudioUnitType_Output;
	cd.componentSubType      = kAudioUnitSubType_RemoteIO;
	cd.componentManufacturer = kAudioUnitManufacturer_Apple;
	cd.componentFlags = 0;
	cd.componentFlagsMask = 0;
	status = AUGraphAddNode(m_AUGraph, &cd, &m_NodeOutput);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// ノード接続
//******************************************************************************
int SMWavetableSynthCtrl::_ConnectNodes()
{
	int result = 0;
	OSStatus status = 0;
	int i = 0;
	
	//ノード接続：サンプラー -> マルチチャンネルミキサー
	for (i = 0; i < SM_MAX_CH_NUM; i++) {
		status = AUGraphConnectNodeInput(
							m_AUGraph,			//入力元グラフ
							m_NodeSampler[i],	//入力元ノード
							0,					//送信元出力番号
							m_NodeMixer,		//出力先ノード
							i					//宛先入力番号
						);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
	}
	
	//ノード接続：マルチチャンネルミキサー -> 出力
	status = AUGraphConnectNodeInput(
							m_AUGraph,		//入力元グラフ
							m_NodeMixer,	//入力元ノード
							0,				//入力元出力番号
							m_NodeOutput,	//出力先ノード
							0				//出力先入力番号
						);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// オーディオユニット取得
//******************************************************************************
int SMWavetableSynthCtrl::_GetAudioUnits()
{
	int result = 0;
	OSStatus status = 0;
	AudioComponentDescription cd;
	int i = 0;
	
	//サンプラーユニットを取得
	for (i = 0; i < SM_MAX_CH_NUM; i++) {
		status = AUGraphNodeInfo(
							m_AUGraph,			//グラフ
							m_NodeSampler[i],	//ノード
							&cd,				//コンポーネント定義
							&m_UnitSampler[i]	//ユニット
						);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
	}
	
	//マルチチャンネルミキサーユニットを取得
	status = AUGraphNodeInfo(
						m_AUGraph,		//グラフ
						m_NodeMixer,	//ノード
						&cd,			//コンポーネント定義
						&m_UnitMixer	//ユニット
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	//出力ユニットを取得
	status = AUGraphNodeInfo(
						m_AUGraph,		//グラフ
						m_NodeOutput,	//ノード
						&cd,			//コンポーネント定義
						&m_UnitOutput	//ユニット
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// サンプラーユニット初期化
//******************************************************************************
int SMWavetableSynthCtrl::_InitSamplerUnits()
{
	int result = 0;
	unsigned char ch = 0;
	OSStatus status = 0;
	UInt32 maxFPS = 0;
	
	//サンプルフレーム最大数を設定
	//画面オフされたときもバックグラウンド演奏を継続するために必要となる設定
	for (ch = 0; ch <  SM_MAX_CH_NUM; ch++) {
		maxFPS = 4096;
		status = AudioUnitSetProperty(
							m_UnitSampler[ch],			//ユニット
							kAudioUnitProperty_MaximumFramesPerSlice,	//プロパティID
							kAudioUnitScope_Global,		//プロパティスコープ
							0,							//プロパティ要素
							&maxFPS,					//プロパティ値
							sizeof(maxFPS)				//プロパティ値のサイズ
						);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
	}
	
	EXIT:;
	return result;
}

//******************************************************************************
// サンプラーユニットセットアップ
//******************************************************************************
int SMWavetableSynthCtrl::_SetupSamplerUnits()
{
	int result = 0;
	unsigned char ch = 0;
	unsigned char presetId = 0;
	unsigned char bankSelectMSB = 0;
	unsigned char bankSelectLSB = 0;
	
	//バンクセレクト初期化
	for (ch = 0; ch < SM_MAX_CH_NUM; ch++) {
		if (ch != SM_WAVETABLE_SYNTH_PERCUSSION_CH) {
			//チャンネル#10以外
			m_BankSelectMSB[ch] = 0;
			m_BankSelectLSB[ch] = 0;
		}
		else {
			//チャンネル#10：ドラム／パーカッション用
			m_BankSelectMSB[ch] = 1; //SoundFontの仕様による
			m_BankSelectLSB[ch] = 0;
		}
	}
	
	//Wavetable読み込み
	for (ch = 0; ch <  SM_MAX_CH_NUM; ch++) {
		result = _LoadInstrument(ch, m_UnitSampler[ch], m_BankSelectMSB[ch], m_BankSelectLSB[ch], presetId);
		if (result != 0) {
			//Wavetable読み込み失敗時はデフォルト値でリトライする
			if (ch != SM_WAVETABLE_SYNTH_PERCUSSION_CH) {
				//チャンネル#10以外
				bankSelectMSB = kAUSampler_DefaultMelodicBankMSB; //121
				bankSelectLSB = kAUSampler_DefaultBankLSB; //0
			}
			else {
				//チャンネル#10：ドラム／パーカッション用
				bankSelectMSB = kAUSampler_DefaultPercussionBankMSB; //120
				bankSelectLSB = kAUSampler_DefaultBankLSB; //0
			}
			result = _LoadInstrument(ch, m_UnitSampler[ch], bankSelectMSB, bankSelectLSB, presetId);
			if (result != 0) {
				//ロード失敗はエラーとせず処理を続行する
				result = 0;
			}
		}
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// ミキサーユニット初期化
//******************************************************************************
int SMWavetableSynthCtrl::_InitMixerUnit()
{
	int result = 0;
	OSStatus status = 0;
	UInt32 busCount = SM_MAX_CH_NUM;
	Float64 sampleRate = SM_WAVETABLE_SYNTH_MIXER_SAMPLE_RATE;
	AudioUnitParameterValue volume = 1.0;
	UInt32 maxFPS = 0;
	int i = 0;
	
	//バス数
	status = AudioUnitSetProperty(
						m_UnitMixer,				//ユニット
						kAudioUnitProperty_ElementCount,	//プロパティID
						kAudioUnitScope_Input,		//プロパティスコープ
						0,							//プロパティ要素
						&busCount,					//プロパティ値
						sizeof(busCount)			//プロパティ値のサイズ
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	for (i = 0; i < SM_MAX_CH_NUM; i++) {
		//ボリューム：入力
		//  macOSではボリュームのデフォルト値がゼロであるため必ず設定する必要あり
		status = AudioUnitSetParameter(
							m_UnitMixer,				//ユニット
							kMultiChannelMixerParam_Volume,	//プロパティID
							kAudioUnitScope_Input,		//プロパティスコープ
							i,							//パラメータ要素
							volume,						//パラメータ
							0							//バッファオフセット
						);
		if (status != noErr) {
			result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
			goto EXIT;
		}
	}
	
	//ボリューム：出力
	//  macOSではボリュームのデフォルト値がゼロであるため必ず設定する必要あり
	status = AudioUnitSetParameter(
						m_UnitMixer,				//ユニット
						kMultiChannelMixerParam_Volume,	//プロパティID
						kAudioUnitScope_Output,		//プロパティスコープ
						0,							//パラメータ要素
						volume,						//パラメータ
						0							//バッファオフセット
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	//出力サンプルレート
	status = AudioUnitSetProperty(
						m_UnitMixer,				//ユニット
						kAudioUnitProperty_SampleRate,	//プロパティID
						kAudioUnitScope_Output,		//プロパティスコープ
						0,							//プロパティ要素
						&sampleRate,				//プロパティ値
						sizeof(sampleRate)			//プロパティ値のサイズ
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
	//サンプルフレーム最大数を設定
	//画面オフされたときもバックグラウンド演奏を継続するために必要となる設定
	maxFPS = 4096;
	status = AudioUnitSetProperty(
						m_UnitMixer,				//ユニット
						kAudioUnitProperty_MaximumFramesPerSlice,	//プロパティID
						kAudioUnitScope_Global,		//プロパティスコープ
						0,							//プロパティ要素
						&maxFPS,					//プロパティ値
						sizeof(maxFPS)				//プロパティ値のサイズ
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// Wavetable読み込み
//******************************************************************************
int SMWavetableSynthCtrl::_LoadInstrument(
		unsigned char ch,
		AudioUnit audioUnit,
		UInt8 bankMSB,
		UInt8 bankLSB,
		UInt8 presetId
	)
{
	int result = 0;
	OSStatus status = 0;
	AUSamplerInstrumentData instData;
	
	//Wavetableファイルパス未設定の場合は何もしない
	if ((m_pWavetableFilePath == nil) || ([m_pWavetableFilePath length] == 0)) {
		goto EXIT;
	}
	
	//Wavetableデータ作成
	NSLog(@"Loading instrument. MSB:%d LSB:%d Preset:%d", bankMSB, bankLSB, presetId);
	instData.fileURL = (CFURLRef)[NSURL fileURLWithPath:m_pWavetableFilePath]; //DLS/SF2ファイルURL
	instData.instrumentType = kInstrumentType_SF2Preset;	//kInstrumentType_DLSPreset と値は同じ
	instData.bankMSB = bankMSB;								//CC#0  MSB
	instData.bankLSB = bankLSB;								//CC#32 LSB
	instData.presetID = presetId;							//プログラムチェンジ
	
	//Wavetableファイル読み込み
	status  = AudioUnitSetProperty(
						audioUnit,						//オーディオユニット
						kAUSamplerProperty_LoadInstrument,	//プロパティID
						kAudioUnitScope_Global,			//プロパティスコープ
						0,								//プロパティ要素
						&instData,						//プロパティ値
						sizeof(instData)				//プロパティ値のサイズ
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AudioToolbox API error.", status, 0);
		NSLog(@"Loading instrument failed.");
		goto EXIT;
	}
	
	m_isWavetableLoaded[ch] = true;
	
EXIT:;
	return result;
}

//******************************************************************************
// コントロール番号サポート情報初期化
//******************************************************************************
void SMWavetableSynthCtrl::_InitControlNoSupport()
{
	int ccNo = 0;
	
	for (ccNo = 0; ccNo < SM_MAX_CC_NUM; ccNo++) {
		m_ControlNoSupport[ccNo] = 1; //ok
	}
	
/*
	//Mac OS X 10.7 (Lion) Apple DLS Music Device のサポート状況
	m_ControlNoSupport[0]  = 1;  // ok : Bank Select MSB
	m_ControlNoSupport[1]  = 1;  // ok : Modulation Wheel or Lever
	m_ControlNoSupport[2]  = 0;  //    : Breath Controller
	m_ControlNoSupport[3]  = 0;  //    : --
	m_ControlNoSupport[4]  = 0;  //    : Foot Controller
	m_ControlNoSupport[5]  = 0;  //    : Portamento Time
	m_ControlNoSupport[6]  = 1;  // ok : Data Entry MSB
	m_ControlNoSupport[7]  = 1;  // ok : Channel Volume, fomerly Main Volume
	m_ControlNoSupport[8]  = 0;  //    : Balance
	m_ControlNoSupport[9]  = 0;  //    : --
	m_ControlNoSupport[10] = 1;  // ok : Pan
	m_ControlNoSupport[11] = 1;  // ok : Expression Controller
	m_ControlNoSupport[12] = 0;  //    : Effect Control 1
	m_ControlNoSupport[13] = 0;  //    : Effect Control 2
	m_ControlNoSupport[14] = 0;  //    : --
	m_ControlNoSupport[15] = 0;  //    : --
	m_ControlNoSupport[16] = 0;  //    : General Purpose Controller 1
	m_ControlNoSupport[17] = 0;  //    : General Purpose Controller 2
	m_ControlNoSupport[18] = 0;  //    : General Purpose Controller 3
	m_ControlNoSupport[19] = 0;  //    : General Purpose Controller 4
	m_ControlNoSupport[20] = 0;  //    : --
	m_ControlNoSupport[21] = 0;  //    : --
	m_ControlNoSupport[22] = 0;  //    : --
	m_ControlNoSupport[23] = 0;  //    : --
	m_ControlNoSupport[24] = 0;  //    : --
	m_ControlNoSupport[25] = 0;  //    : --
	m_ControlNoSupport[26] = 0;  //    : --
	m_ControlNoSupport[27] = 0;  //    : --
	m_ControlNoSupport[28] = 0;  //    : --
	m_ControlNoSupport[29] = 0;  //    : --
	m_ControlNoSupport[30] = 0;  //    : --
	m_ControlNoSupport[31] = 0;  //    : --
	m_ControlNoSupport[32] = 1;  // ok : LSB for Control 0 : Bank Select LSB
	m_ControlNoSupport[33] = 1;  // ok : LSB for Control 1 : Modulation Wheel or Lever
	m_ControlNoSupport[34] = 0;  //    : LSB for Control 2 : Breath Controller
	m_ControlNoSupport[35] = 0;  //    : LSB for Control 3 : --
	m_ControlNoSupport[36] = 0;  //    : LSB for Control 4 : Foot Controller
	m_ControlNoSupport[37] = 0;  //    : LSB for Control 5 : Portamento Time
	m_ControlNoSupport[38] = 1;  // ok : LSB for Control 6 : Data Entry
	m_ControlNoSupport[39] = 1;  // ok : LSB for Control 7 : Channel Volume, formerly Main Volume
	m_ControlNoSupport[40] = 0;  //    : LSB for Control 8 : Balance
	m_ControlNoSupport[41] = 0;  //    : LSB for Control 9 : --
	m_ControlNoSupport[42] = 1;  // ok : LSB for Control 10 : Pan
	m_ControlNoSupport[43] = 1;  // ok : LSB for Control 11 : Expression Controller
	m_ControlNoSupport[44] = 0;  //    : LSB for Control 12 : Effect Control 1
	m_ControlNoSupport[45] = 0;  //    : LSB for Control 13 : Effect Control 2
	m_ControlNoSupport[46] = 0;  //    : LSB for Control 14 : --
	m_ControlNoSupport[47] = 0;  //    : LSB for Control 15 : --
	m_ControlNoSupport[48] = 0;  //    : LSB for Control 16 : General Purpase Controller 1
	m_ControlNoSupport[49] = 0;  //    : LSB for Control 17 : General Purpase Controller 2
	m_ControlNoSupport[50] = 0;  //    : LSB for Control 18 : General Purpase Controller 3
	m_ControlNoSupport[51] = 0;  //    : LSB for Control 19 : General Purpase Controller 4
	m_ControlNoSupport[52] = 0;  //    : LSB for Control 20 : --
	m_ControlNoSupport[53] = 0;  //    : LSB for Control 21 : --
	m_ControlNoSupport[54] = 0;  //    : LSB for Control 22 : --
	m_ControlNoSupport[55] = 0;  //    : LSB for Control 23 : --
	m_ControlNoSupport[56] = 0;  //    : LSB for Control 24 : --
	m_ControlNoSupport[57] = 0;  //    : LSB for Control 25 : --
	m_ControlNoSupport[58] = 0;  //    : LSB for Control 26 : --
	m_ControlNoSupport[59] = 0;  //    : LSB for Control 27 : --
	m_ControlNoSupport[60] = 0;  //    : LSB for Control 28 : --
	m_ControlNoSupport[61] = 0;  //    : LSB for Control 29 : --
	m_ControlNoSupport[62] = 0;  //    : LSB for Control 30 : --
	m_ControlNoSupport[63] = 0;  //    : LSB for Control 31 : --
	m_ControlNoSupport[64] = 1;  // ok : Damper Pedal On/Off
	m_ControlNoSupport[65] = 1;  // ok : Portamento On/Off
	m_ControlNoSupport[66] = 1;  // ok : Sostenuto On/Off
	m_ControlNoSupport[67] = 1;  // ok : Soft Pedal On/Off
	m_ControlNoSupport[68] = 0;  //    : Legato Footswitch
	m_ControlNoSupport[69] = 0;  //    : Hold 2
	m_ControlNoSupport[70] = 0;  //    : Sound Controller 1 : defalut - Sound Variation
	m_ControlNoSupport[71] = 0;  //    : Sound Controller 2 : defalut - Timbre/Harmonic Intens.
	m_ControlNoSupport[72] = 0;  //    : Sound Controller 3 : defalut - Release Time
	m_ControlNoSupport[73] = 0;  //    : Sound Controller 4 : defalut - Attack Time
	m_ControlNoSupport[74] = 0;  //    : Sound Controller 5 : defalut - Brighteness
	m_ControlNoSupport[75] = 0;  //    : Sound Controller 6 : defalut - Decay Time
	m_ControlNoSupport[76] = 0;  //    : Sound Controller 7 : defalut - Vibrato Rate
	m_ControlNoSupport[77] = 0;  //    : Sound Controller 8 : defalut - Vibrato Depth
	m_ControlNoSupport[78] = 0;  //    : Sound Controller 9 : defalut - Vibrato Delay
	m_ControlNoSupport[79] = 0;  //    : Sound Controller 10 : --
	m_ControlNoSupport[80] = 0;  //    : General Purpose Controller 5
	m_ControlNoSupport[81] = 0;  //    : General Purpose Controller 6
	m_ControlNoSupport[82] = 0;  //    : General Purpose Controller 7
	m_ControlNoSupport[83] = 0;  //    : General Purpose Controller 8
	m_ControlNoSupport[84] = 0;  //    : Portamento Control
	m_ControlNoSupport[85] = 0;  //    : --
	m_ControlNoSupport[86] = 0;  //    : --
	m_ControlNoSupport[87] = 0;  //    : --
	m_ControlNoSupport[88] = 0;  //    : High Resolution Velocity Prefix
	m_ControlNoSupport[89] = 0;  //    : --
	m_ControlNoSupport[90] = 0;  //    : --
	m_ControlNoSupport[91] = 1;  // ok : Effect 1 Depth : default - Reverb Send Level
	m_ControlNoSupport[92] = 0;  //    : Effect 2 Depth : formerly Tremolo Depth
	m_ControlNoSupport[93] = 0;  //    : Effect 3 Depth : default - Chorus Send Level
	m_ControlNoSupport[94] = 0;  //    : Effect 4 Depth : formerly Celeste Depth
	m_ControlNoSupport[95] = 0;  //    : Effect 5 Depth : formerly Phaser Depth
	m_ControlNoSupport[96] = 0;  //    : Data Increment
	m_ControlNoSupport[97] = 0;  //    : Data Decrement
	m_ControlNoSupport[98] = 0;  //    : NRPN LSB
	m_ControlNoSupport[99] = 0;  //    : NRPN MSB
	m_ControlNoSupport[100] = 1;  // ok : RPN LSB
	m_ControlNoSupport[101] = 1;  // ok : RPN MSB
	m_ControlNoSupport[102] = 0;  //    : --
	m_ControlNoSupport[103] = 0;  //    : --
	m_ControlNoSupport[104] = 0;  //    : --
	m_ControlNoSupport[105] = 0;  //    : --
	m_ControlNoSupport[106] = 0;  //    : --
	m_ControlNoSupport[107] = 0;  //    : --
	m_ControlNoSupport[108] = 0;  //    : --
	m_ControlNoSupport[109] = 0;  //    : --
	m_ControlNoSupport[110] = 0;  //    : --
	m_ControlNoSupport[111] = 0;  //    : --
	m_ControlNoSupport[112] = 0;  //    : --
	m_ControlNoSupport[113] = 0;  //    : --
	m_ControlNoSupport[114] = 0;  //    : --
	m_ControlNoSupport[115] = 0;  //    : --
	m_ControlNoSupport[116] = 0;  //    : --
	m_ControlNoSupport[117] = 0;  //    : --
	m_ControlNoSupport[118] = 0;  //    : --
	m_ControlNoSupport[119] = 0;  //    : --
	m_ControlNoSupport[120] = 1;  // ok : Channel Mode Message : All Sound Off
	m_ControlNoSupport[121] = 1;  // ok : Channel Mode Message : Reset All Controllers
	m_ControlNoSupport[122] = 0;  //    : Channel Mode Message : Local Control On/Off
	m_ControlNoSupport[123] = 1;  // ok : Channel Mode Message : All Notes Off
	m_ControlNoSupport[124] = 0;  //    : Channel Mode Message : Omni Mode Off
	m_ControlNoSupport[125] = 0;  //    : Channel Mode Message : Omni Mode On
	m_ControlNoSupport[126] = 0;  //    : Channel Mode Message : Mono Mode On
	m_ControlNoSupport[127] = 0;  //    : Channel Mode Message : Poly Mode On
*/
	
	return;
}


