Skip to content

Vampi's FM Tools

vgm2opm and others

Technical discussion which is not directly related to VGM files. Talk about Hardware and Software.

Moderator: Staff

Vampi's FM Tools

Post by vampirefrog »

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
  • User avatar
  • ValleyBell Offline
  • Posts: 4768
  • Joined: 2011-12-01, 20:20:07
  • Location: Germany

Post by ValleyBell »

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.

Post by vampirefrog »

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.

Post by vampirefrog »

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.
  • User avatar
  • grauw Offline
  • Posts: 150
  • Joined: 2015-02-22, 3:40:22

Post by grauw »

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).

Post by vampirefrog »

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:

Image

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);
Maybe I am messing up the octave?

Post by vampirefrog »

Ok, it was the octave

Code: Select all

octave = note / 12 - 1;
I removed the - 1

Post by vampirefrog »

Well, at 3579545 Hz, YM2203 is perfectly in tune, YM2608 is an octave too low and YM2151 is a semitone high, and also I find myself needing a formula for YM2151, which seems to be missing from the manual.
  • User avatar
  • grauw Offline
  • Posts: 150
  • Joined: 2015-02-22, 3:40:22

Post by grauw »

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.

Post by vampirefrog »

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.

Code: Select all

ym2608_write(channel->chip->data, 0, 0x2e);
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:

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

Post by vampirefrog »

I've pushed an update, now YM2151, YM2203, YM2608, YM2610, YM2610B and YM2612 work. There is an envelope bug with YM2612 currently, I'll look into it later.

Edit: fixed the YM2612 envelope bug
  • User avatar
  • grauw Offline
  • Posts: 150
  • Joined: 2015-02-22, 3:40:22

Post by grauw »

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?

Post by vampirefrog »

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.
  • User avatar
  • ValleyBell Offline
  • Posts: 4768
  • Joined: 2011-12-01, 20:20:07
  • Location: Germany

Post by ValleyBell »

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)

Post by vampirefrog »

I see, thanks
Post Reply