Using the MD_YM2413 library
The MD_YM2413 library initializes the hardware to sensible defaults and assigns the built-in piano instrument to all channels. As with any instrument, a note starts with a note on event and ends with a note off event.
The note on event is generated when the noteOn() method is invoked in the
application code. The library provides flexibility on how the note off events are generated.
- Invoking noteOn() without a duration parameter means the user code needs to generate the note off for the channel. This method is suited to applications that directly link a physical event to playing the note (eg, switch on for note on and switch off for note off), or where the music being played includes its own note on and off events (eg, a MIDI score).
- Invoking the noteOn() with a duration parameter causes the library to generate a note off event at the end of the specified total duration. This method suits applications where the sound duration is defined by the music being played (eg, RTTTL tunes). In this case the user code can determine if the noteOff() event has been generated by using the isIdle() method.
For automatic note off events to be managed by the library, the loop() function must invoke run() every iteration through loop(). The run() method executes very quickly if the library has nothing to process, imposing minimal overheads on the user application. run() may be omitted if the application manages all the noteOn() and noteOff() events within the application.
Defining custom instruments
As already mentioned, the YM2413 allows the parameters for one custom instrument to be defined.
It is easy to set up the parameters in the appropriate registers but there is little documentation about how these are used. Once worked out, the data definition can be directly loaded into the IC’s registers. This is the loadInstrument() method in the MD_YM2413 library.
Yamaha’s more advanced synthesizers don’t have built-in instruments and require all instruments to be customized. These, however, use OPL2 or OPL3 register formats. Libraries of these instrument definitions are very easy to find online as a sequence of bytes to load into the hardware registers.
By examining and contrasting OPL2/OPL3 definition with the reduced OPLL set used in the YM2413, it becomes clear that OPL2/3 can be mostly mapped to an OPLL compatible format.
The major difference between formats is how the registers are laid out (sequence of bytes) and the variety of generated wave types. OPL2/3 definitions can have up to 7 different wave types while OPLL is limited to just two – full or half sine wave.
This similarity between instrument definitions allows the library to map between the two formats using some bit twiddling. This is implemented in the library loadInstrumentOPL2() method and in most cases the translation works perfectly well. The few instruments the use the additional wave types in OPL2/3 sound strange, but are still discernible.
Once the instrument profile definition is loaded into the hardware using the relevant library method, it can be assigned to any channel in the same way as the built-in profiles.
Many of the sketches described here were developed for the previous SN76489 project and used as a ready-made starting point to adapt for this new sound hardware.
Playing RTTTL files
This code leverages the MD_RTTTLParse library to scan the RTTTL strings. This makes for a very compact sketch that uses 3 channels using different instruments (piano, synthesizer bass and acoustic bass) to play the RTTTL tunes.
Playing MIDI files
MIDI files are stored on a SD card the the code leverages the MD_MIDIFile library to parse the files.
The MIDI file player posed an interesting challenge. MIDI tracks generally need to have percussion enabled. This restricted the number of melodic channels to 6 and MIDI can have up to 24 channels. This meant that a channel scheduling algorithm needed to be implemented to ensure that idle channels could be reused for new instruments that may need to be played. This works on the assumption that not all instruments are active at the same time. While this works fine for most music playback and much of the time for more complex arrangements, when more than six melodic channels are needed there are obvious gaps in the playback …
Playing VGM files
Video Game Music (VGM) is an audio file format for multiple video game platforms, such as Sega Master System, Game Gear, Mega Drive/Genesis, MSX, Neo Geo, IBM PC AT (Adlib/SoundBlaster), and has expanded to a variety of arcade system boards since its release. There are a large number of VGM file repositories online offering free file download, such as this one.
VGM files capture of the exact data that was transferred to the music hardware by the video game, making it a high fidelity duplicate of the original. The published file format supports a large number of output hardware systems, including YM2413 enabled gaming and systems.
As the files can be quite large, the sketch reads the data from an SD card and manages the timing of direct register writes to the YM2413. For some complex recordings the data transfer rates can be quite high and the music sounds slow as the software is not efficient enough to keep up, although it works well for most VGM tunes.
An interesting feature of the file format is that the files are designed to allow for looping of one section over and over. The sketch is set to loop the ‘looped section’ once only, so most tracks seem to end rather suddenly.
This sketch is implemented as a command line interface that allows the management of music playback.
The YM2413 is a versatile and low-cost FM synthesizer IC that has a lot more to offer and I may come back to it for future projects.
It is easy to see why the YM2413 was often paired up with the SN76489 sound generator. One was good for ‘real’ music playback and the other for sound effects, both essential elements of computer games.