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