テーブル筐体でアストロシティミニを遊んでみる

テーブル筐体にアストロシティミニを組み込んでみました。

上記は送料込みの金額です。

USBエンコーダとMDミニ用スーパーコンバーターで、コンパネがアストロシティミニで使える事に気付いてやってみました。

アストロシティミニの仕様で16:9のモニタにHDMI出力していますが、テーブル筐体の場合は視野角が非常に重要ですのでIO-DATAのADSパネルを選びました。

※MAGIC-NSを2580円で購入しましたが、USBエンコーダは認識しませんでした。もちろんPS4コントローラは使えました。

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());
            }

        }
    }
}

Windows RTでADAMem SDL

はじめにVisual Studio 2012(VS Express for Desktop)をインストールします。
Visual Studio 2012 Express Editionのダウンロードとインストール

つぎにARM用SDLをダウンロードします。
Desktop apps ported to Windows RT

includeフォルダはSDLにリネームしてC:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\SDLに配置します。
SDL.lib、SDLmain.libはC:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\libに配置します。

ADAMem SDLをまずはwin32でビルドしてみました。
ADAMem DOS and ADAMem SDL v1.81 - Page 2 - ColecoVision / Adam - AtariAge Forums

AdamemSDL.c

//	if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_JOYSTICK) < 0 )
	if ( SDL_Init(SDL_INIT_AUDIO) < 0 )
	{
		if (Verbose) printf("FAILED: %s\n", SDL_GetError());
		return 0;
	}

Z80IO.h

;
//#if (__GNUC__ <= 3 && __GNUC__MINOR__ <= 4) \
//	|| (__GNUC__ >= 2 && __GNUC__MINOR__ >= 7)
//#define FASTCALL        __attribute__ ((regparm(3)))
//#ifdef INLINE_MEM
//#define INLINE_MEM_GNU
//#endif
//#else
#define FASTCALL
//#endif

そしてVisual Studio 2012でARMをビルドできるようにします。ARM用のSDL.lib、SDLmain.libはプロジェクトに追加が必要でした。
[how to] compile and port win32 apps to Windows RT ARM

第二種電気工事士の資格に挑戦してみる その4

ついに技能試験当日(12/13)になりました!

 

筆記試験の勉強の前からホーザンの技能試験対策の動画を見て候補問題を1周、

 

準備万端試験対策セットDVDを見ながら候補問題を2周、

 

作業時間短縮のために電気工事士奪取プロジェクトを見ながら候補問題を3周やってみました。

 

あとは息抜きにあにまる電工も見ました!

 

試験勉強でつかったものは早速売却しました。意外と売れるものですね。

  • ホーザン(HOZAN) 電気工事士技能試験工具セット 8000円
  • 練習部材セット 4000円
  • 合格クリップ/合格マルチツール/プレートはずし器/合格クリップ 1600円
  • 合格配線チェッカー 1500円
  • ぜんぶ絵で見て覚える第2種電気工事士筆記試験すいーっと合格 1500円

3DOで遊ぼう会(ヨコハマ)

次世代ゲーム機3DOで遊ぶイベントです。

ハッシュタグ(2020/12/20)

テーブル筐体をレストアしてみる

前からずっと欲しかったテーブル筐体ですが、ヤフオクに部品がバラで出品されていたので、集めて組み立ててみました。

  1. テーブル筐体 木箱 8250円
  2. テーブル筐体 脚 4100円
  3. テーブル筐体 天板 3008円
  4. テーブル筐体 遊び方シール 194円
  5. 液晶モニタ LCD-AD192SEDB 2400円(メルカリ)
  6. ゲーセンイス 3200円
  7. コンパネ 11730円
  8. JAMMAハーネス 1000円
  9. SF2JAMMA(GP25B13A-R1B) 3000円

上記は送料込みの金額です。木箱にはスピーカーとコインセレクタもついていたので配線しました。

第二種電気工事士の資格に挑戦してみる その3

筆記試験が合格したので技能試験の勉強についても書いてみます。

 

前回も書きましたが、筆記試験の勉強の前にあえて、ホーザンの技能試験対策の動画を見て候補問題をひととおりやってみました。

 

動画を見て理解できなくても、複線図を書いて結線していきました。合格配線チェッカーを使って動作確認する事でだんだん分かるようになります。

 

筆記試験が終わったので、メルカリで購入した第二種電気工事士技能試験「準備万端試験対策セット」のDVDを見ながら、今度はきちんと理解しながら候補問題をひととおりやってみました。足りない部材は都度ホームセンターやアマゾンなどで調達するのも楽しいです。

 

もちろん技能試験の動画も繰り返し見ています。HOZAN最高!

電工試験の虎_ホーザン - YouTube