Playing SFX: Difference between revisions

From dreamcast.wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(14 intermediate revisions by the same user not shown)
Line 1: Line 1:
Author: Andress Barajas
Author: Andress Barajas
= Introduction =
= Introduction =
This guide will focus solely on playing sound effects on the Sega Dreamcast. The Sega Dreamcast has '''2MB''' of sound RAM and supports '''64 channels''' which gives you the ability to play many sound effects at once (1 sound effect per channel). A sound effect can be either stereo or mono, and must either be '''16-bit uncompressed PCM''' samples or '''4-bit Yamaha ADPCM'''. All sounds played in this manner must be at most '''65534''' samples in length. That is samples, not bytes (samples != bytes). Each sound effect has a sample rate. The standard sample rate of a sound is '''44.1 kHz''' (44100 Hz/sec => 44100 samples/sec). So mathematically you're getting 65534/44100 => '''1.486''' secs of sound per sound effect using the standard sample rate. You can lesson the sample rate of your sound to get a longer sound effect but audio quality will be affected.
This guide will focus solely on playing sound effects on the Sega Dreamcast. The Sega Dreamcast has '''2MB''' of sound RAM and supports '''64 channels''' which gives you the ability to play many sound effects at once (1 sound effect per channel). A sound effect can be either stereo or mono, and must either be '''8/16-bit uncompressed PCM''' samples or '''4-bit Yamaha ADPCM'''. All sounds played in this manner must be at most '''65534''' samples in length. That is samples, not bytes (samples != bytes). Each sound effect has a sample rate. The standard sample rate of a sound is '''44.1 kHz''' (44100 Hz/sec => 44100 samples/sec). So mathematically you're getting 65534/44100 => '''1.486''' secs of sound per sound effect using the standard sample rate. You can lesson the sample rate of your sound to get a longer sound effect but audio quality will be affected.


This guide does not cover playing music from CD or RAM. If you want to know how to do that, checkout this guide instead(LINK LATER).
This guide does not cover playing music from CD or RAM, look at [[Streaming audio]] instead.


= API =
= API =
The KOS [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html reference manual] for this API contains more detail on each function and also has more functions not listed here. The source code for most of these methods can be found within [https://github.com/KallistiOS/KallistiOS/blob/master/kernel/arch/dreamcast/sound/snd_sfxmgr.c <code>'''snd_sfxmgr.c'''</code>].
== Initializing Sound System ==
== Initializing Sound System ==
  [http://gamedev.allusion.net/docs/kos-2.0.0/sound_8h.html#a1a108c6193ac7ab667c68ab412f04f66 int '''snd_init'''()]
<syntaxhighlight lang="c">int snd_init()</syntaxhighlight>
<syntaxhighlight lang="c">
int snd_init()
</syntaxhighlight>


Initializes the sound system. It is usually called at the beginning of your program.  You can't play sound effects before calling this function.
The [http://gamedev.allusion.net/docs/kos-2.0.0/sound_8h.html#a1a108c6193ac7ab667c68ab412f04f66 <code>'''snd_init()'''</code>] function initializes the sound system. It is usually called at the beginning of your program.  You can't play sound effects before calling this function.
== Load Sound Effect ==
== Load Sound Effect ==
  [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#ac5de88124b6cd04af8e4737b05c4b482 sfxhnd_t '''snd_sfx_load'''(const char* fn)]
<syntaxhighlight lang="c">sfxhnd_t snd_sfx_load(const char* fn)</syntaxhighlight>
<syntaxhighlight lang="c">
sfxhnd_t snd_sfx_load(const char* fn)
</syntaxhighlight>


After initializing the sound system you can start loading sound effects. This function loads a sound effect from a WAV file and returns a handle to it. The sound effect can be either stereo or mono, and must either be 16-bit uncompressed PCM samples or 4-bit Yamaha ADPCM. Keep in mind that you have less than 2MB of sound RAM to work with.
After initializing the sound system you can start loading sound effects. The [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#ac5de88124b6cd04af8e4737b05c4b482 <code>'''snd_sfx_load()'''</code>] function loads a sound effect from a WAV file and returns a handle to it. The sound effect can be either stereo or mono, and must either be 16-bit uncompressed PCM samples or 4-bit Yamaha ADPCM. Keep in mind that you have less than 2MB of sound RAM to work with.
== Play Sound Effect ==
== Play Sound Effect ==
  [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a4fb2bd23ff153c8bd1493a6417ff718f int '''snd_sfx_play'''(sfxhnd_t idx, int vol, int pan)]
<syntaxhighlight lang="c">int snd_sfx_play(sfxhnd_t idx, int vol, int pan)</syntaxhighlight>
<syntaxhighlight lang="c">
int snd_sfx_play(sfxhnd_t idx, int vol, int pan)
</syntaxhighlight>


This function plays a loaded sound effect with the specified volume (for both stereo or mono) and panning values (for mono sounds only). Behind the scenes this picks a channel that is not currently in use and plays it on that channel.  This is important because no two sound effects can play on the same channel at the same time.  
The [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a4fb2bd23ff153c8bd1493a6417ff718f <code>'''snd_sfx_play()'''</code>] function plays a loaded sound effect with the specified volume (for both stereo or mono) and panning values (for mono sounds only). Behind the scenes this picks a channel that is not currently in use and plays it on that channel.  This is important because no two sound effects can play on the same channel at the same time.  
* The volume to play at (between 0 and <s>255</s> '''254'''). 255 will crash your sound system.
* The volume to play at (between 0 and 255).
* The panning value of the '''mono''' sound effect. 0 is all the way to the left, 128 is center, 255 is all the way to the right.
* The panning value of the '''mono''' sound effect. 0 is all the way to the left, 128 is center, 255 is all the way to the right.


  [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a43534fc7fa4676be555482dbe7926f54  int '''snd_sfx_play_chn'''(int chn, sfxhnd_t idx, int vol, int pan)]
<syntaxhighlight lang="c">int snd_sfx_play_chn(int chn, sfxhnd_t idx, int vol, int pan)</syntaxhighlight>
<syntaxhighlight lang="c">
int snd_sfx_play_chn(int chn, sfxhnd_t idx, int vol, int pan)
</syntaxhighlight>


This function works similar to snd_sfx_play(), but allows you to specify the channel to play on.
The [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a43534fc7fa4676be555482dbe7926f54  <code>'''snd_sfx_play_chn()'''</code>] function works similar to snd_sfx_play(), but allows you to specify the channel to play on.
* The channel value can be between 0 and 63.
* The channel value can be between 0 and 63.


== Stop Sound Effects ==
== Stop Sound Effects ==
  [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a26d2c1e14131496e2ba920c5967750c7 void '''snd_sfx_stop'''(int chn)]
<syntaxhighlight lang="c">void snd_sfx_stop(int chn)</syntaxhighlight>
<syntaxhighlight lang="c">
The [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a26d2c1e14131496e2ba920c5967750c7 <code>'''snd_sfx_stop()'''</code>] function stops the specified channel of sound from playing.
void snd_sfx_stop(int chn)
</syntaxhighlight>
This function stops the specified channel of sound from playing


  [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a6874523117d3aae46ab49567ffe33b5e void '''snd_sfx_stop_all'''()]
<syntaxhighlight lang="c">void snd_sfx_stop_all()</syntaxhighlight>
<syntaxhighlight lang="c">
The [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a6874523117d3aae46ab49567ffe33b5e <code>'''snd_sfx_stop_all()'''</code>] function stops all channels currently allocated to sound effects from playing.
void snd_sfx_stop_all()
</syntaxhighlight>
 
This function stops all channels currently allocated to sound effects from playing
== Unload Sound Effects ==  
== Unload Sound Effects ==  
  [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#afaaacbd4c0d4ae869f9a250f61417893 void '''snd_sfx_unload'''(sfxhnd_t idx)]
<syntaxhighlight lang="c">void snd_sfx_unload(sfxhnd_t idx)</syntaxhighlight>
<syntaxhighlight lang="c">
The [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#afaaacbd4c0d4ae869f9a250f61417893 <code>'''snd_sfx_unload()'''</code>] function unloads a previously loaded sound effect, and frees the memory associated with it.
void snd_sfx_unload(sfxhnd_t idx)
</syntaxhighlight>
 
This function unloads a previously loaded sound effect, and frees the memory associated with it.
 
  [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a706323762562d9b6cf3a57870f35d14e  void '''snd_sfx_unload_all'''()]
<syntaxhighlight lang="c">
void snd_sfx_unload_all()
</syntaxhighlight>


This function unloads all previously loaded sound effect, and frees the memory associated with them.
<syntaxhighlight lang="c">void snd_sfx_unload_all()</syntaxhighlight>
The [http://gamedev.allusion.net/docs/kos-2.0.0/sfxmgr_8h.html#a706323762562d9b6cf3a57870f35d14e <code>'''snd_sfx_unload_all()'''</code>] function unloads all previously loaded sound effect, and frees the memory associated with them.
== Shutdown Sound System ==  
== Shutdown Sound System ==  
  [http://gamedev.allusion.net/docs/kos-2.0.0/sound_8h.html#aaaa1b0da74e9d8e2615c6a5aa9a160b1 void '''snd_shutdown'''()]
<syntaxhighlight lang="c">void snd_shutdown()</syntaxhighlight>
<syntaxhighlight lang="c">
The [http://gamedev.allusion.net/docs/kos-2.0.0/sound_8h.html#aaaa1b0da74e9d8e2615c6a5aa9a160b1 <code>'''snd_shutdown()'''</code>] function shuts down the sound system and frees all allocated memory.
void snd_shutdown()
</syntaxhighlight>


Shut down the sound system and frees all allocated memory.
= Code Example =
The following example demonstrates how to play sound effects on their own channel (using <code>'''snd_sfx_play()'''</code>) and on the same channel(using <code>'''snd_sfx_play_chn()'''</code>).  By playing the sound effects on their own channel (pressing A,B,X,Y), you will notice that the sound effects can overlap each other; while playing sound effects on the same channel (with the d-pad), you will only hear one sound effect at a time. Another thing to point out is that all of the beep WAV files referenced here are MONO so I also pan the sound with hard coded values (LEFT, CENTER, RIGHT).  You can also adjust the volume of all the sound effects using the left and right triggers of the controller.


= Code Example =
Error handling is non-existent in this example. You'll want to check if the sound effects are loaded correctly and that you have a controller plugged in when running this demo.
The following example demonstrates how to play sound effects on their own channel(using '''snd_sfx_play''') and on the same channel(using '''snd_sfx_play_chn''').  By playing the sound effects on their own channel(pressing A,B,X,Y) you will notice that the sound effects can overlap each other, while playing sound effects on the same channel(with the d-pad) you will only hear one sound effect at a time. Another thing to point out is that all of the beep WAV files referenced here are MONO so I also pan the sound with hard coded values.  You can also adjust the volume of all the sound effects using the left and right triggers of the controller.


You can find can all the code and resources of this example on [https://github.com/andressbarajas/dreamcastwiki/tree/master/Audio/SFX GITHUB]
You can find all the code and resources of this example on '''[https://github.com/andressbarajas/dreamcastwiki/tree/master/Audio/SFX Github]'''.


'''NOTE: This code sample may not work in Sega Dreamcast emulators.'''
'''NOTE: This code sample may not work in Sega Dreamcast emulators.'''


<syntaxhighlight lang="c">
<syntaxhighlight lang="c" line="line" highlight="22, 25-28, 42, 45, 48, 51, 56, 59, 62, 65, 93-96, 98, 100">
#include <kos.h>
#include <kos.h>
#define LEFT 0
#define CENTER 128
#define RIGHT 255


extern uint8 romdisk[];
extern uint8 romdisk[];
Line 118: Line 94:
         // Play sounds on different channels
         // Play sounds on different channels
         if(button_pressed(current_buttons, changed_buttons, CONT_A)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_A)) {
             snd_sfx_play(beep1, volume, 128);
             snd_sfx_play(beep1, volume, CENTER);
         }
         }
         if(button_pressed(current_buttons, changed_buttons, CONT_B)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_B)) {
             snd_sfx_play(beep2, volume, 255);
             snd_sfx_play(beep2, volume, RIGHT);
         }
         }
         if(button_pressed(current_buttons, changed_buttons, CONT_X)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_X)) {
             snd_sfx_play(beep3, volume, 0);
             snd_sfx_play(beep3, volume, LEFT);
         }
         }
         if(button_pressed(current_buttons, changed_buttons, CONT_Y)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_Y)) {
             snd_sfx_play(beep4, volume, 128);
             snd_sfx_play(beep4, volume, CENTER);
         }
         }


         // Play sounds on same channel
         // Play sounds on same channel
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_DOWN)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_DOWN)) {
             snd_sfx_play_chn(0, beep1, volume, 128);
             snd_sfx_play_chn(0, beep1, volume, CENTER);
         }
         }
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_RIGHT)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_RIGHT)) {
             snd_sfx_play_chn(0, beep2, volume, 255);
             snd_sfx_play_chn(0, beep2, volume, RIGHT);
         }
         }
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_LEFT)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_LEFT)) {
             snd_sfx_play_chn(0, beep3, volume, 0);
             snd_sfx_play_chn(0, beep3, volume, LEFT);
         }
         }
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_UP)) {
         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_UP)) {
             snd_sfx_play_chn(0, beep4, volume, 128);
             snd_sfx_play_chn(0, beep4, volume, CENTER);
         }
         }


Line 168: Line 144:
     }
     }


     // Unload all sound effects from AICA RAM
     // Unload all sound effects from sound RAM
     snd_sfx_unload(beep1);
     snd_sfx_unload(beep1);
     snd_sfx_unload(beep2);
     snd_sfx_unload(beep2);

Latest revision as of 10:04, 13 November 2023

Author: Andress Barajas

Introduction

This guide will focus solely on playing sound effects on the Sega Dreamcast. The Sega Dreamcast has 2MB of sound RAM and supports 64 channels which gives you the ability to play many sound effects at once (1 sound effect per channel). A sound effect can be either stereo or mono, and must either be 8/16-bit uncompressed PCM samples or 4-bit Yamaha ADPCM. All sounds played in this manner must be at most 65534 samples in length. That is samples, not bytes (samples != bytes). Each sound effect has a sample rate. The standard sample rate of a sound is 44.1 kHz (44100 Hz/sec => 44100 samples/sec). So mathematically you're getting 65534/44100 => 1.486 secs of sound per sound effect using the standard sample rate. You can lesson the sample rate of your sound to get a longer sound effect but audio quality will be affected.

This guide does not cover playing music from CD or RAM, look at Streaming audio instead.

API

The KOS reference manual for this API contains more detail on each function and also has more functions not listed here. The source code for most of these methods can be found within snd_sfxmgr.c.

Initializing Sound System

int snd_init()

The snd_init() function initializes the sound system. It is usually called at the beginning of your program. You can't play sound effects before calling this function.

Load Sound Effect

sfxhnd_t snd_sfx_load(const char* fn)

After initializing the sound system you can start loading sound effects. The snd_sfx_load() function loads a sound effect from a WAV file and returns a handle to it. The sound effect can be either stereo or mono, and must either be 16-bit uncompressed PCM samples or 4-bit Yamaha ADPCM. Keep in mind that you have less than 2MB of sound RAM to work with.

Play Sound Effect

int snd_sfx_play(sfxhnd_t idx, int vol, int pan)

The snd_sfx_play() function plays a loaded sound effect with the specified volume (for both stereo or mono) and panning values (for mono sounds only). Behind the scenes this picks a channel that is not currently in use and plays it on that channel. This is important because no two sound effects can play on the same channel at the same time.

  • The volume to play at (between 0 and 255).
  • The panning value of the mono sound effect. 0 is all the way to the left, 128 is center, 255 is all the way to the right.
int snd_sfx_play_chn(int chn, sfxhnd_t idx, int vol, int pan)

The snd_sfx_play_chn() function works similar to snd_sfx_play(), but allows you to specify the channel to play on.

  • The channel value can be between 0 and 63.

Stop Sound Effects

void snd_sfx_stop(int chn)

The snd_sfx_stop() function stops the specified channel of sound from playing.

void snd_sfx_stop_all()

The snd_sfx_stop_all() function stops all channels currently allocated to sound effects from playing.

Unload Sound Effects

void snd_sfx_unload(sfxhnd_t idx)

The snd_sfx_unload() function unloads a previously loaded sound effect, and frees the memory associated with it.

void snd_sfx_unload_all()

The snd_sfx_unload_all() function unloads all previously loaded sound effect, and frees the memory associated with them.

Shutdown Sound System

void snd_shutdown()

The snd_shutdown() function shuts down the sound system and frees all allocated memory.

Code Example

The following example demonstrates how to play sound effects on their own channel (using snd_sfx_play()) and on the same channel(using snd_sfx_play_chn()). By playing the sound effects on their own channel (pressing A,B,X,Y), you will notice that the sound effects can overlap each other; while playing sound effects on the same channel (with the d-pad), you will only hear one sound effect at a time. Another thing to point out is that all of the beep WAV files referenced here are MONO so I also pan the sound with hard coded values (LEFT, CENTER, RIGHT). You can also adjust the volume of all the sound effects using the left and right triggers of the controller.

Error handling is non-existent in this example. You'll want to check if the sound effects are loaded correctly and that you have a controller plugged in when running this demo.

You can find all the code and resources of this example on Github.

NOTE: This code sample may not work in Sega Dreamcast emulators.

#include <kos.h>

#define LEFT 0
#define CENTER 128
#define RIGHT 255

extern uint8 romdisk[];
KOS_INIT_ROMDISK(romdisk);

static void draw_instructions(uint8_t volume);

static cont_state_t* get_cont_state();
static int button_pressed(unsigned int current_buttons, unsigned int changed_buttons, unsigned int button);

int main(int argc, char **argv) {
    uint8_t volume = 128;
    int volume_changed = 1;
    cont_state_t* cond;

    vid_set_mode(DM_640x480, PM_RGB555);
    // Initialize sound system
    snd_init();

    // Load wav files found in romdisk
    sfxhnd_t beep1 = snd_sfx_load("/rd/beep-1.wav");
    sfxhnd_t beep2 = snd_sfx_load("/rd/beep-2.wav");
    sfxhnd_t beep3 = snd_sfx_load("/rd/beep-3.wav");
    sfxhnd_t beep4 = snd_sfx_load("/rd/beep-4.wav");

    unsigned int current_buttons = 0;
    unsigned int changed_buttons = 0;
    unsigned int previous_buttons = 0;

    for(;;) {
        cond = get_cont_state();
        current_buttons = cond->buttons;
        changed_buttons = current_buttons ^ previous_buttons;
        previous_buttons = current_buttons;
        
        // Play sounds on different channels
        if(button_pressed(current_buttons, changed_buttons, CONT_A)) {
            snd_sfx_play(beep1, volume, CENTER);
        }
        if(button_pressed(current_buttons, changed_buttons, CONT_B)) {
            snd_sfx_play(beep2, volume, RIGHT);
        }
        if(button_pressed(current_buttons, changed_buttons, CONT_X)) {
            snd_sfx_play(beep3, volume, LEFT);
        }
        if(button_pressed(current_buttons, changed_buttons, CONT_Y)) {
            snd_sfx_play(beep4, volume, CENTER);
        }

        // Play sounds on same channel
        if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_DOWN)) {
            snd_sfx_play_chn(0, beep1, volume, CENTER);
        }
        if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_RIGHT)) {
            snd_sfx_play_chn(0, beep2, volume, RIGHT);
        }
        if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_LEFT)) {
            snd_sfx_play_chn(0, beep3, volume, LEFT);
        }
        if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_UP)) {
            snd_sfx_play_chn(0, beep4, volume, CENTER);
        }

        // Adjust Volume
        if(cond->ltrig > 0) {
            volume_changed = 1;

            if(volume < 254)
                volume++;
        }
        if(cond->rtrig > 0) {
            volume_changed = 1;

            if(volume > 0)
                volume--;
        }

        // Exit Program
        if(button_pressed(current_buttons, changed_buttons, CONT_START))
            break;

        if(volume_changed) {
            volume_changed = 0;
            draw_instructions(volume);
        }
    }

    // Unload all sound effects from sound RAM
    snd_sfx_unload(beep1);	
    snd_sfx_unload(beep2);
    snd_sfx_unload(beep3);	
    snd_sfx_unload(beep4);
    // OR
    // snd_sfx_unload_all();	

    snd_shutdown();

    return 0;
}

static void draw_instructions(uint8_t volume) {
    int x = 20, y = 20+24;
    int color = 1;
    char current_volume_str[32];
    memset(current_volume_str, 0, 32);
    snprintf(current_volume_str, 32, "Current Volume: %3i", volume);
    
    bfont_draw_str(vram_s + y*640+x, 640, color, "Press A,B,X,Y to play beeps on separate channels");
    y += 48;
    bfont_draw_str(vram_s + y*640+x, 640, color, "Press UP,DOWN,LEFT,RIGHT on D-Pad to play beeps");
    y += 24;
    bfont_draw_str(vram_s + y*640+x, 640, color, "on the same channel");
    y += 48;
    bfont_draw_str(vram_s + y*640+x, 640, color, "Press L-Trigger/R-Trigger to +/- volume");
    y += 24;
    bfont_draw_str(vram_s + y*640+x, 640, color, current_volume_str);
    y += 48;
    bfont_draw_str(vram_s + y*640+x, 640, color, "Press Start to exit program");
}

static cont_state_t* get_cont_state() {
    maple_device_t* cont;
    cont_state_t* state;

    cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER);
    if(cont) {
        state = (cont_state_t*)maple_dev_status(cont);
        return state;
    }

    return NULL;
}

static int button_pressed(unsigned int current_buttons, unsigned int changed_buttons, unsigned int button) {
    if (changed_buttons & button) {
        if (current_buttons & button)
            return 1;
    }

    return 0;
}