Atlas - mixer.c

Home / ext / SDL2 / Xcode-iOS / Demos / src Lines: 1 | Size: 11217 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 * mixer.c 3 * written by Holmes Futrell 4 * use however you want 5 */ 6 7#include "SDL.h" 8#include "common.h" 9 10#define NUM_CHANNELS 8 /* max number of sounds we can play at once */ 11#define NUM_DRUMS 4 /* number of drums in our set */ 12 13static struct 14{ 15 SDL_Rect rect; /* where the button is drawn */ 16 SDL_Color upColor; /* color when button is not active */ 17 SDL_Color downColor; /* color when button is active */ 18 int isPressed; /* is the button being pressed ? */ 19 int touchIndex; /* what mouse (touch) index pressed the button ? */ 20} buttons[NUM_DRUMS]; 21 22struct sound 23{ 24 Uint8 *buffer; /* audio buffer for sound file */ 25 Uint32 length; /* length of the buffer (in bytes) */ 26}; 27 28/* this array holds the audio for the drum noises */ 29static struct sound drums[NUM_DRUMS]; 30 31/* function declarations */ 32void handleMouseButtonDown(SDL_Event * event); 33void handleMouseButtonUp(SDL_Event * event); 34int playSound(struct sound *); 35void initializeButtons(SDL_Renderer *); 36void audioCallback(void *userdata, Uint8 * stream, int len); 37void loadSound(const char *file, struct sound *s); 38 39struct 40{ 41 /* channel array holds information about currently playing sounds */ 42 struct 43 { 44 Uint8 *position; /* what is the current position in the buffer of this sound ? */ 45 Uint32 remaining; /* how many bytes remaining before we're done playing the sound ? */ 46 Uint32 timestamp; /* when did this sound start playing ? */ 47 } channels[NUM_CHANNELS]; 48 SDL_AudioSpec outputSpec; /* what audio format are we using for output? */ 49 int numSoundsPlaying; /* how many sounds are currently playing */ 50} mixer; 51 52/* sets up the buttons (color, position, state) */ 53void 54initializeButtons(SDL_Renderer *renderer) 55{ 56 int i; 57 int spacing = 10; /* gap between drum buttons */ 58 SDL_Rect buttonRect; /* keeps track of where to position drum */ 59 SDL_Color upColor = { 86, 86, 140, 255 }; /* color of drum when not pressed */ 60 SDL_Color downColor = { 191, 191, 221, 255 }; /* color of drum when pressed */ 61 int renderW, renderH; 62 63 SDL_RenderGetLogicalSize(renderer, &renderW, &renderH); 64 65 buttonRect.x = spacing; 66 buttonRect.y = spacing; 67 buttonRect.w = renderW - 2 * spacing; 68 buttonRect.h = (renderH - (NUM_DRUMS + 1) * spacing) / NUM_DRUMS; 69 70 /* setup each button */ 71 for (i = 0; i < NUM_DRUMS; i++) { 72 73 buttons[i].rect = buttonRect; 74 buttons[i].isPressed = 0; 75 buttons[i].upColor = upColor; 76 buttons[i].downColor = downColor; 77 78 buttonRect.y += spacing + buttonRect.h; /* setup y coordinate for next drum */ 79 80 } 81} 82 83/* 84 loads a wav file (stored in 'file'), converts it to the mixer's output format, 85 and stores the resulting buffer and length in the sound structure 86 */ 87void 88loadSound(const char *file, struct sound *s) 89{ 90 SDL_AudioSpec spec; /* the audio format of the .wav file */ 91 SDL_AudioCVT cvt; /* used to convert .wav to output format when formats differ */ 92 int result; 93 if (SDL_LoadWAV(file, &spec, &s->buffer, &s->length) == NULL) { 94 fatalError("could not load .wav"); 95 } 96 /* build the audio converter */ 97 result = SDL_BuildAudioCVT(&cvt, spec.format, spec.channels, spec.freq, 98 mixer.outputSpec.format, 99 mixer.outputSpec.channels, 100 mixer.outputSpec.freq); 101 if (result == -1) { 102 fatalError("could not build audio CVT"); 103 } else if (result != 0) { 104 /* 105 this happens when the .wav format differs from the output format. 106 we convert the .wav buffer here 107 */ 108 cvt.buf = (Uint8 *) SDL_malloc(s->length * cvt.len_mult); /* allocate conversion buffer */ 109 cvt.len = s->length; /* set conversion buffer length */ 110 SDL_memcpy(cvt.buf, s->buffer, s->length); /* copy sound to conversion buffer */ 111 if (SDL_ConvertAudio(&cvt) == -1) { /* convert the sound */ 112 fatalError("could not convert .wav"); 113 } 114 SDL_free(s->buffer); /* free the original (unconverted) buffer */ 115 s->buffer = cvt.buf; /* point sound buffer to converted buffer */ 116 s->length = cvt.len_cvt; /* set sound buffer's new length */ 117 } 118} 119 120/* called from main event loop */ 121void 122handleMouseButtonDown(SDL_Event * event) 123{ 124 125 int x, y, mouseIndex, i, drumIndex; 126 127 mouseIndex = 0; 128 drumIndex = -1; 129 130 SDL_GetMouseState(&x, &y); 131 /* check if we hit any of the drum buttons */ 132 for (i = 0; i < NUM_DRUMS; i++) { 133 if (x >= buttons[i].rect.x 134 && x < buttons[i].rect.x + buttons[i].rect.w 135 && y >= buttons[i].rect.y 136 && y < buttons[i].rect.y + buttons[i].rect.h) { 137 drumIndex = i; 138 break; 139 } 140 } 141 if (drumIndex != -1) { 142 /* if we hit a button */ 143 buttons[drumIndex].touchIndex = mouseIndex; 144 buttons[drumIndex].isPressed = 1; 145 playSound(&drums[drumIndex]); 146 } 147 148} 149 150/* called from main event loop */ 151void 152handleMouseButtonUp(SDL_Event * event) 153{ 154 int i; 155 int mouseIndex = 0; 156 /* check if this should cause any of the buttons to become unpressed */ 157 for (i = 0; i < NUM_DRUMS; i++) { 158 if (buttons[i].touchIndex == mouseIndex) { 159 buttons[i].isPressed = 0; 160 } 161 } 162} 163 164/* draws buttons to screen */ 165void 166render(SDL_Renderer *renderer) 167{ 168 int i; 169 SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255); 170 SDL_RenderClear(renderer); /* draw background (gray) */ 171 /* draw the drum buttons */ 172 for (i = 0; i < NUM_DRUMS; i++) { 173 SDL_Color color = 174 buttons[i].isPressed ? buttons[i].downColor : buttons[i].upColor; 175 SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); 176 SDL_RenderFillRect(renderer, &buttons[i].rect); 177 } 178 /* update the screen */ 179 SDL_RenderPresent(renderer); 180} 181 182/* 183 finds a sound channel in the mixer for a sound 184 and sets it up to start playing 185*/ 186int 187playSound(struct sound *s) 188{ 189 /* 190 find an empty channel to play on. 191 if no channel is available, use oldest channel 192 */ 193 int i; 194 int selected_channel = -1; 195 int oldest_channel = 0; 196 197 if (mixer.numSoundsPlaying == 0) { 198 /* we're playing a sound now, so start audio callback back up */ 199 SDL_PauseAudio(0); 200 } 201 202 /* find a sound channel to play the sound on */ 203 for (i = 0; i < NUM_CHANNELS; i++) { 204 if (mixer.channels[i].position == NULL) { 205 /* if no sound on this channel, select it */ 206 selected_channel = i; 207 break; 208 } 209 /* if this channel's sound is older than the oldest so far, set it to oldest */ 210 if (mixer.channels[i].timestamp < 211 mixer.channels[oldest_channel].timestamp) 212 oldest_channel = i; 213 } 214 215 /* no empty channels, take the oldest one */ 216 if (selected_channel == -1) 217 selected_channel = oldest_channel; 218 else 219 mixer.numSoundsPlaying++; 220 221 /* point channel data to wav data */ 222 mixer.channels[selected_channel].position = s->buffer; 223 mixer.channels[selected_channel].remaining = s->length; 224 mixer.channels[selected_channel].timestamp = SDL_GetTicks(); 225 226 return selected_channel; 227} 228 229/* 230 Called from SDL's audio system. Supplies sound input with data by mixing together all 231 currently playing sound effects. 232*/ 233void 234audioCallback(void *userdata, Uint8 * stream, int len) 235{ 236 int i; 237 int copy_amt; 238 SDL_memset(stream, mixer.outputSpec.silence, len); /* initialize buffer to silence */ 239 /* for each channel, mix in whatever is playing on that channel */ 240 for (i = 0; i < NUM_CHANNELS; i++) { 241 if (mixer.channels[i].position == NULL) { 242 /* if no sound is playing on this channel */ 243 continue; /* nothing to do for this channel */ 244 } 245 246 /* copy len bytes to the buffer, unless we have fewer than len bytes remaining */ 247 copy_amt = 248 mixer.channels[i].remaining < 249 len ? mixer.channels[i].remaining : len; 250 251 /* mix this sound effect with the output */ 252 SDL_MixAudioFormat(stream, mixer.channels[i].position, 253 mixer.outputSpec.format, copy_amt, SDL_MIX_MAXVOLUME); 254 255 /* update buffer position in sound effect and the number of bytes left */ 256 mixer.channels[i].position += copy_amt; 257 mixer.channels[i].remaining -= copy_amt; 258 259 /* did we finish playing the sound effect ? */ 260 if (mixer.channels[i].remaining == 0) { 261 mixer.channels[i].position = NULL; /* indicates no sound playing on channel anymore */ 262 mixer.numSoundsPlaying--; 263 if (mixer.numSoundsPlaying == 0) { 264 /* if no sounds left playing, pause audio callback */ 265 SDL_PauseAudio(1); 266 } 267 } 268 } 269} 270 271int 272main(int argc, char *argv[]) 273{ 274 int done; /* has user tried to quit ? */ 275 SDL_Window *window; /* main window */ 276 SDL_Renderer *renderer; 277 SDL_Event event; 278 int i; 279 int width; 280 int height; 281 282 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { 283 fatalError("could not initialize SDL"); 284 } 285 window = SDL_CreateWindow(NULL, 0, 0, 320, 480, SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI); 286 renderer = SDL_CreateRenderer(window, 0, 0); 287 288 SDL_GetWindowSize(window, &width, &height); 289 SDL_RenderSetLogicalSize(renderer, width, height); 290 291 /* initialize the mixer */ 292 SDL_memset(&mixer, 0, sizeof(mixer)); 293 /* setup output format */ 294 mixer.outputSpec.freq = 44100; 295 mixer.outputSpec.format = AUDIO_S16LSB; 296 mixer.outputSpec.channels = 2; 297 mixer.outputSpec.samples = 256; 298 mixer.outputSpec.callback = audioCallback; 299 mixer.outputSpec.userdata = NULL; 300 301 /* open audio for output */ 302 if (SDL_OpenAudio(&mixer.outputSpec, NULL) != 0) { 303 fatalError("Opening audio failed"); 304 } 305 306 /* load our drum noises */ 307 loadSound("ds_kick_big_amb.wav", &drums[3]); 308 loadSound("ds_brush_snare.wav", &drums[2]); 309 loadSound("ds_loose_skin_mute.wav", &drums[1]); 310 loadSound("ds_china.wav", &drums[0]); 311 312 /* setup positions, colors, and state of buttons */ 313 initializeButtons(renderer); 314 315 /* enter main loop */ 316 done = 0; 317 while (!done) { 318 while (SDL_PollEvent(&event)) { 319 switch (event.type) { 320 case SDL_MOUSEBUTTONDOWN: 321 handleMouseButtonDown(&event); 322 break; 323 case SDL_MOUSEBUTTONUP: 324 handleMouseButtonUp(&event); 325 break; 326 case SDL_QUIT: 327 done = 1; 328 break; 329 } 330 } 331 render(renderer); /* draw buttons */ 332 333 SDL_Delay(1); 334 } 335 336 /* cleanup code, let's free up those sound buffers */ 337 for (i = 0; i < NUM_DRUMS; i++) { 338 SDL_free(drums[i].buffer); 339 } 340 /* let SDL do its exit code */ 341 SDL_Quit(); 342 343 return 0; 344} 345
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.