//******************************************************************************
//
// Simple MIDI Library / SMAppleDLSDevCtrl
//
// Apple DLS (Downloadable Sounds) デバイス制御クラス
//
// Copyright (C) 2010-2021 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#import "YNBaseLib.h"
#import "SMAppleDLSDevCtrl.h"


//******************************************************************************
// コンストラクタ
//******************************************************************************
SMAppleDLSDevCtrl::SMAppleDLSDevCtrl()
{
	m_AUGraph = NULL;
	m_UnitOut = NULL;
}

//******************************************************************************
// デストラクタ
//******************************************************************************
SMAppleDLSDevCtrl::~SMAppleDLSDevCtrl()
{
	if (m_AUGraph != NULL) {
		DisposeAUGraph(m_AUGraph);
	}
	m_AUGraph = NULL;
	m_UnitOut = NULL;
}

//******************************************************************************
// 初期化
//******************************************************************************
int SMAppleDLSDevCtrl::Initialize()
{
	int result = 0;
	
	if (m_AUGraph != NULL) {
		DisposeAUGraph(m_AUGraph);
	}
	m_AUGraph = NULL;
	m_UnitOut = NULL;
	
	//オーディオ処理グラフ生成
	result = _CreateAUGraph(&m_AUGraph, &m_UnitOut);
	if (result != 0) goto EXIT;
	
	//コントロール番号サポート情報初期化
	_InitControlNoSupport();
	
EXIT:;
	return result;
}

//******************************************************************************
// デバイスオープン
//******************************************************************************
int SMAppleDLSDevCtrl::Open()
{
	int result = 0;
	OSStatus status = 0;
	
	if (m_AUGraph == NULL) goto EXIT;
	
	//グラフ開始
	status = AUGraphStart(m_AUGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}
//******************************************************************************
// デバイスクローズ
//******************************************************************************
int SMAppleDLSDevCtrl::Close()
{
	int result = 0;
	OSStatus status = 0;
	
	if (m_AUGraph == NULL) goto EXIT;
	
	//演奏停止
	status = AUGraphStop(m_AUGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// MIDIメッセージ送信
//******************************************************************************
int SMAppleDLSDevCtrl::SendShortMsg(
		unsigned char* pMsg,
		unsigned int size
	)
{
	int result = 0;
	OSStatus status = 0;
	unsigned char data[3] = {0, 0, 0};
	unsigned char controlNo = 0;
	
	if (m_AUGraph == NULL) goto EXIT;
	
	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) {
			//Apple DLSデバイスがサポートしていないコントロール番号なので送信しない
			//  Mac OS 10.7(Lion)より、特定のコントロール番号以外でMIDIメッセージを
			//  送信しようとすると、MusicDeviceMIDIEventがエラーを返すようになった。
			//  非常に不可解なOSの挙動であるが、エラーが発生するコントロール番号については
			//  送信しないことにする。
			//  TODO: Apple DLSデバイスの仕様確認
			goto EXIT;
		}
		else {
			//送信処理を続行する
		}
	}
	
	//MIDIイベント送信
	status = MusicDeviceMIDIEvent(
					m_UnitOut,	//出力ユニット
					data[0],	//ステータス
					data[1],	//データ1
					data[2],	//データ2
					0			//サンプルオフセット
				);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

//******************************************************************************
// システムエクスクルーシブ送信
//******************************************************************************
int SMAppleDLSDevCtrl::SendLongMsg(
		unsigned char* pMsg,
		unsigned int size
	)
{
	int result = 0;
	OSStatus status = 0;
	
	if (m_AUGraph == NULL) goto EXIT;
	
	//システムエクスクルーシブ送信
	status = MusicDeviceSysEx(
					m_UnitOut,	//出力ユニット
					pMsg,		//データ位置
					(UInt32)size	//データサイズ
				);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, size);
		goto EXIT;
	}
	
EXIT:;
	return result;
}

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

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

//******************************************************************************
// オーディオ処理グラフ生成
//******************************************************************************
int SMAppleDLSDevCtrl::_CreateAUGraph(
		AUGraph* pOutGraph,
		AudioUnit* pOutUnit
	)
{
	int result = 0;
	OSStatus status = 0;
	AUGraph auGraph;
	//ベースSDK10.5ではAudioComponentDescription未定義でエラーになる
	AudioComponentDescription cd;
	AudioComponentDescription cdout;
//	ComponentDescription cd;
//	ComponentDescription cdout;
	AUNode nodeSynth;
	AUNode nodeLimiter;
	AUNode nodeOut;
	AudioUnit unitOut;
	
	*pOutGraph = NULL;
	*pOutUnit = NULL;
	
	//------------------------------------------------
	// グラフ生成
	//------------------------------------------------
	//オーディオ処理グラフ生成
	status = NewAUGraph(&auGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	
	//------------------------------------------------
	// ノード生成
	//------------------------------------------------
	//ノード生成：ミュージックデバイス／DLSシンセ
	cd.componentType         = kAudioUnitType_MusicDevice;
	cd.componentSubType      = kAudioUnitSubType_DLSSynth;
	cd.componentManufacturer = kAudioUnitManufacturer_Apple;
	cd.componentFlags = 0;
	cd.componentFlagsMask = 0;
	status = AUGraphAddNode(auGraph, &cd, &nodeSynth);
	if (status != noErr) {
		goto EXIT;
	}
	//ノード生成：エフェクト／ピークリミッタ
	cd.componentType         = kAudioUnitType_Effect;
	cd.componentSubType      = kAudioUnitSubType_PeakLimiter;
	cd.componentManufacturer = kAudioUnitManufacturer_Apple;
	cd.componentFlags = 0;
	cd.componentFlagsMask = 0;
	status = AUGraphAddNode(auGraph, &cd, &nodeLimiter);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	//ノード生成：出力／標準出力
	cd.componentType         = kAudioUnitType_Output;
	cd.componentSubType      = kAudioUnitSubType_DefaultOutput;
	cd.componentManufacturer = kAudioUnitManufacturer_Apple;
	cd.componentFlags = 0;
	cd.componentFlagsMask = 0;
	status = AUGraphAddNode(auGraph, &cd, &nodeOut);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	
	//------------------------------------------------
	// ノード接続
	//------------------------------------------------
	//ノード接続：DLSシンセ -> ピークリミッタ
	status = AUGraphConnectNodeInput(
						auGraph,	//入力元グラフ
						nodeSynth,	//入力元ノード
						0,			//入力元出力番号
						nodeLimiter,//出力先ノード
						0			//出力先入力番号
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	//ノード接続：ピークリミッタ -> 標準出力 
	status = AUGraphConnectNodeInput(
						auGraph,	//入力元グラフ
						nodeLimiter,//入力元ノード
						0,			//入力元出力番号
						nodeOut,	//出力先ノード
						0			//出力先入力番号
					);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	
	//------------------------------------------------
	// グラフ開始準備
	//------------------------------------------------
	//グラフを開く
	//  サンドボックス対応のため MIDITrail.entitlements に
	//  com.apple.security.temporary-exception.audio-unit-host  YES
	//  を登録してる場合であっても、AUGraphOpen を呼び出すと、次の警告ダイアログが表示される。
	//    要求された Audio Unit を使用するには、"MIDITrail"のセキュリティ設定を下げる必要があります。
	//    続けてもよろしいですか？ [セキュリティ設定を下げる] [キャンセル]
	//  キャンセルを選択するとAUGraphOpenの戻り値が -3000 (invalidComponentID) となる。
	//  よって AUGraphOpen が失敗した場合は、エラー終了とせずに処理を続行できるようにする。
	//  以降、本クラスへのメソッド呼び出しは、処理なしで正常終了する。
	status = AUGraphOpen(auGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		YN_SHOW_ERR();
		result = 0;
		goto EXIT;
	}
	//出力ユニットを取得
	status = AUGraphNodeInfo(
					auGraph,	//グラフ
					nodeSynth,	//入力ノード
					&cdout,		//コンポーネント定義
					&unitOut	//出力ユニット
				);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	//グラフ初期化
	status = AUGraphInitialize(auGraph);
	if (status != noErr) {
		result = YN_SET_ERR(@"AUGraph API error.", status, 0);
		goto EXIT;
	}
	
	*pOutGraph = auGraph;
	*pOutUnit = unitOut;

EXIT:;
	return result;
}

//******************************************************************************
//コントロール番号サポート情報初期化
//******************************************************************************
void SMAppleDLSDevCtrl::_InitControlNoSupport()
{
	//Mac OS X 10.7 (Lion) のサポート状況
	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;
}


