Vampi's FM Tools
vgm2opm and others
Technical discussion which is not directly related to VGM files. Talk about Hardware and Software.
Moderator: Staff
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
Vampi's FM Tools
Hello! I have taken a look at vgm2opm and decided it is possible to improve it by adding support for more chips, and possibly optimizations and fixes. I've also written some tools for dumping existing voice files, such as dmp, ins, opm and others.
Upon request I might write conversion tools, for example dmp -> opm. Currently I don't plan this, as I don't have a need for it.
Anyway, it is currently in alpha and fairly untested, it supports the OPN* chips for now, you may have a look here: https://github.com/vampirefrog/fmtools
Upon request I might write conversion tools, for example dmp -> opm. Currently I don't plan this, as I don't have a need for it.
Anyway, it is currently in alpha and fairly untested, it supports the OPN* chips for now, you may have a look here: https://github.com/vampirefrog/fmtools
- ValleyBell Offline
- Posts: 4791
- Joined: 2011-12-01, 20:20:07
- Location: Germany
A small note about your vgm2opm tool: I don't think it is correct to remap the detune values.
I'm not 100% sure, but I think VOPM uses the raw value (0..7). The conversion table is something specific to TFI files and the detune conversion was a bug in Shriu's vgm2opm, because it was a modified vgm2tfi.
I'm not 100% sure, but I think VOPM uses the raw value (0..7). The conversion table is something specific to TFI files and the detune conversion was a bug in Shriu's vgm2opm, because it was a modified vgm2tfi.
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
Thank you, I will have a closer look. I've also added an FM toy, for now it is linux only, JACK + ALSA. It loads voice files and has sound chips on different MIDI channels, so you can compare the sounds. Channel 1 is YM2151, Channel 2 is YM2203 and so on. Right now it only works with YM2151 and .OPM files. It will be useful for testing. I will also require someone to help me with the Windows version, perhaps a very simple gui with one dialog and a MIDI device selector or something, I dunno.
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
I've added YM2203 to fmtoy.
I also need some help converting MIDI notes to YM notes for varying chip clocks. I am not sure how the Frequency registers work on OPN. It sounds fine if I leave the "F-Num 2" 3 bits alone and simply fill in the "F-Num 1" 8 bits and the octave. It is not clear what I have to stick in there.
I also need some help converting MIDI notes to YM notes for varying chip clocks. I am not sure how the Frequency registers work on OPN. It sounds fine if I leave the "F-Num 2" 3 bits alone and simply fill in the "F-Num 1" 8 bits and the octave. It is not clear what I have to stick in there.
The frequency to fnum formula is described in the application manual. It’s essentially the same on all the OP* FM chips (though the precision -nr of bits- and multiplier may be more or less), except for the OPM which uses note numbers and has a frequency table internally.
If you omit the top three bits of the fnum, you will get sounds, but it’s three octaves too low (so have to compensate in the octave number), and the frequency precision is reduced. The octave number is simply a left shift of the fnum by the specified amount to arrive at the final 20-bit fnum (iirc) that it uses internally. Each left shift doubles the frequency, so the pitch increases by an octave, and so if you leave the top bits unused, it lowers the frequency (like a shift to the right, halving the frequency for each bit).
Note that FM chips specify a frequency (higher value = higher pitch), while PSG chips specify a period (higher value = lower pitch). In the FM chips, the fnum determines the stepping speed at which it scans through its sine table, whereas on PSG chips, the period specifies a counter which needs to reach zero to generate the next output value (e.g. 0/1 output toggle on AY3, next entry in wave table on SCC).
If you omit the top three bits of the fnum, you will get sounds, but it’s three octaves too low (so have to compensate in the octave number), and the frequency precision is reduced. The octave number is simply a left shift of the fnum by the specified amount to arrive at the final 20-bit fnum (iirc) that it uses internally. Each left shift doubles the frequency, so the pitch increases by an octave, and so if you leave the top bits unused, it lowers the frequency (like a shift to the right, halving the frequency for each bit).
Note that FM chips specify a frequency (higher value = higher pitch), while PSG chips specify a period (higher value = lower pitch). In the FM chips, the fnum determines the stepping speed at which it scans through its sine table, whereas on PSG chips, the period specifies a counter which needs to reach zero to generate the next output value (e.g. 0/1 output toggle on AY3, next entry in wave table on SCC).
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
I'm not sure what I'm doing wrong, here is my code:
For A4, it is 1038 (I set the clock to 8000000 just for this test), just like in the application manual:
Maybe I am messing up the octave?
For A4, it is 1038 (I set the clock to 8000000 just for this test), just like in the application manual:
Code: Select all
uint8_t octave = note / 12 - 1;
float m = midi_note_freq(note);
uint16_t fnum = (144 * m * (1 << 20) / fmtoy->ym2203_clock) / (1 << (octave - 1));
ym2203_w(0, 0, 0xa4 + chip_channel);
ym2203_w(0, 1, octave << 3 | (fnum >> 8 & 0x07)); // here
ym2203_w(0, 0, 0xa0 + chip_channel);
ym2203_w(0, 1, fnum & 0xff);
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
YM2608 is probably an octave too low because it has an extra divider inside, since it was designed to operate at double the clock frequency, typically 8 MHz. If you’re emulating it, feed it 7159090 Hz and the pitch should be the same as the YM2203 at 3579545 Hz, since it’s designed to sound the same at double the frequency (for compatibility). Btw the divider is actually selectable on the YM2608 via a prescaler register, so you can also run it at 3.58 MHz when you initialise it correctly.
YM2151 is octave number + key number, and also detune in in 1/64 steps (1.6 cent / step). Of the 12 keys, I think the value mappings are: 0->0, 1->1, 2->2, 3->4, 4->5, 5->6, 6->8, 7->9, 8->10, 9->12, 10->13, 11->14. In other words, x*4/3 (integer division). Note that note 0 is C#, not C, so that may explain the semitone? See figure 2.4 in the manual.
YM2151 is octave number + key number, and also detune in in 1/64 steps (1.6 cent / step). Of the 12 keys, I think the value mappings are: 0->0, 1->1, 2->2, 3->4, 4->5, 5->6, 6->8, 7->9, 8->10, 9->12, 10->13, 11->14. In other words, x*4/3 (integer division). Note that note 0 is C#, not C, so that may explain the semitone? See figure 2.4 in the manual.
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
Yes, in the manual, the first note is indeed C#, good catch!
Yes, I was able to set the clock divider by writing address 0x2e, without writing any data. This seems to work for YM2608, but not YM2610 and YM2612. It looks like I'll have to clock those at double frequency.
My next question is why YM2610B issues this warning:
https://github.com/vgmrips/vgmplay/blob ... fm.c#L3961
It seems to check only on channel 0 slot 0 and 3, and it also resets it. In MAME, it still issues the warning, but doesn't reset it:
https://github.com/mamedev/mame/blob/ma ... .cpp#L3307
Does YM2610B not have 6 FM channels? Is channel 0 not valid?
I am calling ym2610b_update_one, and the warning is not in that function, but when you write to some registers, ym2610_update_one gets called with DUMMYBUF. I've also not found any YM2610B packs that use all 6 channels, they seem to only use the 4 channels that are common with YM2610. Anyway, there are two workarounds - either define ym2610b_write and make it specific to YM2610B, or check if we're using DUMMYBUF (bufL = bufR = 0). Either that or remove the warning.
Here is what I used:
Yes, I was able to set the clock divider by writing address 0x2e, without writing any data. This seems to work for YM2608, but not YM2610 and YM2612. It looks like I'll have to clock those at double frequency.
Code: Select all
ym2608_write(channel->chip->data, 0, 0x2e);
https://github.com/vgmrips/vgmplay/blob ... fm.c#L3961
It seems to check only on channel 0 slot 0 and 3, and it also resets it. In MAME, it still issues the warning, but doesn't reset it:
https://github.com/mamedev/mame/blob/ma ... .cpp#L3307
Does YM2610B not have 6 FM channels? Is channel 0 not valid?
I am calling ym2610b_update_one, and the warning is not in that function, but when you write to some registers, ym2610_update_one gets called with DUMMYBUF. I've also not found any YM2610B packs that use all 6 channels, they seem to only use the 4 channels that are common with YM2610. Anyway, there are two workarounds - either define ym2610b_write and make it specific to YM2610B, or check if we're using DUMMYBUF (bufL = bufR = 0). Either that or remove the warning.
Here is what I used:
Code: Select all
#ifdef YM2610B_WARNING
#define FM_KEY_IS(SLOT) ((SLOT)->key)
#define FM_MSG_YM2610B "YM2610-%p.CH%d is playing,Check whether the type of the chip is YM2610B\n"
if(bufL) {
/* Check YM2610B warning message */
if( FM_KEY_IS(&F2610->CH[0].SLOT[3]) )
{
LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,0));
}
if( FM_KEY_IS(&F2610->CH[3].SLOT[3]) )
{
LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,3));
}
}
#endif
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
Since normal operation for the YM2608 is “double” frequency, there should be no need to set 0x2e. Indeed the prescaler option was removed entirely from the YM2610 and YM2612.
About the YM2610B, the warning checks channels 0 and 3 (only slot 3, a simplification I guess), which are not present on the YM2610, so the warning indeed seems to be for the YM2610 and not the YM2610B… but either way strange that you would get the warning at all. Maybe the memory wasn’t initialised to zero, so it thinks a key is pressed?
About the YM2610B, the warning checks channels 0 and 3 (only slot 3, a simplification I guess), which are not present on the YM2610, so the warning indeed seems to be for the YM2610 and not the YM2610B… but either way strange that you would get the warning at all. Maybe the memory wasn’t initialised to zero, so it thinks a key is pressed?
- vampirefrog Offline
- Webmaster
- Posts: 1508
- Joined: 2014-01-28, 5:51:54
It's not an initialization error, it simply has to do with the fact that the only YM2610B-specific function is ym2610b_update_one, whereas everything else, such as init and write is handled by ym2610_* functions. The problem occurs when calling ym2610_write, which for some commands calls ym2610_update_one with zero samples, which issues that warning., instead of calling ym2610b_update_one, which doesn't have that warning. You can see my fix here. It simply bypasses the warning if no samples are to be rendered.
- ValleyBell Offline
- Posts: 4791
- Joined: 2011-12-01, 20:20:07
- Location: Germany
The current version of VGMPlay calls ym2610_update_req(), which in turn calls ym2610b_update_one(). That's how VGMPlay works around the issue.
Turning those channels off is either a leftover from old MAME code or (more probably) I inserted it in order to prevent it from spamming the console. (because it would output that warning once per sample, i.e. 44.1 KHz)
Turning those channels off is either a leftover from old MAME code or (more probably) I inserted it in order to prevent it from spamming the console. (because it would output that warning once per sample, i.e. 44.1 KHz)