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

Post by vampirefrog »

For YM2608, YM2610* and YM2612 I used a different bit shift for the fnum formula, and now they all make the same note:

Code: Select all

uint16_t fnum = (144 * m * (1 << 21) / channel->chip->clock) / (1 << (octave - 1));
And for YM2151 and YM2203:

Code: Select all

uint16_t fnum = (144 * m * (1 << 20) / channel->chip->clock) / (1 << (octave - 1));

Post by vampirefrog »

Here's what I ended up using for YM2151 pitch:

Code: Select all

static void fmtoy_ym2151_set_pitch(struct fmtoy *fmtoy, uint8_t chip_channel, float pitch, struct fmtoy_channel *channel) {
	float kf = 3584 + 64 * 12 * log2(pitch * 3579545.0 / channel->chip->clock / 440.0);
	int octave = (int)kf / 64 / 12;
	int k = ((int)kf / 64) % 12;
	const uint8_t opm_notes[12] = { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14 };
	ym2151_write_reg(channel->chip->data, 0x28 + chip_channel, octave << 4 | opm_notes[k]);
	ym2151_write_reg(channel->chip->data, 0x30 + chip_channel, (int)kf % 64 << 2);
}
  • User avatar
  • grauw Offline
  • Posts: 150
  • Joined: 2015-02-22, 3:40:22

Post by grauw »

I see, to get the correct pitch regardless of the clock.

Post by vampirefrog »

While looking for some info on the YM2151 LFO frequencies, besides the crappy scan in the YM2151 User Manual PDF, I've found a much better scan in "Inside X68000", which contains several pages on the YM2151. In the X68000, however, it is clocked at 4MHz, so the values are different. I'm simply trying to extrapolate a formula:

Image
  • ctr Offline
  • Posts: 492
  • Joined: 2013-07-17, 23:32:39

Post by ctr »

Not sure if the frequency shown is of an entire cycle of the waveform or just a step, but probably the former makes most sense. in MAME, the LFO frequency is calculated in a different way for each nibble of the frequency byte. The most significant nibble sets the period of the timer (delay between each step), which the least significant sets the phase increment on each step.

Post by vampirefrog »

I did notice that, but I didn't know how to turn it into a formula:

At init:

Code: Select all

PSG->lfo_timer_add = (1<<LFO_SH) * (clock/64.0) / PSG->sampfreq;
At register write:

Code: Select all

chip->lfo_overflow    = ( 1 << ((15-(v>>4))+3) ) * (1<<LFO_SH);
chip->lfo_counter_add = 0x10 + (v & 0x0f);
Every sample:

Code: Select all

PSG->lfo_timer += PSG->lfo_timer_add;
if (PSG->lfo_timer >= PSG->lfo_overflow)
{
	PSG->lfo_timer   -= PSG->lfo_overflow;
	PSG->lfo_counter += PSG->lfo_counter_add;
	PSG->lfo_phase   += (PSG->lfo_counter>>4);
	PSG->lfo_phase   &= 255;
	PSG->lfo_counter &= 15;
}
Also, here's the chapter about sound from Inside X68000
insidex68ksound.pdf
(2.97 MiB) Downloaded 332 times
Here's the LFO frequency table at 4MHz clock:

Code: Select all

FF 59.1278 BF 3.6955 7F 0.2310 3F 0.0144
FE 57.2205 BE 3.5763 7E 0.2235 3E 0.0140
FD 55.3131 BD 3.4571 7D 0.2161 3D 0.0135
FC 53.4058 BC 3.3379 7C 0.2086 3C 0.0130
FB 51.4984 ВB 3.2187 7B 0.2012 3B 0.0126
FA 49.5911 BA 3.0994 7A 0.1937 3A 0.0121
F9 47.6837 B9 2.9802 79 0.1863 39 0.0116
F8 45.7764 B8 2.8610 78 0.1788 38 0.0112
F7 43.8690 B7 2.7418 77 0.1714 37 0.0107
F6 41.9617 B6 2.6226 76 0.1639 36 0.0102
F5 40.0543 B5 2.5034 75 0.1565 35 0.0098
F4 38.1470 B4 2.3842 74 0.1490 34 0.0093
F3 36.2396 B3 2.2650 73 0.1416 33 0.0088
F2 34.3323 B2 2.1458 72 0.1341 32 0.0084
F1 32.4249 B1 2.0265 71 0.1267 31 0.0079
F0 30.5176 B0 1.9073 70 0.1192 30 0.0075
EF 29.5639 AF 1.8477 6F 0.1155 2F 0.0072
EE 28.6102 ΑΕ 1.7881 6E 0.1118 2E 0.0070
ED 27.6566 AD 1.7285 6D 0.1080 2D 0.0068
EC 25.7029 АС 1.6689 6C 0.1043 2C 0.0065
ЕВ 25.7492 AB 1.6093 6B 0.1006 2B 0.0063
EA 24.7955 AA 1.5497 6A 0.0969 2A 0.0061
E9 23.8419 A9 1.4901 69 0.0931 29 0.0058
E8 22.8882 A8 1.4305 68 0.0894 28 0.0056
E7 21.9345 A7 1.3709 67 0.0857 27 0.0054
E6 20.9808 A6 1.3113 66 0.0820 26 0.0051
E5 20.0272 A5 1.2517 65 0.0782 25 0.0049
E4 19.0735 A4 1.1921 64 0.0745 24 0.0047
E3 18.1198 A3 1.1325 63 0.0708 23 0.0044
E2 17.1661 A2 1.0729 62 0.0671 22 0.0042
E1 16.2125 A1 1.0133 61 0.0633 21 0.0040
E0 15.2588 AO 0.9537 60 0.0596 20 0.0037
DF 14.7820 9F 0.9239 5F 0.0577 1F 0.0036
DE 14.3051 9E 0.8941 5E 0.0559 1E 0.0035
DD 13.8283 90 0.8643 5D 0.0540 1D 0.0034
DC 13.3514 9C 0.8345 5C 0.0522 1C 0.0033
DB 12.8746 9B 0.8047 5B 0.0503 1B 0.0031
DA 12.3978 9A 0.7749 5A 0.0484 1A 0.0030
D9 11.9209 99 0.7451 59 0.0466 19 0.0029
D8 11.4441 98 0.7153 58 0.0447 18 0.0028
D7 10.9673 97 0.6855 57 0.0428 17 0.0027
D6 10.4904 96 0.6557 56 0.0410 16 0.0026
D5 10.0136 95 0.6258 55 0.0391 15 0.0024
D4  9.5367 94 0.5960 54 0.0373 14 0.0023
D3  9.0599 93 0.5662 53 0.0354 13 0.0022
D2  8.5831 92 0.5364 52 0.0335 12 0.0021
D1  8.1062 91 0.5066 51 0.0317 11 0.0020
DO  7.6294 90 0.4768 50 0.0298 10 0.0019
CF  7.3910 8F 0.4619 4F 0.0289 0F 0.0018
СЕ  7.1526 8E 0.4470 4E 0.0279 0E 0.0017
CD  6.9141 8D 0.4321 4D 0.0270 0D 0.0017
CC  6.6757 8C 0.4172 4C 0.0261 0C 0.0016
CB  6.4373 8B 0.4023 4B 0.0251 0B 0.0016
CA  6.1989 8A 0.3874 4A 0.0242 0A 0.0015
C9  5.9605 89 0.3725 49 0.0233 09 0.0015
C8  5.7220 88 0.3576 48 0.0224 08 0.0014
C7  5.4836 87 0.3427 47 0.0214 07 0.0013
C6  5.2452 86 0.3278 46 0.0205 06 0.0013
C5  5.0068 85 0.3129 45 0.0196 05 0.0012
C4  4.7684 84 0.2980 44 0.0186 04 0.0012
C3  4.5300 83 0.2831 43 0.0177 03 0.0011
C2  4.2915 82 0.2682 42 0.0168 02 0.0010
C1  4.0531 81 0.2533 41 0.0158 01 0.0010
C0  3.8147 80 0.2384 40 0.0149 00 0.0009

Post by vampirefrog »

It looks like you might be right about that nybble stuff, as plotting the data shows that it's not a curve, but a polygon made of straight lines that overall looks like a curve.

Image

Post by vampirefrog »

So far the formula seems to be 4000000*(16+x%16)/2^(36-x>>4)

Also, here is the YM2151 User manual LFO table (for 3579545Hz), recalculated with this formula (the original is unreadable):

Code: Select all

FF 52.9127   BF 3.3070   7F 0.2067   3F 0.0129
FE 51.2058   BE 3.2004   7E 0.2000   3E 0.0125
FD 49.4989   BD 3.0937   7D 0.1934   3D 0.0121
FC 47.7921   BC 2.9870   7C 0.1867   3C 0.0117
FB 46.0852   BB 2.8803   7B 0.1800   3B 0.0113
FA 44.3784   BA 2.7736   7A 0.1734   3A 0.0108
F9 42.6715   B9 2.6670   79 0.1667   39 0.0104
F8 40.9646   B8 2.5603   78 0.1600   38 0.0100
F7 39.2578   B7 2.4536   77 0.1534   37 0.0096
F6 37.5509   B6 2.3469   76 0.1467   36 0.0092
F5 35.8441   B5 2.2403   75 0.1400   35 0.0088
F4 34.1372   B4 2.1336   74 0.1333   34 0.0083
F3 32.4303   B3 2.0269   73 0.1267   33 0.0079
F2 30.7235   B2 1.9202   72 0.1200   32 0.0075
F1 29.0166   B1 1.8135   71 0.1133   31 0.0071
F0 27.3098   B0 1.7069   70 0.1067   30 0.0067
EF 26.4563   AF 1.6535   6F 0.1033   2F 0.0065
EE 25.6029   AE 1.6002   6E 0.1000   2E 0.0063
ED 24.7495   AD 1.5468   6D 0.0967   2D 0.0060
EC 23.8960   AC 1.4935   6C 0.0933   2C 0.0058
EB 23.0426   AB 1.4402   6B 0.0900   2B 0.0056
EA 22.1892   AA 1.3868   6A 0.0867   2A 0.0054
E9 21.3358   A9 1.3335   69 0.0833   29 0.0052
E8 20.4823   A8 1.2801   68 0.0800   28 0.0050
E7 19.6289   A7 1.2268   67 0.0767   27 0.0048
E6 18.7755   A6 1.1735   66 0.0733   26 0.0046
E5 17.9220   A5 1.1201   65 0.0700   25 0.0044
E4 17.0686   A4 1.0668   64 0.0667   24 0.0042
E3 16.2152   A3 1.0134   63 0.0633   23 0.0040
E2 15.3617   A2 0.9601   62 0.0600   22 0.0038
E1 14.5083   A1 0.9068   61 0.0567   21 0.0035
E0 13.6549   A0 0.8534   60 0.0533   20 0.0033
DF 13.2282   9F 0.8268   5F 0.0517   1F 0.0032
DE 12.8015   9E 0.8001   5E 0.0500   1E 0.0031
DD 12.3747   9D 0.7734   5D 0.0483   1D 0.0030
DC 11.9480   9C 0.7468   5C 0.0467   1C 0.0029
DB 11.5213   9B 0.7201   5B 0.0450   1B 0.0028
DA 11.0946   9A 0.6934   5A 0.0433   1A 0.0027
D9 10.6679   99 0.6667   59 0.0417   19 0.0026
D8 10.2412   98 0.6401   58 0.0400   18 0.0025
D7  9.8144   97 0.6134   57 0.0383   17 0.0024
D6  9.3877   96 0.5867   56 0.0367   16 0.0023
D5  8.9610   95 0.5601   55 0.0350   15 0.0022
D4  8.5343   94 0.5334   54 0.0333   14 0.0021
D3  8.1076   93 0.5067   53 0.0317   13 0.0020
D2  7.6809   92 0.4801   52 0.0300   12 0.0019
D1  7.2542   91 0.4534   51 0.0283   11 0.0018
D0  6.8274   90 0.4267   50 0.0267   10 0.0017
CF  6.6141   8F 0.4134   4F 0.0258   0F 0.0016
CE  6.4007   8E 0.4000   4E 0.0250   0E 0.0016
CD  6.1874   8D 0.3867   4D 0.0242   0D 0.0015
CC  5.9740   8C 0.3734   4C 0.0233   0C 0.0015
CB  5.7607   8B 0.3600   4B 0.0225   0B 0.0014
CA  5.5473   8A 0.3467   4A 0.0217   0A 0.0014
C9  5.3339   89 0.3334   49 0.0208   09 0.0013
C8  5.1206   88 0.3200   48 0.0200   08 0.0013
C7  4.9072   87 0.3067   47 0.0192   07 0.0012
C6  4.6939   86 0.2934   46 0.0183   06 0.0011
C5  4.4805   85 0.2800   45 0.0175   05 0.0011
C4  4.2672   84 0.2667   44 0.0167   04 0.0010
C3  4.0538   83 0.2534   43 0.0158   03 0.0010
C2  3.8404   82 0.2400   42 0.0150   02 0.0009
C1  3.6271   81 0.2267   41 0.0142   01 0.0009
C0  3.4137   80 0.2134   40 0.0133   00 0.0008
opmlfo.png

Post by vampirefrog »

Can someone clarify to me what PMD/AMD does? When translating from OPN to OPM, what do I set it to? Can the LFO only do one of two types of modulation?
  • ctr Offline
  • Posts: 492
  • Joined: 2013-07-17, 23:32:39

Post by ctr »

phase (or frequency) / amplitude modulation depth. it's a global setting and is used together with each channel PMS/AMS settings.

When the depth value is set to the maximum value (127), the LFO PMS settings in figure 2.8 and AMS settings in figure 2.15 applies. Multiply the values with the PMD or AMD setting divided by 127 to get the actual sensitivity for a channel.

For OPN, i can just redirect you to maxim's doc. Or maybe the YM2608 datasheet if you know japanese.

the LFO can only generate one waveform at a time, but both kinds of modulation (phase or amplitude modulation) can be enabled simultaneously on all channels. this is the case on both OPN and OPM.

Post by vampirefrog »

My confusion came from the fact that you're writing to the same register, but the values are interpreted separately depending on bit 7. So I suspect, haven't tried it yet, that in order to translate from OPN to OPM, you'd have to write values 0x7f and 0x80 to the PMD/AMD register. PMD is in two's complement, and 0x80 should be the maximum value, I believe.

Post by vampirefrog »

I am starting to think this project was a bad idea because:

1. It is more useful to have vgm2mid and vgm2opm in the same package
2. vgm2mid does not fit with the concept of FM Tools
3. The analyzer code for each chip can be made modular, so you'd have, for example, code for YM2151 to collect OPM voices, collect MIDI events and,
4. The same analyzer code can contain the text generator, which would make for a new, modular vgm2txt, which, in turn,
5. The new code for vgm2txt can be used as a library, so, for example, one could reuse this code for an UI application, either for desktop or compiled with emscripten to make a nice VGM in-detail editor/viewer

The way I see it, vgm2txt can use a callback or something similar to generate the text output, and the callback would receive a ton of parameters, such as: vgm offset, current samples, wait samples for current command, chip ID, which chip (first or second), chip state (clock, registers) and a string describing the command. Also, if we store chip state we can have more useful output (notes instead of fnums).

Then, a similar callback can be used in a GUI application to fill out a table that the user sees. Also, it would be very easy to make a vgm2csv program that you can load as a spreadsheet or into a database.

Anyway, I think the vgm2* tools belong in vgmtools and I'll probably delete this project.
  • cyberic Offline
  • Posts: 6
  • Joined: 2018-02-25, 17:42:38

Post by cyberic »

I just created an account to say this:
"Thank you! And please do not delete this project"!
I think that fmtool is really interesting to compare the output of different YM chips.
Being able to emulate then and address them in MIDI is really useful

Post by vampirefrog »

cyberic wrote:I just created an account to say this:
"Thank you! And please do not delete this project"!
I think that fmtool is really interesting to compare the output of different YM chips.
Being able to emulate then and address them in MIDI is really useful
Thanks, I am glad you like it. I will keep fmtoy as a separate repo, no worries.
  • cyberic Offline
  • Posts: 6
  • Joined: 2018-02-25, 17:42:38

Post by cyberic »

cool, thx!
Post Reply