~brandontschaefer/+junk/sdl-mir

« back to all changes in this revision

Viewing changes to Xcode-iOS/Demos/src/mixer.c

  • Committer: Brandon Schaefer
  • Date: 2013-04-23 00:11:44 UTC
  • Revision ID: brandon.schaefer@canonical.com-20130423001144-tqamjeuhn4t2u60e
* Backup branch

Show diffs side-by-side

added added

removed removed

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