SDL  2.0
mixer.c
Go to the documentation of this file.
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 
13 static 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 ? */
21 
22 struct 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 */
29 static struct sound drums[NUM_DRUMS];
30 
31 /* function declarations */
34 int playSound(struct sound *);
36 void audioCallback(void *userdata, Uint8 * stream, int len);
37 void loadSound(const char *file, struct sound *s);
38 
39 struct
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 ? */
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) */
53 void
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  */
87 void
88 loadSound(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 */
121 void
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 */
151 void
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 */
165 void
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 */
186 int
187 playSound(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 */
233 void
234 audioCallback(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 
271 int
272 main(int argc, char *argv[])
273 {
274  int done; /* has user tried to quit ? */
275  SDL_Window *window; /* main window */
278  int i;
279  int width;
280  int height;
281 
283  fatalError("could not initialize SDL");
284  }
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 }
Definition: mixer.c:22
#define SDL_MIX_MAXVOLUME
Definition: SDL_audio.h:616
Uint8 * buffer
Definition: mixer.c:24
#define SDL_PollEvent
#define SDL_OpenAudio
GLuint64EXT * result
GLdouble s
Definition: SDL_opengl.h:2063
Uint8 g
Definition: SDL_pixels.h:298
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
int isPressed
Definition: mixer.c:18
void fatalError(const char *string)
Definition: common.c:32
Uint8 * buf
Definition: SDL_audio.h:232
#define SDL_BuildAudioCVT
Uint32 remaining
Definition: mixer.c:45
#define SDL_RenderFillRect
void loadSound(const char *file, struct sound *s)
Definition: mixer.c:88
#define SDL_MixAudioFormat
Uint8 b
Definition: SDL_pixels.h:299
#define SDL_CreateWindow
struct @64 mixer
int main(int argc, char *argv[])
Definition: mixer.c:272
#define NUM_DRUMS
Definition: mixer.c:11
GLenum GLsizei len
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
A structure to hold a set of audio conversion filters and buffers.
Definition: SDL_audio.h:226
SDL_AudioSpec spec
Definition: loopwave.c:31
#define SDL_GetWindowSize
void initializeButtons(SDL_Renderer *)
Definition: mixer.c:54
Uint32 timestamp
Definition: mixer.c:46
#define SDL_PauseAudio
#define SDL_memcpy
int playSound(struct sound *)
Definition: mixer.c:187
Uint8 r
Definition: SDL_pixels.h:297
#define SDL_ConvertAudio
GLuint GLuint stream
int touchIndex
Definition: mixer.c:19
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Uint8 channels
Definition: SDL_audio.h:182
static SDL_Renderer * renderer
Uint8 a
Definition: SDL_pixels.h:300
uint8_t Uint8
Definition: SDL_stdinc.h:179
#define SDL_free
struct _cl_event * event
#define SDL_RenderSetLogicalSize
void handleMouseButtonUp(SDL_Event *event)
Definition: mixer.c:152
#define SDL_Quit
int done
Definition: checkkeys.c:28
static struct sound drums[NUM_DRUMS]
Definition: mixer.c:29
#define SDL_LoadWAV(file, spec, audio_buf, audio_len)
Definition: SDL_audio.h:451
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
int x
Definition: SDL_rect.h:66
void audioCallback(void *userdata, Uint8 *stream, int len)
Definition: mixer.c:234
int w
Definition: SDL_rect.h:67
void handleMouseButtonDown(SDL_Event *event)
Definition: mixer.c:122
#define SDL_Delay
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:164
GLuint buffer
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
#define SDL_RenderClear
#define SDL_INIT_AUDIO
Definition: SDL.h:78
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
int numSoundsPlaying
Definition: mixer.c:49
int h
Definition: SDL_rect.h:67
SDL_AudioFormat format
Definition: SDL_audio.h:181
The type used to identify a window.
Definition: SDL_sysvideo.h:73
Uint32 length
Definition: mixer.c:25
uint32_t Uint32
Definition: SDL_stdinc.h:203
GLuint color
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
Uint8 * position
Definition: mixer.c:44
#define NUM_CHANNELS
Definition: mixer.c:10
#define SDL_Init
SDL_Color upColor
Definition: mixer.c:16
General event structure.
Definition: SDL_events.h:557
#define SDL_malloc
#define SDL_SetRenderDrawColor
SDL_Rect rect
Definition: mixer.c:15
SDL_AudioSpec outputSpec
Definition: mixer.c:48
SDL_Color downColor
Definition: mixer.c:17
void render(SDL_Renderer *renderer)
Definition: mixer.c:166
static struct @63 buttons[NUM_DRUMS]
#define SDL_RenderGetLogicalSize
int y
Definition: SDL_rect.h:66
#define SDL_GetMouseState
#define SDL_INIT_VIDEO
Definition: SDL.h:79
#define SDL_CreateRenderer
#define SDL_memset
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:64
#define SDL_RenderPresent
Uint32 type
Definition: SDL_events.h:559
struct @64::@65 channels[NUM_CHANNELS]