Currently when the VGM chip clock frequency does not match the frequency of the sound expansion’re playing it back on, we don’t compensate for that.
As a result sometimes songs play back at a pitch slightly too high or too low. E.g. the X1 Turbo’s YM2151 OPM and YM2149 PSG are clocked at 4 MHz while on the MSX they are clocked at 3.58 MHz, which makes songs play two semitones too low (e.g. Sorcerian).
This is normally acceptable, so we’ve been able to get by with this trade-off, however it becomes a problem when playing back on multiple chips with a different base frequency, e.g. when playing multi-PSG tracks on the internal PSG (3.58 MHz) and an OPNA (4 MHz). Also on chips with PCM playback the PCM will play too slowly relative to the music, this happens with e.g. Psycho Soldier.
However we currently already do frequency translation in one place: for the YM2612 OPN2 emulation on YM2151 OPM and YM2608 OPNA. This means that an implementation for it is already exists. Though it currently hardcodes the frequencies, calculating the factor dynamically is only a matter doing of a 32-bit division when connecting the driver.
For chips which specify note numbers like the YM2151, the translation involves an addition by log2(targetfreq) - log2(sourcefreq). For chips which specify frequencies like the other FM chips it needs a multiplication by targetfreq / sourcefreq. Period-based chips need a multiplication by sourcefreq / targetfreq.
I think it’s best to start with a frequency translator aimed at the OPN series. This could then be used for frequency translating the PSG, OPN, OPNA, OPN2 and OPNB which should cover the most useful bases. After that, one for the YM2151 and the OPL series (Y8950 and OPL(2) mostly).
It’s probably good enough to adjust just the note frequencies. Envelope generators are also clocked of course, but the frequency differences are usually low enough that the slightly lower or higher rates aren’t really audible.
It should be implemented like an intermediate driver which is connected between chip and driver only if the frequency deviates by more than a few %. This so that it doesn’t impact performance unnecessarily. I think it would also be good if this function can be enabled / disabled by a command line parameter, in case there are issues or the user would like to hear the untranslated version. By default it should probably be off, perhaps until the implementation for all relevant chips is completed.