
2026/09/08 23:54
ZX スペクトラム:1 ビットサウンドの実験
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
著者は、古いシステム(Apple II や IBM PC など)に着想を得た巧妙なソフトウェア技術を活用し、ZX スペクトラムの 1 ビットスピーカーからトーン、コード、マルチチャンネルサウンドを生成するための高度な音声ルーチンを開発した。主な成功は、パルス符号変調(PCM)とソフトウェクミックスを組み合わせて、リッチでマルチチャンネルのオーディオを実現したことにある:3 チャンネルのデータを合計し、その周波数カウンタを結合させた後、総和の上位ビットが単一のスピーカーを駆動することで、アルペジオ手法よりも充実した音質を生成する。このアプローチは、正確な Z80 タイミング(データ書き込み間隔に 219 サイクルの遅延を入れることで音声の不具合を防ぐ)と、1 バイトあたりに 8 サンプルを格納して約 16 kHz の再生速度を実現するためのビット操作ルーチン(例:RRC/RLC とマスク処理)に依存している。代替 PCM ストラテジーである「アルペジオ」は、単一のトーンを素早く連続させてコードをシミュレートするが、特徴的な「ブザー音」を生じさせ、ミックス手法よりも音の充実度は低い。パルス幅変調(PWM)も試行したが、ハードウェアタイマーが存在しないため失敗に終わった:手動でのサイクルカウントではリセットアーチファクトと高いピッチのサイレンのような音が現れ、これはシステム固有かエミュレータ依存の要因かもしれない。プレジェネレートされた波形ファイルやサンプルシャッフルを用いた実験は、アルペジオよりは性能が良かったが、合計手法には及ばなかった。PCM、アルペジオ、ミックスのための実用可能な実装は GitHub 上でオープンソースコードとして公開されており、現代のマルチスピーカーシステムを持たない制約のあるレガシーハードウェア上で創造的な音声シンセサイズを探求する今後の開発者にとって堅固な基盤を提供している。
本文
ZX スペクトラム:1 ビットスピーカーによる高度なサウンド技術の実証と課題
この記事は、ZX Spectrum Tour の一環として開発された 1 ビットビイパー(スピーカー)向けサウンドルーチンの実装記録です。Apple II や IBM PC スピーカーで得られた知見を踏まえ、限られたハードウェアリソースの中で可能なオーディオ技術を探求しました。
今週の成果と概要
- マルチチャンネルサウンドの実現: 1 ビットスピーカーによるコード演奏(メロディの再生)に成功しました。
- PCM と PWM の実験:
- 単純な 1 ビット PCM(パルス符号変調)の再生を再現。
- 高度な PWM(パルス幅変調)技術についても実験を試みたが、完全な実成に至らなかった。
- 失敗も記録する: この週は失敗が多く含まれており、包括的なガイドというよりは「成功と失敗の記録集」として捉えてください。
パルス符号変調(PCM)とパルス幅変調(PWM)
1. PCM(パルス符号変調)とは
波形を離散的な値としてサンプリングし、それをハードウェアに送信してスピーカー電圧に変換する技術です。
- 基本原理:
- 波形を 0 から最大値までの一連の値としてサンプリング。
- サンプリングレートが高いほど周波数制御の粒度が細かく、ビット深度(振幅制御)が高いほど質が良い。
- ナイキストの定理: サンプルングレートの半分を超える周波数は正確に再現不可能。
- 一般的な仕様:
- 高品質:16 ビット @ 44 kHz
- 基本用途:8 ビット @ 8 kHz で十分対応可能。
2. ZX Spectrum の制約と PCM の実装
Spectrum は 1 MHz クラスのメモリアクセス速度を持ち、3.5 MHz(48K システム)での動作を想定しています。
- サンプリング計算:
- 目標再生周波数:16 kHz / 秒(20,480 サンプル/分)。
- 書き込み間隔:219 サイクル待つ必要あり。
- データ効率:1 バイトあたり 8 サンプルのパッキングが可能(約 2 KB/秒の消費)。
- 音質上の課題:
- スピーカーは「ON/OFF」のみ(1 ビット)のため、本来の PCM は劣化します。
- 歪みとノイズ: アンプが破損するレベルの歪みが発生し、低音量でも目立つ雑音が混じる。
- 実装の難易度: 特別な課題はなく、サイクルカウントによる CPU コントロールでまともな再生が可能。
3. PWM(パルス幅変調)とは
電気信号の「ON」状態の時間長を音声信号の強さに対応させる技術です。
- 特徴:
- PCM よりもアナログ的な挙動に近いが、1 ビットデータによる積極的な制御。
- 物理的にスピーカーは数十マイクロ秒で動作するが、ナノ秒単位で信号を切り替えることでより精密な音量制御が可能。
- 実装の困難さ:
- IBM PC は専用タイマーがあり、7 ビット PCM データを送れるが、Spectrum はサイクルカウントが必要。
- 完全な PWM システムの実成には失敗したが、技術的アプローチは堅牢であるとの見解。
Z80 におけるサイクル正確な遅延の実装
Z80 の「T ステート」カウントは不規則なため、単純な指令数を積算するだけでは正確なタイマー制御が難しいです。以下のようなテクニックでタイミングを調整します。
基本指令のサイクル数
| 指令パターン | サイクル数 | 特徴・用途 |
|---|---|---|
(1 バイト) | 4 | ベース単位の遅延。全ての 1 バイト 8 ビット操作同様。 |
/ 構造化 16 ビット | 6 | 2 サイクル分の不整合吸収に使用可能。 |
(直接命令) | 7 | 総待機時間が長い場合、サイクル精度低下対策に有効。 |
(条件付きでも) | 10 | 正確なタイミングループ内で判断処理に優れる(JR/DJNZ より)。 |
遅延ループの設計パターン
- 短時間待機:
と組み合わせて 2 サイクル単位を吸収。INC HL - 長時間待機:
を主体にし、他の指令で不整合を埋める形を採用。DJNZ- 例:
は 13n+2 サイクルを消費。LD B,n; LBL: DJNZ LBL
- 例:
PCM 再生ルーチンの詳細実装
各バイトから 8 サンプルを抽出し、上位ビットから再生する方式を採用しました。
アルゴリズムの流れ
- メインループの開始: タイミング上の真ん中に位置して処理を開始。
- 中断管理: エントリー時とリターン時に
/DI
を使用。EI - データ処理:
- HL にポインタ、DE にバイト数を設定。
- バイトを読み込み、右回転 (
) でRRCA
(ビット 4) にターゲットを合わせる。$10
- サイクル調整:
- 最初のビット出力までに必要な合計サイクル数を計算。
/INC HL
やDEC HL
ループで正確なタイミング(219 サイクル)に合わせ。DJNZ
コードスニペット:PCM 再生ループ
以下のアセンブリコードは、219 サイクルの遅延を正確に扱いながら 1 ビット PCM を出力する実装です。
pcmout: di .lp: ????????????? ; バイトから 8 サンプルを再生し、最終的に... out ($fe),a ; +11 (219) ...最後のビット出力 ;; バイトカウンターを調整してループに戻る dec de ; + 6 ( 6) ld a,d ; + 4 (10) or e ; + 4 (14) jp nz,.lp ; +10 (24) ;; おしまい;中断を再活性化して戻す ei ret
バイト処理とループの最適化
- 回転処理: 8 ビットデータのうち、特定のビットだけを分離するため
を使用(3 回連続)。RRCA - サイクル補正:
- ループ内での処理後、出力までの間に 141 サイクル分の待ち時間が必要になる。
- これを満たすためのループ構造:
inc hl ; + 6 (73) dec hl ; + 6 (79) ld b,9 ; + 7 (86) jp 1F ; +10 (96)
マクロによる自動化 (SJASM)
手作業での反復処理を避けるため、マクロを使用しました。
repeat 7 rlc c ; 次のビットへ移動 ld c,a ; カウンター保存 and $10 ; サウンドビット($10)のみ分離 or $01 ; テスト用の出力 ld b,1 ; 遅延カウンタ設定 dec b ; 補正 ld b,13 ; ループ回数設定 djnz 1B ; 正確なサイクル数待機 (182 サイクル) out ($fe),a ; ビット出力 endrepeat
- 評価: 音質は未だに悪く、ノイズ除去による改善は限定的でしたが、機能としては成功しています。
コード演奏(マルチチャンネルサウンド)
アルペジオやソフトウェアミキシングなど、当時の一般的なマルチチャンネル手法を実装しました。
1. アルペジオ(Arpeggiation)
単一チャンネルに対し、周波数を切り替えてコード効果を生み出す手法です。C64 や Amiga でもよく使われていました。
- 実装概要:
- 外部ループ内で短時間のトーン再生ルーチンを反復。
- メモリ境界($4000~$7FFF)を避けるため、作業領域を
に設定。$8100
- コード例:
chord: ld (.f1),hl ld (.f2),de ld (.f3),bc di ld b,$18 ; ノート数制御 1 push bc ; 周波数保存 ld bc,$0d0 ; サイクルカウント調整 ld de,$0000 ; ポインタ初期化 .f1: call sound ; ノート 1 .f2: call sound ; ノート 2 .f3: call sound ; ノート 3 pop bc ; 周波数復元 djnz 1B ; ループ完了待ち ei ret - 音質評価:
- 少しザラつきがあるが、明確なコード(和音)として認識できる。
- ノート変更時のタイミング調整により改善可能。
2. ソフトウェアミキシング
複数の音声チャンネルの信号を足し合わせ、1 ビットスピーカーに出力する手法です。
-
基本原理:
- 3 つの周波数カウンターを維持し、それぞれの値を更新。
- 上位ビットを合計し、その値(0~3)に基づいてスピーカーの ON/OFF を決定。
- Apple II と違い、Spectrum では直接スピーカー値を書き込めるため柔軟。
-
実装コード (マクロ版):
macro count_channel count,freq ld hl,(count) ; カウンタ読み出し ld de,(freq) ; 周波数読み出し add hl,de ; 加算更新 bit 7,h ; 符号ビット確認 jp m,1F ; 負なら分岐処理 1 inc a ; カウンタインクリメント dec de ; 周波数デクリメント 2 ld (count),hl ; 結果書き込み endmacro chord: di ; インターラプトオフ .lp: xor a ; 合計値クリア count_channel .counter1,freq1 count_channel .counter2,freq2 count_channel .counter3,freq3 add a,a ; 合計計算 (A を倍加) and $10 ; 必要なビットのみ抽出 or $0f ; テスト出力 out ($fe),a ; スピーカー出力 dec bc ; ループ制御 ld a,b; or c ; カウンタ終了判定 jp nz,.lp ei ret -
音質評価:
- アルペジオよりも遥かに優れた音質。
- 音が豊かで、1 ビットシステム特有の粗さを感じさせない。
- 結論: 限られたリソースで高品質なサウンドを再現可能であることが証明された。
他のアプローチと PWM の再考
アルペジオやミキシング以外にも実験を試みたが、いくつかの課題が残りました。
1. 周波数切り替え速度に関する検証
- 課題: 周波数を頻繁に切り替えると、波形が歪んで音程から外れる。
- 理論: 完全な波形を生成できないため。
- 解決策案: 事前に波形データを生成し、PCM プレイヤーで再生する(Apple IIgs の手法)。
- しかし、低周波数領域での時間的な不均衡(長いパルス)や、タイムスライスの影響による歪みは残る。
2. サンプルシャッフル(Apple IIgs 的アプローチ)
- アイデア: デジタルチャンネルのサンプルを交互に出力するのではなく、物理的なサンプル単位でシャッフルして再生する。
- 結果: 悪くはないが、チャンネルミキシングほどの「清潔感」やまとまりは得られない。
3. PWM(パルス幅変調)の実行不能性
ハードウェアタイマーを利用した正確な PWM は ZX Spectrum では困難でした。
- 物理的な制約: スピーカーの動作速度(約 50 マイクロ秒)に合わせ、NOP コードを挿入してパルス幅を制御する必要があるが、これが実験的に失敗しました。
- 課題: パルスの終了後にスピーカー状態をリセットするため、「ブーイング音」といった高周波ノイズが発生する。
今後の展望と結論
- 実用的な解決策: Fuse エミュレータなどの高度なソフトウェアシミュレーターを使用することで、ローパスフィルター効果を適用し、より正確な音を再現できる見込み。
- 技術的限界: 純粋なソフトウェアのみでは、ハードウェアの物理的な制約(増幅器の欠如など)を完全に克服するのは困難。
- まとめ: 限られた 1 ビットスピーカーであっても、アルペジオやソフトウェアミキシングを用いることで、驚くほど良好なオーディオ体験を実現することができました。
注: 今回のプロジェクトで使用したコードは GitHub リポジトリにアップロードされており、ビルドファイルや音声サンプルを参照して確認可能です。