Difference between revisions of "Playing SFX"

From dreamcast.wiki
Jump to navigation Jump to search
Line 80: Line 80:
 
'''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">
 
#include <kos.h>
 
#include <kos.h>
  

Revision as of 11:59, 30 May 2020

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 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).

API

Initializing Sound System

 int snd_init()
int snd_init()

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)
sfxhnd_t snd_sfx_load(const char* fn)

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.

Play Sound Effect

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

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 volume to play at (between 0 and 255 254). 255 will crash your sound system.
  • 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)
int snd_sfx_play_chn(int chn, sfxhnd_t idx, int vol, int pan)

This 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)
void snd_sfx_stop(int chn)

This function stops the specified channel of sound from playing

 void snd_sfx_stop_all()
void snd_sfx_stop_all()

This function stops all channels currently allocated to sound effects from playing

Unload Sound Effects

 void snd_sfx_unload(sfxhnd_t idx)
void snd_sfx_unload(sfxhnd_t idx)

This function unloads a previously loaded sound effect, and frees the memory associated with it.

 void snd_sfx_unload_all()
void snd_sfx_unload_all()

This function unloads all previously loaded sound effect, and frees the memory associated with them.

Shutdown Sound System

 void snd_shutdown()
void snd_shutdown()

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 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 GITHUB

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

  1 #include <kos.h>
  2 
  3 extern uint8 romdisk[];
  4 KOS_INIT_ROMDISK(romdisk);
  5 
  6 static void draw_instructions(uint8_t volume);
  7 
  8 static cont_state_t* get_cont_state();
  9 static int button_pressed(unsigned int current_buttons, unsigned int changed_buttons, unsigned int button);
 10 
 11 int main(int argc, char **argv) {
 12     uint8_t volume = 128;
 13     int volume_changed = 1;
 14     cont_state_t* cond;
 15 
 16     vid_set_mode(DM_640x480, PM_RGB555);
 17     // Initialize sound system
 18     snd_init();
 19 
 20     // Load wav files found in romdisk
 21     sfxhnd_t beep1 = snd_sfx_load("/rd/beep-1.wav");
 22     sfxhnd_t beep2 = snd_sfx_load("/rd/beep-2.wav");
 23     sfxhnd_t beep3 = snd_sfx_load("/rd/beep-3.wav");
 24     sfxhnd_t beep4 = snd_sfx_load("/rd/beep-4.wav");
 25 
 26     unsigned int current_buttons = 0;
 27     unsigned int changed_buttons = 0;
 28     unsigned int previous_buttons = 0;
 29 
 30     for(;;) {
 31         cond = get_cont_state();
 32         current_buttons = cond->buttons;
 33         changed_buttons = current_buttons ^ previous_buttons;
 34         previous_buttons = current_buttons;
 35         
 36         // Play sounds on different channels
 37         if(button_pressed(current_buttons, changed_buttons, CONT_A)) {
 38             snd_sfx_play(beep1, volume, 128);
 39         }
 40         if(button_pressed(current_buttons, changed_buttons, CONT_B)) {
 41             snd_sfx_play(beep2, volume, 255);
 42         }
 43         if(button_pressed(current_buttons, changed_buttons, CONT_X)) {
 44             snd_sfx_play(beep3, volume, 0);
 45         }
 46         if(button_pressed(current_buttons, changed_buttons, CONT_Y)) {
 47             snd_sfx_play(beep4, volume, 128);
 48         }
 49 
 50         // Play sounds on same channel
 51         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_DOWN)) {
 52             snd_sfx_play_chn(0, beep1, volume, 128);
 53         }
 54         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_RIGHT)) {
 55             snd_sfx_play_chn(0, beep2, volume, 255);
 56         }
 57         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_LEFT)) {
 58             snd_sfx_play_chn(0, beep3, volume, 0);
 59         }
 60         if(button_pressed(current_buttons, changed_buttons, CONT_DPAD_UP)) {
 61             snd_sfx_play_chn(0, beep4, volume, 128);
 62         }
 63 
 64         // Adjust Volume
 65         if(cond->ltrig > 0) {
 66             volume_changed = 1;
 67 
 68             if(volume < 254)
 69                 volume++;
 70         }
 71         if(cond->rtrig > 0) {
 72             volume_changed = 1;
 73 
 74             if(volume > 0)
 75                 volume--;
 76         }
 77 
 78         // Exit Program
 79         if(button_pressed(current_buttons, changed_buttons, CONT_START))
 80             break;
 81 
 82         if(volume_changed) {
 83             volume_changed = 0;
 84             draw_instructions(volume);
 85         }
 86     }
 87 
 88     // Unload all sound effects from AICA RAM
 89     snd_sfx_unload(beep1);	
 90     snd_sfx_unload(beep2);
 91     snd_sfx_unload(beep3);	
 92     snd_sfx_unload(beep4);
 93     // OR
 94     // snd_sfx_unload_all();	
 95 
 96     snd_shutdown();
 97 
 98     return 0;
 99 }
100 
101 static void draw_instructions(uint8_t volume) {
102     int x = 20, y = 20+24;
103     int color = 1;
104     char current_volume_str[32];
105     memset(current_volume_str, 0, 32);
106     snprintf(current_volume_str, 32, "Current Volume: %3i", volume);
107     
108     bfont_draw_str(vram_s + y*640+x, 640, color, "Press A,B,X,Y to play beeps on separate channels");
109     y += 48;
110     bfont_draw_str(vram_s + y*640+x, 640, color, "Press UP,DOWN,LEFT,RIGHT on D-Pad to play beeps");
111     y += 24;
112     bfont_draw_str(vram_s + y*640+x, 640, color, "on the same channel");
113     y += 48;
114     bfont_draw_str(vram_s + y*640+x, 640, color, "Press L-Trigger/R-Trigger to +/- volume");
115     y += 24;
116     bfont_draw_str(vram_s + y*640+x, 640, color, current_volume_str);
117     y += 48;
118     bfont_draw_str(vram_s + y*640+x, 640, color, "Press Start to exit program");
119 }
120 
121 static cont_state_t* get_cont_state() {
122     maple_device_t* cont;
123     cont_state_t* state;
124 
125     cont = maple_enum_type(0, MAPLE_FUNC_CONTROLLER);
126     if(cont) {
127         state = (cont_state_t*)maple_dev_status(cont);
128         return state;
129     }
130 
131     return NULL;
132 }
133 
134 static int button_pressed(unsigned int current_buttons, unsigned int changed_buttons, unsigned int button) {
135     if (changed_buttons & button) {
136         if (current_buttons & button)
137             return 1;
138     }
139 
140     return 0;
141 }