BSD-licensed Yamaha FM sound cores (OPM, OPN, OPL, and others)

ymfm

ymfm is a collection of BSD-licensed Yamaha FM sound cores (OPM, OPN, OPL, and others), written by Aaron Giles

Supported environments

This code should compile cleanly in any environment that has C++17 support. It has been tested on gcc, clang, and Microsoft Visual C++ 2019.

Supported chip families

Currently, support is present for the following chips (organized by header file):

  • ymfm_opm.h:
    • YM2151 (OPM)
    • YM2164 (OPP)
  • ymfm_opn.h:
    • YM2149 (SSG)
    • YM2203 (OPN)
    • YM2608 (OPNA)
    • YM2610 (OPNB)
    • YM2610B (OPNB2)
    • YM2612 (OPN2)
    • YM3438 (OPN2C)
    • YMF276 (OPN2L)
  • ymfm_opl.h:
    • YM3526 (OPL)
    • Y8950 (MSX-Audio)
    • YM3812 (OPL2)
    • YMF262 (OPL3)
    • YMF278B (OPL4) -- partial (only the FM side)
    • YM2413 (OPLL)
    • YM2423 (OPLL-X)
    • YMF281 (OPLLP)
    • DS1001 (Konami 053982)
  • ymfm_opq.h:
    • YM3806 (OPQ) -- preliminary
  • ymfm_opz.h:
    • YM2414 (OPZ) -- preliminary

General approach

Check out the examples directory for some example usage patterns. I'm not a big fan of makefiles for simple things, so instructions on how to compile each example are provided at the top.

Clocking

The general philosophy of the emulators provided here is that they are clock-independent. Much like the actual chips, you (the consumer) control the clock; the chips themselves have no idea what time it is. They just tick forward each time you ask them to.

The way you move things along is via the generate() function, which ticks the internal system forward one or more samples, and writes out an array out chip-specific output_data. But what, exactly, is a "sample", and how long is it?

This is where the external clock comes in. Most of the Yamaha chips are externally clocked in the MHz range. They then divide that clock by a factor (sometimes dynamically controllable), and then the internal operators are pipelined to further divide the clock.

For example, the YM2151 internally divides the clock by 2, and has 32 operators to iterate through. Thus, for a nominal input lock of 3.58MHz, you end up at around a 55.9kHz sample rate. Fortunately, all the chip implementations can compute this for you; just pass the raw external clock value to the sample_rate() method and it will hand you back the output sample rate you want.

Then call generate() that many times per second to output the results.

But what if I want to output at a "normal" rate, like 44.1kHz? Sorry, you'll have to rate convert as needed.

Reading and Writing

To read or write to the chips, you can call the read() and write() methods. The offset provided corresponds to the addressing input lines in a (hopefully) logical way.

For reads, almost all chips have a status register, which you can read via read_status(). Some chips have a data port that can be read via read_data(). And chips with extended addressing may also have read_status_hi() and read_data_hi().

For writes, almost all chips have an address register and a data register, and so you can reliably count on there being a write_address() and write_data() method as well. If the chip supports extended addressing, it may also have write_address_hi() and write_data_hi().

Comments
  • ADPCM: fixed address checking on read/write

    ADPCM: fixed address checking on read/write

    @aaronsgiles Thanks for merging https://github.com/aaronsgiles/ymfm/pull/35 .

    We have changed the definition of at_end() and at_limit() in https://github.com/aaronsgiles/ymfm/pull/33#issuecomment-1114387840 .

    So, we need to modify the address checking not only on clock(), but also on read()/write(). Please check this patch.

    P.S. I have resumed verification of real YM2608's ADPCM. https://github.com/hyano/opna-analyze/blob/main/doc/OPNA.md If I can find the internal behavior I will try to implement and send you another PR.

  • Attack glitch issue

    Attack glitch issue

    I'm using on my tracker the approach @aaronsgiles proposed some weeks ago on #1 (about writing only 1 register per clock() as a conservative approach to avoid spam the chips). So far so good for YM2612 and YM2610, however, on YM2151, first notes of certain instruments have some missing registers writes, after that first note subsequent notes are sounding just fine (so the first note is being triggered before the preset data was processed internally). This is an issue that #1 fixed on the other chips, but not for YM2151.

    Does the YM2151 have a specific quirk I must know about? Maybe this is related with it having a slower internal clock (3,5mhz vs ~8mhz)? Maybe is there a recommended order to write instrument data?

    I double checked and my tracker is indeed sending 1 register write per clock. Here is an example .vgm: https://deflemask.com/waits_bug.vgm

    This is how it sounds on my tracker: https://deflemask.com/waits_bug.wav

    And this is how it should sound: https://deflemask.com/waits_ok.wav

  • Delays are required

    Delays are required

    Hi Aaron! Is there a #define we can use in ymfm to disable the required delays (INSTANT_MODE or something like that)?

    If not, how much delay we should add between writes? I suppose it is not a delay in time but in N calls to clock() before some writes(addr, reg). Am I right?

  • ADPCM: fixed end/limit address checking

    ADPCM: fixed end/limit address checking

    It is a fixed patch I have sent in PR #31 first. In the original behavior, ADPCM sounds doesn't play with loop if stop address and limit address are same. I reffered the behavior and implementation of fmgen.

    https://github.com/aaronsgiles/ymfm/pull/31#issue-1190632498


    This patch fixes checking of end and limit addresses in ADPCM.

    In the original implementation, loop playback of ADPCM sound was sometimes incorrect because the limit address checking was done first and it changed the data address to 0.

  • ADPCM: fixed end/limit address checking and reading external data

    ADPCM: fixed end/limit address checking and reading external data

    This patch fixes checking of end and limit addresses in ADPCM.

    In the original implementation, loop playback of ADPCM sound was sometimes incorrect because the limit address checking was done first and it changed the data address to 0.

  • LFO speed appears to be a little bit slower than hardware or Nuked

    LFO speed appears to be a little bit slower than hardware or Nuked

    LFO is running considerably at a lower rate (confirmed on YM2612 and YM2610), it is really noticeable on high LFO speeds (like speed 7 MAX SPEED as shown in my example). Image for comparison with NUKED/HW: https://deflemask.com/LFO_rate.png

    https://deflemask.com/LFO_rate_test.vgm https://deflemask.com/LFO_nuked.wav https://deflemask.com/LFO_ymfm.wav

  • ym2612: Odd edge case

    ym2612: Odd edge case

    I'm writing a Mega Drive emulator and discovered that in the intro phase of Venom . Spider-Man - Separation Anxiety (USA, Europe), there is a jarringly loud sound. I can't seem to figure out where to start looking for the source of the problem, but maybe the author of the code will know immediately upon hearing it.

    I've made recordings using YMFM and Nuked-OPN2 (for reference), the sounds in question are near the end of the recordings. I've not tested this myself in MAME but I have been told the issue exists there also.

    https://anonfiles.com/v2Bco4y0y1/ymfm_ogg https://anonfiles.com/xbBfo5ydy1/nuked_ogg

  • SSG-EG issues: the sequel

    SSG-EG issues: the sequel

    Another quirk in the SSG-EG emulation was found!

    https://deflemask.com/ssgeg_issue_6_ymfm.wav https://deflemask.com/ssgeg_issue_6_nuked.wav https://deflemask.com/ssgeg_issue_6.vgm

  • YM2151 Noise Freq is reversed

    YM2151 Noise Freq is reversed

    For OPM, register 0x0F is noise frequency (bits 0-5) and noise_enable (bit 7) In YMFM, the noise frequency output is highest when this value = 0 and lowest when this value = 1F. The real hardware behaves in the reverse of this. (1F = highest freq, 00 = lowest freq)

    We did a test using the same VGM data on two emulators and a real chip. The emulator(s) using YMFM get the pitch backwards. The real HW and emulators using either Nuked OPM or the previous MAME core produce the expected results (1F = lowest, 00 = highest)

    VGM attached Noise Sweep VGMs.zip

  • OPN: fixed behavior of F-Num2(registers 0xa4,0xac)

    OPN: fixed behavior of F-Num2(registers 0xa4,0xac)

    This patch fixes the behavior of registers A4-A6h and AC-AEh in OPN. In real chip, there is one latch for each of A4-A6h and AC-AEh.

    In some games, if this behavior is not reproduced, a bug in the sound driver causes incorrect pitch.

    Although this patch is little bit redundant, it uses unused registers for latching as same as the original implementation. Please modify, if you have better idea.

  • Slight differences in YM2612 emulation

    Slight differences in YM2612 emulation

    (Crossposting from Mametesters)

    I've recorded what may be a good test case for the YM2612's "ladder effect", a track from Earthworm Jim 2 which Tommy Tallarico has mentioned was made specifically to exploit said effect (I'm assuming to add to the cringiness of the level where it's used): https://youtu.be/V2he1ez_JKc

    Hardware used: Japanese Mega Drive VA1 (note that this may accentuate these effects, in comparison with later hardware revisions) Sound in the video was amplified by 20dB, since Mega Drive recordings tend to sound pretty faint.

    Steps to reproduce in MAME: Load the genesis driver with the game "ejim2u" and then open this savestate (created in MAME 0.240) or:

    • Start a game
    • Pause, then enter A, C, C, A, B, A, B, Left to enter the sound test
    • Select the sount test, press A until the track number is 0
    • Press C to play the track
  • 2xYM3812: Music sounds wrong in Battlantis

    2xYM3812: Music sounds wrong in Battlantis

    (Bug report from mametesters.org) (https://mametesters.org/view.php?id=8391)

    The Main BGM on the 1st act in Battlantis from Konami sounds wrong after the OPL rewrite for MAME 0.231. Battlantis sound hardware is quite unique in that it uses 2 YM3812 chips for both the sound and music.

    PCB footage of the game: https://www.nicovideo.jp/watch/sm29650400

    Here's what it sounds like currently in MAME: https://mametesters.org/file_download.php?file_id=6160&type=bug Here's what is sounded like prior to the rewirte: https://mametesters.org/file_download.php?file_id=6161&type=bug

    And if it helps too, here's a link to the vgmrip of Battlantis: https://vgmrips.net/packs/pack/battlantis-arcade Thank you for your amazing work Aaron;)

  • Improvements to ADPCM-B implementation

    Improvements to ADPCM-B implementation

    Specific fixes:

    • Implement EOS and PLAYING flags as sticky bits that require manual clearing on all implementations.
    • Implement correct end-of-sample behavior, skipping final 3 nibbles.
    • Implement read access patterns that match hardware tests.
    • Match behavior of "dummy" reads to hardware tests.
    • Hook up verified edge case behaviors for over-reading and over-writing data.
    • Add framework for running ADPCM tests to compare against hardware results.
  • OPZ can not handle key on/off  properly

    OPZ can not handle key on/off properly

    The following code in the opz_registers::write(uint16_t, uint8_t, uint32_t, uint32_t) is not correct.

    // handle writes to the key on index
    if ((index & 0xf8) == 0x20 && bitfield(index, 0, 3) == bitfield(m_regdata[0x08], 0, 3))
    

    OPZ does not see the 0x08 register for key on/off . So. change it like the following.

    // handle writes to the key on index
    if ((index & 0xf8) == 0x20)
    
  • Reface DX mode

    Reface DX mode

    Feature request for a Reface DX core. I believe this is highly similar to the DX7, but with envelope levels from 0 to 127 instead of maxing out at 99, a feedback on every operator, and a square and saw feedback mode (are these just alternate waveforms?). Needs a little more research beyond my understanding.

    The Reface DX is a portable keyboard with a 4-operator synthesizer, at a $300 price point, and with what is currently considered one of the easiest FM synthesis configuration interfaces available. This makes it a good entry-level choice for people who want a physical keyboard, although it's fairly limited compared to an OPL3. Yamaha maintains an online community repository of patches. A software implementation would allow using these patches in DAWs and tracker software which implement such a feature, using ymfm as the synthesizer core.

  • Another SSG-EG quirk?

    Another SSG-EG quirk?

    With this .vgm: https://deflemask.com/ssgeg_issue_10.vgm

    Sometimes ymfm is doing an attack of the SSG-EG where NUKED/HW seems not doing so: https://deflemask.com/ssgeg_issue_10.png

    On nuked you will hear only some little "pock" sounds always (it sounds like filling a glass with water), ymfm sounds like an alarm. https://deflemask.com/ssgeg_issue_10_ymfm.wav https://deflemask.com/ssgeg_issue_10_nuked.wav

    I say "sometimes" because 5 seconds ago I have triggered the same .vgm and I have heard a more accurate version on YMFM. This one: https://deflemask.com/ssgeg_issue_10_ymfm_2.wav I'm really confused here.

    Do you get the second version 100% of the time? I'm trying here to be sure if this is a bug in the emulation or in my implementation.

Related tags
C/C++ language server supporting multi-million line code base, powered by libclang. Emacs, Vim, VSCode, and others with language server protocol support. Cross references, completion, diagnostics, semantic highlighting and more

Archived cquery is no longer under development. clangd and ccls are both good replacements. cquery cquery is a highly-scalable, low-latency language s

Jan 2, 2023
The whole design is modular, parametric (cost and others), field repairable, and super extensible
The whole design is modular, parametric (cost and others), field repairable, and super extensible

Easy-Transceiver The whole design is modular, parametric (cost and others), field repairable, and super extensible. It is almost trivial to add suppor

Oct 2, 2022
Allows you to easily control via MQTT any Micronova equiped pellet stove. (MCZ, Extraflame, Laminox, and many others brands!)
Allows you to easily control via MQTT any Micronova equiped pellet stove. (MCZ, Extraflame, Laminox, and many others brands!)

micronova_controller Kits are available on Tindie! Currently out of stock. V2 will be in stock soon! Here is an overview of the additions: possibility

Dec 19, 2022
Apple cctools and ld64 port for Linux, *BSD and macOS

Apple cctools and ld64 port for Linux, *BSD and macOS

Dec 30, 2022
Sega Master System / Game Gear / SG-1000 emulator for iOS, macOS, Raspberry Pi, Windows, Linux, BSD and RetroArch.
Sega Master System / Game Gear / SG-1000 emulator for iOS, macOS, Raspberry Pi, Windows, Linux, BSD and RetroArch.

Gearsystem is a very accurate, cross-platform Sega Master System / Game Gear / SG-1000 emulator written in C++ that runs on Windows, macOS, Linux, BSD, iOS, Raspberry Pi and RetroArch.

Dec 18, 2022
Tiny implementation of the GNU/Linux CGroupFS (sans resource controllers) as a PUFFS or FUSE filesystem for BSD platforms

CGrpFS CGrpFS is a tiny implementation of the GNU/Linux CGroup filesystem for BSD platforms. It takes the form of a either a PUFFS or FUSE filesystem,

Nov 8, 2022
Qt5 "Hello, world!" app for Linux, BSD, Windows, Mac.
Qt5

hello world in qt5 Contributions in all forms (code, bug reports, community engagement, localization, etc) are warmly welcomed. Development activity I

Jan 26, 2022
New linux driver and tools for RME HDSPe sound cards and extension modules

snd-hdspe New linux kernel ALSA driver for RME HDSPe MADI / AES / RayDAT / AIO and AIO Pro sound cards and extension modules. In addition to the funct

Dec 21, 2022
StochFuzz - Sound and Cost-effective Fuzzing of Stripped Binaries by Incremental and Stochastic Rewriting
StochFuzz - Sound and Cost-effective Fuzzing of Stripped Binaries by Incremental and Stochastic Rewriting

StochFuzz: A New Solution for Binary-only Fuzzing StochFuzz is a (probabilistically) sound and cost-effective fuzzing technique for stripped binaries.

Dec 5, 2022
Plua is a superset of Lua for classic PalmOS devices with added support for graphics, UI, networking, events and sound.

Plua2c Plua is a superset of Lua for classic PalmOS devices with added support for graphics, UI, networking, events and sound. This is the source code

Jan 5, 2023
Plays native alert sound and shows native dialogs/alerts in your Flutter app.
Plays native alert sound and shows native dialogs/alerts in your Flutter app.

flutter_platform_alert 2021 © Weizhong Yang a.k.a zonble. A simple plugin to present native alerts, including playing alert sounds and showing alert d

Dec 21, 2022
The purpose of this library is to control a piezo crystal and make sound with it

Beeper-Library The purpose of this library is to control a piezo crystal and make sound with it. The piezo may either have electronic or be connected

Jan 20, 2022
No loss LV2 sound effect plugin

B.Spacr Description: LV2 sound effect plugin B.Spacr is a unique LV2 effect plugin that enables a clear and brilliant audibility of your music product

Aug 24, 2022
Bollu learns physically based sound sythesis

Kavariance bollu learns digital audio synthesis from the blog at the bottom of the sea. the name is a pun on Kaveri, A wav/river goddess, and Covarian

Dec 29, 2021
usb to 5 din midi converter-filter-router, sound generator
usb to 5 din midi converter-filter-router, sound generator

multi What is multi? It's a PCB (shield/hat) hosting a seeeduino Xiao. It has 6 potentiometers, 2 pushbuttons and a 1/8" audio out connected to the Xi

Dec 31, 2022
This repository provides the implementation of a ADC real-time viewer for an analog sound sensor.

Real-time sound analysing using microcontroller FRDM-KL25Z Acest cod este realizat pentru o platforma autonoma realizata cu kitul de la NXP care are r

Jan 20, 2022
Portedplugins - A collection of plugins for the SuperCollider sound environment, all of which are ported / remixed from elsewhere

PortedPlugins A collection of plugins for the SuperCollider sound environment, all of which are ported / remixed from elsewhere - including hardware s

Dec 20, 2022
Aims to be an accurate C port of Impulse Tracker 2.15's IT replayer (with selectable IT2 sound drivers)

it2play Aims to be an accurate C port of Impulse Tracker 2.15's IT replayer (with selectable IT2 sound drivers). This is a direct port of the original

Sep 27, 2022
Dec 31, 2022