//Kitao更新 private void psg_reset(huc6280_state info) { int i, j; for (i = 0; i < 8; i++) { info.Psg[i] = new PSG(); } info.DdaFadeOutL = new int[8]; //Kitao追加 info.DdaFadeOutR = new int[8]; //Kitao追加 info.MainVolumeL = 0; info.MainVolumeR = 0; info.LfoFrq = 0; info.LfoCtrl = 0; info.Channel = 0; //Kitao追加。v2.65 info.bWaveCrash = false; //Kitao追加 //Kitao更新。v0.65.waveデータを初期化。 for (i = 0; i < N_CHANNEL; i++) { for (j = 0; j < 32; j++) { info.Psg[i].wave[j] = -14; //最小値で初期化。ファイプロ,フォーメーションサッカー'90,F1トリプルバトルで必要。 } } for (j = 0; j < 32; j++) { info.Psg[3].wave[j] = 17; //ch3は最大値で初期化。F1トリプルバトル。v2.65 } //Kitao更新。v1.10。キュー処理をここに統合 // _QueueWriteIndex = 0; // _QueueReadIndex = 0; }
//Kitao追加 private void set_VOL(huc6280_state info) { //Sint32 v; if (info.PsgVolumeEffect == 0) { //info->VOL = 0.0; //ミュート info.VOL = 1.0 / 128.0; } else if (info.PsgVolumeEffect == 3) { info.VOL = info.Volume / (double)(OVERSAMPLE_RATE * 4.0 / 3.0); // 3/4。v1.29追加 } else { info.VOL = info.Volume / (double)(OVERSAMPLE_RATE * info.PsgVolumeEffect); //Kitao追加。_PsgVolumeEffect=ボリューム調節効果。 } /*if (!APP_GetCDGame()) //Huカードゲームのときだけ、ボリューム101~120を有効化。v2.62 * { * v = APP_GetWindowsVolume(); * if (v > 100) * _VOL *= ((double)(v-100) * 3.0 + 100.0) / 100.0; //101~120は通常の3.0倍の音量変化。3.0倍のVol120でソルジャーブレイド最適。ビックリマンワールドOK。3.1倍以上だと音が薄くなる&音割れの心配もあり。 * }*/ }
/*Sint32 * PSG_AdvanceClock( * Sint32 clock) * { * return 0; * }*/ //Kitao追加。PSGのボリュームも個別に設定可能にした。 /*static void * PSG_SetVolume( * Uint32 volume) // 0 - 65535*/ private void PSG_SetVolume(huc6280_state info) { /*if (volume < 0) volume = 0; * if (volume > 65535) volume = 65535;*/ //_Volume = (double)volume / 65535.0 / PSG_DECLINE; info.Volume = 1.0 / PSG_DECLINE; set_VOL(info); }
/*----------------------------------------------------------------------------- * [Read] * PSGポートの読み出しに対する動作を記述します。 * -----------------------------------------------------------------------------*/ private byte PSG_Read(huc6280_state chip, uint regNum) { huc6280_state info = chip; if (regNum == 0) { return((byte)info.Channel); } return(info.Port[regNum & 15]); }
private void PSG_SetMuteMask(huc6280_state chip, uint MuteMask) { byte CurChn; for (CurChn = 0x00; CurChn < N_CHANNEL; CurChn++) { PSG_SetMutePsgChannel(chip, CurChn, ((MuteMask >> CurChn) & 0x01) != 0); } return; }
//Kitao追加 private void PSG_SetMutePsgChannel(huc6280_state chip, int num, bool bMute) { huc6280_state info = chip; info.bPsgMute[num] = bMute; if (bMute) { info.DdaFadeOutL[num] = 0; info.DdaFadeOutR[num] = 0; } }
//Kitao追加。ボリュームミュート、ハーフなどをできるようにした。 /*static void * PSG_SetVolumeEffect( * Uint32 volumeEffect) * { * _PsgVolumeEffect = (Sint32)volumeEffect; //※数値が大きいほどボリュームは小さくなる * set_VOL(); * }*/ //Kitao追加 private void PSG_ResetVolumeReg(huc6280_state chip) { huc6280_state info = chip; int i; info.MainVolumeL = 0; info.MainVolumeR = 0; for (i = 0; i < N_CHANNEL; i++) { info.Psg[i].volume = 0; info.Psg[i].outVolumeL = 0; info.Psg[i].outVolumeR = 0; info.DdaFadeOutL[i] = 0; info.DdaFadeOutR[i] = 0; } }
/*----------------------------------------------------------------------------- * [SetSampleRate] * -----------------------------------------------------------------------------*/ /*void * PSG_SetSampleRate( * Uint32 sampleRate) * { * //_SampleRate = sampleRate; * }*/ /*----------------------------------------------------------------------------- * [Deinit] * PSGを破棄します。 * -----------------------------------------------------------------------------*/ private void PSG_Deinit(huc6280_state chip) { huc6280_state info = chip; /*memset(info->Psg, 0, sizeof(_Psg)); * memset(info->DdaFadeOutL, 0, sizeof(_DdaFadeOutL)); //Kitao追加 * memset(info->DdaFadeOutR, 0, sizeof(_DdaFadeOutR)); //Kitao追加 * info->MainVolumeL = 0; * info->MainVolumeR = 0; * info->LfoFrq = 0; * info->LfoCtrl = 0; * info->bWaveCrash = FALSE; //Kitao追加 * // _bPsgInit = FALSE;*/ info = null; }
/*----------------------------------------------------------------------------- * [Write] * PSGポートの書き込みに対する動作を記述します。 * -----------------------------------------------------------------------------*/ private void PSG_Write(huc6280_state chip, uint regNum, byte data) { huc6280_state info = chip; //v1.10更新。キュー処理をここに統合して高速化。 //Kitao更新。clockは考慮せずにシンプルにして高速化した。 /* if (((_QueueWriteIndex + 1) & (APUQUEUE_SIZE-1)) == _QueueReadIndex) * { * PRINTF("PSG Queue Over!"); // キューが満タン * return; * } * _Queue[_QueueWriteIndex].reg = (Uint8)(regNum & 15); * _Queue[_QueueWriteIndex].data = data; * _QueueWriteIndex++; //Kitao更新 * _QueueWriteIndex &= APUQUEUE_SIZE-1; //Kitao更新 */ write_reg(chip, (byte)regNum, data); }
//private void PSG_SetVolume(huc6280_state info) { } /*----------------------------------------------------------------------------- * [Init] * PSGを初期化します。 * -----------------------------------------------------------------------------*/ //Sint32 private huc6280_state PSG_Init(int clock, int sampleRate) { huc6280_state info; info = new huc6280_state(); if (info == null) { return(null); } info.PSG_FRQ = clock & 0x7FFFFFFF; PSG_SetHoneyInTheSky(info, ((clock >> 31) & 0x01) != 0); // PSG_SetHoneyInTheSky(0x01); info.PsgVolumeEffect = 0; info.Volume = 0; info.VOL = 0.0; //PSG_SetVolume(APP_GetPsgVolume());//Kitao追加 PSG_SetVolume(info); psg_reset(info); if (!_bTblInit) { create_volume_table(); create_noise_table(); _bTblInit = true; } //PSG_SetSampleRate(sampleRate); info.SAMPLE_RATE = sampleRate; info.RESMPL_RATE = info.PSG_FRQ / OVERSAMPLE_RATE / info.SAMPLE_RATE; // _bPsgInit = TRUE; return(info); }
/*----------------------------------------------------------------------------- * [Mix] * PSGの出力をミックスします。 * -----------------------------------------------------------------------------*/ private void PSG_Mix(huc6280_state chip, int[][] pDst, int nSample) // Sint16* pDst, // 出力先バッファ //Kitao更新。PSG専用バッファにしたためSint16に。 //void* chip, //Sint32** pDst, //Sint32 nSample) // 書き出すサンプル数 { huc6280_state info = (huc6280_state)chip; PSG PSGChn; int i; int j; int sample; //Kitao追加 int lfo; int sampleAllL; //Kitao追加。6chぶんのサンプルを足していくためのバッファ。精度を維持するために必要。6chぶん合計が終わった後に、これをSint16に変換して書き込むようにした。 int sampleAllR; //Kitao追加。上のRチャンネル用 int smp; //Kitao追加。DDA音量,ノイズ音量計算用 int[] bufL = pDst[0]; int[] bufR = pDst[1]; // if (!_bPsgInit) // return; for (j = 0; j < nSample; j++) { sampleAllL = 0; sampleAllR = 0; for (i = 0; i < N_CHANNEL; i++) { PSGChn = info.Psg[i]; if ((PSGChn.bOn) && ((i != 1) || (info.LfoCtrl == 0)) && (!info.bPsgMute[i])) //Kitao更新 { if (PSGChn.bDDA) { smp = PSGChn.ddaSample * PSGChn.outVolumeL; sampleAllL += smp + (smp >> 3) + (smp >> 4) + (smp >> 5) + (smp >> 7) + (smp >> 12) + (smp >> 14) + (smp >> 15); //Kitao更新。サンプリング音の音量を実機並みに調整。v2.39,v2.40,v2.62,v2.65再調整した。 smp = PSGChn.ddaSample * PSGChn.outVolumeR; sampleAllR += smp + (smp >> 3) + (smp >> 4) + (smp >> 5) + (smp >> 7) + (smp >> 12) + (smp >> 14) + (smp >> 15); //Kitao更新。サンプリング音の音量を実機並みに調整。v2.39,v2.40,v2.62,v2.65再調整した。 } else if (PSGChn.bNoiseOn) { sample = _NoiseTable[PSGChn.phase >> 17]; if (PSGChn.noiseFrq == 0) //Kitao追加。noiseFrq=0(dataに0x1Fが書き込まれた)のときは音量が通常の半分とした。(ファイヤープロレスリング3、パックランド、桃太郎活劇、がんばれゴルフボーイズなど) { smp = sample * PSGChn.outVolumeL; sampleAllL += (smp >> 1) + (smp >> 12) + (smp >> 14); //(1/2 + 1/4096 + (1/32768 + 1/32768)) smp = sample * PSGChn.outVolumeR; sampleAllR += (smp >> 1) + (smp >> 12) + (smp >> 14); } else //通常 { smp = sample * PSGChn.outVolumeL; sampleAllL += smp + (smp >> 11) + (smp >> 14) + (smp >> 15); //Kitao更新。ノイズの音量を実機並みに調整(1 + 1/2048 + 1/16384 + 1/32768)。この"+1/32768"で絶妙(主観。大魔界村,ソルジャーブレイドなど)になる。v2.62更新 smp = sample * PSGChn.outVolumeR; sampleAllR += smp + (smp >> 11) + (smp >> 14) + (smp >> 15); //Kitao更新。ノイズの音量を実機並みに調整 } PSGChn.phase += PSGChn.deltaNoisePhase; //Kitao更新 } else if (PSGChn.deltaPhase != 0) { //Kitao更新。オーバーサンプリングしないようにした。 sample = PSGChn.wave[PSGChn.phase >> 27]; if (PSGChn.frq < 128) { sample -= sample >> 2; //低周波域の音量を制限。ブラッドギアのスタート時などで実機と同様の音に。ソルジャーブレイドなども実機に近くなった。v2.03 } sampleAllL += sample * PSGChn.outVolumeL; //Kitao更新 sampleAllR += sample * PSGChn.outVolumeR; //Kitao更新 //Kitao更新。Lfoオンが有効になるようにし、Lfoの掛かり具合を実機に近づけた。v1.59 if ((i == 0) && (info.LfoCtrl > 0)) { //_LfoCtrlが1のときに0回シフト(そのまま)で、はにいいんざすかいが実機の音に近い。 //_LfoCtrlが3のときに4回シフトで、フラッシュハイダースが実機の音に近い。 lfo = info.Psg[1].wave[info.Psg[1].phase >> 27] << (int)((info.LfoCtrl - 1) << 1); //v1.60更新 info.Psg[0].phase += (uint)((double)(65536.0 * 256.0 * 8.0 * info.RESMPL_RATE) / (double)(info.Psg[0].frq + lfo) + 0.5); info.Psg[1].phase += (uint)((double)(65536.0 * 256.0 * 8.0 * info.RESMPL_RATE) / (double)(info.Psg[1].frq * info.LfoFrq) + 0.5); //v1.60更新 } else { PSGChn.phase += PSGChn.deltaPhase; } } } //Kitao追加。DDA消音時はノイズ軽減のためフェードアウトで消音する。 // ベラボーマン(「わしがばくだはかせじゃ」から数秒後)やパワーテニス(タイトル曲終了から数秒後。点数コール),将棋初心者無用(音声)等で効果あり。 if (info.DdaFadeOutL[i] > 0) { --info.DdaFadeOutL[i]; } else if (info.DdaFadeOutL[i] < 0) { ++info.DdaFadeOutL[i]; } if (info.DdaFadeOutR[i] > 0) { --info.DdaFadeOutR[i]; } else if (info.DdaFadeOutR[i] < 0) { ++info.DdaFadeOutR[i]; } sampleAllL += info.DdaFadeOutL[i]; sampleAllR += info.DdaFadeOutR[i]; } //Kitao更新。6ch合わさったところで、ボリューム調整してバッファに書き込む。 sampleAllL = (int)((double)sampleAllL * info.VOL); //if ((sampleAllL>32767)||(sampleAllL<-32768)) PRINTF("PSG Sachitta!");//test用 // if (sampleAllL> 32767) sampleAllL= 32767; //Volをアップしたのでサチレーションチェックが必要。v2.39 // if (sampleAllL<-32768) sampleAllL=-32768; // パックランドでUFO等にやられたときぐらいで、通常のゲームでは起こらない。音量の大きなビックリマンワールドもOK。パックランドも通常はOKでサチレーションしたときでもわずかなので音質的に大丈夫。 // なので音質的には、PSGを2つのDirectXチャンネルに分けて鳴らすべき(処理は重くなる)だが、現状はパックランドでもサチレーション処理だけで音質的に問題なし(速度優先)とする。 sampleAllR = (int)((double)sampleAllR * info.VOL); //if ((sampleAllR>32767)||(sampleAllR<-32768)) PRINTF("PSG Satitta!");//test用 // if (sampleAllR> 32767) sampleAllR= 32767; //Volをアップしたのでサチレーションチェックが必要。v2.39 // if (sampleAllR<-32768) sampleAllR=-32768; // bufL[j] = sampleAllL << 1; bufR[j] = sampleAllR << 1; //キューを参照しPSGレジスタを更新する。Kitao更新。高速化のためサブルーチンにせずここで処理。キューの参照はシンプルにした(テンポの安定性向上)。 /*while (_QueueReadIndex != _QueueWriteIndex) //v1.10更新。キュー処理をここへ統合し高速化。 * { * write_reg(_Queue[_QueueReadIndex].reg, _Queue[_QueueReadIndex].data); * _QueueReadIndex++; //Kitao更新 * _QueueReadIndex &= APUQUEUE_SIZE-1; //Kitao更新 * }*/ } }
/*----------------------------------------------------------------------------- * [write_reg] * PSGポートの書き込みに対する動作を記述します。 * -----------------------------------------------------------------------------*/ //static inline void private void write_reg(huc6280_state info, byte reg, byte data) { uint i; uint frq;//Kitao追加 PSG PSGChn; info.Port[reg & 15] = data; switch (reg & 15) { case 0: // register select info.Channel = (uint)(data & 7); break; case 1: // main volume info.MainVolumeL = (uint)((data >> 4) & 0x0F); info.MainVolumeR = (uint)(data & 0x0F); /* LMAL, RMAL は全チャネルの音量に影響する */ for (i = 0; i < N_CHANNEL; i++) { PSGChn = info.Psg[i]; PSGChn.outVolumeL = _VolumeTable[PSGChn.volume + (info.MainVolumeL + PSGChn.volumeL) * 2]; PSGChn.outVolumeR = _VolumeTable[PSGChn.volume + (info.MainVolumeR + PSGChn.volumeR) * 2]; } break; case 2: // frequency low PSGChn = info.Psg[info.Channel]; PSGChn.frq &= ~(uint)0xFF; PSGChn.frq |= data; //Kitao更新。update_frequencyは、速度アップのためサブルーチンにせず直接実行するようにした。 frq = (PSGChn.frq - 1) & 0xFFF; if (frq != 0) { PSGChn.deltaPhase = (uint)((double)(65536.0 * 256.0 * 8.0 * info.RESMPL_RATE) / (double)frq + 0.5); //Kitao更新。速度アップのためfrq以外は定数計算にした。精度向上のため、先に値の小さいOVERSAMPLE_RATEのほうで割るようにした。+0.5は四捨五入で精度アップ。プチノイズ軽減のため。 } else { PSGChn.deltaPhase = 0; } break; case 3: // frequency high PSGChn = info.Psg[info.Channel]; PSGChn.frq &= ~(uint)0xF00; PSGChn.frq |= (uint)((data & 0x0F) << 8); //Kitao更新。update_frequencyは、速度アップのためサブルーチンにせず直接実行するようにした。 frq = (PSGChn.frq - 1) & 0xFFF; if (frq != 0) { PSGChn.deltaPhase = (uint)((double)(65536.0 * 256.0 * 8.0 * info.RESMPL_RATE) / (double)frq + 0.5); //Kitao更新。速度アップのためfrq以外は定数計算にした。精度向上のため、先に値の小さいOVERSAMPLE_RATEのほうで割るようにした。+0.5は四捨五入で精度アップ。プチノイズ軽減のため。 } else { PSGChn.deltaPhase = 0; } break; case 4: // ON, DDA, AL PSGChn = info.Psg[info.Channel]; if (info.bHoneyInTheSky) //はにいいんざすかいのポーズ時に、微妙なボリューム調整タイミングの問題でプチノイズが載ってしまうので、現状はパッチ処理で対応。v2.60更新 { if ((PSGChn.bOn) && (data == 0)) //発声中にdataが0の場合、LRボリュームも0にリセット。はにいいんざすかいのポーズ時のノイズが解消。(data & 0x1F)だけが0のときにリセットすると、サイレントデバッガーズ等でNG。発声してない時にリセットするとアトミックロボでNG。v2.55 { //PRINTF("test %X %X %X %X",info->Channel,PSGChn->bOn,info->MainVolumeL,info->MainVolumeR); if ((info.MainVolumeL & 1) == 0) //メインボリュームのbit0が0のときだけ処理(はにいいんざすかいでイレギュラーな0xE。他のゲームは0xF。※ヘビーユニットも0xEだった)。これがないとミズバク大冒険で音が出ない。実機の仕組みと同じかどうかは未確認。v2.53追加 { PSGChn.volumeL = 0; } if ((info.MainVolumeR & 1) == 0) //右チャンネルも同様とする { PSGChn.volumeR = 0; } } } PSGChn.bOn = ((data & 0x80) != 0); if ((PSGChn.bDDA) && ((data & 0x40) == 0)) //DDAからWAVEへ切り替わるとき or DDAから消音するとき { //Kitao追加。DDAはいきなり消音すると目立つノイズが載るのでフェードアウトする。 info.DdaFadeOutL[info.Channel] = (int)((double)(PSGChn.ddaSample * PSGChn.outVolumeL) * ((1 + (1 >> 3) + (1 >> 4) + (1 >> 5) + (1 >> 7) + (1 >> 12) + (1 >> 14) + (1 >> 15)) * SAMPLE_FADE_DECLINE)); //元の音量。v2.65更新 info.DdaFadeOutR[info.Channel] = (int)((double)(PSGChn.ddaSample * PSGChn.outVolumeR) * ((1 + (1 >> 3) + (1 >> 4) + (1 >> 5) + (1 >> 7) + (1 >> 12) + (1 >> 14) + (1 >> 15)) * SAMPLE_FADE_DECLINE)); } PSGChn.bDDA = ((data & 0x40) != 0); //Kitao追加。dataのbit7,6が01のときにWaveインデックスをリセットする。 //DDAモード時にWaveデータを書き込んでいた場合はここでWaveデータを修復(初期化)する。ファイヤープロレスリング。 if ((data & 0xC0) == 0x40) { PSGChn.waveIndex = 0; if (info.bWaveCrash) { for (i = 0; i < 32; i++) { PSGChn.wave[i] = -14; //Waveデータを最小値で初期化 } info.bWaveCrash = false; } } PSGChn.volume = (uint)(data & 0x1F); PSGChn.outVolumeL = _VolumeTable[PSGChn.volume + (info.MainVolumeL + PSGChn.volumeL) * 2]; PSGChn.outVolumeR = _VolumeTable[PSGChn.volume + (info.MainVolumeR + PSGChn.volumeR) * 2]; break; case 5: // LAL, RAL PSGChn = info.Psg[info.Channel]; PSGChn.volumeL = (uint)((data >> 4) & 0xF); PSGChn.volumeR = (uint)(data & 0xF); PSGChn.outVolumeL = _VolumeTable[PSGChn.volume + (info.MainVolumeL + PSGChn.volumeL) * 2]; PSGChn.outVolumeR = _VolumeTable[PSGChn.volume + (info.MainVolumeR + PSGChn.volumeR) * 2]; break; case 6: // wave data //Kitao更新。DDAモードのときもWaveデータを更新するようにした。v0.63。ファイヤープロレスリング PSGChn = info.Psg[info.Channel]; data &= 0x1F; info.bWaveCrash = false; //Kitao追加 if (!PSGChn.bOn) //Kitao追加。音を鳴らしていないときだけWaveデータを更新する。v0.65。F1トリプルバトルのエンジン音。 { PSGChn.wave[PSGChn.waveIndex++] = 17 - data; //17。Kitao更新。一番心地よく響く値に。ミズバク大冒険,モトローダー,ドラゴンスピリット等で調整。 PSGChn.waveIndex &= 0x1F; } if (PSGChn.bDDA) { //Kitao更新。ノイズ軽減のため6より下側の値はカットするようにした。v0.59 if (data < 6) //サイバーナイトで6に決定 { data = 6; //ノイズが多いので小さな値はカット } PSGChn.ddaSample = 11 - data; //サイバーナイトで11に決定。ドラムの音色が最適。v0.74 if (!PSGChn.bOn) //DDAモード時にWaveデータを書き換えた場合 { info.bWaveCrash = true; } } break; case 7: // noise on, noise frq if (info.Channel >= 4) { PSGChn = info.Psg[info.Channel]; PSGChn.bNoiseOn = ((data & 0x80) != 0); PSGChn.noiseFrq = (uint)(0x1F - (data & 0x1F)); if (PSGChn.noiseFrq == 0) { PSGChn.deltaNoisePhase = (uint)((double)(2048.0 * info.RESMPL_RATE) + 0.5); //Kitao更新 } else { PSGChn.deltaNoisePhase = (uint)((double)(2048.0 * info.RESMPL_RATE) / (double)PSGChn.noiseFrq + 0.5); //Kitao更新 } } break; case 8: // LFO frequency info.LfoFrq = data; //Kitaoテスト用 //PRINTF("LFO Frq = %X",info->LfoFrq); break; case 9: // LFO control Kitao更新。シンプルに実装してみた。実機で同じ動作かは未確認。はにいいんざすかいの音が似るように実装。v1.59 if ((data & 0x80) != 0) //bit7を立てて呼ぶと恐らくリセット { info.Psg[1].phase = 0; //LfoFrqは初期化しない。はにいいんざすかい。 //Kitaoテスト用 //PRINTF("LFO control = %X",data); } info.LfoCtrl = (uint)(data & 7); //ドロップロックほらホラで5が使われる。v1.61更新 if ((info.LfoCtrl & 4) != 0) { info.LfoCtrl = 0; //ドロップロックほらホラ。実機で聴いた感じはLFOオフと同じ音のようなのでbit2が立っていた(負の数扱い?)ら0と同じこととする。 } //Kitaoテスト用 //PRINTF("LFO control = %X, Frq =%X",data,info->LfoFrq); break; default: // invalid write break; } return; }
//Kitao追加。v2.60 private void PSG_SetHoneyInTheSky(huc6280_state chip, bool bHoneyInTheSky) { huc6280_state info = chip; info.bHoneyInTheSky = bHoneyInTheSky; }
//Kitao追加 private bool PSG_GetMutePsgChannel(huc6280_state chip, int num) { huc6280_state info = chip; return(info.bPsgMute[num]); }