Skip to content

CMF: Multiple changes to align with SBFMDRV#268

Open
dmitrysmagin wants to merge 11 commits into
adplug:masterfrom
dmitrysmagin:cmf-smfmdrv-fix
Open

CMF: Multiple changes to align with SBFMDRV#268
dmitrysmagin wants to merge 11 commits into
adplug:masterfrom
dmitrysmagin:cmf-smfmdrv-fix

Conversation

@dmitrysmagin

@dmitrysmagin dmitrysmagin commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Thanks to a project link (https://github.com/viiri/fmdrv ) indicated in #41 I've got a perfect reference to compare cmf.cpp with.

Using ai-tools (not gonnal lie) I've collected the differences between SBFMDRV and adplug and applied changes that really affect the sounding and thus can fix 'missing notes' bug.
Note that certain FMDRV quirks were only documented but not reproduced because adplug's behavior seems to be more correct.

Vital changes:

  • Meta / SysEx parsing — skip 0xF0/0xF7 SysEx and 0xFF meta events by their encoded length (clamped to the buffer), not by MSB scanning; 0x2F still ends the song.
  • Reset / default instruments — program every melodic channel with the driver's default instrument at reset, matching fmdrv's opl_reset2().
  • F-number generation — use fmdrv's verbatim block_note_tbl + fnum_tbl lookup tables in getFreq() (transpose/pitch-bend applied in 1/64-semitone units).
  • Default bank + instrument selection — re-order cDefaultPatches to fmdrv's bank (preserving carrier output level); make the file's instrument table authoritative with program numbers taken modulo the file's count; default bank is only a zero-instrument fallback.
  • Velocity — apply fmdrv's exact formula (63 - (((vel|0x80)*level)>>8) | KSL) unconditionally on both melodic and percussion paths; drop the sqrt approximation and USE_VELOCITY; fix wrong-register bug (iChannel → iOPLChannel).
  • Voice allocation, note on/off — adopt fmdrv's four-tier find_opl_voice (reuse own released voice → never-used → any released → steal longest, keyed off first); reprogram on owner-channel change rather than patch; note-off keys off all matching voices and retains owner for reuse; 0xC0 invalidates parked voices and live-reprograms sounding ones.
  • Rhythm-mode reset — 0x67 does a full chip reset like fmdrv's switch_mode() instead of toggling one bit; keeps the 0xBD rhythm-enable bit set (adapted from the transient 0xBD = 0x00).

Modifications out of scope of SBFMDRV compatibility:

  • Certain CMF files (presumably, converted from MIDI files) may still contain midi-like track 9 with notes-as-instruments (CHARGEN.CMF). Such files correctly set percussion mode and contain correct percussion instruments, but use notes on track 9 without setting any patch for this track. As result, adplug plays track 9 as a regular melodic track with patch 0. The fix remaps channel 9 to correct cmf drum channels.
  • Edge case hit with CHARGEN.CMF - getrefresh() returns an exact rate 23.125 which is then printed into *.ref via "%.2f" formatter. It turns out that rounding in this case varies between different C++ runtimes. 23.125 would print as 23.12 on the gcc and 23.13 on the msvc (pre-ucrt runtime). Testing failed because of that. Applied trimming to 2 decimal digits after point.

CHARGEN.zip

@dmitrysmagin

dmitrysmagin commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

I've quickly tested some of CMF files I had and noticed that CMF files with midi-style drum track 9 are not that uncommon:
CHARGEN.CMF - the most prominent one, heavy drum usage
Others use drums sporadically and because of that went unnoticed:
END_2.CMF
INTRO.CMF
TUNE.CMF
And some tunes from the game "Magic Candle II":
Magic Candle II\mc206a.cmf
Magic Candle II\mc209a.cmf
Magic Candle II\mc210a.cmf
Magic Candle II\mc212a.cmf
cmf_with_midi_drum_track_9.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants