7

Java で Sega Master System エミュレーターをプログラミングし (この質問は Java 固有のものではありません)、SN76489 サウンド チップ以外はすべて完了しました。このチップがどのように音を出すかは簡単です - 私が抱えている問題は、それを PC/ラップトップ/JVM が実行されているものなら何でも再生できる形式に変換することです。

手順は次のとおりです。

SN76489 はおよそ 221khz のサンプル レートで実行されるため、出力される波の周波数が最大 110khz になることを意味します (ただし、実際には、これほど高くなるものはないと思います)。したがって、ダウンサンプリングする前にローパス フィルターを実装する必要があります。

次に、それを 44.1khz にダウンサンプリングして、オーディオ ライン (私の場合は Java ソース データ ライン) 経由で出力できるようにします。

このためには、ローパス フィルターを 22.05khz に設定する必要がありますが、問題は、ローパス フィルターが実際にどのように機能するか (数学的に言えば) わからないことです。1つ書くことができるようにするためにこれが必要です。

現在、私のサウンド チップは 0.2 秒のバッファを作成し、上記のように 221khz でサンプルを保存します。これを理解しているので、ダウンサンプリングできますが、最初にローパス フィルターを適用せずにダウンサンプリングすると、結果のサウンド ストリームにエイリアシング グリッチが発生する可能性があることを理解しています。

これを行うための最も単純な数学的なアルゴリズムを推奨できる人はいますか?変数が関係しているため、ローパスは決して「正確」ではないことを理解していますが、私の脳にとって十分に馬鹿げた健全な説明が必要です(実際にはそうではありません)以前にウェーブ処理を扱った) を理解する必要があります。

SN76489 は 3 つの方形波と 1 つのノイズ チャネルを同時に出力します。これらは合計され、ミキサー/アンプに出力されます。チェーンのこの段階で、ローパス フィルターを実行してから、ダウンサンプリングして波を増幅します。人々が私に与えることができるどんな助けも大歓迎です。背景を読む必要があることは理解していますが、「何」を読む必要があるか知りたいです。どうもありがとう。

更新:私は最終的により単純なアプローチを思いつきましたが、まだ十分ではありません。SN76489 は、レジスタ値から各トーン チャネルを生成することによって機能します。1 の極性が出力され、値が減分されます。値が 0 になるまで、値がリセットされ、極性が -1 に切り替えられます。 . 次に、この値にボリュームを掛けて、そのサンプルの最終的な振幅を取得し、他のチャンネルと合計します。

ここでは、必要なナイキスト制限を超える方形波を生成するレジスタ値が生成されないようにするだけです。これにより、はるかに優れた信号が得られますが、まだブーンという音やポッピングが残っています。可能な最大周波数を 18,473Hz にする必要があるため、理由はわかりません。このポッピング/バズ音は、チップがチャンネルをある周波数から次の周波数に切り替えるときに、現在の波形が完全に終了しないためでしょうか? 例として、チップは 1111 を出力し、次に 00 を出力します - 完全な 4 つのゼロの代わりに新しい周波数に切り替えます - これはエイリアシングを引き起こす可能性がありますね?

4

2 に答える 2

2

編集:私はあなたの質問に答えるフィルターの実装を以下に含めました。ただし、このような高いサンプルレートで信号を使用して高次フィルターを実装すると、1 秒あたり何百万もの操作が消費されます。最初にチップの出力のスペクトル分析を行うのが最善かもしれません。数 kHz を超える音のエネルギーがない場合、アンチエイリアシング フィルターは処理リソースの無駄遣いです。適度に高い周波数までのエネルギーがある場合でも、最初に信号をデシメーションしてから、第 2 段階のデシメーションを適用する前にフィルタリングする価値がある場合があります。補足として、44.1 kHz よりもはるかに低いレートにデシメートすることもできます。おそらく、マスター システム エミュレーターのサンプル レートは 8 または 10 kHz で問題ありません (ここでは、ハイファイについて話しているわけではありません)。とにかく、指定したサンプリング レートとカットオフを使用してローパス フィルターを実装する方法についての質問に答えます。. .

まず、ローパス フィルターを設計します。matlab の decimate 関数は私の耳には良さそうに聞こえるので、この例ではそのアプローチをコピーします。ドキュメントには次のように書かれています

間引きされたベクトル y の長さは、入力ベクトル x よりも r 倍短くなります。デフォルトでは、decimate は、カットオフ周波数が 0.8*(Fs/2)/r の 8 次ローパス チェビシェフ タイプ I フィルターを使用します。順方向と逆方向の両方で入力シーケンスをフィルター処理して、すべての位相歪みを除去し、効果的にフィルター次数を 2 倍にします。

Cheby フィルターは、通過帯域のリップルが少し増える代わりに、Butterworth の設計よりも急峻な除去を行うため、適切な選択です。リアルタイム システムで双方向の IIR フィルタリングを実行することはできませんが、目的には問題ありません。次の Matlab コードを使用してフィルター係数を作成できます。. . .

sr = 221e3;
srDesired = 44.1e3;
order = 8;
passBandRipple = 1; %//dB

Wp = 0.8 * (srDesired/2) / (sr/2);

[b,a] = cheby1 (order, passBandRipple, Wp, 'low');

freqz(b,a,[],sr, 'half');

sos = tf2sos(b,a)

これにより、次のような応答を持つ 8 次 IIR フィルターが得られます。これは私たちが望んでいるように見えます。このアプリケーションでは、位相応答は重要ではありません。間引きの前にナイキスト限界に近い信号を十分に減衰させるため、カットオフは 0.8* 22.050 kHz です。

ここに画像の説明を入力

最後の tf2sos コマンドは、作成したばかりのフィルターを、双二次フィルター セクションのカスケードを使用して実現できる 2 次セクションに変換します。このコマンドの出力は次のとおりです。. .

セクションA

b=1.98795003258633e-07、3.97711540624783e-07、1.98854354149782e-07、
a=1 -1.81843900641769、0.835282840946310

セクション B

b=1、2.02501937393162、1.02534004997240、
a=1、-1.77945624664044、0.860871442492022

セクション C

b=1、1.99938921206706、0.999702296645625、
a=1、-1.73415546937221、0.907015729252152

セクション D

b=1、1.97498006006623、0.975285456788754、
a=1、-1.72600279649390、0.966884508765457

これで、フィルター カスケードの各バイクアッド ステージにこれらのフィルター係数を使用できます。このフィルターは、次の例のようなコードを使用して実現できます。これは C コードですが、かなり簡単に Java に変換できるはずです。以下のコードには a0 係数がないことに注意してください。上記の 2 次セクションは、a0 が常に 1 になるように正しく正規化されています。そのままにしておきます。

//Make the following vars private members of your filter class
// b0, b1, b2, a1, a2 calculated above
// m1, m2 are the memory locations
// dn is the de-denormal coeff (=1.0e-20f) 

void processBiquad(const float* in, float* out, unsigned length)
{
    for(unsigned i = 0; i < length; ++i)
    {
        register float w = in[i] - a1*m1 - a2*m2 + dn;
        out[i] = b1*m1 + b2*m2 + b0*w;
        m2 = m1; m1 = w;
    }
    dn = -dn;
}

このフィルターのクラスを作成してから、a と b の値を上で指定した値に設定して、4 つの個別のクラス (フィルターごとに 1 つ) をインスタンス化する必要があります。次に、あるステージの入力を次のステージの出力にフックして、カスケードを提供します。

于 2012-04-18T09:27:41.513 に答える
1

http://en.wikipedia.org/wiki/Butterworth_filter

C コード ジェネレーター: http://www-users.cs.york.ac.uk/~fisher/mkfilter/ (Java に簡単に変換できるはずです)

于 2012-04-18T08:37:03.920 に答える