CのNESのゲーム開発。第14章から第16章。 音を扱う

このパートでは、サウンドの操作に関する基本情報。 NESのオーディオサブシステムは非常に低レベルであり、その説明は非常に紛らわしく、特定の用語を使用しているため、場所での説明はあまりわかりにくい場合があります。
<<<前 次>>>
画像
出所


サウンドを使い始める


NESプラットフォームが提供するツールの概要。 ただし、さらに上位レベルに進み、Famitrackerライブラリを使用します。


コンソールのサウンド機能を感じる最も簡単な方法は、SnoBrowが開発したサウンドテストデモを使用することです。 すべてのエミュレーターとの互換性はありませんが、FCEUXはサポートされています。


[選択]ボタンをクリックすると、サウンドチャンネルが切り替わり、[開始]でそれらがオンになります。 利用可能な4つのチャネル:
1-蛇行1
2-蛇行2
3-三角信号
4-ノイズ


サウンドコプロセッサ(APU)は、レジスタ$ 4000〜$ 4017で制御されます。


4000ドル-4003ドル=蛇行1
$ 4004- $ 4007 =蛇行2
$ 4008- $ 400B =三角信号
$ 400C- $ 400F =チャネルノイズ
$ 4010- $ 4013 = DMC、デルタ変調チャネル
4015ドル=チャネル管理
4017ドル=フレームカウンター


蛇行1
チャネルは、DDLC VVVVスキームに従ってレジスタ$ 4000のビットによって制御されます。


D-デューティサイクル。 10-スムーズなサウンド、01または11-トレラント、00-不快
LおよびC-チャネル操作モード
V-ボリューム


チャンネルモード

Lは0です。 C = 0:
エンベロープジェネレーターを取得します。 ボリュームはフェードし、Vはサウンドの持続時間に対応します。


Lは1です。 C = 0:
これで、Vは、音量を最大にして音の繰り返し間隔を調整します。


Lは0です。 C = 1:
Vはチャンネルの音量を制御します。 メモの期間は、レジスタ$ 4003のLビットによって制御されます。


Lは1です。 C = 1:
Vは音量も制御します;ノートは、4000ドルのレジスターへの新しい書き込みまで連続して再生されます。 通常、サウンドの持続時間はフレームカウンターに関連付けられ、トランジションは同じフレームごとのボリューム減衰で行われます。


ボリュームをゼロにすることでチャネルがオフになります-レジスタに4000を書き込む0x30。


$ 4001-周波数スイングレジスタ、ビット:EPPP NSSS
E-効果をオンにします
P-スイング期間
Nは方向です。 0-ダウン、1-アップ
Sは別の期間制御ですが、別の


この効果が有効な場合、ノートは最後までしか再生されません。 L = 1と組み合わせて、4000ドルのレジスタで興味深い効果を得ることができます。 ビットNは、エフェクトがオフの場合でも低周波数に影響します。 一部のゲームはこのハックを使用します。


$ 4002-ノートの頻度を設定するタイマーの最下位8ビット
$ 4003-LLLL LTTT-5ビットのノートデュレーションタイマー(パラメーターLとCの少なくとも1つが$ 4000にリセットされている場合のみ動作します)および周波数タイマーの3つの最上位ビット。 持続時間タイマーが短いほど、音は長くなります。 何らかの理由で、ノートの頻度は000〜00001000に制限されています。 より高い音、つまり短い振動周期で音を出すことはできません。 しかし、それは厄介なきしみになります。


アドレス4004〜4007ドルには、2番目のチャネルのまったく同様の制御レジスタがあります。


4008ドル-400億ドル-デルタチャネル


4008ドル-CRRR RRRR。 C-常にメモのフラグ。 Rはわかりにくいレジスタです。 理論的には、ノートの持続時間に影響するはずです-常時オンの場合は0xFF、オフの場合は0x80。 ただし、ここで0x7Fを設定すると、期間はレジスタ$ 400BのLビットで調整されます。
4009ドル-使用しません。
$ 400A-LSBビット
400億ドル-LLLL LTTT。 5ビットの持続時間と3ビットの周波数タイマー。 ロジックは蛇行チャンネルと同じです。
チャンネルの音量は調整できません。 さらに、彼は同じ設定で方形波より1オクターブ低い音を再生します。 ここには周波数制限がないので、非常に高いきしみ音を得ることができます。


$ 400C- $ 400F-ノイズチャンネル
$ 400C-xxLC VVVV-蛇行と同じですが、デューティサイクルはありません。
$ 400D-未使用
400ドル-ZxxxTTTT。 Zはノイズのタイプです。 0はホワイトノイズを与え、1つはクラングを与えます。 T-ノイズ周波数タイマー;値が低いほど、トーンが高くなります。
$ 400F-LLLL Lxxx。 音符の長さ。 レジスター$ 400CのビットLとCは、蛇行のように機能します。
チャネルをオフにするには、ボリュームをリセットする必要があります($ 400C = 0x30)


DMCチャネルを使用すると、メモリから非圧縮オーディオを再生できます。 サンプリング周波数は調整可能であり、ここで妥協する必要があります。 高い周波数は適切な品質を提供しますが、サウンドを保存するために適切な量のメモリが必要です。 低周波は品質を損ない、不快な口sを追加します。 この楽器は、個々の単語の発声(「ファイト!」)やリールのような短い音に適しています。


4010ドル-ILxx RRRR。 チャネルの中断、L-サンプルのループ、R-周波数が含まれます。
$ 4011-xDDD DDDD-ボリューム。 このレジスタを適切なタイミングでプルすると、原則として、適切な音質を得ることができます。
$ 4012は、サンプルの先頭のアドレスです。 この方法で8ビットが16に追加されます。11AAAAAA AA00 0000です。したがって、使用可能なアドレスの範囲が$ C000から$ FFC0になります。
$ 4013-サンプル期間(バイト単位)。 補足もされていますが、最大12ビット:LLLL LLLL0001。許容サイズは$ 11から$ FF1バイトです。 これで十分でない場合は、通常、複数のサンプルを組み合わせて連続して再生できます。


4015ドル-xxxDNT21。 チャンネルが含まれています。
D-DMC
N-ノイズ
T-三角チャンネル
2-2番目の蛇行
1-最初の蛇行


$ 4,017-理論的にはこれはフレームカウンターですが、めったに使用されません。 ほとんどのゲームは、起動時にここに0x40を書き込み、これに限定されます。


DMCは、対応するビットがレジスタ$ 4015に書き込まれたときにオンになり、サンプルの終了後にオフになります。 もう一度プレイするには、ビットをもう一度引く必要があります。
残りのチャンネルは、レジスターのアドレスでシニアを記録することでオンになります(最初の方形波に4003ドルなど)。 そこにすべてのフレームを書き込むと、不快なクリックが発生します。


一部のマッパーは独自のサウンドチャネルを追加します。 VRC7には一般にFMシンセサイザーが搭載されていますが、1つのゲームでのみ使用されています-ラグランジュポイント。


PCMサウンドは原則として使用できますが、私は使用する必要はありません。 これは、メモリとプロセッサ時間の両方で非常に高価です-静的スプラッシュ画面を除いて十分なリソースがあります。


簡単なツイーターを作ってみましょう。 以前のレッスンのどのデモにも追加できます。 オーディオチャンネルがオンになっていることを確認してください。


シンプルな効果
*((unsigned char*)0x4015) = 0x0f; //  if (((joypad1old & START) == 0)&&((joypad1 & START) != 0)){ *((unsigned char*)0x4000) = 0x0f; *((unsigned char*)0x4003) = 0x01; } //    if (((joypad1old & START) == 0)&&((joypad1 & START) != 0)){ *((unsigned char*)0x4000) = 0x0f; *((unsigned char*)0x4001) = 0xab; *((unsigned char*)0x4003) = 0x01; } //    if (((joypad1old & START) == 0)&&((joypad1 & START) != 0)){ *((unsigned char*)0x400c) = 0x0f; *((unsigned char*)0x400e) = 0x0c; *((unsigned char*)0x400e) = 0x00; } 

このコードとSoundTestをいじってみると、すぐにすべてを忘れてFamitrackerを開いてください。


音楽を追加する


有名なNerdy Nightsチュートリアルでは、独自の音楽エンジンを作成する必要があることが示唆されました。 FamitrackerとFamitone2のすべてがすでに書かれていることがわかります。


ファミトラッカー 。 最新のバイナリバージョンで対応します。 Nuance:場合によっては、MIDIファイルをインポートする必要があります。その後、バージョン0.4.2に制限する必要があります。 インポートは常にうまく機能せず、このバージョンの後、最終的に壊れました。


Famitrackerは何でもできますが、ゆっくりと面倒です。 したがって、 Famitone2を使用することをお勧めします。


いくつかの制限がありますが、回避することができます。 ボリュームコントロールはありませんが、追加の楽器を作成することでシミュレートできます。 一部の場所では、エンベロープのボリュームが最大値よりも小さくなります-これも他のツールを使用します。 テンポの変更、ループ、減衰以外の効果はありません。


...ここでは、私にはまったく知られていないあらゆる種類の音楽の繊細さの実装について説明します。 オリジナル


主な制限は、C1からD6までのノートの範囲が狭いことです。 アセンブラーのソースコードで制限を変更できます。 音の周波数をタイマーサンプルに変換するため表を次に示します。


優れたトラッカービデオ:



...トラッカーのボタンの説明を省略します。


主なことは、すべてのトラックを1つのファイルにまとめることです。 次に、アセンブリに変換されます。
text2data TestMusic.txt -ca65
そして、初期化コードに挿入します:


 .include "MUSIC/famitone2.s" music_data: .include "MUSIC/TestMusic.s" 

初期化コードにはdetectNTSC:ラベルがあります。これにより、コンソールバージョン-NTSC(米国/日本)、または欧州標準PALを確認できます。 タイミングに影響するため、これは重要です。


Famiton構築IFの操作のために、reset.sに追加するものがあります。


非表示のテキスト
 FT_BASE_ADR =$0100 ;   ,        .define FT_THREAD 1 .define FT_PAL_SUPPORT 1 .define FT_NTSC_SUPPORT 1 FT_DPCM_OFF = $C000 FT_SFX_STREAMS = 1 .define FT_DPCM_ENABLE 0 .define FT_SFX_ENABLE 0 ;  DPCM  SFx 

Famitone2.sの関数のラベル:


非表示のテキスト
 .export _Reset_Music, _Play_Music, _Music_Update ... _Reset_Music: lda NTSC_MODE ldx #<music_data ;   ldy #>music_data ;   FamiToneInit: ... _Play_Music: FamiToneMusicPlay: ... _Music_Update: FamiToneUpdate: 

これで、関数プロトタイプを宣言し、コードからそれらを取得できます。


 void Reset_Music(void); void __fastcall__ Play_Music(unsigned char song); //   fastcall    ,     void Music_Update(void); 

そのため、Reset_Music()を呼び出してトラックをオンにし、Play_Music(1)で別のトラックに切り替えることができます。 フレームごとに1回、Music_Update()を呼び出します。 一時停止または停止機能をインポートすることもできますが、ボリュームコントロールを使用して手動でこれを行う方が簡単です。
*((unsigned char*)0x4015) = 0;
Music_Updateの呼び出しをスキップして、音楽をオフにできます。 再度開始するには、$ 4015に0x0Fを書き込み、Music_Updateを再度呼び出す必要があります。 Famitoneは一般的な初期化中にのみ有効にするため、ノイズチャネルは手動で再起動する必要があります。


 *((unsigned char*)0x4015) = 0x0f; //   *((unsigned char*)0x400c) = 0x30; //    *((unsigned char*)0x400f) = 0x00; //    

...または、ハンマーで叩きながら、ライブラリ関数の停止と再生を使用します。


これは、音楽を使用した以前のデモです。
Dropbox
Github


音響効果


各エフェクトはFamitrackerの個別のトラックで、1つのファイルにインポートされていました。


効果の持続時間を除き、制限があります。 各チャネルはC00の効果で完了し、1つのNSFファイルにすべてを保存する必要があります。 Famitone2 / toolsに配置し、アセンブラーに変換する必要があります。
nsf2data SoundFx.nsf -ca65
そして、結果のSoundFx.sをreset.sに含めます。


 sounds_data: .include “MUSIC/SoundFx.s” 

次に、エンジンの効果をオンにします。
.define FT_SFX_ENABLE 1
インポート用の適切なラベルも追加する必要があります。


 .export _Play_Fx _Play_Fx: ldx #0 FamiToneSfxPlay: 

SoundFxチャネルは1つだけ使用されるため、その番号はXで記述されます。 さらにチャネルがある場合は、FamiToneSfxPlayを呼び出す前に、この行を削除して目的のチャネルをオンにする必要があります。


この効果は、Play_Fx()関数を呼び出すことでオンになります。引数はチャンネル番号であり、NSFファイルのトラック番号でもあり、最初から番号が付けられます。


 void __fastcall__ Play_Fx(unsigned char effect); 

デモには3つの効果があります。1つ目はジャンプ、2つ目と3つ目はそれぞれ上ボタンと下ボタンです


 if (((joypad1old & UP) == 0) && ((joypad1 & UP) != 0)) Play_Fx(1); 

[スタート]ボタンは、バックグラウンドトラックを切り替えます。
Dropbox
Github


このチュートリアルでは、DPCMサンプルのトピックについては説明しません。 カートリッジには大量のメモリが必要ですが、エフェクトには優れています。 必要に応じて、wavファイルをFamitrackerにインポートしてDMCに変換し、Famitone2からアクセスできます。 いつかこのトピックに関するセクションを書くと思います。



Source: https://habr.com/ru/post/J349742/


All Articles