Visual Studio 2012(VS Express for Desktop)で、ファミコンエミュレータNemuをビルドしていきます。
はじめにダウンロードしたソースコードをビルドすると以下のようなエラーがでます。
エラー 2 ファイル FormMain.resx を処理できませんでした。インターネットまたは制限付きゾーン内にあるか、ファイルに Web のマークが付いているためです。これらのファイルを処理するには、Web のマークを削除してください。
以下を参考に対処します。
https://qiita.com/RollSystems/items/67f3a673f9784193779b
つぎにパッケージマネージャ コンソールからSharpDXをインストールし、DirectSound関連を修正します。Any CPUでビルドすれば、Windows RTでもそのまま動きます!
PM> Install-Package SharpDX.DirectSound -Version 4.2.0
NesApu.cs
using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; //using Microsoft.DirectX; //using Microsoft.DirectX.DirectSound; using SharpDX; using SharpDX.DirectSound; using SharpDX.Multimedia; namespace Nemu { /// /// ファミコンのサウンドプロセッサ ////// 矩形波 2ch /// 三角波 1ch /// ノイズ 1ch /// ////// デバイス 三角波 ノイズ 矩形波 /// ---------------------------- ------ ------ ------ /// 三角波生成器 ○ /// 線形カウンタ ○ /// プログラマブルタイマ ○ ○ ○ /// 長さカウンタ ○ ○ ○ /// 4 bit DA コンバータ ○ ○ ○ /// エンベロープディケイユニット ○ ○ /// スウィープユニット ○ /// デューティサイクル生成器 ○ /// 波長コンバータ ○ /// 乱数生成器 ○ /// /// class NesApu { // TODO 矩形波 1・2 のスウィープについて見直す // 矩形波 1 と 2 のスウィープ処理で、まだ実装できてない箇所がある。 // 増加・減少後の処理で、 // // 1's compliment (NOT) is being used for square wave channel 1 // 2's compliment (NEG) is being used for square wave channel 2 // // ↑この辺がよく分からない。 // TODO スペランカーのタイトルの音がならないのを修正 // ゲーム中のディケイもちょっとおかしい。 // たぶん、軽くするために ChannelEnable の場合のみの // 処理を追加したのが原因だと思う。 // 作ってる途中は鳴ってた //----------------------------------------------------------- /// /// 44.1 KHz で出力する場合の 1 Hz ごとの /// クロック数 /// CPU クロック / 44100 = 40.584415584415584415582766439909 /// const double AdditionLimit44 = 40.584415584415584415582766439909; /// /// 1 / 4 フレーム分のクロック数 /// const double AdditionQuarterFrame = 7457.3863636363636363633333333333; /// /// 1 / 2 フレーム分のクロック数 /// //const double AdditionHalfFrame = 14914.772727272727272727272727273; /// /// 長さカウンタのテーブル (32 個) ////// 長さカウンタの設定用レジスタに渡される値は、 /// このテーブルのインデックス値になる。 /// このテーブルはルックアップテーブルとして使われる。 /// /// static byte[] LengthCounterTable = { // 1 2 3 4 5 6 7 8 0x05, 0x7F, 0x0A, 0x01, 0x14, 0x02, 0x28, 0x03, // 9 10 11 12 13 14 15 16 0x50, 0x04, 0x1E, 0x05, 0x07, 0x06, 0x0E, 0x07, //17 18 19 20 21 22 23 24 0x06, 0x08, 0x0C, 0x09, 0x18, 0x0A, 0x30, 0x0B, //25 26 27 28 29 30 31 32 0x60, 0x0C, 0x24, 0x0D, 0x08, 0x0E, 0x10, 0x0F }; /// /// ノイズチャネルのプログラマブルタイマのテーブル ////// ノイズチャネルの設定用レジスタ (0x400E) で渡される /// 値は、このテーブルのインデックス値になる。 /// 上のテーブルと同様に、このテーブルは /// ルックアップテーブルとして使われる。 /// /// static int[] NoiseWavelengthTable = { // 0 1 2 3 4 5 6 7 0x002, 0x004, 0x008, 0x010, 0x020, 0x030, 0x040, 0x050, // 8 9 A B C D E F 0x065, 0x07F, 0x0BE, 0x0FE, 0x17D, 0x1FC, 0x3F9, 0x7F2 }; //----------------------------------------------------------- /// /// システムリセット時の最初の初期化処理が終わったかどうか ////// システムリセットから最初の 2048 クロックは処理を停止する /// /// static bool SoundFirstInitFlag = false; /// /// システムリセット時の最初の初期化処理用カウンタ /// static int SoundFirstInitCounter = 0; //----------------------------------------------------------- /// /// 矩形波チャネル 1 有効フラグ /// (0x4015で設定する) /// static bool SquareChannel1Enable = false; /// /// 矩形波チャネル 2 有効フラグ /// (0x4015で設定する) /// static bool SquareChannel2Enable = false; /// /// 三角波チャネル 有効フラグ /// (0x4015で設定する) /// static bool TriangleChannelEnable = false; /// /// ノイズチャネル 有効フラグ /// (0x4015で設定する) /// static bool NoiseChannelEnable = false; /// /// DMC チャネル 有効フラグ /// (0x4008で設定する) /// static bool DMCChannelEnable = false; //----------------------------------------------------------- /// /// 矩形波 1 チャネル 長さカウンタ無効フラグ /// (0x4000で設定する) /// static bool Square1LengthCounterDisable = true; /// /// 矩形波 1 のデューティサイクルタイプ /// (0x4001で設定する) ////// Value positive/negative /// ----- ----- /// 00 2/14 /// 01 4/12 /// 10 8/ 8 /// 11 12/ 4 /// /// static int Square1DutyCycleType = 0; /// /// 矩形波 1 のデューティサイクルカウンタ /// Square1DutyCycleType とセットで使う ////// 0 ~ 15 の値を取る /// /// static int Square1DutyCycleCounter = 0; /// /// 矩形波 1 用プログラマブルタイマ (11 bit長) /// static int Square1ProgrammableTimer = 0; /// /// 矩形波 1 長さカウンタ (7 bit長) /// static int Square1LengthCounter = 0; /// /// 矩形波 1 ディケイカウンタ /// static int Square1DecayCounter = 0; /// /// 矩形波 1 のボリューム値もしくはディケイレート /// (0x4000で設定する) /// static int Square1VolumeAndDecayRate = 0; /// /// ディケイがかかったボリューム値 /// static int Square1DecayedVolume = 0; /// /// 矩形波 1 ディケイ無効 /// (0x4000で設定する) /// static bool Square1DecayDisable = true; /// /// 矩形波 1 スウィープユニット有効 /// (0x4001で設定する) /// static bool Square1SweepEnable = false; /// /// 矩形波 1 スウィープユニット更新レート /// (0x4001で設定する) /// static int Square1SweepUpdateRate = 0; /// /// 矩形波 1 スウィープ方向 /// (0x4001で設定する) /// 0 か 1 の値を取る ////// 1 : 減少 /// 0 : 増加 /// /// static int Square1SweepDirection = 0; /// /// 矩形波 1 スウィープ右シフト量 /// (0x4001で設定する) /// static int Square1SweepShiftAmount = 0; /// /// 矩形波 1 のスウィープ実行開始カウンタ /// static int Square1SweepCounter = 0; /// /// 矩形波 1 のスウィープ動作時に特定の条件が揃ったら /// 矩形波 1 チャネルの音を停止する /// static bool Square1SilenceFlag = false; //----------------------------------------------------------- /// /// 矩形波 2 チャネル 長さカウンタ無効フラグ /// (0x4004で設定する) /// static bool Square2LengthCounterDisable = true; /// /// 矩形波 2 のデューティサイクルタイプ /// (0x4004で設定する) ////// Value positive/negative /// ----- ----- /// 00 2/14 /// 01 4/12 /// 10 8/ 8 /// 11 12/ 4 /// /// static int Square2DutyCycleType = 0; /// /// 矩形波 2 のデューティサイクルカウンタ /// Square2DutyCycleType とセットで使う ////// 0 ~ 15 の値を取る /// /// static int Square2DutyCycleCounter = 0; /// /// 矩形波 2 用プログラマブルタイマ (11 bit長) /// static int Square2ProgrammableTimer = 0; /// /// 矩形波 2 長さカウンタ (7 bit長) /// static int Square2LengthCounter = 0; /// /// 矩形波 2 ディケイカウンタ /// static int Square2DecayCounter = 0; /// /// 矩形波 2 のボリューム値もしくはディケイレート /// (0x4004で設定する) /// static int Square2VolumeAndDecayRate = 0; /// /// ディケイがかかったボリューム値 /// static int Square2DecayedVolume = 0; /// /// 矩形波 2 ディケイ無効 /// (0x4004で設定する) /// static bool Square2DecayDisable = true; /// /// 矩形波 2 スウィープユニット有効 /// (0x4005で設定する) /// static bool Square2SweepEnable = false; /// /// 矩形波 2 スウィープユニット更新レート /// (0x4005で設定する) /// static int Square2SweepUpdateRate = 0; /// /// 矩形波 2 スウィープ方向 /// (0x4005で設定する) /// 0 か 1 の値を取る ////// 1 : 減少 /// 0 : 増加 /// /// static int Square2SweepDirection = 0; /// /// 矩形波 2 スウィープ右シフト量 /// (0x4005で設定する) /// static int Square2SweepShiftAmount = 0; /// /// 矩形波 2 のスウィープ実行開始カウンタ /// static int Square2SweepCounter = 0; /// /// 矩形波 2 のスウィープ動作時に特定の条件が揃ったら /// 矩形波 2 チャネルの音を停止する /// static bool Square2SilenceFlag = false; //----------------------------------------------------------- /// /// 三角波チャネル 長さカウンタ無効フラグ /// (0x4015で設定する) /// static bool TriangleLengthCounterDisable = true; /// /// 三角波チャネルのロードモードが ON の時 True /// static bool TriangleLoadModeFlag = false; /// /// 三角波生成カウンタ (5 bit長) /// static int TriangleStepCounter = 0; /// /// 三角波用 線形カウンタ (7 bit長) /// static int LinearCounter = 0; /// /// 三角波用 プログラマブルタイマー (11 bit長) /// static int TriangleProgrammableTimer = 0; /// /// 三角波長さカウンタ (7 bit長) /// static int TriangleLengthCounter = 0; //----------------------------------------------------------- /// /// ノイズチャネル シフトレジスタ /// リセット時、 1 に初期化される /// static int NoiseShiftRegister = 1; /// /// ノイズシフト実行フラグ ////// ノイズシフト動作は、乱数生成器が /// 2 回呼び出されるごとに 1 回実行されるので、 /// その状態を保存するためのフラグ /// /// static bool NoiseShiftFlag = false; /// /// ノイズチャネル 長さカウンタ無効フラグ /// (0x400Dで設定する) /// static bool NoiseLengthCounterDisable = true; /// /// ノイズ 用プログラマブルタイマ (11 bit長) /// static int NoiseProgrammableTimer = 0; /// /// ノイズ 長さカウンタ (7 bit長) /// static int NoiseLengthCounter = 0; /// /// ノイズ ディケイカウンタ /// static int NoiseDecayCounter = 0; /// /// ノイズ のボリューム値もしくはディケイレート /// (0x400Cで設定する) /// static int NoiseVolumeAndDecayRate = 0; /// /// ノイズ のディケイがかかったボリューム値 /// static int NoiseDecayedVolume = 0; /// /// ノイズ ディケイ無効 /// (0x400Cで設定する) /// static bool NoiseDecayDisable = true; /// /// ノイズ 乱数生成モード /// (0x400Eで設定する) ////// 0 : 32767 ビット長の乱数 /// 1 : 93 ビット長の乱数 /// (同じものが出てくるまでのサイクル) /// /// static int NoiseRandomGeneratorMode = 0; //----------------------------------------------------------- /// /// 1 / 4 フレームをカウントするカウンタ /// static double ClockQuarterFrame = 0; /// /// 1 / 2 フレーム長を計算するためのフラグ /// 1 / 4 フレームのカウンタから計算するため、 /// 2 回に 1 回カウントするために必要 /// static bool HalfFrameFlag = false; //----------------------------------------------------------- /// /// AdditionLimit に達するまでクロックを足していく /// AdditionLimit に達したら、1Hz 分の波形をバッファに格納する /// static double ClockAddition = 0; /// /// 矩形波 1 の音量を 1 フレームの長さに達するまで足していく /// static int Square1Addition = 0; /// /// 矩形波 2 の音量を 1 フレームの長さに達するまで足していく /// static int Square2Addition = 0; /// /// 三角波の音量を 1 フレームの長さに達するまで足していく /// static int TriangleAddition = 0; /// /// ノイズチャネルの音量を 1 フレームの長さに達するまで足していく /// static int NoiseAddition = 0; /// /// 後で音量の平均値を出すために足し合わせた回数を保存しておく /// static int AdditionCount = 0; /// /// 1 秒分のリングバッファ /// static short[] RingBuffer = new short[44100]; /// /// リングバッファの現在の参照先 /// static volatile int RingBufferPointer = 0; /// /// リングバッファの現在の再生位置 /// static volatile int CurrentPosition = 0; /// /// スレッド実行中フラグ /// static public volatile bool StillRunning = false; //----------------------------------------------------------- //static Semaphore semaphore; /// /// DirectSound デバイス /// // static Device device = null; static DirectSound device = null; /// /// セカンダリバッファ /// // static SecondaryBuffer secondaryBuffer = null; static SecondarySoundBuffer secondaryBuffer = null; /// /// セカンダリバッファの種類設定 /// // static BufferDescription bufferDescription = null; static SoundBufferDescription bufferDescription = null; /// /// リングバッファの読み込み位置通知イベント用 /// static AutoResetEvent[] autoResetEvent = null; /// /// リングバッファの読み込み位置通知イベント用 /// // static BufferPositionNotify[] bufferPositionNotify = null; static NotificationPosition[] bufferPositionNotify = null; /// /// リングバッファの読み込み位置通知イベント用 /// // static Notify notify = null; /// /// サウンドを鳴らすスレッド /// イベント待ちループを回す /// static Thread thread = null; /// /// /// static FileStream fs = null; /// /// /// //static string filename = "../../Sample.wav"; /// /// バッファサイズ /// const int buffersize = 44100; //----------------------------------------------------------- /// /// メンバ変数の初期化 /// public static void InitMemberVariable() { StillRunning = false; SoundFirstInitFlag = false; SoundFirstInitCounter = 0; ClockQuarterFrame = 0; HalfFrameFlag = false; RingBufferPointer = 0; CurrentPosition = 0; // チャネル有効フラグ SquareChannel1Enable = false; SquareChannel2Enable = false; TriangleChannelEnable = false; NoiseChannelEnable = false; DMCChannelEnable = false; // 矩形波 1 Square1LengthCounterDisable = true; Square1DutyCycleType = 0; Square1DutyCycleCounter = 0; Square1ProgrammableTimer = 0; Square1LengthCounter = 0; Square1DecayCounter = 0; Square1VolumeAndDecayRate = 0; Square1DecayedVolume = 0; Square1DecayDisable = true; Square1SweepEnable = false; Square1SweepUpdateRate = 0; Square1SweepDirection = 0; Square1SweepShiftAmount = 0; Square1SweepCounter = 0; Square1SilenceFlag = false; // 矩形波 2 Square2LengthCounterDisable = true; Square2DutyCycleType = 0; Square2DutyCycleCounter = 0; Square2ProgrammableTimer = 0; Square2LengthCounter = 0; Square2DecayCounter = 0; Square2VolumeAndDecayRate = 0; Square2DecayedVolume = 0; Square2DecayDisable = true; Square2SweepEnable = false; Square2SweepUpdateRate = 0; Square2SweepDirection = 0; Square2SweepShiftAmount = 0; Square2SweepCounter = 0; Square2SilenceFlag = false; // 三角波 TriangleLengthCounterDisable = true; TriangleLoadModeFlag = false; TriangleStepCounter = 0; LinearCounter = 0; TriangleProgrammableTimer = 0; TriangleLengthCounter = 0; // ノイズチャネル NoiseShiftRegister = 1; NoiseShiftFlag = false; NoiseLengthCounterDisable = true; NoiseProgrammableTimer = 0; NoiseLengthCounter = 0; NoiseDecayCounter = 0; NoiseVolumeAndDecayRate = 0; NoiseDecayedVolume = 0; NoiseDecayDisable = true; NoiseRandomGeneratorMode = 0; // 音量の積算用 Square1Addition = 0; Square2Addition = 0; TriangleAddition = 0; AdditionCount = 0; ClockAddition = 0; } /// /// DirectSoundの初期化 /// public static void Init() { InitMemberVariable(); try { //semaphore = new Semaphore(0, 1); // デバイスの作成 // device = new Device(); device = new DirectSound(); // 協調レベルの設定 // device.SetCooperativeLevel(FormManager.formMain, CooperativeLevel.Priority); device.SetCooperativeLevel(FormManager.formMain.Handle, CooperativeLevel.Priority); // リングバッファ を無音に設定 //for (int i = 0; i < RingBuffer.Length; i++) //{ // RingBuffer[i] = 128; //} // bufferDescription = new BufferDescription(GetFormat()); // bufferDescription.Flags = BufferDescriptionFlags.ControlPositionNotify // | BufferDescriptionFlags.ControlVolume // | BufferDescriptionFlags.LocateInSoftware; // bufferDescription.BufferBytes = (44100 * 2) / 20 * 5; WaveFormat waveFormat = new WaveFormat(); bufferDescription = new SoundBufferDescription(); bufferDescription.Format = waveFormat; bufferDescription.Flags = BufferFlags.ControlPositionNotify | BufferFlags.ControlVolume | BufferFlags.Software; bufferDescription.BufferBytes = (44100 * 2) / 20 * 5; autoResetEvent = new AutoResetEvent[6]; for (int i = 0; i < 5; i++) { autoResetEvent[i] = new AutoResetEvent(true); //中間地点通知用 }; //autoResetEvent[0] = new AutoResetEvent(false); //再生終了通知用 autoResetEvent[5] = new AutoResetEvent(false); //再生終了通知用 // セカンダリバッファにファイルからバッファをロードします // secondaryBuffer = new SecondaryBuffer(bufferDescription, device); secondaryBuffer = new SecondarySoundBuffer(device, bufferDescription); //secondaryBuffer.Volume = 1000; // notify = new Notify(secondaryBuffer); // bufferPositionNotify = new BufferPositionNotify[5]; // for (int i = 0; i < 5; i++) // { // bufferPositionNotify[i].Offset = ((44100 * 2) / 20 * i); // bufferPositionNotify[i].EventNotifyHandle = autoResetEvent[i].Handle; // } // notify.SetNotificationPositions(bufferPositionNotify); var list = new List(); for (int i = 0; i < 4; i++) { list.Add(new NotificationPosition() { WaitHandle = autoResetEvent[i], Offset = i * (44100 * 2) / 20 + 1, }); } list.Add(new NotificationPosition() { Offset = 4 * (44100 * 2) / 20 - 1, WaitHandle = autoResetEvent[4] }); bufferPositionNotify = list.ToArray(); secondaryBuffer.SetNotificationPositions(bufferPositionNotify); // セカンダリバッファを無音にする short[] buf = new short[44100 / 20 * 5]; Array.Copy(RingBuffer, buf, 44100 / 20 * 5); // secondaryBuffer.Write(0, buf, LockFlag.EntireBuffer); secondaryBuffer.Write(buf, 0, LockFlags.EntireBuffer); secondaryBuffer.Volume = -10000; thread = new Thread(new ThreadStart(StreamLoadThread)); thread.Priority = ThreadPriority.Normal; thread.Start(); // secondaryBuffer.Play(0, BufferPlayFlags.Looping); secondaryBuffer.Play(0, PlayFlags.Looping); secondaryBuffer.Volume = -1000; } catch (Exception) { // 失敗 Logger.Write("NesApu エラー : DirectSoundの初期化に失敗しました。"); } //semaphore.Release(1); } /// /// バッファ読み込み位置イベント待ちループ /// (別スレッドで起動) /// static void StreamLoadThread() { //int size = buffersize / 2; short[] buf = new short[44100 / 20]; short[] nullbuf = new short[44100 / 20]; for (int i = 0; i < nullbuf.Length; i++) nullbuf[i] = 128; StillRunning = true; //Console.WriteLine("*** StreamLoadThread start"); while (true) { int eventNumber; int pos = 0; //int rPointer; eventNumber = AutoResetEvent.WaitAny(autoResetEvent); //Console.WriteLine("Write : {0}, Play {1}, Ev{2}", secondaryBuffer.WritePosition, secondaryBuffer.PlayPosition,eventNumber); if (eventNumber == 5) { secondaryBuffer.Volume = -10000; secondaryBuffer.Stop(); return; } //pos = 44100 / 60 * (eventNumber); //semaphore.WaitOne(); //if (CurrentPosition > RingBufferPointer) // continue; int source; source = RingBufferFilled - (44100 / 20); if (source < 0) source += 44100; Array.Copy(RingBuffer, source, buf, 0, (44100 / 20)); CurrentPosition += (44100 / 20); if (CurrentPosition >= 44100) { CurrentPosition = 0; } //semaphore.Release(); //pos = 44100 / 30 * (eventNumber - 1); //if (pos < 0) //{ // pos = 44100 + pos; //} switch (eventNumber) { case 0: pos = (44100 * 2 / 20) * 4; //if (secondaryBuffer.WritePosition > (44100 / 60) * 2) // continue; break; case 1: pos = 0; break; case 2: pos = (44100 * 2 / 20); break; case 3: pos = (44100 * 2 / 20) * 2; break; case 4: pos = (44100 * 2 / 20) * 3; break; } //try //{ // secondaryBuffer.Write(pos, buf, LockFlag.None); secondaryBuffer.Write(buf, pos, LockFlags.None); //Console.WriteLine("Write : {0}, Play {1}, Ev{2}", secondaryBuffer.WritePosition, secondaryBuffer.PlayPosition, eventNumber); //Console.WriteLine("Write : {0}, Play {1}", secondaryBuffer.WritePosition, secondaryBuffer.PlayPosition); //} //catch (Exception) //{ // return; //} /*switch (eventNumber) { case 0: secondaryBuffer.Write(0, buf, LockFlag.None); break; case 1: secondaryBuffer.Write(size, buf, LockFlag.None); break; case 2: Console.WriteLine("Sound Notify"); return; }*/ } } ////// バッファ読み込み位置イベント待ちループを停止 /// static public void StopStreamLoadThread() { if (thread != null) { //if (thread.ThreadState == ThreadState.Running) //{ //thread.Abort(); //} autoResetEvent[5].Set(); thread.Join(); StillRunning = false; } if (secondaryBuffer != null) { secondaryBuffer.Stop(); secondaryBuffer.Dispose(); } if (autoResetEvent != null) { for (int i = 0; i < 5; i++) autoResetEvent[i].Close(); } } ////// WaveFormatを作って返す /// /// ////* static WaveFormat GetFormat() { WaveFormat waveFormat = new WaveFormat(); //byte[] buf = new byte[36]; //fs.Read(buf, 0, 36); // RIFFなWAVE以外の場合はWAVEにデコードした際のフォーマットを作る。 // (WAV再生するだけならいらないので省略) //if (buf[0] == (byte)'R' && buf[1] == (byte)'I' && buf[2] == (byte)'F' && buf[3] == (byte)'F' // && buf[8] == (byte)'W' && buf[9] == (byte)'A' && buf[10] == (byte)'V' && buf[11] == (byte)'E') //{ waveFormat.FormatTag = WaveFormatTag.Pcm; waveFormat.Channels = 1; waveFormat.SamplesPerSecond = 44100; waveFormat.BitsPerSample = 16; waveFormat.BlockAlign = 2; waveFormat.AverageBytesPerSecond = waveFormat.BlockAlign * waveFormat.SamplesPerSecond; //} //else{ //} //fs.Seek(0, System.IO.SeekOrigin.Begin); return waveFormat; } */ //----------------------------------------------------------- /// /// 矩形波 1 の設定レジスタ 1 に書き込み (0x4000) /// /// public static void WriteSquare1ControlRegister1(byte value) { // [Bit 6-7] デューティサイクルタイプ Square1DutyCycleType = value >> 6; // [Bit 5] 長さカウンタ無効フラグ Square1LengthCounterDisable = (value & 0x20) > 0 ? true : false; // [Bit 4] ディケイユニット無効フラグ Square1DecayDisable = (value & 0x10) > 0 ? true : false; // [Bit 0-3] ディケイレート Square1VolumeAndDecayRate = value & 0x0F; } ////// 矩形波 1 の設定レジスタ 2 に書き込み (0x4001) /// /// public static void WriteSquare1ControlRegister2(byte value) { // [Bit 0-2] スウィープ右シフト量 Square1SweepShiftAmount = value & 0x07; // [Bit 3] スウィープ方向 Square1SweepDirection = (value >> 3) & 0x01; // [Bit 4-6] スウィープ更新レート Square1SweepUpdateRate = (value >> 4) & 0x07; // [Bit 7] スウィープ有効 Square1SweepEnable = (value & 0x80) > 0 ? true : false; } ////// 矩形波 1 のプログラマブルタイマに書き込み (0x4002) /// /// public static void WriteSquare1ProgrammableTimerRegister(byte value) { Square1ProgrammableTimer &= 0xF00; Square1ProgrammableTimer |= value; } ////// 矩形波 1 の長さカウンタレジスタに書き込み (0x4003) /// /// public static void WriteSquare1LengthCounterRegister(byte value) { Square1ProgrammableTimer &= 0xFF; Square1ProgrammableTimer |= ((value & 0x07) << 8); if (SquareChannel1Enable == true) { Square1LengthCounter = LengthCounterTable[(value >> 3) & 0x1F]; } // ディケイボリュームレベルを最大音量にリセット Square1DecayedVolume = 0x0F; } //----------------------------------------------------------- ////// 矩形波 2 の設定レジスタ 1 に書き込み (0x4004) /// /// public static void WriteSquare2ControlRegister1(byte value) { // [Bit 6-7] デューティサイクルタイプ Square2DutyCycleType = value >> 6; // [Bit 5] 長さカウンタ無効フラグ Square2LengthCounterDisable = (value & 0x20) > 0 ? true : false; // [Bit 4] ディケイユニット無効フラグ Square2DecayDisable = (value & 0x10) > 0 ? true : false; // [Bit 0-3] ディケイレート Square2VolumeAndDecayRate = value & 0x0F; } ////// 矩形波 2 の設定レジスタ 2 に書き込み (0x4005) /// /// public static void WriteSquare2ControlRegister2(byte value) { // [Bit 0-2] スウィープ右シフト量 Square2SweepShiftAmount = value & 0x07; // [Bit 3] スウィープ方向 Square2SweepDirection = (value >> 3) & 0x01; // [Bit 4-6] スウィープ更新レート Square2SweepUpdateRate = (value >> 4) & 0x07; // [Bit 7] スウィープ有効 Square2SweepEnable = (value & 0x80) > 0 ? true : false; } ////// 矩形波 2 のプログラマブルタイマに書き込み (0x4006) /// /// public static void WriteSquare2ProgrammableTimerRegister(byte value) { Square2ProgrammableTimer &= 0xF00; Square2ProgrammableTimer |= value; } ////// 矩形波 2 の長さカウンタレジスタに書き込み (0x4007) /// /// public static void WriteSquare2LengthCounterRegister(byte value) { Square2ProgrammableTimer &= 0xFF; Square2ProgrammableTimer |= ((value & 0x07) << 8); if (SquareChannel2Enable == true) { Square2LengthCounter = LengthCounterTable[(value >> 3) & 0x1F]; } // ディケイボリュームレベルを最大音量にリセット Square2DecayedVolume = 0x0F; } //----------------------------------------------------------- ////// 三角波の線形カウンタレジスタに書き込み (0x4008) /// /// public static void WriteTriangleLinearCounterRegister(byte value) { // [Bit 7] 長さカウンタ無効フラグ TriangleLengthCounterDisable = (value & 0x80) > 0 ? true : false; // [Bit 0-6] 線形カウンタのカウント値 // StepLinearCounterで、NesStorage.IO2[0x08]からリロードされるので // ここでの処理は無し } // (0x4009 は使われていない) ////// 三角波のプログラマブルタイマレジスタに書き込み (0x400A) /// /// public static void WriteTriangleProgrammableTimerRegister(byte value) { // [Bit 0-7] プログラマブルタイマーの下位 8 ビット TriangleProgrammableTimer &= 0xF00; TriangleProgrammableTimer |= value; } ////// 三角波の長さカウンタレジスタに書き込み (0x400B) /// /// public static void WriteTriangleLengthCounterRegister(byte value) { // [Bit 0-2] 三角波のプログラマブルタイマーの上位 3 ビット TriangleProgrammableTimer &= 0xFF; TriangleProgrammableTimer |= ((value & 0x07) << 8); // [Bit 3-7] 長さカウンタの値をロード if (TriangleChannelEnable == true) { TriangleLengthCounter = LengthCounterTable[(value >> 3) & 0x1F]; } // ロードモード ON TriangleLoadModeFlag = true; } //----------------------------------------------------------- ////// ノイズ の設定レジスタに書き込み (0x400C) /// /// public static void WriteNoiseControlRegister1(byte value) { // [Bit 6-7] 使われていない // [Bit 5] 長さカウンタ無効フラグ NoiseLengthCounterDisable = (value & 0x20) > 0 ? true : false; // [Bit 4] ディケイユニット無効フラグ NoiseDecayDisable = (value & 0x10) > 0 ? true : false; // [Bit 0-3] ディケイレート NoiseVolumeAndDecayRate = value & 0x0F; } // (0x400D は使われていない) ////// ノイズ の設定レジスタ 2 に書き込み (0x400E) /// /// public static void WriteNoiseControlRegister2(byte value) { int rate; // [Bit 0-3] サンプリングレート rate = value & 0x0F; NoiseProgrammableTimer = NoiseWavelengthTable[rate]; // [Bit 4-6] 使われていない // [Bit 7] 乱数タイプ NoiseRandomGeneratorMode = (value & 0x80) > 0 ? 1 : 0; } ////// ノイズ 長さカウンタレジスタに書き込み (0x400F) /// /// public static void WriteNoiseLengthCounterRegister(byte value) { // [Bit 0-2] 使われていない // [Bit 3-7] 長さカウンタの値をロード //if (NoiseChannelEnable == true) //{ NoiseLengthCounter = LengthCounterTable[(value >> 3) & 0x1F]; //} // ディケイボリュームレベルを最大音量にリセット NoiseDecayedVolume = 0x0F; } //----------------------------------------------------------- ////// チャネル有効レジスタに書き込み (0x4015) /// /// public static void WriteChannelEnableRegister(byte value) { // [Bit 0] 矩形波チャネル 1 if ((value & 0x01) > 0) { SquareChannel1Enable = true; } else { SquareChannel1Enable = false; Square1LengthCounter = 0; } // [Bit 1] 矩形波チャネル 2 if ((value & 0x02) > 0) { SquareChannel2Enable = true; } else { SquareChannel2Enable = false; Square2LengthCounter = 0; } // [Bit 2] 三角波チャネル if ((value & 0x04) > 0) { TriangleChannelEnable = true; } else { TriangleChannelEnable = false; TriangleLengthCounter = 0; } // [Bit 3] ノイズチャネル if ((value & 0x08) > 0) { NoiseChannelEnable = true; } else { NoiseChannelEnable = false; NoiseLengthCounter = 0; } // [Bit 4] DMC チャネル DMCChannelEnable = (value & 0x10) > 0 ? true : false; } ////// 長さカウンタステータスレジスタから読み込み (0x4015) /// ///public static byte ReadLengthCounterStatusRegister() { byte status; status = 0; // 矩形波 1 の状態 if (Square1LengthCounter != 0) status |= 0x01; // 矩形波 2 の状態 if (Square2LengthCounter != 0) status |= 0x02; // 三角波の状態 if (TriangleLengthCounter != 0) status |= 0x04; // ノイズチャネルの状態 if (NoiseLengthCounter != 0) status |= 0x08; return status; } //----------------------------------------------------------- /// /// 矩形波 1 のデューティサイクルカウンタ /// このメソッドが呼び出されるたびに /// 三角波をバッファに格納していく /// public static void IncrementSquare1DutyCycleCounter() { Square1DutyCycleCounter++; if (Square1DutyCycleCounter == 16) Square1DutyCycleCounter = 0; } ////// 矩形波 2 のデューティサイクルカウンタ /// このメソッドが呼び出されるたびに /// 三角波をバッファに格納していく /// public static void IncrementSquare2DutyCycleCounter() { Square2DutyCycleCounter++; if (Square2DutyCycleCounter == 16) Square2DutyCycleCounter = 0; } ////// 三角波 ステップカウンタ /// public static void IncrementTriangleStepCounter() { if (LinearCounter != 0) { TriangleStepCounter++; // 6 ビット目に達したら 0 クリアする if (TriangleStepCounter >= 0x20) TriangleStepCounter = 0; } } // DEBUG static int bitshift = 0; ////// ノイズチャネル 乱数生成器 /// public static void NoiseRandomNumberGenerator() { int bit0; int bit1; bit0 = 0; bit1 = 0; // 乱数生成は 2 回につき 1 回だけ実行される NoiseShiftFlag = !NoiseShiftFlag; if (NoiseShiftFlag == true) return; NoiseShiftRegister <<= 1; NoiseShiftRegister &= 0x7FFF; //NoiseShiftRegister |= bitshift; switch (NoiseRandomGeneratorMode) { case 0: // 32767 bit0 = (NoiseShiftRegister & 0x2000) > 0 ? 1 : 0; bit1 = (NoiseShiftRegister & 0x4000) > 0 ? 1 : 0; break; case 1: // 93 bit0 = (NoiseShiftRegister & 0x100) > 0 ? 1 : 0; bit1 = (NoiseShiftRegister & 0x4000) > 0 ? 1 : 0; break; } //bitshift = (bit0 ^ bit1); NoiseShiftRegister |= (bit0 ^ bit1); } ////// /// static byte VolumePrev = 0; ////// 現在処理中の(音声に関する)フレームの音量の値を返す /// 44.1 KHz の場合、1 秒間に 44,100 フレーム /// ///public static short GetCurrentVolume() { short result; short volume = 0; // 矩形波 1 if (SquareChannel1Enable == true && Square1SilenceFlag == false) { if (Square1LengthCounter != 0) volume += (short)((Square1Addition / AdditionCount) - 0); } // 矩形波 2 if (SquareChannel2Enable == true && Square2SilenceFlag == false) { if (Square2LengthCounter != 0) volume += (short)((Square2Addition / AdditionCount) - 0); } // 三角波 if (TriangleChannelEnable == true) { if (TriangleLengthCounter != 0 && LinearCounter != 0) volume += (short)((TriangleAddition / AdditionCount) - 0); } // ノイズチャネル if (NoiseChannelEnable == true) { if (NoiseLengthCounter != 0) volume += (short)((NoiseAddition / AdditionCount) - 0); } // DEBUG //if (volume != 0) // Console.WriteLine("{0}", volume); // 聞こえるくらいボリュームを上げる volume <<= 8; // 積算用の変数をクリア AdditionCount = 0; Square1Addition = 0; Square2Addition = 0; TriangleAddition = 0; NoiseAddition = 0; //VolumeChange += volume; //VolumeChange /= 3; /* if (VolumePrev == 128) { result = (byte)(128 + (volume / 2)); } else { result = (byte)(128 + volume); if (result == 128) result = (byte)(128 + (VolumePrev - 128)); } VolumePrev = result; */ //if (volume > 64) // Console.WriteLine("V {0}", volume); //volume = (byte)(Math.Abs(prev_volume - volume) >> 1); // prev_volume = volume; return volume; } /// /// 矩形波 1 のディケイの計算をする /// public static void CalculateSquare1DecayedVolume() { if (Square1DecayedVolume > 0) { Square1DecayedVolume--; if (Square1DecayedVolume == 0) { // ディケイリロード有効 if (Square1LengthCounterDisable == true) { Square1DecayedVolume = 0x0F; } } } } ////// 矩形波 2 のディケイの計算をする /// public static void CalculateSquare2DecayedVolume() { if (Square2DecayedVolume > 0) { Square2DecayedVolume--; if (Square2DecayedVolume == 0) { // ディケイリロード有効 if (Square2LengthCounterDisable == true) { Square2DecayedVolume = 0x0F; } } } } ////// ノイズチャネルのディケイの計算をする /// public static void CalculateNoiseDecayedVolume() { if (NoiseDecayedVolume > 0) { NoiseDecayedVolume--; if (NoiseDecayedVolume == 0) { // ディケイリロード有効 if (NoiseLengthCounterDisable == true) { NoiseDecayedVolume = 0x0F; } } } } ////// 矩形波 1 の音量を積算する /// public static void CalculateSquare1Addition() { int square_value; int positive_threshold; //if (SquareChannel1Enable == false) // return; square_value = 0; positive_threshold = 0; switch (Square1DutyCycleType) { case 0: // 2/14 positive_threshold = 2; break; case 1: // 4/12 positive_threshold = 4; break; case 2: // 8/ 8 positive_threshold = 8; break; case 3: // 12/ 4 positive_threshold = 12; break; } // デューティサイクルカウンタが正の状態になっていたら if (Square1DutyCycleCounter < positive_threshold) { // ディケイが無効の時は音量そのまま if (Square1DecayDisable == true) { square_value = Square1VolumeAndDecayRate; } // ディケイが有効の時はディケイがかかった音量 else { square_value = Square1DecayedVolume; } } Square1Addition += square_value; } ////// ファミコンの波形は 1.79MHz で生成されるので、 /// 44.1 KHz 等、PC の標準的なサンプリング周波数に /// ダウンサンプリングするために、いったん /// 1 フレーム内 (1 / 44,100 秒) の音量の和を出す。 /// 後で平均値を求めて 1 フレームの音量を決定する /// ////// 矩形波 2 の音量を積算する /// public static void CalculateSquare2Addition() { int square_value; int positive_threshold; //if (SquareChannel2Enable == false) // return; square_value = 0; positive_threshold = 0; switch (Square2DutyCycleType) { case 0: // 2/14 positive_threshold = 2; break; case 1: // 4/12 positive_threshold = 4; break; case 2: // 8/ 8 positive_threshold = 8; break; case 3: // 12/ 4 positive_threshold = 12; break; } // デューティサイクルカウンタが正の状態になっていたら if (Square2DutyCycleCounter < positive_threshold) { // ディケイが無効の時は音量そのまま if (Square2DecayDisable == true) { square_value = Square2VolumeAndDecayRate; } // ディケイが有効の時はディケイがかかった音量 else { square_value = Square2DecayedVolume; } } Square2Addition += square_value; } ////// ファミコンの波形は 1.79MHz で生成されるので、 /// 44.1 KHz 等、PC の標準的なサンプリング周波数に /// ダウンサンプリングするために、いったん /// 1 フレーム内 (1 / 44,100 秒) の音量の和を出す。 /// 後で平均値を求めて 1 フレームの音量を決定する /// ////// 三角波の音量を積算する /// public static void CalculateTriangleAddition() { int triangle_value; //if (TriangleChannelEnable == false) // return; // 5 ビット目が立っていなかったら // 逆数にする if ((TriangleStepCounter & 0x10) == 0) triangle_value = 0x0F - (TriangleStepCounter & 0x0F); else triangle_value = (TriangleStepCounter & 0x0F); TriangleAddition += triangle_value; } ////// ファミコンの波形は 1.79MHz で生成されるので、 /// 44.1 KHz 等、PC の標準的なサンプリング周波数に /// ダウンサンプリングするために、いったん /// 1 フレーム内 (1 / 44,100 秒) の音量の和を出す。 /// 後で平均値を求めて 1 フレームの音量を決定する /// ////// ノイズチャネルの音量を積算する /// public static void CalculateNoiseAddition() { int noise_value; noise_value = 0; if ((NoiseShiftRegister & 0x4000) == 0) { //Console.WriteLine("NOISE : {0}", NoiseShiftRegister); // ディケイが無効の時は音量そのまま if (NoiseDecayDisable == true) { noise_value = NoiseVolumeAndDecayRate; } // ディケイが有効の時はディケイがかかった音量 else { noise_value = NoiseDecayedVolume; } } NoiseAddition += noise_value; } // TODO APU の中から呼び出すように変更するか検討する // 今のところこれは、CPUの 1 フレーム経過後の // 処理から呼び出されるようになっているが、 // APU内のクロック毎の処理から呼び出した方が // 読みやすいかもしれない。 ////// ファミコンの波形は 1.79MHz で生成されるので、 /// 44.1 KHz 等、PC の標準的なサンプリング周波数に /// ダウンサンプリングするために、いったん /// 1 フレーム内 (1 / 44,100 秒) の音量の和を出す。 /// 後で平均値を求めて 1 フレームの音量を決定する /// ////// 長さカウンタを 1 ステップ分だけ進める /// /// public static void StepLengthCounter() { // 矩形波 1 の長さカウンタ if (Square1LengthCounterDisable == false) { if (Square1LengthCounter != 0) Square1LengthCounter--; } // 矩形波 2 の長さカウンタ if (Square2LengthCounterDisable == false) { if (Square2LengthCounter != 0) Square2LengthCounter--; } // 三角波 の長さカウンタ if (TriangleLengthCounterDisable == false) { if (TriangleLengthCounter != 0) TriangleLengthCounter--; } // ノイズチャネル の長さカウンタ if (NoiseLengthCounterDisable == false) { if (NoiseLengthCounter != 0) NoiseLengthCounter--; } } ////// 三角波の線形カウンタを 1 ステップ分だけ進める /// public static void StepLinearCounter() { if (TriangleLoadModeFlag == true) { LinearCounter = (NesStorage.IO2[0x08] & 0x7F); } else if (LinearCounter > 0) { LinearCounter--; } if ((byte)(NesStorage.IO2[0x08] & 0x80) == 0) { TriangleLoadModeFlag = false; } } ////// 矩形波 1 のスウィープ処理を実行 /// public static void Square1Sweep() { int new_wavelength; int wavelength; // 処理しない条件 if (SquareChannel1Enable == false) return; if (Square1SweepShiftAmount == 0) return; if (Square1LengthCounter == 0) return; // 初期化 new_wavelength = 0; wavelength = NesStorage.IO2[0x02]; wavelength |= ((NesStorage.IO2[0x03] & 0x07) << 8); // 処理しない条件 if (wavelength <= 0x008) return; // 周波数レジスタの更新処理 switch (Square1SweepDirection) { case 0: // 増加 new_wavelength = wavelength + (wavelength >> Square1SweepShiftAmount); // 下から数えて 12 ビット目が立っていたら // (計算結果からキャリーが発生したら)、 // 動作を停止する if ((new_wavelength & 0x800) > 0) { Square1SilenceFlag = true; return; } break; case 1: // 減少 new_wavelength = wavelength - (wavelength >> Square1SweepShiftAmount) - 1; break; } Square1SilenceFlag = false; NesStorage.IO2[0x02] = (byte)(new_wavelength & 0xFF); NesStorage.IO2[0x03] &= 0xF8; NesStorage.IO2[0x03] = (byte)((new_wavelength >> 8) & 0x07); } ////// 矩形波 2 のスウィープ処理を実行 /// public static void Square2Sweep() { int new_wavelength; int wavelength; // 処理しない条件 if (SquareChannel2Enable == false) return; if (Square2SweepShiftAmount == 0) return; if (Square2LengthCounter == 0) return; // 初期化 new_wavelength = 0; wavelength = NesStorage.IO2[0x06]; wavelength |= ((NesStorage.IO2[0x07] & 0x07) << 8); // 処理しない条件 if (wavelength <= 0x008) return; // 周波数レジスタの更新処理 switch (Square2SweepDirection) { case 0: // 増加 new_wavelength = wavelength + (wavelength >> Square2SweepShiftAmount); // 下から数えて 12 ビット目が立っていたら // (計算結果からキャリーが発生したら)、 // 動作を停止する if ((new_wavelength & 0x800) > 0) { Square2SilenceFlag = true; return; } break; case 1: // 減少 new_wavelength = wavelength - (wavelength >> Square2SweepShiftAmount) - 1; break; } Square2SilenceFlag = false; NesStorage.IO2[0x06] = (byte)(new_wavelength & 0xFF); NesStorage.IO2[0x07] &= 0xF8; NesStorage.IO2[0x07] = (byte)((new_wavelength >> 8) & 0x07); } static int RingBufferFilled = 0; ////// プログラマブルタイマをクロック分だけ進める /// /// public static void StepProgrammableTimer(int clock) { // システムリセットから最初の 2048 クロックは // 処理を停止する if (SoundFirstInitFlag == false) { SoundFirstInitCounter += clock; if (SoundFirstInitCounter > 2048) SoundFirstInitFlag = true; else return; clock = SoundFirstInitCounter - 2048; } // 矩形波 1 --------------------------------------------- if (SquareChannel1Enable == true) { Square1ProgrammableTimer -= clock; // 終わりまで行ったらリロードする while (Square1ProgrammableTimer < 0) { int timer; timer = NesStorage.IO2[0x02]; timer |= ((NesStorage.IO2[0x03] & 0x07) << 8); timer += (Square1ProgrammableTimer + 1); Square1ProgrammableTimer = timer; // デューティサイクルジェネレータで波形を作る IncrementSquare1DutyCycleCounter(); } } // 矩形波 2 --------------------------------------------- if (SquareChannel2Enable == true) { Square2ProgrammableTimer -= clock; // 終わりまで行ったらリロードする while (Square2ProgrammableTimer < 0) { int timer; timer = NesStorage.IO2[0x06]; timer |= ((NesStorage.IO2[0x07] & 0x07) << 8); timer += (Square2ProgrammableTimer + 1); Square2ProgrammableTimer = timer; // デューティサイクルジェネレータで波形を作る IncrementSquare2DutyCycleCounter(); } } // 三角波 ----------------------------------------------- if (TriangleChannelEnable == true) { TriangleProgrammableTimer -= clock; // 終わりまで行ったらリロードする while (TriangleProgrammableTimer < 0) { int timer; timer = NesStorage.IO2[0x0A]; timer |= ((NesStorage.IO2[0x0B] & 0x07) << 8); timer += (TriangleProgrammableTimer + 1); TriangleProgrammableTimer = timer; // 三角波生成器で波形を作る IncrementTriangleStepCounter(); } } // ノイズ ----------------------------------------------- if (NoiseChannelEnable == true) { NoiseProgrammableTimer -= clock; // 終わりまで行ったらリロードする while (NoiseProgrammableTimer < 0) { int rate; int timer; rate = NesStorage.IO2[0x0E] & 0x0F; timer = NoiseWavelengthTable[rate]; timer += (NoiseProgrammableTimer + 1); NoiseProgrammableTimer = timer; // 乱数生成器で波形を作る NoiseRandomNumberGenerator(); } } // 矩形波・三角波の音量を積算する ----------------------- for (int i = 0; i < clock; i++) { AdditionCount++; //if (SquareChannel1Enable == true) CalculateSquare1Addition(); //if (SquareChannel2Enable == true) CalculateSquare2Addition(); //if (TriangleChannelEnable == true) CalculateTriangleAddition(); //if (NoiseChannelEnable == true) CalculateNoiseAddition(); } // 1 / 4 フレームの計算 --------------------------------- ClockQuarterFrame += clock; if (ClockQuarterFrame > AdditionQuarterFrame) { // 三角波の線形カウンタ計算 if (TriangleChannelEnable == true) { ClockQuarterFrame -= AdditionQuarterFrame; StepLinearCounter(); } // 矩形波 1 のディケイの計算 if (SquareChannel1Enable == true) { Square1DecayCounter++; if (Square1DecayCounter > Square1VolumeAndDecayRate) { Square1DecayCounter = 0; CalculateSquare1DecayedVolume(); } } // 矩形波 2 のディケイの計算 if (SquareChannel2Enable == true) { Square2DecayCounter++; if (Square2DecayCounter > Square2VolumeAndDecayRate) { Square2DecayCounter = 0; CalculateSquare2DecayedVolume(); } } if (NoiseChannelEnable == true) { // ノイズチャネルのディケイの計算 NoiseDecayCounter++; if (NoiseDecayCounter > NoiseVolumeAndDecayRate) { NoiseDecayCounter = 0; CalculateNoiseDecayedVolume(); } } // 1 / 2 フレームの計算 // スウィープユニット用 HalfFrameFlag = !HalfFrameFlag; if (HalfFrameFlag == false) { // 矩形波 1 のスウィープ処理 if (SquareChannel1Enable == true && Square1SweepEnable == true) { Square1SweepCounter++; if (Square1SweepCounter >= Square1SweepUpdateRate + 1) { Square1SweepCounter = 0; Square1Sweep(); } } // 矩形波 2 のスウィープ処理 if (SquareChannel2Enable == true && Square2SweepEnable == true) { Square2SweepCounter++; if (Square2SweepCounter >= Square2SweepUpdateRate + 1) { Square2SweepCounter = 0; Square2Sweep(); } } } } // クロック積算 (44.1 KHz 用) --------------------------- ClockAddition += clock; if (ClockAddition > AdditionLimit44) { ClockAddition -= AdditionLimit44; //semaphore.WaitOne(); RingBuffer[RingBufferPointer] = GetCurrentVolume(); RingBufferPointer++; if ((RingBufferPointer % (44100 / 20)) == 0) { RingBufferFilled = RingBufferPointer; //RingBufferFilled *= (44100 / 60); //Console.WriteLine("FIll {0} {1}", RingBufferFilled, RingBufferPointer); } if (RingBufferPointer >= 44100) { RingBufferPointer = 0; /*try { secondaryBuffer.Write(0, RingBuffer, LockFlag.None); secondaryBuffer.Play(0, BufferPlayFlags.Default); } catch (Exception) { }*/ } //semaphore.Release(); //Console.WriteLine("Volume : {0}", GetCurrentVolume()); } } } }