using System; using System.Runtime.InteropServices; // DLL Import using System.IO; using System.Windows.Forms; using System.Drawing; using System.Drawing.Imaging; using System.ComponentModel; namespace SDR { public partial class Form1:Form // メインのフォームのクラス { Graphics grfx; Osc osc1 = new Osc(); // 入力信号用の発信器を作成 Osc osc2 = new Osc(); // ミキサ用の発信器を作成 Osc osc3 = new Osc(); // BFO用の発信器を作成 private BackgroundWorker bw = new BackgroundWorker(); volatile bool abortflag = false; // スレッド中止フラグ String FileName = ""; bool FileMode = false; // ファイル読み込みモードフラグ bool silent = false; // サイレントモード WaveFile wave = new WaveFile(); // WAVEファイル読み込みクラス作成 Fft fft = new Fft(1024, 2); // FFT演算用クラス作成 Spectrum sp = new Spectrum(); // スペクトラム表示用クラス Signal signal = new Signal(); // 信号処理用のクラス作成 Filter filter = new Filter(); // 選局用のフィルタクラスを作成 int TrackValue = 0; // チューニング用のスライダー値 double afgain = 1.0; // 出力ボリューム値 double cursor = 10.0; // チューニングカーソル double freq; // 周波数表示用変数 double level; // 信号強度用変数 double sm_level; //S Meter用信号強度 2018.8.15 delegate void SetTextCallback(double level); //2018.8.1 // // 起動時の初期化 // public Form1() { InitializeComponent(); // デフォルトの初期設定処理を行なう pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);//FFT表示用のイメージを作成 grfx = Graphics.FromImage(pictureBox1.Image);// 作成したイメージをフォームに貼り付ける comboBox1.SelectedIndex = 2; // FFT窓関数をBlackmannに指定 // signal.form = this; //2018.7.25 } // // 画面が表示される際に一度だけ呼ばれる // private void Form1_Load(object sender, EventArgs e) { int x1 = 3; //S=0set smeter(x1); //S_Meter Draw Call  ここまで追加 bw.DoWork += new DoWorkEventHandler(SignalWork); // マルチスレッド動作用のワーカーを作成 bw.RunWorkerAsync(); // 信号処理用のスレッドを開始 } // // フォームが閉じられた時の処理 // private void Form1_FormClosed(object sender, FormClosedEventArgs e) { abortflag = true; // 信号処理スレッドを中止させる } // // スペクトラム表示スタイルを取得する // private int GetDispStyle() { if(radioButton1.Checked) return 0; // 0kHzから4kHzモード else if(radioButton2.Checked) return 1; // -4kHzから4kHzモード else return 2; // -8kHzから8kHzモード } // // テキストボックス内の文字列から数値を得る // private double GetTextValue(TextBox tb) { String s = tb.Text; // テキストボックスの文字列を取得 double val; try {val = Double.Parse(s);}// テキストボックスの文字を数字に変換を試みる catch(FormatException) // 数字としておかしな文字列なら {val = 0; tb.Text = "0";}// 数値は0にしてテキストボックス内のおかしな文字列も0にする return val;// 値をもって帰る } // // FIRフィルタの特性を設定する // void SetupFilter() { int taps; double high, low; taps = (int)GetTextValue(textBox7); // TAP数を得る high = GetTextValue(textBox8) / 16000; // 上限周波数を得る low = GetTextValue(textBox9) / 16000; // 下限周波数を得る if (taps < 3) taps = 3; // TAP数の下限は3 if (taps > 1024) taps = 1023; // TAP数の上限は1023 if (high > 0.5) high = 0.5; // 上限周波数は0.5まで(ナイキスト周波数) if (low > 0.5) low = 0.5; // 下限周波数は0.5まで(ナイキスト周波数) filter.CreateFirFilter(taps, low, high); // FIRフィルタの帯域を設定する } private void smeter(double sm_level) //Sメーター表示 { int i; //目盛記入用ピクセル値 int x2 = 0; //2018.8.15 //Meter Scale Draw Pen Pen redpen1 = new Pen(Color.Red, 2); Pen redpen2 = new Pen(Color.Red, 1); Pen whitepen1 = new Pen(Color.White, 2); Pen whitepen2 = new Pen(Color.White, 1); Pen yellowpen = new Pen(Color.Yellow, 2); //Meter Brush SolidBrush black = new SolidBrush(Color.Black);  //塗りつぶし用 SolidBrush white = new SolidBrush(Color.White); SolidBrush red = new SolidBrush(Color.Red); Font font = new Font("MS UI Gothic", 8); // S_Meter Font用 Font font1 = new Font("MS UI Gothic", 7); // S_Meter Font用 //Meter Indicate if (sm_level < 0) { sm_level = 3; } int x1 = (int)sm_level; //2018.8.15 Graphics g, g2; //g offscreen g2 pictureBox2 screen Bitmap canvas = new Bitmap(pictureBox2.Width, pictureBox2.Height); g = Graphics.FromImage(canvas); g2 = CreateGraphics(); g.FillRectangle(black, 0, 0, 133, 35);  //S_Meter背景を黒くぬりつぶす g.DrawLine(whitepen1, 3, 30, 75, 30); //S_Meter目盛記入 g.DrawLine(redpen1, 75, 30, 129, 30); //S_Meter 9over部目盛記入 for (i = 3; i < 80; i += 18) //S1,S3,S5,S9 scale { g.DrawLine(whitepen1, i, 20, i, 30); } for (i = 12; i < 70; i += 18) //S2,S4,S6 scale { g.DrawLine(whitepen2, i, 25, i, 30); } for (i = 93; i < 130; i += 18) //+20,+40,+60dB scale { g.DrawLine(redpen1, i, 20, i, 30); } for (i = 84; i < 121; i += 18) //+10,+30,+50dB scale { g.DrawLine(redpen2, i, 25, i, 30); } g.DrawString("1 3 5 7 9", font, white, 0, 10);//S_Meter数値記入 g.DrawString("+20 +40 +60", font1, red, 80, 10); g.DrawLine(yellowpen, x1, 5, x1, 35); //Meter針記入 pictureBox2.Image = canvas; //PictureBox2に貼り付け pictureBox2.Refresh(); // x2 = x1; } // // 信号処理スレッド // private void SignalWork(object sender, DoWorkEventArgs e) { Int16[] SoundData = new Int16[2048]; // サウンドカードとやりとりするデータ用のバッファ double[] SignalReal = new double[1024]; // Signal data (1024 samples) double[] SignalImag = new double[1024]; // Imag data (1024 samples) double[] LocalReal = new double[1024]; // Local signal (real) double[] LocalImag = new double[1024]; // Local signal (imag) double[] FftReal = new double[1024]; // FFT計算用のバッファ(real) double[] FftImag = new double[1024]; // FFT計算用のバッファ(imag) double[] Spectrum = new double[1024]; // スペクトラム表示するデータ(dB単位); int cnt = 0; //2018.8.17 Smeter用ループカウンタ SoundCard.WaveFormatSetup(16000); // サウンドカードのサンプル周波数を16KHzに設定 if(SoundCard.SetupInBuffer() != 0) // サウンドデータを受け取るリングバッファを設定 MessageBox.Show("入力デバイスの初期化に失敗しました"); if(SoundCard.SetupOutBuffer() != 0) // サウンドデータを出力するリングバッファを設定 MessageBox.Show("出力デバイスの初期化に失敗しました"); SoundCard.StartCapture(); // サウンドカードのサンプリング動作を開始する while(!abortflag) // 中断フラグが立つまで無限に続ける { if(SoundCard.CheckQueue() != 0) // サウンドデータが揃ったら信号処理を始める { // // サウンドカードまたはファイルからの入力 // SoundCard.GetRecData(SoundData); // 16ビットのサウンドデータをサウンドカードから取得する SoundCard.SoundToFloat(SignalReal, SignalImag, SoundData, GetTextValue(textBox3)); // 無条件にサウンドデータから浮動小数点に変換する if (radioButton4.Checked) // スペクトラム表示はライン入力を表示する指定か? { Array.Copy(SignalReal, FftReal, 1024); Array.Copy(SignalImag, FftImag, 1024); }// ライン入力信号を保存 // // 入力セレクタの処理 // if (FileMode) // ファイルモードなら { FileMode = wave.ReadWaveFile(FileName, SoundData); // ファイルから信号データを取得する SoundCard.SoundToFloat(SignalReal, SignalImag, SoundData, GetTextValue(textBox3)); // WAVEファイルから浮動小数点に変換する } if (radioButton11.Checked) // 発振器が指定されてるか? { if (radioButton18.Checked) // 変調がオフなら単なる発振器として動作 osc1.CreateSignal(SignalReal, SignalImag, GetTextValue(textBox1), GetTextValue(textBox3)); // 複素信号を発生させる else // 変調が指定されているなら { Array.Copy(SignalReal, LocalReal, 1024); // 入力信号の実部だけを取り出し保存する osc1.CreateSignal(SignalReal, SignalImag, GetTextValue(textBox1), 0.1); // -20dBのキャリアを作成する if (radioButton19.Checked) // AM変調が指定されているか? signal.AmMod(SignalReal, SignalImag, LocalReal); // キャリアを振幅変調する else // FM変調が指定されているか? signal.FmMod(SignalReal, SignalImag, LocalReal); // キャリアを周波数変調する } } if (radioButton13.Checked) // ノイズジェネレータが指定されているか? signal.CreateNoise(SignalReal, SignalImag, GetTextValue(textBox3)); if (checkBox2.Checked) // I/Qリバースが指定されているか? signal.SwapData(SignalReal, SignalImag); // I/Qデータを入れ替える if (radioButton5.Checked) // スペクトラム表示はセレクタ出力を表示する指定か? { Array.Copy(SignalReal, FftReal, 1024); Array.Copy(SignalImag, FftImag, 1024); }// セレクタ出力信号を保存 // // 第1ミクサの処理 // if (checkBox3.Checked) // 第1ミキサはONか? { osc2.CreateSignal(LocalReal, LocalImag, GetTextValue(textBox4) * (checkBox5.Checked ? -1 : 1), 1.0); // 局部発振を作成する signal.MultiplySignal(SignalReal, SignalImag, LocalReal, LocalImag); // 掛け算して周波数変換を行なう cursor = 10.0; // まずカーソル表示はデフォルトでOFFにする(10.0は常に表示範囲外) freq = (double)GetTextValue(textBox4) / 1000; // 現在のチューニング周波数を求める if (checkBox7.Checked) // もしBFOがオンなら freq -= (double)GetTextValue(textBox10) / 1000; // チューニング周波数をBFO周波数分だけずらす try { textBox5.Text = (GetTextValue(textBox4) - (checkBox7.Checked ? GetTextValue(textBox10) : 0)).ToString(); }// 受信周波数を表示 catch (InvalidOperationException) { } // エラーがあればここへ(実際は来ない) if (radioButton5.Checked && checkBox5.Checked) // FFT表示が入力信号でリバースなら赤チューニングカーソルを表示する cursor = freq; // チューニングカーソルを表示するため周波数を指定する } if (radioButton6.Checked) // スペクトラム表示はミキサ出力を表示する指定か? { Array.Copy(SignalReal, FftReal, 1024); Array.Copy(SignalImag, FftImag, 1024); }// ミキサ出力を保存 // // フィルタの処理 // if (checkBox6.Checked) // フィルタはONか? filter.FirFilter(SignalReal, SignalImag); // FIRフィルタを適用する if (radioButton7.Checked) // スペクトラム表示はフィルタ出力を表示する指定か? { Array.Copy(SignalReal, FftReal, 1024); Array.Copy(SignalImag, FftImag, 1024); }// フィルタ出力を保存 // // 第2ミクサ(Beat Frequency Oscillator)の処理 // if (checkBox7.Checked) // 第2ミキサはONか? { osc3.CreateSignal(LocalReal, LocalImag, GetTextValue(textBox10), 1.0); // 局部発振(BFO)を作成する signal.MultiplySignal(SignalReal, SignalImag, LocalReal, LocalImag); // 掛け算して周波数変換を行なう } level = signal.GetAbs(null, SignalReal, SignalImag); // このパケット内で最大の信号強度を求める if (checkBox8.Checked) // AGCは有効か? sm_level = signal.ApplyAgc(SignalReal, SignalImag, level); // AGC処理を行なう // signal.ApplyAgc(SignalReal, SignalImag, level); // AGC処理を行なう SetText(sm_level); //2018.8.22 if (radioButton8.Checked) // スペクトラム表示は第2ミキサ出力を表示する指定か? { Array.Copy(SignalReal, FftReal, 1024); Array.Copy(SignalImag, FftImag, 1024); }// 第2ミキサ出力を保存 // // 検波の処理 // if (radioButton14.Checked) // SSBモードか { // すでにBFOで振幅成分が得られているのでなにもしない } else if (radioButton15.Checked) // AMモードか { signal.GetAbs(SignalReal, SignalReal, SignalImag); // AMなら振幅成分を取り出す // sm_level = (int)level; //2018.8.14 // SetText(sm_level); //2018.8.14 signal.MulConst(SignalReal, SignalImag, 3.16); // 他のモードの出力と揃える為10dB増幅する } else if (radioButton16.Checked) // FMモードか { signal.GetPhase(SignalReal, SignalReal, SignalImag); // FMならまず位相成分を取り出す signal.GetDivision(SignalReal); // 位相を微分して周波数に変換する } if (!radioButton17.Checked) // スルー指定なら実信号化しない Array.Copy(SignalReal, SignalImag, 1024); // 得られた振幅成分を虚部にもコピーして実信号化する if (radioButton9.Checked) // スペクトラム表示は検波出力を表示する指定か? { Array.Copy(SignalReal, FftReal, 1024); Array.Copy(SignalImag, FftImag, 1024); }// 検波出力を保存 // // サウンド出力 // signal.MulConst(SignalReal, SignalImag, checkBox1.Checked ? 0 : afgain); // ミュート時には0を、それ以外はafgainを掛ける SoundCard.FloatToSound(SoundData, SignalReal, SignalImag); // 浮動小数点からサウンドデータに変換 if (silent) // 画面が消えていたら無音にする Array.Clear(SoundData, 0, 2048); // サウンドデータをクリア SoundCard.PutPlayData(SoundData); // サウンドデータをサウンドカードに出力する // // スペクトラム表示 // fft.ApplyWindow(FftReal, FftImag); // 信号に窓関数を適用する fft.GetFft(FftReal, FftImag, false); // 順方向のFFTを行い周波数成分(複素数)を得る fft.GetDecibel(Spectrum, FftReal, FftImag); // 複素数の絶対値をdB値に変換する // sm_level = ((int)Spectrum[500] + (int)Spectrum[450] + (int)Spectrum[400])/3; //2018.8.1 center freq をSmeter Levelとする // SetText(sm_level);//2017.7.29 sp.Display(grfx, Spectrum, GetDispStyle(), cursor); // スペクトラム表示をアップデートする try { pictureBox1.Refresh(); } // スペクトラム表示画面を更新する catch { }; // 画面更新の際にエラーが起きた際の受け皿(実際には発生しない) // smeter( sm_level); //2018.8.4 } System.Threading.Thread.Sleep(10); // 他のスレッドが動作できるようにOSに制御を返す } if(FileMode) // ファイル入力モードに設定されていたなら wave.CloseWaveFile(); // ファイルをクローズする SoundCard.HeapCleanUp(); // 信号処理スレッドを抜けたのでサウンドカードを閉じる abortflag = false; // スレッドが終了した事を親スレッドに伝える // smeter(sm_level); //2018.8.11 } // // FFTの窓関数が変更された際の処理 // private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { if (fft != null) // FFTクラスが生成済みなら fft.SetupWindow(comboBox1.SelectedIndex); // 窓関数を変更する } // // ファイルモードが選択された際の処理 // private void radioButton12_CheckedChanged(object sender, EventArgs e) { if(radioButton12.Checked) // ファイルモードが選択されたか? { FileName = textBox2.Text + ".wav"; // WAVEファイル名を生成 FileMode = true; // ファイルモードフラグをセット } else { // ファイルモードではなくなった場合 wave.CloseWaveFile(); // 現在オープンしているファイルをクローズする FileMode = false; // ファイルモードフラグをクリアする } } // // トラックバー(ボリューム用)が動かされたら // private void trackBar1_Scroll(object sender, EventArgs e) { afgain = (double)trackBar1.Value / trackBar1.Maximum; // AFゲインを設定する } // // トラックバー(チューニング用)が動かされた時の処理 // private void trackBar2_Scroll(object sender, EventArgs e) { TrackValue = trackBar2.Value; // トラックバーの値を得る if(checkBox4.Checked) // スライダーのチェックが付けられていれば textBox4.Text = (TrackValue * 16000 / 256 - 8000).ToString();// 局発の周波数を上書きする } // // 選曲用のフィルタのチェックボックスがチェックされた時の処理 // private void checkBox6_CheckedChanged(object sender, EventArgs e) { if(checkBox6.Checked) // 検波用フィルタがオンになったら SetupFilter(); // フィルタ設定を反映する } // // 選曲用フィルタの特性の反映ボタンが押された際の処理 // private void button1_Click(object sender, EventArgs e) { SetupFilter(); // フィルタ設定を反映する } // // 入力ファイル名が変更されたら // private void textBox2_TextChanged(object sender, EventArgs e) { radioButton10.Checked = true; // 入力を一旦ライン入力に変更する } // // 発振器の周波数が変更されたら // private void textBox1_TextChanged(object sender, EventArgs e) { radioButton10.Checked = true; // 入力を一旦ライン入力に変更する } // // ワンタッチ受信機モードボタン // private void button3_Click(object sender, EventArgs e) { textBox1.Text = "1000"; // 発振器の周波数は100Hzに textBox2.Text = "7m"; // 入力ファイル名は7m textBox3.Text = "0.1"; // ゲインは-20dBに textBox4.Text = "-7000"; // 受信周波数は-7000Hzに textBox5.Text = "0"; // オフセット周波数は0Hzに textBox7.Text = "255"; // TAP数は255に textBox8.Text = "1500"; // 上限周波数は1500Hzに textBox9.Text = "0"; // 下限周波数は0Hzに textBox10.Text = "-1500"; // BFO周波数は-1500Hzに trackBar2.Value = 16; // -7000Hzにスライダを移動 checkBox3.Checked = true; // ミクサON checkBox4.Checked = true; // スライダON checkBox5.Checked = true; // リバースON checkBox6.Checked = true; // フィルタON checkBox7.Checked = true; // 第2ミクサON checkBox8.Checked = true; // AGC ON radioButton3.Checked = true; // スペクトラム表示は-8kHzから8kHz radioButton5.Checked = true; // スペクトラム表示はミキサ入力 radioButton12.Checked = true; // 入力はファイルモード radioButton14.Checked = true; // 検波はSSBモード SetupFilter(); // フィルタ設定を反映する } // // ワンタッチでモードリセット // private void button4_Click(object sender, EventArgs e) { trackBar1.Value = 20; // ボリューム最大の位置に trackBar2.Value = 144; // 1000Hzの位置に textBox1.Text = "1000"; // 発振器の周波数は100Hzに textBox2.Text = "lsb"; // 受信ファイル名はlsbに textBox3.Text = "0.1"; // ゲインは-20dBに textBox4.Text = "1000"; // チューニング周波数は1000Hzに textBox5.Text = "0"; // オフセット周波数は0KHzに textBox7.Text = "255"; // TAP数は255に textBox8.Text = "1500"; // 上限周波数は1500Hzに textBox9.Text = "0"; // 下限周波数は0Hzに textBox10.Text = "-1500"; // BFO周波数は0KHzに radioButton3.Checked = true; // -8KHzから8kHz表示モードに radioButton5.Checked = true; // ライン入力を指定 radioButton10.Checked = true; // リバースON radioButton14.Checked = true; // SSBモード radioButton18.Checked = true; // 変調はなし checkBox1.Checked = false; // ミュートOFF checkBox2.Checked = false; // リバースOFF checkBox3.Checked = false; // ミクサOFF checkBox4.Checked = false; // スライダOFF checkBox5.Checked = false; // リバースOFF checkBox6.Checked = false; // フィルタOFF checkBox7.Checked = false; // 第2ミクサOFF checkBox8.Checked = false; // AGC OFF comboBox1.SelectedIndex = 2; // FFT窓関数をBlackmannに指定 cursor = 10.0; // カーソルを消す SetupFilter(); // フィルタ設定を反映する } // ここから2018.7.29追加 public void SetText(double sm_level) { int ant_level; //textbox6 表示ANT入力レベル if (this.textBox6.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { sm_level }); } else { // level = 10000 * level; ant_level = -30 - (int)sm_level; //2018.8.30 sm_level = (97 - (int)sm_level) * 1.5 - 6; textBox6.Text = ant_level.ToString() + "dBm"; smeter(sm_level); //2018.8.11 } } //ここまで } class Spectrum // スペクトラム表示をするクラス { Pen pen = new Pen(Color.Magenta); Pen green = new Pen(Color.LightGreen); Pen red = new Pen(Color.Red); SolidBrush white = new SolidBrush(Color.White); SolidBrush black = new SolidBrush(Color.Black); Font font = new Font("MS UI Gothic", 8); int bias = 80; // スペアナ表示の際のレベル調整値 // // スペクトラムアナライザの描画 // public void Display(Graphics grfx, double[] spectrum, int style, double cursor) { int i, j; grfx.FillRectangle(white, 0, 0, 512, 200); // 画面を白でクリア if (style < 2) // 0-8K または -4k -4k(表示幅が8KHzの場合) { for (i = 1; i < 8; i++) // 1KHzから7KHzまで描く { grfx.DrawString((i - ((style == 1) ? 4 : 0)).ToString() + "k", font, black, i * 64 - 8, 181);//周波数を表示 grfx.DrawLine(green, i * 64, 0, i * 64, 180);// 緑で縦線を描画 } } else { // -8K to 8K 表示(表示幅が16KHzの場合) for (i = 1; i < 16; i++) // 1khzから15kHzまで描く { grfx.DrawString((i - 8).ToString() + "k", font, black, i * 32 - 8, 181);// 周波数を表示 grfx.DrawLine(green, i * 32, 0, i * 32, 180); //緑で縦線を描画 } } for (i = 1; i < 9; i++) // dB表示の横線を描画する { grfx.DrawString("-" + (i * 10).ToString() + "dB", font, black, 480, i * 20 - 6);//dB表示を右側に grfx.DrawLine(green, 0, i * 20, 480, i * 20); // 緑で横線を描く } if (style == 0) { // 0KHzから8KHz表示モード? for (i = 0; i < 512; i++) { j = (int)(spectrum[i] * 2 + bias); // 表示座標を得る if (j > 0) // 表示範囲か? grfx.DrawLine(pen, i, 180, i, 180 - j);//そうならマゼンタで描画する } if (cursor > 0 && cursor < 8.0) // 表示範囲内にカーソルがあるか? grfx.DrawLine(red, (int)(cursor * 64), 180, (int)(cursor * 64), 0);//そうなら赤で描画する } else if (style == 1) { // -4KHzから4KHz表示モード for (i = 0; i < 256; i++) // 左半分 { j = (int)(spectrum[i + 768] * 2 + bias); // 表示範囲を得る if (j > 0)// 表示範囲か? grfx.DrawLine(pen, i, 180, i, 180 - j); //そうならマゼンタで描画する } for (i = 0; i < 256; i++) // 右半分 { j = (int)(spectrum[i] * 2 + bias);// 表示範囲を得る if (j > 0)// 表示範囲か? grfx.DrawLine(pen, i + 256, 180, i + 256, 180 - j);//そうならマゼンタで描画する } if (Math.Abs(cursor) < 4.0) // 表示範囲内にカーソルがあるか? grfx.DrawLine(red, (int)(cursor * 64 + 256), 180, (int)(cursor * 64 + 256), 0);//そうなら赤で描画する } else { // -8Kから8K表示モード for (i = 0; i < 256; i++) // 左半分 { j = (int)(spectrum[i * 2 + 512] + spectrum[i * 2 + 513] + bias);// 表示範囲を得る(2点の平均を求める) if (j > 0)// 表示範囲か? grfx.DrawLine(pen, i, 180, i, 180 - j);//そうならマゼンタで描画する } for (i = 0; i < 256; i++) // 右半分 { j = (int)(spectrum[i * 2] + spectrum[i * 2 + 1] + bias);// 表示範囲を得る(2点の平均を求める) if (j > 0)// 表示範囲か? grfx.DrawLine(pen, i + 256, 180, i + 256, 180 - j);//そうならマゼンタで描画する } if (Math.Abs(cursor) < 8.0) // 表示範囲内にカーソルがあるか? grfx.DrawLine(red, (int)(cursor * 32 + 256), 180, (int)(cursor * 32 + 256), 0);//そうなら赤で描画する } grfx.DrawLine(green, 0, 180, 512, 180); // グラフの一番下の線を描く } } class WaveFile // WAVEファイルを扱うクラス { byte[] ch = new byte[4]; // 4バイトの検索用バッファ BinaryReader br; // バイナリリーダクラスオブジェクトを作成 int datalen; // WAVEデータの長さ // // WAVEファイルをオープンする // public bool SetupWaveFile(String FileName) { try { br = new BinaryReader(File.OpenRead(FileName));// ファイルを開いてみる } catch (FileNotFoundException) // 見つからなければ { br = null; // ファイルが見つからない? return false; // エラーで帰る } for (; ; ) { try { ch[3] = br.ReadByte(); // 1バイト読む } catch (EndOfStreamException) { return CloseWaveFile(); // もう読めなければファイルを閉じてエラーで帰る } if (ch[0] == 'd' && ch[1] == 'a' && ch[2] == 't' && ch[3] == 'a')// dataという4文字が見つかったか? break; // 見つかったらループを抜ける ch[0] = ch[1]; ch[1] = ch[2]; ch[2] = ch[3]; // 一文字ずつずらす } datalen = br.ReadInt32(); // WAVEデータの長さを読み込む datalen -= datalen % 4096; // 4096単位でしかアクセスしないので長さを切りそろえる if (datalen == 0) // ファイルが短いならエラーで帰る return CloseWaveFile(); // ファイルをクローズしてエラーで帰る return true; // 成功リターン } // // WAVEファイルからデータを読み込む // public bool ReadWaveFile(String filename, Int16[] buf) { Array.Clear(buf, 0, 2048); // まずバッファをクリア if (br == null) // オープンされていない? { if (!SetupWaveFile(filename)) // オープンを試みる return false; // 見つからなければエラーリターン } for (int i = 0; i < 2048; i++) // 1024サンプル取り出す { try { buf[i] = br.ReadInt16(); // 16ビットずつ読み込む } catch (EndOfStreamException) { return CloseWaveFile(); // エラー終了 } } datalen -= 4096; // データ長を減算する if (datalen == 0) // 最終データを読み込んだか? { CloseWaveFile(); // ファイルをクローズして return SetupWaveFile(filename); // 再度オープンを試みる } return true; // 正常終了 } // // WAVEファイルをクローズする // public bool CloseWaveFile() { if (br != null) // バイナリリーダが存在してるなら br.Close(); // クローズする br = null; // もうbrは空なので検出用にnullにしておく return false; // 常にfalseで帰る(エラーリターンに利用) } } class SoundCard // サウンドカードに直接アクセスするクラス { // // C#からサウンドカードを扱うためのDLLのライブラリの定義 // [DllImport("SoundDll.dll", EntryPoint = "WaveFormatSetup")] public static extern void WaveFormatSetup(int speed); [DllImport("SoundDll.dll", EntryPoint = "SetupInBuffer")] public static extern int SetupInBuffer(); [DllImport("SoundDll.dll", EntryPoint = "SetupOutBuffer")] public static extern int SetupOutBuffer(); [DllImport("SoundDll.dll", EntryPoint = "StartCapture")] public static extern void StartCapture(); [DllImport("SoundDll.dll", EntryPoint = "CheckQueue")] public static extern int CheckQueue(); [DllImport("SoundDll.dll", EntryPoint = "GetRecData")] public static extern void GetRecData(Int16[] a); [DllImport("SoundDll.dll", EntryPoint = "PutPlayData")] public static extern void PutPlayData(Int16[] a); [DllImport("SoundDll.dll", EntryPoint = "HeapCleanUp")] public static extern void HeapCleanUp(); // // サウンドデータ(16ビット整数)から浮動小数点に変換する // public static void SoundToFloat(double[] real, double[] imag, Int16[] sound, double level) { for (int i = 0; i < 1024; i++) { real[i] = (double)sound[i * 2] / 32767.0 * level; // +-32767を浮動小数点に imag[i] = (double)sound[i * 2 + 1] / 32767.0 * level;// +-32767を浮動小数点に } } // // 浮動小数点からサウンドデータ(16ビット整数)に変換する // public static void FloatToSound(Int16[] sound, double[] real, double[] imag) { for (int i = 0; i < 1024; i++) { if (real[i] >= 1.0) // 1より大きければ real[i] = 1.0; // 上限の1にする else if (real[i] <= -1.0) // 1より小さければ real[i] = -1.0; // 下限の-1にする if (imag[i] >= 1.0) // 1より大きければ imag[i] = 1.0; // 上限の1にする else if (imag[i] <= -1.0) // 1より小さければ imag[i] = -1.0; // 下限の-1にする sound[i * 2] = (Int16)(real[i] * 32767.0); // +-1の浮動小数点を+-32767の整数にする sound[i * 2 + 1] = (Int16)(imag[i] * 32767.0);// 同上(虚部) } } } }