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