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