For now, we have been working with the visual part; like displaying backgrounds, sprites, colors, backgrounds, etc. But a game is not complete if it doesn't have sound; both sound effects and music to enhance the game experience.
Therefore, it is important to know how to add sound to our game; from the different effects such as attack, character voice or even more complex effects, it is important to be able to add them and enjoy them once our game is running.
We can not forget the music; since for many the soundtrack of Mega Drive games, has been our childhood and today just listening to a couple of chords, transports us to that time. That's why music is so important in a videogame.
In this chapter, we are going to show how the Mega Drive is able to play sound and even music and how we can add it to our game.
We will begin by talking about the sound system that is included in the Sega Mega Drive, and how the different elements that compose it work. In addition, we will talk about how this music can be created and finally, we will see some examples of how to add music and sound to our games using SGDK.
Let's start talking about the Sega Mega Drive Sound System; as we have seen in the architecture chapter, Sega Mega Drive has two sound chips:
- Yamaha YM2612 chip; with FM1 sound (6 channels).
- PSG chip (SN76496); 8-bit sound with the ability to output 3 pulse waves and 1 noise channel. It is inside the VDP.
These two chips are orchestrated by the Zilog z80 processor; this is the processor that sends or receives the sound information, using the sound Ram (8Kb); through the 8-bit bus that connects both chips.
We will start talking about the Yamaha YM2612; which is the chip mainly in charge of emitting FM sound or samples, so we have up to 6 channels to play music or sound.
It can output up to 5 FM signals at the same time, and one sample signal, although care must be taken when mixing these signals. It is important, when working with this chip, not to create bottlenecks when mixing sounds or even mix them wrongly.
In addition to the Yamaha YM2612 sound chip, the Sega Mega Drive has a sound chip for 8-bit sound thanks to the SN76496 chip from Texas Instruments2 , which allows sound to be emitted through several channels and is also the chip used to emit sound in backward compatibility mode (Master System).
This chip emits sound by 4 channels; 3 for wave generation (tones), and another one for noise. This chip is included in the VDP itself, and can be used together with the YM2612 as a sound system for the Sega Mega Drive.
This chip is also used in other home electronics projects, such as the Durango project.
We have seen the two sound chips provided by the Sega Mega Drive; however, by themselves they could not be used; as they need the Zilog Z80 processor, in order to be orchestrated.
This 8-bit processor has two functionalities; the first one, to support the sound system together with 8 Kb of RAM, to orchestrate the sound chips. On the other hand, in backward compatibility mode it is the main processor for Sega Master System games.
It is important to know this processor, since when working with the sound, it is necessary to program it, unlike the rest of the components, which usually use the Motorola 68000 processor; for this reason, it is complicated to use sound in Sega Mega Drive.
As we have been talking about, the sound system is composed of the two chips (Yamaha and PSG), which are orchestrated by the Zilog Z80 processor; therefore, it is necessary to send the information to this processor, and execute the different instructions for the chips to emit the relevant sound.
This requires knowledge and programming of the Zilog Z80 processor, normally the programming is not done as we are used to in this book using the C programming language. Instead, we use assembly language for the Z80 processor.
printc: ; Routine print character
ld a,c
call dtoa2d ; Split A register into D and E
ld a,d ; Print first digit in D
cp '0' ; Don't bother printing leading 0
jr z,printc2
rst 16 ; Spectrum:Print character in 'A'
In the above snippet, we see a bit of assembler for the z80 (in this case, it is for ZX Spectrum); creating a program for the Z80 to orchestrate the sound chips, is what is commonly called a Sound Driver.
There are several implementations of sound drivers for Sega Mega Drive; such as GEMS 3, MUCOM88 4, 4PCM, XGM or XGM2. Each one has been used in several games and used to compose music since they brought tools for it, like a tracker 5 to compose.
Although we have named several Drivers, we are going to focus on the XGM Driver; this driver is one of those used by SGDK, and that comes by default. Although we can use others, in this case we will focus on this Driver. It was developed to be used entirely with the z80 processor; so we can use the Motorola 68000 for other uses. It has been developed to be used with the SGDK since it has been developed by Stephane Dallongeville (the author of SGDK).
Among its features, it has:
- It only uses Z80.
- Developed just to minimize the CPU having to decode the sound.
- It allows FM and PSG sound at the same time, using the channels both to send sound or the different samples; being able to reach up to 13 sound channels (5FM + 4PCM + 4PSG).
- It allows you to play sound effects in PCM format with 16 priority levels; this is useful when working with different sources.
It is important to know that a second version of the XGM Driver was released in January 2024; this driver is included in the SGDK 2.00 version. This driver includes improvements with respect to the previous version since it had many problems when compiling and generating the binary files; since they consumed a lot of space in the ROM (20/25% of the ROM could be the music). This new Driver contains the next features:
- Runs 100% on the Z80 (uses the m68K to calculate the times).
- Support for music pause and playback.
- Adjustable execution speed (tempo).
- VGM Format Input.
- Reduces ROM usage with respect to XGM.
- FM and PSG adjustable volume.
- 3 PCM channels for 8 bits.
You can find more information about the XGM2 at the SGDK 2.00 Documentation.
We have been talking about the Drivers that are in charge of giving the instructions to the Z80 processor to orchestrate the different sound chips; but another very important aspect is to talk about how we can create the music of our game. For it, Trackers or sequencers are usually used, to create the different instructions that later the Driver will read and will proceed to execute in the sound chips.
Although many Drivers already had some integrated editors, such as GEMS or MUCOM88. More modern and sophisticated programs are used today to be able to create music on our computer in a much simpler way.
In this book, we will discuss the use of Deflemask, which is one of the most used. This program will allow us to create our music and export it to different systems; among its features that includes:
- Real-time emulation of the different sound chips (including yamaha).
- MIDI6 devices support.
- ROMS generation support.
- Use of the VGM7 format as output.
This program is not open Source software and has a cost of $9.99; it is one of the most used to create music, so it is not a bad price at all. Besides there are a lot of tutorials on the internet about this music creation software.
After seeing how the sound system is composed; the Drivers and how to create the sound or music, We are going to see two examples; the first example we are going to see the original XGM Driver, and all the functions related.
The second example, we are going to use the new XGM2 Driver; also, we are going to see the related functions for use with this Driver. Of course in both examples, we are going to see how to import all the resources.
Let's check the first example that you can find in the Github Repository that accompanies this book; the folder with this example is ej15.musicandsound. We are going to show how to import music resources or sound effects using the rescomp tool, which is in charge of reading the files and converting them to binary. We have to differentiate that the files with music are in VGM format, while the files with sound effects are in WAV format.
We will start by showing how to import a VGM file for use with the XGM driver.
XGM name "file" timing options
Where:
- name: Resource's Name.
- file: Path inside the res folder to the file containing the music.
- timing: Indicates the tempo to be loaded; depending on the type of system, it may have the following value:
- -1/AUTO: (NTSC or PAL depending on the information stored in the VGM).
- 0/NTSC: Indicates the system will be NTSC.
- 1/PAL: Indicates that the system will be PAL.
- options: additional parameters for the tool that imports VGM files.
As you can see, you can pass additional parameters to the xgmtool tool; this tool is used to convert the VGM file to binary. If you need more information about xgmtool, you can consult the SGDK documentation.
If on the other hand we want to import a file with a sound effect, we can import a Wav file; with that sound. Let's see how we can import a Wav file using rescomp.
WAV name wav-file driver out-rate far
Where:
- name: Resource's Name.
- wav-file: Path of the wav file inside the res folder.
- driver: Sound driver to use; it can be:
- 0/PCM: Single-channel 8-bit driver.
- 1/2/2ADPCM: 2-channel 4-bit driver.
- 3/4/4PCM: 4 channels at 8 bits.
- 5/6/XGM: 4 channels with 8 bits.
- XGM2: It can mix up to 3 samples (8 bit signed) at either 13.3 Khz or 6.65 Khz while playing XGM music.
- out-rate: Output rate to decode the output. Only used for Z80_DRIVER_PCM.
- far: Additional parameter to add information at the end of the ROM (used for Bank-switch).
Once we have seen how each of the resources can be imported, we are going to show which sound resources we are going to import in this example:
XGM music1 "music/infiltration_phase.vgm" AUTO
XGM music2 "music/guaguas2.vgm" AUTO
WAV sound1 "sound/Explosion2.wav" XGM
WAV sound2 "sound/Jump4.wav" XGM
WAV sound3 "sound/Teleport4.wav" XGM
Where we can see that we imported 2 vgm files, and three audio effects. You can consult the rest of the imported resources in the res folder of the example.
Once the resources have been imported, we can now focus on the code; this set will perform the following actions:
- A Button: Play Music 1.
- B Button: Play Music 2.
- C Button: Play current Sound Effect.
- Start Button: Stop Sound.
- Left Button: Select Next Sound Effect.
- Right Button: Select Previous Sound Effect.
With this in mind, we will proceed to show part of the source code:
u8 sound;
const u8* sounds[3];
We will use these two global variables to reference which sound we have collected and to store the sound effects information.
sound=1;
sounds[0]=sound1;
sounds[1]=sound2;
sounds[2]=sound3;
As we can see, the three sound effects have been initialized and the variable has been set to 1, so we will load the first sound by default.
After seeing the global variables and how we are going to initialize them, we will review the inputHandler
function, which is in charge of managing each time we press a button in the controller. We are going to review this function:
void inputHandler(u16 joy, u16 changed,
u16 state){
if(joy == JOY_1){
...
Remember that this function will get the values of the buttons that have been pressed; in this way, we can check one by one which buttons are pressed and perform each action; let's see what happens on each button:
if(changed & state & BUTTON_A){
if(XGM_isPlaying()){
XGM_stopPlay();
}
XGM_startPlay(music1);
}
Pressing the A button will stop the previous music and play the first tune again. As you can see, several functions are used which are specific to the use of the XGM driver. Let's take a look at these functions.
The XGM_isPlaying
function returns non-zero if the XGM driver is playing a song.
The XGM_stopPlay
function, for playback of the current music from the XGM driver.
The XGM_startPlay
function plays a music resource using the XGM driver. It receives by parameter the resource that we have imported using rescomp.
We can see that when pressing button B, the same thing happens but it plays the second song:
if(changed & state & BUTTON_B){
if(XGM_isPlaying()){
XGM_stopPlay();
}
XGM_startPlay(music2);
}
In the case of pressing the C button, the current sound will be played; let's see the code fragment:
if(changed & state & BUTTON_C){
XGM_setPCM(sound,sounds[sound-1]
,sizeof(sounds[sound-1]));
XGM_startPlayPCM(sound,14,
SOUND_PCM_CH4);
}
We can see that two new functions are used, in order to reproduce a sound effect. Let's take a look at these functions.
The XGM_setPCM
function initializes the sound to be played using the XGM Driver; it receives the following parameters:
- id: Sound Identifier
- sound: Sound to initialize (name of the resource imported with rescomp).
- length: length of the sound to be played.
The XGM_startPlayPCM
function plays a previously initialized sound effect; it receives the following parameters:
- id: Sound Identifier assigned in the previous step.
- priority: defines the priority with which the sound will be played. It can have a value between 0 and 15; 0 being the lowest and 15 the highest.
- channel: channel to play the sound; you can select the different channels allowed by the XGM driver. In this case,
SOUND_PCM_CH4
indicates that channel 4 will be used as PCM. See the SGDK documentation for all available channels.
Now that we have seen what happens when the C button is pressed, let's see what happens when the Start button is pressed.
if(changed & state & BUTTON_START){
XGM_stopPlay();
XGM_stopPlayPCM(SOUND_PCM_CH4);
}
In this case it is about stopping the playback of both the music and the sound effect that is playing. Two functions are used for this purpose; XGM_stopPlay
and XGM_stopPlayPCM
for the playback of the current music.
On the other hand, the function XGM_stopPlayPCM
; for the playback of the sound effect being played on a specific channel; receives the following parameters:
- channel: channel that will stop the sound playback. It has the same data as in the previous function. See the SGDK documentation for all available channels.
Finally, pressing the left or right buttons will select the previous or next sound effect; but will not play it.
if(changed & state & BUTTON_RIGHT){
sound++;
if(sound==4) sound=1;
}else if (changed & state & BUTTON_LEFT)
{
sound--;
if(sound==0) sound=3;
}
Let's focus now on the new XGM2 Driver; for this case, we are going to reuse the previous example, and change for use with the XGM2 Sound Driver. This example can be found in the repository that companies this book, at the folder ej15b.xgm2.
NOTE: You will need the SGDK 2.00 version or later to use this example.
First, let's focus on how to import the resources using rescomp; in this case a new object type is used to import the music:
XGM2 name "file" options
Where:
- name: Resource's Name.
- file: VGM File Name; must be at the res folder.
- options: options to be added to call the new tool xgm2tool; incorporated in SGDK version 2.00.
In the case of WAV files, it is done in the same way of the previous example but indicating the driver type as XGM2; for example:
WAV sound1 "sound/Explosion2.wav" XGM2
After importing all the resources, we can focus on the code. The code is the same as the previous example; but we are going to use the new added functions to use the XGM2 sound driver.
Let's focus on the main
function. We can see two new functions; the XGM2_setFMVolume
and the XGM2_setPSGVolume
functions. These functions control the FM and PSG chips Volume. This is a new feature added with the XGM2 Sound Driver.
The function XGM2_setFMVolume
; set the current FM sound volume; receive the next parameter:
- volume: an integer between 0 and 100; to set the FM sound volume.
On the other hand the function XGM2_setPSGVolume
; this function controls the PSG chip volume. It Receives the next parameter:
- volume: an integer between 0 and 100; to set the FM sound volume.
Now let's focus on the inputhandler
function; this function has the same functionality and structure as the earlier example. But now we are going to see the new functions for use with the XGM2 Sound Driver.
First, we are going to see the function XGM2_play
; this function play the current resource using the XGM2 Sound Driver; lets see the Parameters:
- resource: resource pointer to be played.
Other functions that we can see are XGM2_stop
; this function stops current music. Also, we can see the function XG2_isPlaying
; this function returns TRUE
if there is music playing or FALSE
otherwise.
Let's focus on the PCM sound functions for the XGM2 Sound Driver.
The function XGM2_playPCM
plays a PCM sound using one channel. Let's see the parameters:
- resource: Resource Pointer to be played.
- len: Resource len; must be 256 Multiple.
- channel: Channel to be used; you can use the
SoundPCMChannel
enum to select the channel. See the SGDK documentation for more information.
For last, we can see the XGM2_stopPCM
this function stops the current PCM sound that is currently played; this function receive the next parameter:
- channel: Channel to be used; you can use the
SoundPCMChannel
enum to select the channel. See the SGDK documentation for more information.
There are more functions for use with the XGM2 Sound Driver; check the SGDK Documentation for more information.
Once we have finished reviewing the buttons and how each case works, we can compile and run our example. We leave it to the reader to review how we display the screen by loading an image and a TileSet. If everything goes correctly, you will be able to see and hear this example in the emulator.
I want to thank Diego Escudero for providing the melodies for this example.
- Mega Drive Architecture: https://www.copetti.org/writings/consoles/mega-drive-genesis/
- Games and Sound Drivers List: http://gdri.smspower.org/wiki/index.php/Mega_Drive/Genesis_Sound_Driver_List
- GEMS: https://segaretro.org/GEMS
- MUCOM88: https://onitama.tv/mucom88/index_en.html
- XGM: https://raw.githubusercontent.com/Stephane-D/SGDK/master/bin/xgm.txt
- XGM2: https://github.com/Stephane-D/SGDK/blob/master/bin/xgm2.txt
- Deflemask: https://www.deflemask.com/
- Sound Effects (Open Game Art): https://opengameart.org/content/sound-effects-mini-pack15
Footnotes
-
FM sound; refers to the sound generated through the variation of its frequency; in such a way that a signal is generated by varying that frequency. ↩
-
Texas Instruments is a registered trademark. All rights reserved. ↩
-
GEMS (Genesis Editor for Music and Sound effects), is a sound driver for Sega Mega Drive developed by Recreational Brainware. ↩
-
MUCOM88, is a sound driver developed by Yuzo Koshiro, considered one of the greatest composers of music for videogames (composer of the Streets of Rage soundtrack). ↩
-
Music Tracker is a sequencer for composing music electronically, either with a synthesizer or with a computer program. ↩
-
MIDI; a technology standard that describes a protocol, interface and connectors for using musical instruments to communicate with the computer. ↩
-
VGM; music audio format for different devices; mainly intended for video games. ↩