Shop OBEX P1 Docs P2 Docs Learn Events
OPN2cog - The sound of the SEGA Mega Drive in a cog! — Parallax Forums

OPN2cog - The sound of the SEGA Mega Drive in a cog!

Wuerfel_21Wuerfel_21 Posts: 5,052
edited 2021-05-09 15:41 in Propeller 2

(or SEGA Genesis if you're in yankland...)

This is an emulation of the Yamaha "OPN2" YM2612 FM synth chip that runs in one P2 cog.

Features:

  • 6 channels of 4-operator phase-modulation synthesis
  • All 24 oscillators updated at the same rate as the real chip (~53 kHz)
  • Channels are not mixed, but multiplexed onto the DAC, like in the real chip.
  • Emulates DAC distortion
  • Emulates output lowpass filter
  • Supports SSG-EG
  • Mostly supports CH3 special mode (rate scaling doesn't account for it)
  • Supports CH6 raw DAC mode
  • Bonus: SEGA PSG (SN76489 variant) emulation!

Misfeatures:

  • Envelope generators are updated at 1/12 the real rate (~1.5kHz instead of ~18 kHz)
    This causes slight clicking on certain envelope rates and other artifacts.

  • Timers and CSM mode are not implemented.

  • PSG sound is somewhat aliased due to relatively low update rate (~53 kHz)
  • Requires at least 250 MHz P2 clock to run properly.
    (Too low clock speed -> low and/or inconsistent pitch)

  • Some instruments sound a little off, but maybe I'm just going insane.
    Still orders of magnitude better than those silly AtGames units, haha

Videos

MEGA JUKE

Spin API example

(Note: the spectrograph in these is not running on the P2, it's just there so it's more than a still image)

What's included

File name Purpose
OPN2cog.spin2 OPN2 emulation core
OPN2_ROM.DAT OPN2 logsin/exponent ROM
megajuke.c MEGA JUKE program.
MEGAJUKE.DAT MEGA JUKE data file.
megajuke_builder.rb Script for building MEGAJUKE.DAT
megajuke_tracks.yml Track list for MEGA JUKE. Add your own favorite tunes!
ExampleSpinAPI.spin2 Example that plays a weird little tune using the Spin API and VGI patch files
*.vgi VGI patch files used by the Spin API example
ExampleVGMPlay.spin2 In-memory VGM player. Copy a VGM file from the tunes directory and load it!
ExampleSSG-EG.spin2 Example/test that plays a note normally and then with all 8 SSG-EG modes
tunes/ Folder of lots of VGM dumps!
romgen.rb Script for building OPN2_ROM.DAT
eg_analyzer.rb Script for computing the EG rate lookup tables
lfo_analyzer.rb Script for computing the LFO FM lookup table
logo.gal GraphicsGale layered file for the logo

Running MEGA JUKE

The MEGA JUKE program streams register dumps from a file, using the 9P server in loadp2 (in theory, replacing _vfs_open_host with _vfs_open_sdcard should make it run from SD card, too, but in practice it hangs after reading the track text, IDK why. SD code is probably not quite thread safe)

  • Have the latest flexspin/flexprop (5.4.3) installed
  • Open megajuke.c and edit the pin constants near the top.
  • Compile with flexspin -2 megajuke.c -D_BAUD=2000000
  • Load with loadp2 -p [your port here] -t -9 . -b 2000000 megajuke.binary

Comments

  • pik33pik33 Posts: 2,366

    I didn't have this hardware (so no nostalgia) but I like FM synthesis :) and I had an Soundblaster with Adlib complatible 2/4 op FM synthesizers.

  • The OPL series chips used in the AdLib/Soundblaster are actually quite similar to the OPN series. Though OPL3 will not fit in 1 cog. (But I think OPL2 extended with some OPL3 features will)

  • This is awesome @Wuerfel_21

  • Cluso99Cluso99 Posts: 18,069

    Fantastic. Thanks for posting the YouTube sample. I didn’t listen to the whole track but did listen to samples along the track.

  • ColeyColey Posts: 1,110

    Excellent, we'll have a Megadrive in no time at all ;)

  • @Wuerfel_21 said:
    The OPL series chips used in the AdLib/Soundblaster are actually quite similar to the OPN series. Though OPL3 will not fit in 1 cog. (But I think OPL2 extended with some OPL3 features will)

    Yeah I was wondering about whether an Adlib OPL2 device could be emulated at some point. Did you attempt the OPL3 and run into some COG performance limits?

  • @rogloh said:
    Did you attempt the OPL3 and run into some COG performance limits?

    No, but I can see them from a mile away. The OPN2 has 24 operators and barely fits at 250 MHz. The OPL2 has 18. The OPL3 has 36.

  • pik33pik33 Posts: 2,366

    So it is time to try FM synth using P2 and check how many operators can fit. I have this HDMI driver wihch uses "Amiga based" frequencies (Paula x 80, 90, 100 which gives ~280/320./360 MHz ) so I will try this with Paula related frequencies.
    Making a Paula like FM can be interesting but it will need more processing (or maybe not?) than "normal" phase accumulator thing.

  • @pik33 said:
    Making a Paula like FM can be interesting but it will need more processing (or maybe not?) than "normal" phase accumulator thing.

    Not really. The way the Yamaha chips do it is simple: Just add the output from the modulator(s) together with the phase accumulation and then generate the waveform based on that. I think/hope the OPN2cog code is commented well enough to read, so you can do that to get an idea of how it works.

  • I've just PR'd this and SNEcog into the OBEX: https://github.com/parallaxinc/propeller/pull/231

  • Ahle2Ahle2 Posts: 1,179
    edited 2021-05-12 07:36

    @Wuerfel_21,

    Fantastic work!! I haven't compared it thoroughly to the real thing yet, but it sounds really close when listening to the tunes I know well. This is a great contribution to the P2 community! :smile:

  • roglohrogloh Posts: 5,786
    edited 2021-09-25 03:22

    I've been able to patch this code to work with a CS4344 I2S DAC. I used the PMOD I2S board with the P2-EVAL and managed to get something working with it and can hear the music playing in the jukebox demo. Output sample rate is ~159kHz for I2S and double that for analog output.
    Here's the patch you need. Just call start_with_i2s(...) instead of start(...) and pass the I2S pin group as an argument. See the file for pin order. Without I2S enabled there is no run time overhead added in the critical loop. Enabling it will add some cycles, but it seemed to still work at 250MHz...YMMV.
    Added bonus is the analog audio still happens with I2S enabled as well.

    p.s. Listening to more of this with I2S I actually think the 2x faster analog audio output sounds clearer, maybe it's a bad sample rate conversion due to mismatch of digital output sampling and sample generation inside the COG itself or just the fact that the rate is halved.

    EDIT: Updated this code to sum every second sample and playback (still at 159kHz). Sounds way better now. :smile:

  • @rogloh said:
    I've been able to patch this code to work with a CS4344 I2S DAC. I used the PMOD I2S board with the P2-EVAL and managed to get something working with it and can hear the music playing in the jukebox demo. Output sample rate is ~159kHz for I2S and double that for analog output.
    Here's the patch you need. Just call start_with_i2s(...) instead of start(...) and pass the I2S pin group as an argument. See the file for pin order. Without I2S enabled there is no run time overhead added in the critical loop. Enabling it will add some cycles, but it seemed to still work at 250MHz...YMMV.
    Added bonus is the analog audio still happens with I2S enabled as well.

    p.s. Listening to more of this with I2S I actually think the 2x faster analog audio output sounds clearer, maybe it's a bad sample rate conversion due to mismatch of digital output sampling and sample generation inside the COG itself or just the fact that the rate is halved.

    As evident in the source, OPN2cog multiplexes the output between the channels like the real chip (which is where the ridiculous 319.6kHz come from. The synth as a whole updates at 53.26kHz). I don't see any sample summing in your code, so if it was actually outputting at exactly halved rate, half the channels would be missing. And if they aren't, you're missing samples (try 320MHz clock?)

  • @Wuerfel_21 said:
    As evident in the source, OPN2cog multiplexes the output between the channels like the real chip (which is where the ridiculous 319.6kHz come from. The synth as a whole updates at 53.26kHz). I don't see any sample summing in your code, so if it was actually outputting at exactly halved rate, half the channels would be missing. And if they aren't, you're missing samples (try 320MHz clock?)

    Aha, if that is the case then that would explain why it doesn't sound as good. :smile:

    Yes I tried it at the higher rate, but this particular I2S DAC doesn't take sample rates over 200kHz according to its data sheet and so it didn't work. I guess we could try storing every second sample and summing them, maybe with a small clock boost for the extra overhead.

  • I meant to try setting _CLKFREQ to 320Mhz. That should rule out any skipped samples. In that case with 159kHz sampling, you should get half the channels missing.

  • RaymanRayman Posts: 14,640

    This was from May? I missed it somehow... Sound great!

    How would you say this compares to SimpleSound from @Ahle2 ?

    I'm guessing 16 bits makes it sound better. But, there's a huge library of those 8-bit music samples out there...

    For a game, guess you want to play some background music and mix in sound effects from the game. SimpleSound made that pretty easy...
    Does this support that too?

  • @Rayman said:
    This was from May? I missed it somehow... Sound great!

    How would you say this compares to SimpleSound from @Ahle2 ?

    Well, this is more akin to SIDcog, in that it emulates a vintage synth chip.

    I'm guessing 16 bits makes it sound better. But, there's a huge library of those 8-bit music samples out there...

    It's not doing 16 bit sampling, it's just 5 FM synth channels and one 8 bit sampling channel (and 4 PSG channels). (The logo just says "16 BIT" because it's supposed to look like a Mega Drive console)

    For a game, guess you want to play some background music and mix in sound effects from the game. SimpleSound made that pretty easy...
    Does this support that too?

    There's no built-in music/sfx routine. The sampling channel isn't really usable most applications, because you need to constantly rewrite the DAC register. I could make a version that can go through the samples on it's own though.

  • roglohrogloh Posts: 5,786
    edited 2021-09-25 03:59

    @Wuerfel_21 said:
    I meant to try setting _CLKFREQ to 320Mhz. That should rule out any skipped samples. In that case with 159kHz sampling, you should get half the channels missing.

    I've modified the code in my earlier post to sum every second sample before playback, which is still getting done at 159kHz and 250MHz. The music seems to sound so much better this way over I2S now it includes all samples (as you can imagine). I also tried operating the P2 at 320MHz and it worked too.

    Also: not included in the code was this one line change (which I wasn't sure would affect the pitch or not). This change keeps the i2s output rate locked to the generation rate stopping it from drifting due to quantization error in the divisors.

    To include it for trying out, change this line

      sampleRate := clkfreq/OPN_SAMPLE_RATE
    

    to this:

      sampleRate := 64*(clkfreq/(OPN_SAMPLE_RATE*64))
    

    Potentially (if needed) that tweaked calculation could be made only when i2s is enabled in the start method in order to keep the audio pure when i2s is not used.

  • Good job!

  • Here's OPN2Cog V1.1

    Because the forum upload limit changed, I can't actually post the ZIP with all the example data anymore, so won't update the OP until that's cleared. In the meantime, have this ZIP without them.

    Main change is that there's now an "Ultra" version that requires 300 MHz clock speed, but offers improved quality. There's also an improvement to the DAC distortion simulation and both of the VGM file playing examples now correctly implement write delay (so files exported straight from trackers play correctly now).

  • pik33pik33 Posts: 2,366
    edited 2022-03-01 09:08

    Downloaded.
    As I don't have any experience with this chip/system I have several questions

    - Where to get the music for it from?

    • What is the file extension and format of it?
    • Do I need a CPU emulator to play these files?

    As the prop2play works at Paula*100=354 MHz, the Ultra version can fit :) but I have to finish the SIDCog integration first.

    Edit: Found a big zip in the first post.

  • Wuerfel_21Wuerfel_21 Posts: 5,052
    edited 2022-03-01 09:52

    @pik33 said:
    Downloaded.
    As I don't have any experience with this chip/system I have several questions

    - Where to get the music for it from?

    • What is the file extension and format of it?

    VGM format is basically a big stream of "write chip register" and "wait" commands. There's a specification for it out there somewhere (I recommend you reference an older version, the most recent versions are more complex and in pratice you'll only find the first couple really simple versions for MD music). In addition, a file may contain a data block to store any samples it needs. I think(?) you can get away with allocating 64k for that. There's two examples for playing VGM (reference the newer versions from the V1.1 ZIP, they both had some issues fixed). ExampleVGMPlay.spin2 is in Spin2 and plays a file loaded in memory, whereas megajuke.c is in C and plays files from filesystem. Since the commands can get pretty fast, especially when sample playing is involved, you ideally want to constantly load the next bit of data in the background.

    • Do I need a CPU emulator to play these files?

    No.

Sign In or Register to comment.