//--------------------------------------------------------------
//	適応線スペクトル強調器（ALE）
//
//		作成者：三上直樹，2022/11/24 作成，ⓒ三上直樹 2022
//--------------------------------------------------------------

using System;
using System.Linq;
using System.Windows.Forms;

using Mikami.WaveformAudio;			// 三上作成のライブラリ用
using Mikami.WindowsApplication;	// 三上作成のライブラリ用

namespace ALE_LMS_波形
{
	public partial class Form1 : Form
	{
		private const int N_ALL_ = 24000;		// データ数，3秒の長さに相当
		private const double FS_ = 8000.0;		// 標本化周波数 ＝ 8 kHz
		private const float A0_ = 5500.0f;		// 入力正弦波の振幅
		private const int ORDER_ = 128;			// ADF の次数
		private const int MAX_DELAY_ = 20;		// 相関除去のための遅延器数の最大値

		private readonly float dpiFactor_;		// 異なる DPI のシステムに対応するスケール変換係数
		private readonly int xw_;				// 時間軸の長さ（ピクセル数）

		private WaveformAudio wfaIn_ = new WaveformAudio();
		private WaveformAudio wfaOut_ = new WaveformAudio();
		private Timer timer_ = new Timer();			// 再生終了の監視用

		private float[] xn_ = new float[N_ALL_];	// 入力信号
		private float[] yn_ = new float[N_ALL_];	// 出力信号
		private const int D_PIXEL_ = 80;			// 80 pixels = 10 ms;
		private int n_bgn_ = 0;						// 波形描画の際の始点

		private double mu_ = 0.001;					// μ の値
		private int delay_ = 10;					// 相関除去のための遅延器数
		private double[] frq_ = new double[]{ 200, 400 };	// 入力正弦波の周波数
		private double[] amp_ = new double[]{ 1.0, 0.0 };	// 相対振幅
		private double snr_ = 10.0;					// S/N 比（単位：dB）
		private bool aleOk_ = false;

		public Form1()
		{
			InitializeComponent();

			textBox1.Text = mu_.ToString();
			textBox2.Text = delay_.ToString();
			textBox3.Text = frq_[0].ToString();
			textBox4.Text = amp_[0].ToString("0.0");
			textBox5.Text = frq_[1].ToString();
			textBox6.Text = amp_[1].ToString("0.0");
			textBox7.Text = snr_.ToString("0.0");
			
			// TextBox を全選択状態にする
			foreach (var tBox in Controls.OfType<TextBox>())
			{
				tBox.Click += (s, e) => tBox.SelectAll();	// クリックされた場合
				tBox.Enter += (s, e) => tBox.SelectAll();	// アクティブになった場合
			}

			// 異なる DPI のシステムに対応するスケール変換の係数を取得
			dpiFactor_ = CreateGraphics().DpiX/96.0f;

			xw_ = pictureBox1.Width - (int)(15*dpiFactor_);

			hScrollBar1.LargeChange = xw_;
			hScrollBar1.Maximum = N_ALL_ - 1;

			GenerateInput(frq_, amp_, snr_);	// 信号処理の実行
			
			EnterToTab.Set(this);		// Enter を Tab のように使う設定
			FormCloseConfirm.Set(this);	// アプリケーション終了の際の確認の設定

			button3.Enabled = false;

			// 再生終了の監視用タイマの設定
			timer_.Tick += MyTimer_Tick;
			timer_.Interval = 100;	//  100 ms ごとに timer_Tick イベント発生
		}

		// 再生（button1, button2）
		private void button1_Click(object sender, EventArgs e)
		{
			if ((Button) sender == button1) wfaIn_.Play();	// 入力再生
			else							wfaOut_.Play();	// 出力再生
			CommonProc.Disable(button1, button2, button4);
			foreach (var tx in Controls.OfType<TextBox>()) tx.Enabled = false;
			button3.Enabled = true;
			timer_.Enabled = true;			// タイマのスタート
		}

		// 再生中止処理
		private void button3_Click(object sender, EventArgs e)
		{
			if (wfaIn_.IsPlay)  wfaIn_.ResetPlay();
			if (wfaOut_.IsPlay) wfaOut_.ResetPlay();

			CommonProc.Enable(button1, button2, button4);
			button3.Enabled = false;
			foreach (var tx in Controls.OfType<TextBox>()) tx.Enabled = true;
		}

		// ALE の実行，出力信号の表示指令
		private void button4_Click(object sender, EventArgs e)
		{
			// ALE 実行
			if (Ale())
				wfaOut_.SetData(yn_.Select(y => A0_*y).ToArray(), (int)FS_);

			pictureBox2.Invalidate();	// 出力信号の表示指令
		}

		// 横スクロールバーのスクロール
		private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
		{
			n_bgn_ = hScrollBar1.Value;
			foreach (var pBox in Controls.OfType<PictureBox>()) pBox.Invalidate();
		}

		// 入力波形の描画
		private void pictureBox1_Paint(object sender, PaintEventArgs e)
		{
			Waveform(e.Graphics, xn_, xw_);
		}

		
		// 出力波形の描画
		private void pictureBox2_Paint(object sender, PaintEventArgs e)
		{
			Waveform(e.Graphics, yn_, xw_, aleOk_);
		}

		// 時間軸の描画
		private void pictureBox3_Paint(object sender, PaintEventArgs e)
		{
			Axis(e.Graphics);
		}

		// μ の入力
		private void textBox1_Leave(object sender, EventArgs e)
		{
			if (!textBox1.ToDouble(out mu_, x => (x > 0) && (x <= 1.0),
								   "0 より大きい 1.0 以下の数を入力してください．")) return;
			button4.Enabled = true;
		}

		// 相関除去のための遅延器数の入力
		private void textBox2_Leave(object sender, EventArgs e)
		{
			if (!textBox2.ToInt(out delay_, x => (x > 0) && (x <= MAX_DELAY_),
								"0 より大きい " + MAX_DELAY_.ToString() +
								" 以下の数を入力してください．")) return;
			button4.Enabled = true;
		}

		// 入力正弦波の周波数の入力
		private void textBox3_Leave(object sender, EventArgs e)
		{
			string msg = "0 より大きい 5000 未満の数を入力してください．";
			if (!textBox3.ToDouble(out frq_[0], x => (x > 0) && (x < 5000), msg))
				return;
			if (!textBox5.ToDouble(out frq_[1], x => (x > 0) && (x < 5000), msg))
				return;
			GenerateInput(frq_, amp_, snr_);
		}

		// 入力正弦波の相対振幅の入力
		private void textBox4_Leave(object sender, EventArgs e)
		{
			string msg = "0 以上 1.0 以下の数を入力してください．";
			if (!textBox4.ToDouble(out amp_[0], x => (x >= 0) && (x <= 1.0), msg))
				return;
			if (!textBox6.ToDouble(out amp_[1], x => (x >= 0) && (x <= 1.0), msg))
				return;
			GenerateInput(frq_, amp_, snr_);
		}

		// S/N 比の入力（0 dB 以上）
		private void textBox7_Leave(object sender, EventArgs e)
		{
			string msg = "0 以上の数を入力してください．";
			if (!textBox7.ToDouble(out snr_, x => x >= 0, msg)) return;
			GenerateInput(frq_, amp_, snr_);
		}

		// Tick イベント発生に対するイベントハンドラ
		private void MyTimer_Tick(object sender, EventArgs e)
		{
			if (wfaIn_.IsPlay || wfaOut_.IsPlay) return;

			// 再生中でなければ以下の処理を行う
			timer_.Enabled = false;						// タイマのストップ

			button3_Click(null, null);
		}
	}
}
