changeset 486:859dd2ef3197

Added some seriously INCOMPLETE mixer code.
author Ryan C. Gordon <icculus@icculus.org>
date Sun, 27 Feb 2005 19:55:29 +0000
parents 137c0b00ea4c
children 78176684050d
files mixer/DESIGN mixer/converters.c mixer/mixercore.c
diffstat 3 files changed, 380 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mixer/DESIGN	Sun Feb 27 19:55:29 2005 +0000
@@ -0,0 +1,42 @@
+- Mixes internally in Float32. This simplifies the code immensely by only
+having one format to screw with. It also makes life easy for end-user
+callbacks. A native Float32 format should be added to SDL, too, so there
+isn't unnecessary conversion if we can avoid it (i.e. - a CoreAudio backend).
+
+- "Chunks" are just Sound_Samples...you can lock the mixer to screw with them
+(i.e. - seeking in a playing Sample, etc). The mixer adds some opaque state
+to Sound_Sample (current play position, how much is decoded, etc), some of
+which can be queried and set.
+
+- There is no "stopped" state. You are either in the playing list or you are
+not, but state doesn't reset, so removing a sample from the list is more like
+pausing it. If you put it back in the playing list without rewinding it, it
+starts where it was.
+
+- Fire and forget mixing is easy; flag a sample as "auto free" and it'll
+delete itself when it's done playing. No need to set up a callback just to
+clean up.
+
+- No channels. You can mix as many samples as you have resources to
+accomodate.
+
+- No groups. This can be layered on top of the library if needed. If you
+need atomic operations, lock the mixer.
+
+- No music channel. Samples are samples. You can mix a MIDI as a sound effect
+if you want, or a WAV file for background music. If you have the horsepower
+to decode multiple compressed files at once, go for it.
+
+- You can prebuffer/predecode as much of a sample as you like.
+
+- Every sample mixes with a per-channel gain, plus a master gain that is
+global to the mixer.
+
+- Can handle non-power of two resampling.
+
+- post mix hook, sample finished hook. Effects callback?
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mixer/converters.c	Sun Feb 27 19:55:29 2005 +0000
@@ -0,0 +1,208 @@
+/*
+ * SDL_sound -- An sound processing toolkit.
+ * Copyright (C) 2001  Ryan C. Gordon.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * This file implements the mixer itself. Largely, this is handled in the
+ *  SDL audio callback.
+ *
+ * Documentation is in SDL_sound.h ... It's verbose, honest.  :)
+ *
+ * Please see the file COPYING in the source's root directory.
+ *
+ *  This file written by Ryan C. Gordon. (icculus@clutteredmind.org)
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "SDL.h"
+#include "SDL_thread.h"
+#include "SDL_sound.h"
+
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h"
+
+
+static void conv_mix_buf_u8_mono(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint8 *stream = _stream;
+    register Uint32 i;
+    register Uint32 max = len * 2;
+    for (i = 0; i < max; i += 2)
+    {
+        *stream = (Uint8) ((((mixbuf[i]+mixbuf[i+1])*0.5f) * 127.0f) + 128.0f);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s8s */
+
+static void conv_mix_buf_s8_mono(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Sint8 *stream = (Sint8 *) _stream;
+    register Uint32 max = len * 2;
+    for (i = 0; i < max; i += 2)
+    {
+        *stream = (Sint8) (((mixbuf[i] + mixbuf[i+1]) * 0.5f) * 127.0f);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s8s */
+
+static void conv_mix_buf_s16_lsb_mono(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Sint16 *stream = (Sint16 *) _stream;
+    register Sint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i += 2)
+    {
+        val = (Sint16) (((mixbuf[i] + mixbuf[i+1]) * 0.5f) * 32767.0f);
+        *stream = SDL_SwapLE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_lsb_mono */
+
+static void conv_mix_buf_s16_msb_mono(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Sint16 *stream = (Sint16 *) _stream;
+    register Sint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i += 2)
+    {
+        val = (Sint16) (((mixbuf[i] + mixbuf[i+1]) * 0.5f) * 32767.0f);
+        *stream = SDL_SwapBE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_msb_mono */
+
+static void conv_mix_buf_u16_lsb_mono(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Uint16 *stream = (Uint16 *) _stream;
+    register Uint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i += 2)
+    {
+        val = (Uint16)((((mixbuf[i]+mixbuf[i+1])*0.5f) * 32767.0f) + 32768.0f);
+        *stream = SDL_SwapLE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_lsb_mono */
+
+static void conv_mix_buf_u16_msb_mono(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Uint16 *stream = (Uint16 *) _stream;
+    register Uint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i += 2)
+    {
+        val = (Uint16)((((mixbuf[i]+mixbuf[i+1])*0.5f) * 32767.0f) + 32768.0f);
+        *stream = SDL_SwapBE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_lsb_mono */
+
+static void conv_mix_buf_u8_stereo(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Uint8 *stream = _stream;
+    register Uint32 max = len;
+    for (i = 0; i < max; i++)
+    {
+        *stream = (Uint8) ((mixbuf[i] * 127.0f) + 128.0f);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s8s */
+
+static void conv_mix_buf_s8_stereo(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Sint8 *stream = (Sint8 *) _stream;
+    register Uint32 max = len;
+    for (i = 0; i < max; i++)
+    {
+        *stream = (Sint8) (mixbuf[i] * 127.0f);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s8s */
+
+static void conv_mix_buf_s16lsb_stereo(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Sint16 *stream = (Sint16 *) _stream;
+    register Sint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i++)
+    {
+        val = (Sint16) (mixbuf[i] * 32767.0f);
+        *stream = SDL_SwapLE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_lsb_stereo */
+
+static void conv_mix_buf_s16msb_stereo(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Sint16 *stream = (Sint16 *) _stream;
+    register Sint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i++)
+    {
+        val = (Sint16) (mixbuf[i] * 32767.0f);
+        *stream = SDL_SwapBE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_msb_stereo */
+
+static void conv_mix_buf_u16lsb_stereo(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Uint16 *stream = (Uint16 *) _stream;
+    register Uint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i++)
+    {
+        val = (Uint16) ((mixbuf[i] * 32767.0f) + 32768.0f);
+        *stream = SDL_SwapLE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_lsb_stereo */
+
+static void conv_mix_buf_u16msb_stereo(void *userdata, Uint8 *_stream, int len)
+{
+    register Uint32 i;
+    register Uint16 *stream = (Uint16 *) _stream;
+    register Uint16 val;
+    register Uint32 max = len / 2;
+    for (i = 0; i < max; i++)
+    {
+        val = (Uint16) ((mixbuf[i] * 32767.0f) + 32768.0f);
+        *stream = SDL_SwapBE16(val);
+        stream++;
+    } /* for */
+} /* conv_mix_buf_s16_msb_stereo */
+
+/* end of converters.c ... */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mixer/mixercore.c	Sun Feb 27 19:55:29 2005 +0000
@@ -0,0 +1,130 @@
+/*
+ * SDL_sound -- An sound processing toolkit.
+ * Copyright (C) 2001  Ryan C. Gordon.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * This file implements the mixer itself. Largely, this is handled in the
+ *  SDL audio callback.
+ *
+ * Documentation is in SDL_sound.h ... It's verbose, honest.  :)
+ *
+ * Please see the file COPYING in the source's root directory.
+ *
+ *  This file written by Ryan C. Gordon. (icculus@clutteredmind.org)
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "SDL.h"
+#include "SDL_thread.h"
+#include "SDL_sound.h"
+
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h"
+
+
+
+typedef struct S_PlayingList
+{
+    Sound_Sample *sample;
+    struct S_PlayingList *next;
+} PlayingList;
+
+static PlayingList *playlist = NULL;
+
+static inline void mix_predecoded(Sound_Sample *samp,
+                                  UInt32 *samp_frames_left,
+                                  float *gains)
+{
+    Sound_SampleInternal *internal = (Sound_SampleInternal *) samp->opaque;
+    Uint32 sfl = *samp_frames_left;  /* move to a local. */
+    Uint32 max = internal->buffer_size - internal->mix_position;
+    float *wptr;  /* write pointer */
+
+    /* !!! FIXME: max must be converted to sample frame count... */
+
+    if (max > sfl)  /* we have more data than mix buffer? */
+        max = sfl;
+
+    assert(max > 0);
+    *samp_frames_left -= max;
+
+    wptr = mixbuf + ((mixbufsize / sizeof (float)) - (max * MAX_CHANNELS));
+    internal->mix(wptr, internal->buffer, max, gains);
+} /* mix_predecoded */
+
+
+static void mix_playing_samples(Uint8 *stream, int len)
+{
+    PlayingList *samples = playlist;
+    const int frames = len / framesize;
+    const Uint32 ticks = SDL_GetTicks();  /* used for calculating fade. */
+
+    while (samples)  /* iterate linked list of playing samples... */
+    {
+        Sound_Sample *samp = samples->sample;
+        Uint32 sample_frames_left = mixbuf_frames;
+        float gains[MAX_CHANNELS];
+
+        calculate_gains(samp, ticks, gains);
+        while (sample_frames_left)
+        {
+            mix_predecoded(samp, &sample_frames_left);
+            if (!decode_more(samp))
+                break;
+        } /* while */
+
+        samples = samples->next;  /* set up for next iteration. */
+    } /* while */
+} /* mix_playing_samples */
+
+
+static inline void run_pre_mix(void)
+{
+    if (premixer)
+        premixer(mixbuf, mixbufsize);
+    else  /* !!! FIXME: Do memset in another thread after mix is done. */
+        memset(mixbuf, '\0', mixbufsize * sizeof (float) * 2);
+} /* run_pre_mix */
+
+
+static inline void run_post_mix(void)
+{
+    if (postmixer)
+        postmixer(mixbuf, mixbufsize);
+} /* run_post_mix */
+
+
+/* this is where it happens: the SDL audio callback. */
+static void audio_callback(void *userdata, Uint8 *stream, int len)
+{
+    mixer_callback_running = 1;
+    run_pre_mix();
+    mix_playing_samples();
+    run_post_mix();
+    mixer_callback_running = 0;
+} /* audio_callback */
+
+/* end of mixercore.c ... */
+