Windows RTでNemu

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 の音量を積算する
        /// 
        /// ファミコンの波形は 1.79MHz で生成されるので、
        /// 44.1 KHz 等、PC の標準的なサンプリング周波数に
        /// ダウンサンプリングするために、いったん
        /// 1 フレーム内 (1 / 44,100 秒) の音量の和を出す。
        /// 後で平均値を求めて 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;
        }

        /// 
        /// 矩形波 2 の音量を積算する
        /// 
        /// ファミコンの波形は 1.79MHz で生成されるので、
        /// 44.1 KHz 等、PC の標準的なサンプリング周波数に
        /// ダウンサンプリングするために、いったん
        /// 1 フレーム内 (1 / 44,100 秒) の音量の和を出す。
        /// 後で平均値を求めて 1 フレームの音量を決定する
        /// 
        /// 
        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 ステップ分だけ進める
        /// 
        ///
        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());
            }

        }
    }
}