# rix-midi —— 《大富翁 2》RIX → MIDI 轉換器(by Qwen 3.8:27B) 把 DOS 遊戲《大富翁 2》的 `.RIX` 音樂檔,轉成標準 `.MIDI` 樂譜。 可以一次轉單一個、也可以整個資料夾批次轉。 > 同一批 `.RIX` 用 **adplay / AdPlug** 也可以轉成 WAV(純音訊), > 這裡只處理「轉成 MIDI 樂譜」這件比較有挑戰的事。 --- ## 為什麼 RIX 不能直接「拿出來」當 MIDI? `.RIX` **不是存音符**的格式。它是 **AdLib OPL 晶片**的「機器碼」: 一段由「時鐘節拍(tick)」驅動的下載程式,每個 tick 告訴 OPL 晶片 要寫哪些「暫存器」(頻率、開聲、音量……)。所以 RIX 裡只有 「要讓硬體發出什麼」的指令,沒有現成的音符——得「還原」出來。 ## 原理(我們怎麼還原) 1. **借用 AdPlug** 來 decode + 模擬 OPL 晶片(它已把 RIX 的機器碼 正確地「跑成」一串 OPL 暫存器寫入)。 2. **劫持 OPL 的 write()**:AdPlug 發聲前會走 `Copl::write(reg,val)`, 我們繼承它的模擬器、順手把「每個 tick、每個聲道的 A / B 暫存器」記下。 3. **暫存器 → 頻率 → 音符**(OPL 的標準公式): ``` F-number = A暫存器(低8位) | B暫存器bit0-1(高2位) block = B暫存器 bit2-4 (決定八度倍頻) key-on = B暫存器 bit5 (1=開、0=關) 頻率Hz = F-number × 2^block × 3579545 / (72 × 2^24) × 8 MIDI音符 = round( 69 + 12×log2( 頻率 / 440 ) ) # 69 = A4, 60 = 中音C ``` 4. **節奏一致**:測出 RIX 是「70 tick / 秒」。把 MIDI 速度設成 `tempo = 1e6 × 96 ÷ 70 ≈ 1,371,428 µs/quat`(96 ticks/quat), 讓 1 個 RIX tick == 1 個 MIDI tick,整首的時值就和原曲一致。 5. **樂器**:OPL 不分旋律/和聲,依每個聲道音高的「中位數」粗略指派 GM 樂器(偏低→貝斯、其餘→方波 Lead)。聽起來像原曲的務實選擇。 ### 已驗證 對全部 23 首,**MIDI 時長與同曲 WAV 時長比對:22 首幾乎完全一致(ratio 1.00)**、 1 首(RICHX)因 WAV 尾端多靜音而略短。音高也抽樣驗證正確(中音 C=60)。 --- ## 編譯 依賴 **AdPlug**(RIX/OPL)與 **libbinio**。`build.sh` 會自動找/編它們, 再以**靜態連結**產出自給自足的執行檔(不用設 `LD_LIBRARY_PATH`): ```bash ./build.sh # 在本機 AdPlug=/tmp/AdPlug、libbinio=/tmp/libbinio 的前提下 ``` 想指定位置: ```bash ADPLUG=/path/to/AdPlug BINIO=/path/to/libbinio ./build.sh ``` ## 使用 ```bash ./rix2midi --help # 單一個 .RIX → 同名 .mid ./rix2midi ./RICHU.RIX # 產出 RICHU.mid ./rix2midi ./RICHU.RIX 我的.mid # 指定輸出檔名 # 批次:把「整個資料夾」裡所有 .RIX 轉到「輸出資料夾」 ./rix2midi ../ ../mid # ../ 所有 .RIX → ../mid/*.mid ./rix2midi -v ../ ../mid # 加 -v 看 tick 數等詳細訊息 ``` ## 檔案 | 檔案 | 說明 | |--------------|----------------------------------------| | `rix2midi.cpp` | 轉換器本體(含完整中文註解) | | `build.sh` | 一鍵編譯(找/編 AdPlug、靜態連結) | | `rix2midi` | 編譯出的執行檔 | ## 小限制(MIDI 本質) - 只能還原 OPL「發聲的音色」層次的音高+時值,是**旋律骨架**, 不是完整編曲(沒有真正的和聲/鼓聲)——這是 RIX/OPL 格式的天花板。 - 樂器指派是「猜」的(依音域),不是原曲精確編配。 - 若你想要「聽起來就等於 DOS 原版」的 WAV,用 adplay 那一套(`../wav/`)。