Ryan C. Gordon - Tue Sep 11 12:05:54 PDT 2001
authorSam Lantinga <slouken@libsdl.org>
Tue, 11 Sep 2001 19:14:36 +0000
changeset 113c0c3018bd787
parent 112 7f8dd3c0cbdc
child 114 83ab4ef4458b
Ryan C. Gordon - Tue Sep 11 12:05:54 PDT 2001
* Reworked playwave.c to make it more useful as a mixer testbed
* Added a realtime sound effect API to SDL_mixer.h
* Added the following standard sound effects:
panning, distance attenuation, basic positional audio, stereo reversal
* Added API for mixer versioning: Mix_Linked_Version() and MIX_VERSION()
CHANGES
Makefile.am
SDL_mixer.h
effect_position.c
effect_stereoreverse.c
effects_internal.c
effects_internal.h
mixer.c
playwave.c
     1.1 --- a/CHANGES	Tue Sep 11 18:49:18 2001 +0000
     1.2 +++ b/CHANGES	Tue Sep 11 19:14:36 2001 +0000
     1.3 @@ -1,5 +1,11 @@
     1.4  
     1.5  1.2.1:
     1.6 +Ryan C. Gordon - Tue Sep 11 12:05:54 PDT 2001
     1.7 + * Reworked playwave.c to make it more useful as a mixer testbed
     1.8 + * Added a realtime sound effect API to SDL_mixer.h
     1.9 + * Added the following standard sound effects:
    1.10 +	panning, distance attenuation, basic positional audio, stereo reversal
    1.11 + * Added API for mixer versioning: Mix_Linked_Version() and MIX_VERSION()
    1.12  Sam Lantinga - Tue Sep 11 11:48:53 PDT 2001
    1.13   * Updated MikMod code to version 3.1.9a
    1.14  Torbjörn Andersson - Tue Sep 11 11:22:29 PDT 2001
     2.1 --- a/Makefile.am	Tue Sep 11 18:49:18 2001 +0000
     2.2 +++ b/Makefile.am	Tue Sep 11 19:14:36 2001 +0000
     2.3 @@ -21,7 +21,11 @@
     2.4  	music_ogg.c		\
     2.5  	music_ogg.h		\
     2.6  	wavestream.c		\
     2.7 -	wavestream.h
     2.8 +	wavestream.h		\
     2.9 +	effect_position.c	\
    2.10 +	effect_stereoreverse.c	\
    2.11 +	effects_internal.c	\
    2.12 +	effects_internal.h
    2.13  
    2.14  if USE_MIKMOD
    2.15  MIKMOD_LIB = mikmod/libmikmod.la
     3.1 --- a/SDL_mixer.h	Tue Sep 11 18:49:18 2001 +0000
     3.2 +++ b/SDL_mixer.h	Tue Sep 11 19:14:36 2001 +0000
     3.3 @@ -29,6 +29,7 @@
     3.4  #include "SDL_rwops.h"
     3.5  #include "SDL_audio.h"
     3.6  #include "SDL_byteorder.h"
     3.7 +#include "SDL_version.h"
     3.8  #include "begin_code.h"
     3.9  
    3.10  /* Set up for C function definitions, even when using C++ */
    3.11 @@ -36,6 +37,30 @@
    3.12  extern "C" {
    3.13  #endif
    3.14  
    3.15 +/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL
    3.16 +*/
    3.17 +#define MIX_MAJOR_VERSION	1
    3.18 +#define MIX_MINOR_VERSION	2
    3.19 +#define MIX_PATCHLEVEL		1
    3.20 +
    3.21 +/* This macro can be used to fill a version structure with the compile-time
    3.22 + * version of the SDL_mixer library.
    3.23 + */
    3.24 +#define MIX_VERSION(X)							\
    3.25 +{									\
    3.26 +	(X)->major = MIX_MAJOR_VERSION;					\
    3.27 +	(X)->minor = MIX_MINOR_VERSION;					\
    3.28 +	(X)->patch = MIX_PATCHLEVEL;					\
    3.29 +}
    3.30 +
    3.31 +
    3.32 +/* This function gets the version of the dynamically linked SDL_mixer library.
    3.33 +   it should NOT be used to fill a version structure, instead you should
    3.34 +   use the MIX_VERSION() macro.
    3.35 + */
    3.36 +extern DECLSPEC const SDL_version * Mix_Linked_Version(void);
    3.37 +
    3.38 +
    3.39  /* The default mixer has 8 simultaneous mixing channels */
    3.40  #ifndef MIX_CHANNELS
    3.41  #define MIX_CHANNELS	8
    3.42 @@ -128,6 +153,270 @@
    3.43   */
    3.44  extern DECLSPEC void Mix_ChannelFinished(void (*channel_finished)(int channel));
    3.45  
    3.46 +
    3.47 +/* Special Effects API by ryan c. gordon. (icculus@linuxgames.com) */
    3.48 +
    3.49 +#define MIX_CHANNEL_POST  -2
    3.50 +
    3.51 +/* This is the format of a special effect callback:
    3.52 + *
    3.53 + *   myeffect(int chan, void *stream, int len, void *udata);
    3.54 + *
    3.55 + * (chan) is the channel number that your effect is affecting. (stream) is
    3.56 + *  the buffer of data to work upon. (len) is the size of (stream), and
    3.57 + *  (udata) is a user-defined bit of data, which you pass as the last arg of
    3.58 + *  Mix_RegisterEffect(), and is passed back unmolested to your callback.
    3.59 + *  Your effect changes the contents of (stream) based on whatever parameters
    3.60 + *  are significant, or just leaves it be, if you prefer. You can do whatever
    3.61 + *  you like to the buffer, though, and it will continue in its changed state
    3.62 + *  down the mixing pipeline, through any other effect functions, then finally
    3.63 + *  to be mixed with the rest of the channels and music for the final output
    3.64 + *  stream.
    3.65 + */
    3.66 +typedef void (*Mix_EffectFunc_t)(int chan, void *stream, int len, void *udata);
    3.67 +
    3.68 +/*
    3.69 + * This is a callback that signifies that a channel has finished all its
    3.70 + *  loops and has completed playback. This gets called if the buffer
    3.71 + *  plays out normally, or if you call Mix_HaltChannel(), implicitly stop
    3.72 + *  a channel via Mix_AllocateChannels(), or unregister a callback while
    3.73 + *  it's still playing.
    3.74 + */
    3.75 +typedef void (*Mix_EffectDone_t)(int chan, void *udata);
    3.76 +
    3.77 +
    3.78 +/* Register a special effect function. At mixing time, the channel data is
    3.79 + *  copied into a buffer and passed through each registered effect function.
    3.80 + *  After it passes through all the functions, it is mixed into the final
    3.81 + *  output stream. The copy to buffer is performed once, then each effect
    3.82 + *  function performs on the output of the previous effect. Understand that
    3.83 + *  this extra copy to a buffer is not performed if there are no effects
    3.84 + *  registered for a given chunk, which saves CPU cycles, and any given
    3.85 + *  effect will be extra cycles, too, so it is crucial that your code run
    3.86 + *  fast. Also note that the data that your function is given is in the
    3.87 + *  format of the sound device, and not the format you gave to Mix_OpenAudio(),
    3.88 + *  although they may in reality be the same. This is an unfortunate but
    3.89 + *  necessary speed concern. Use Mix_QuerySpec() to determine if you can
    3.90 + *  handle the data before you register your effect, and take appropriate
    3.91 + *  actions.
    3.92 + * You may also specify a callback (Mix_EffectDone_t) that is called when
    3.93 + *  the channel finishes playing. This gives you a more fine-grained control
    3.94 + *  than Mix_ChannelFinished(), in case you need to free effect-specific
    3.95 + *  resources, etc. If you don't need this, you can specify NULL.
    3.96 + * You may set the callbacks before or after calling Mix_PlayChannel().
    3.97 + * Things like Mix_SetPanning() are just internal special effect functions,
    3.98 + *  so if you are using that, you've already incurred the overhead of a copy
    3.99 + *  to a separate buffer, and that these effects will be in the queue with
   3.100 + *  any functions you've registered. The list of registered effects for a
   3.101 + *  channel is reset when a chunk finishes playing, so you need to explicitly
   3.102 + *  set them with each call to Mix_PlayChannel*().
   3.103 + * You may also register a special effect function that is to be run after
   3.104 + *  final mixing occurs. The rules for these callbacks are identical to those
   3.105 + *  in Mix_RegisterEffect, but they are run after all the channels and the
   3.106 + *  music have been mixed into a single stream, whereas channel-specific
   3.107 + *  effects run on a given channel before any other mixing occurs. These
   3.108 + *  global effect callbacks are call "posteffects". Posteffects only have
   3.109 + *  their Mix_EffectDone_t function called when they are unregistered (since
   3.110 + *  the main output stream is never "done" in the same sense as a channel).
   3.111 + *  You must unregister them manually when you've had enough. Your callback
   3.112 + *  will be told that the channel being mixed is (MIX_CHANNEL_POST) if the
   3.113 + *  processing is considered a posteffect.
   3.114 + *
   3.115 + * After all these effects have finished processing, the callback registered
   3.116 + *  through Mix_SetPostMix() runs, and then the stream goes to the audio
   3.117 + *  device. 
   3.118 + *
   3.119 + * returns zero if error (no such channel), nonzero if added.
   3.120 + *  Error messages can be retrieved from Mix_GetError().
   3.121 + */
   3.122 +extern DECLSPEC int Mix_RegisterEffect(int chan, Mix_EffectFunc_t f,
   3.123 +										Mix_EffectDone_t d, void *arg);
   3.124 +
   3.125 +
   3.126 +/* You may not need to call this explicitly, unless you need to stop an
   3.127 + *  effect from processing in the middle of a chunk's playback.
   3.128 + * Posteffects are never implicitly unregistered as they are for channels,
   3.129 + *  but they may be explicitly unregistered through this function by
   3.130 + *  specifying MIX_CHANNEL_POST for a channel.
   3.131 + * returns zero if error (no such channel or effect), nonzero if removed.
   3.132 + *  Error messages can be retrieved from Mix_GetError().
   3.133 + */
   3.134 +extern DECLSPEC int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f);
   3.135 +
   3.136 +
   3.137 +/* You may not need to call this explicitly, unless you need to stop all
   3.138 + *  effects from processing in the middle of a chunk's playback. Note that
   3.139 + *  this will also shut off some internal effect processing, since
   3.140 + *  Mix_SetPanning() and others may use this API under the hood. This is
   3.141 + *  called internally when a channel completes playback.
   3.142 + * Posteffects are never implicitly unregistered as they are for channels,
   3.143 + *  but they may be explicitly unregistered through this function by
   3.144 + *  specifying MIX_CHANNEL_POST for a channel.
   3.145 + * returns zero if error (no such channel), nonzero if all effects removed.
   3.146 + *  Error messages can be retrieved from Mix_GetError().
   3.147 + */
   3.148 +extern DECLSPEC int Mix_UnregisterAllEffects(int channel);
   3.149 +
   3.150 +
   3.151 +#define MIX_EFFECTSMAXSPEED  "MIX_EFFECTSMAXSPEED"
   3.152 +
   3.153 +/*
   3.154 + * These are the internally-defined mixing effects. They use the same API that
   3.155 + *  effects defined in the application use, but are provided here as a
   3.156 + *  convenience. Some effects can reduce their quality or use more memory in
   3.157 + *  the name of speed; to enable this, make sure the environment variable
   3.158 + *  MIX_EFFECTSMAXSPEED (see above) is defined before you call
   3.159 + *  Mix_OpenAudio().
   3.160 + */
   3.161 +
   3.162 +
   3.163 +/* Set the panning of a channel. The left and right channels are specified
   3.164 + *  as integers between 0 and 255, quietest to loudest, respectively.
   3.165 + *
   3.166 + * Technically, this is just individual volume control for a sample with
   3.167 + *  two (stereo) channels, so it can be used for more than just panning.
   3.168 + *  If you want real panning, call it like this:
   3.169 + *
   3.170 + *   Mix_SetPanning(channel, left, 255 - left);
   3.171 + *
   3.172 + * ...which isn't so hard.
   3.173 + *
   3.174 + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and
   3.175 + *  the panning will be done to the final mixed stream before passing it on
   3.176 + *  to the audio device.
   3.177 + *
   3.178 + * This uses the Mix_RegisterEffect() API internally, and returns without
   3.179 + *  registering the effect function if the audio device is not configured
   3.180 + *  for stereo output. Setting both (left) and (right) to 255 causes this
   3.181 + *  effect to be unregistered, since that is the data's normal state.
   3.182 + *
   3.183 + * returns zero if error (no such channel or Mix_RegisterEffect() fails),
   3.184 + *  nonzero if panning effect enabled. Note that an audio device in mono
   3.185 + *  mode is a no-op, but this call will return successful in that case.
   3.186 + *  Error messages can be retrieved from Mix_GetError().
   3.187 + */
   3.188 +extern DECLSPEC int Mix_SetPanning(int channel, Uint8 left, Uint8 right);
   3.189 +
   3.190 +
   3.191 +/* Set the position of a channel. (angle) is an integer from 0 to 360, that
   3.192 + *  specifies the location of the sound in relation to the listener. (angle)
   3.193 + *  will be reduced as neccesary (540 becomes 180 degrees, -100 becomes 260).
   3.194 + *  Angle 0 is due north, and rotates clockwise as the value increases.
   3.195 + *  For efficiency, the precision of this effect may be limited (angles 1
   3.196 + *  through 7 might all produce the same effect, 8 through 15 are equal, etc).
   3.197 + *  (distance) is an integer between 0 and 255 that specifies the space
   3.198 + *  between the sound and the listener. The larger the number, the further
   3.199 + *  away the sound is. Using 255 does not guarantee that the channel will be
   3.200 + *  culled from the mixing process or be completely silent. For efficiency,
   3.201 + *  the precision of this effect may be limited (distance 0 through 5 might
   3.202 + *  all produce the same effect, 6 through 10 are equal, etc). Setting (angle)
   3.203 + *  and (distance) to 0 unregisters this effect, since the data would be
   3.204 + *  unchanged.
   3.205 + *
   3.206 + * If you need more precise positional audio, consider using OpenAL for
   3.207 + *  spatialized effects instead of SDL_mixer. This is only meant to be a
   3.208 + *  basic effect for simple "3D" games.
   3.209 + *
   3.210 + * If the audio device is configured for mono output, then you won't get
   3.211 + *  any effectiveness from the angle; however, distance attenuation on the
   3.212 + *  channel will still occur. While this effect will function with stereo
   3.213 + *  voices, it makes more sense to use voices with only one channel of sound,
   3.214 + *  so when they are mixed through this effect, the positioning will sound
   3.215 + *  correct. You can convert them to mono through SDL before giving them to
   3.216 + *  the mixer in the first place if you like.
   3.217 + *
   3.218 + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and
   3.219 + *  the positioning will be done to the final mixed stream before passing it
   3.220 + *  on to the audio device.
   3.221 + *
   3.222 + * This is a convenience wrapper over Mix_SetDistance() and Mix_SetPanning().
   3.223 + *
   3.224 + * returns zero if error (no such channel or Mix_RegisterEffect() fails),
   3.225 + *  nonzero if position effect is enabled.
   3.226 + *  Error messages can be retrieved from Mix_GetError().
   3.227 + */
   3.228 +extern DECLSPEC int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance);
   3.229 +
   3.230 +
   3.231 +/* Set the "distance" of a channel. (distance) is an integer from 0 to 255
   3.232 + *  that specifies the location of the sound in relation to the listener.
   3.233 + *  Distance 0 is overlapping the listener, and 255 is as far away as possible
   3.234 + *  A distance of 255 does not guarantee silence; in such a case, you might
   3.235 + *  want to try changing the chunk's volume, or just cull the sample from the
   3.236 + *  mixing process with Mix_HaltChannel().
   3.237 + * For efficiency, the precision of this effect may be limited (distances 1
   3.238 + *  through 7 might all produce the same effect, 8 through 15 are equal, etc).
   3.239 + *  (distance) is an integer between 0 and 255 that specifies the space
   3.240 + *  between the sound and the listener. The larger the number, the further
   3.241 + *  away the sound is.
   3.242 + * Setting (distance) to 0 unregisters this effect, since the data would be
   3.243 + *  unchanged.
   3.244 + * If you need more precise positional audio, consider using OpenAL for
   3.245 + *  spatialized effects instead of SDL_mixer. This is only meant to be a
   3.246 + *  basic effect for simple "3D" games.
   3.247 + *
   3.248 + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and
   3.249 + *  the distance attenuation will be done to the final mixed stream before
   3.250 + *  passing it on to the audio device.
   3.251 + *
   3.252 + * This uses the Mix_RegisterEffect() API internally.
   3.253 + *
   3.254 + * returns zero if error (no such channel or Mix_RegisterEffect() fails),
   3.255 + *  nonzero if position effect is enabled.
   3.256 + *  Error messages can be retrieved from Mix_GetError().
   3.257 + */
   3.258 +extern DECLSPEC int Mix_SetDistance(int channel, Uint8 distance);
   3.259 +
   3.260 +
   3.261 +/*
   3.262 + * !!! FIXME : Haven't implemented, since the effect goes past the
   3.263 + *              end of the sound buffer. Will have to think about this.
   3.264 + *               --ryan.
   3.265 + */
   3.266 +#if 0
   3.267 +/* Causes an echo effect to be mixed into a sound. (echo) is the amount
   3.268 + *  of echo to mix. 0 is no echo, 255 is infinite (and probably not
   3.269 + *  what you want).
   3.270 + *
   3.271 + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and
   3.272 + *  the reverbing will be done to the final mixed stream before passing it on
   3.273 + *  to the audio device.
   3.274 + *
   3.275 + * This uses the Mix_RegisterEffect() API internally. If you specify an echo
   3.276 + *  of zero, the effect is unregistered, as the data is already in that state.
   3.277 + *
   3.278 + * returns zero if error (no such channel or Mix_RegisterEffect() fails),
   3.279 + *  nonzero if reversing effect is enabled.
   3.280 + *  Error messages can be retrieved from Mix_GetError().
   3.281 + */
   3.282 +extern no_parse_DECLSPEC int Mix_SetReverb(int channel, Uint8 echo);
   3.283 +#endif
   3.284 +
   3.285 +/* Causes a channel to reverse its stereo. This is handy if the user has his
   3.286 + *  speakers hooked up backwards, or you would like to have a minor bit of
   3.287 + *  psychedelia in your sound code.  :)  Calling this function with (flip)
   3.288 + *  set to non-zero reverses the chunks's usual channels. If (flip) is zero,
   3.289 + *  the effect is unregistered.
   3.290 + *
   3.291 + * This uses the Mix_RegisterEffect() API internally, and thus is probably
   3.292 + *  more CPU intensive than having the user just plug in his speakers
   3.293 + *  correctly. Mix_SetReverseStereo() returns without registering the effect
   3.294 + *  function if the audio device is not configured for stereo output.
   3.295 + *
   3.296 + * If you specify MIX_CHANNEL_POST for (channel), then this the effect is used
   3.297 + *  on the final mixed stream before sending it on to the audio device (a
   3.298 + *  posteffect).
   3.299 + *
   3.300 + * returns zero if error (no such channel or Mix_RegisterEffect() fails),
   3.301 + *  nonzero if reversing effect is enabled. Note that an audio device in mono
   3.302 + *  mode is a no-op, but this call will return successful in that case.
   3.303 + *  Error messages can be retrieved from Mix_GetError().
   3.304 + */
   3.305 +extern DECLSPEC int Mix_SetReverseStereo(int channel, int flip);
   3.306 +
   3.307 +/* end of effects API. --ryan. */
   3.308 +
   3.309 +
   3.310  /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
   3.311     them dynamically to the next sample if requested with a -1 value below.
   3.312     Returns the number of reserved channels.
   3.313 @@ -243,3 +532,6 @@
   3.314  #include "close_code.h"
   3.315  
   3.316  #endif /* _MIXER_H_ */
   3.317 +
   3.318 +/* end of SDL_mixer.h ... */
   3.319 +
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/effect_position.c	Tue Sep 11 19:14:36 2001 +0000
     4.3 @@ -0,0 +1,555 @@
     4.4 +/*
     4.5 +    MIXERLIB:  An audio mixer library based on the SDL library
     4.6 +    Copyright (C) 1997-1999  Sam Lantinga
     4.7 +
     4.8 +    This library is free software; you can redistribute it and/or
     4.9 +    modify it under the terms of the GNU Library General Public
    4.10 +    License as published by the Free Software Foundation; either
    4.11 +    version 2 of the License, or (at your option) any later version.
    4.12 +
    4.13 +    This library is distributed in the hope that it will be useful,
    4.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    4.16 +    Library General Public License for more details.
    4.17 +
    4.18 +    You should have received a copy of the GNU Library General Public
    4.19 +    License along with this library; if not, write to the Free
    4.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    4.21 +
    4.22 +    This file by Ryan C. Gordon (icculus@linuxgames.com)
    4.23 +
    4.24 +    These are some internally supported special effects that use SDL_mixer's
    4.25 +    effect callback API. They are meant for speed over quality.  :)
    4.26 +*/
    4.27 +
    4.28 +/* $Id$ */
    4.29 +
    4.30 +#include <stdio.h>
    4.31 +#include <stdlib.h>
    4.32 +#include <sys/time.h>
    4.33 +#include <unistd.h>
    4.34 +#include "SDL.h"
    4.35 +#include "SDL_mixer.h"
    4.36 +
    4.37 +#define __MIX_INTERNAL_EFFECT__
    4.38 +#include "effects_internal.h"
    4.39 +
    4.40 +/* profile code:
    4.41 +    #include <sys/time.h>
    4.42 +    #include <unistd.h>
    4.43 +    struct timeval tv1;
    4.44 +    struct timeval tv2;
    4.45 +    
    4.46 +    gettimeofday(&tv1, NULL);
    4.47 +
    4.48 +        ... do your thing here ...
    4.49 +
    4.50 +    gettimeofday(&tv2, NULL);
    4.51 +    printf("%ld\n", tv2.tv_usec - tv1.tv_usec);
    4.52 +*/
    4.53 +
    4.54 +
    4.55 +/*
    4.56 + * Positional effects...panning, distance attenuation, etc.
    4.57 + */
    4.58 +
    4.59 +typedef struct _Eff_positionargs
    4.60 +{
    4.61 +    volatile float left_f;
    4.62 +    volatile float right_f;
    4.63 +    volatile Uint8 left_u8;
    4.64 +    volatile Uint8 right_u8;
    4.65 +    volatile float distance_f;
    4.66 +    volatile Uint8 distance_u8;
    4.67 +    volatile int in_use;
    4.68 +    volatile int channels;
    4.69 +} position_args;
    4.70 +
    4.71 +static position_args **pos_args_array = NULL;
    4.72 +static position_args *pos_args_global = NULL;
    4.73 +static int position_channels = 0;
    4.74 +
    4.75 +/* This just frees up the callback-specific data. */
    4.76 +static void _Eff_PositionDone(int channel, void *udata)
    4.77 +{
    4.78 +    if (channel < 0) {
    4.79 +        if (pos_args_global != NULL) {
    4.80 +            free(pos_args_global);
    4.81 +            pos_args_global = NULL;
    4.82 +        }
    4.83 +    }
    4.84 +
    4.85 +    else if (pos_args_array[channel] != NULL) {
    4.86 +        free(pos_args_array[channel]);
    4.87 +        pos_args_array[channel] = NULL;
    4.88 +    }
    4.89 +}
    4.90 +
    4.91 +
    4.92 +static void _Eff_position_u8(int chan, void *stream, int len, void *udata)
    4.93 +{
    4.94 +    volatile position_args *args = (volatile position_args *) udata;
    4.95 +    Uint8 *ptr = (Uint8 *) stream;
    4.96 +    int i;
    4.97 +
    4.98 +        /*
    4.99 +         * if there's only a mono channnel (the only way we wouldn't have
   4.100 +         *  a len divisible by 2 here), then left_f and right_f are always
   4.101 +         *  1.0, and are therefore throwaways.
   4.102 +         */
   4.103 +    if (len % sizeof (Uint16) != 0) {
   4.104 +        *(ptr++) = (Uint8) (((float) *ptr) * args->distance_f);
   4.105 +        len--;
   4.106 +    }
   4.107 +
   4.108 +    for (i = 0; i < len; i += sizeof (Uint8) * 2) {
   4.109 +        *(ptr++) = (Uint8)((((float) *ptr) * args->left_f) * args->distance_f);
   4.110 +        *(ptr++) = (Uint8)((((float) *ptr) * args->right_f) * args->distance_f);
   4.111 +    }
   4.112 +}
   4.113 +
   4.114 +
   4.115 +/*
   4.116 + * This one runs about 10.1 times faster than the non-table version, with
   4.117 + *  no loss in quality. It does, however, require 64k of memory for the
   4.118 + *  lookup table. Also, this will only update position information once per
   4.119 + *  call; the non-table version always checks the arguments for each sample,
   4.120 + *  in case the user has called Mix_SetPanning() or whatnot again while this
   4.121 + *  callback is running.
   4.122 + */
   4.123 +static void _Eff_position_table_u8(int chan, void *stream, int len, void *udata)
   4.124 +{
   4.125 +    volatile position_args *args = (volatile position_args *) udata;
   4.126 +    Uint8 *ptr = (Uint8 *) stream;
   4.127 +    Uint32 *p;
   4.128 +    int i;
   4.129 +    Uint8 *l = ((Uint8 *) _Eff_volume_table) + (256 * args->left_u8);
   4.130 +    Uint8 *r = ((Uint8 *) _Eff_volume_table) + (256 * args->right_u8);
   4.131 +    Uint8 *d = ((Uint8 *) _Eff_volume_table) + (256 * args->distance_u8);
   4.132 +
   4.133 +        /*
   4.134 +         * if there's only a mono channnel, then l[] and r[] are always
   4.135 +         *  volume 255, and are therefore throwaways. Still, we have to
   4.136 +         *  be sure not to overrun the audio buffer...
   4.137 +         */
   4.138 +    while (len % sizeof (Uint32) != 0) {
   4.139 +        *(ptr++) = d[l[*ptr]];
   4.140 +        if (args->channels == 2)
   4.141 +            *(ptr++) = d[r[*ptr]];
   4.142 +        len -= args->channels;
   4.143 +    }
   4.144 +
   4.145 +    p = (Uint32 *) ptr;
   4.146 +
   4.147 +    for (i = 0; i < len; i += sizeof (Uint32)) {
   4.148 +#if (SDL_BYTE_ORDER == SDL_BIG_ENDIAN)
   4.149 +        *(p++) = (d[l[(*p & 0xFF000000) >> 24]] << 24) |
   4.150 +                 (d[r[(*p & 0x00FF0000) >> 16]] << 16) |
   4.151 +                 (d[l[(*p & 0x0000FF00) >>  8]] <<  8) |
   4.152 +                 (d[r[(*p & 0x000000FF)      ]]      ) ;
   4.153 +#else
   4.154 +        *(p++) = (d[r[(*p & 0xFF000000) >> 24]] << 24) |
   4.155 +                 (d[l[(*p & 0x00FF0000) >> 16]] << 16) |
   4.156 +                 (d[r[(*p & 0x0000FF00) >>  8]] <<  8) |
   4.157 +                 (d[l[(*p & 0x000000FF)      ]]      ) ;
   4.158 +#endif
   4.159 +    }
   4.160 +}
   4.161 +
   4.162 +
   4.163 +static void _Eff_position_s8(int chan, void *stream, int len, void *udata)
   4.164 +{
   4.165 +    volatile position_args *args = (volatile position_args *) udata;
   4.166 +    Sint8 *ptr = (Sint8 *) stream;
   4.167 +    int i;
   4.168 +
   4.169 +        /*
   4.170 +         * if there's only a mono channnel (the only way we wouldn't have
   4.171 +         *  a len divisible by 2 here), then left_f and right_f are always
   4.172 +         *  1.0, and are therefore throwaways.
   4.173 +         */
   4.174 +    if (len % sizeof (Sint16) != 0) {
   4.175 +        *(ptr++) = (Sint8) (((float) *ptr) * args->distance_f);
   4.176 +        len--;
   4.177 +    }
   4.178 +
   4.179 +    for (i = 0; i < len; i += sizeof (Sint8) * 2) {
   4.180 +        *(ptr++) = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f);
   4.181 +        *(ptr++) = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f);
   4.182 +    }
   4.183 +}
   4.184 +
   4.185 +
   4.186 +/*
   4.187 + * This one runs about 10.1 times faster than the non-table version, with
   4.188 + *  no loss in quality. It does, however, require 64k of memory for the
   4.189 + *  lookup table. Also, this will only update position information once per
   4.190 + *  call; the non-table version always checks the arguments for each sample,
   4.191 + *  in case the user has called Mix_SetPanning() or whatnot again while this
   4.192 + *  callback is running.
   4.193 + */
   4.194 +static void _Eff_position_table_s8(int chan, void *stream, int len, void *udata)
   4.195 +{
   4.196 +    volatile position_args *args = (volatile position_args *) udata;
   4.197 +    Sint8 *ptr = (Sint8 *) stream;
   4.198 +    Uint32 *p;
   4.199 +    int i;
   4.200 +    Sint8 *l = ((Uint8 *) _Eff_volume_table) + (256 * args->left_u8);
   4.201 +    Sint8 *r = ((Uint8 *) _Eff_volume_table) + (256 * args->right_u8);
   4.202 +    Sint8 *d = ((Sint8 *) _Eff_volume_table) + (256 * args->distance_u8);
   4.203 +
   4.204 +
   4.205 +    while (len % sizeof (Uint32) != 0) {
   4.206 +        *(ptr++) = d[l[*ptr]];
   4.207 +        if (args->channels > 1)
   4.208 +            *(ptr++) = d[r[*ptr]];
   4.209 +        len -= args->channels;
   4.210 +    }
   4.211 +
   4.212 +    p = (Uint32 *) ptr;
   4.213 +
   4.214 +    for (i = 0; i < len; i += sizeof (Uint32)) {
   4.215 +#if (SDL_BYTE_ORDER == SDL_BIG_ENDIAN)
   4.216 +        *(p++) = (d[l[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) |
   4.217 +                 (d[r[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) |
   4.218 +                 (d[l[((Sint16)(Sint8)((*p & 0x0000FF00) >>  8))+128]] <<  8) |
   4.219 +                 (d[r[((Sint16)(Sint8)((*p & 0x000000FF)      ))+128]]      ) ;
   4.220 +#else
   4.221 +        *(p++) = (d[r[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) |
   4.222 +                 (d[l[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) |
   4.223 +                 (d[r[((Sint16)(Sint8)((*p & 0x0000FF00) >>  8))+128]] <<  8) |
   4.224 +                 (d[l[((Sint16)(Sint8)((*p & 0x000000FF)      ))+128]]      ) ;
   4.225 +#endif
   4.226 +    }
   4.227 +
   4.228 +
   4.229 +}
   4.230 +
   4.231 +
   4.232 +/* !!! FIXME : Optimize the code for 16-bit samples? */
   4.233 +
   4.234 +static void _Eff_position_u16lsb(int chan, void *stream, int len, void *udata)
   4.235 +{
   4.236 +    volatile position_args *args = (volatile position_args *) udata;
   4.237 +    Uint16 *ptr = (Uint16 *) stream;
   4.238 +    int i;
   4.239 +
   4.240 +    for (i = 0; i < len; i += sizeof (Uint16) * 2) {
   4.241 +#if (SDL_BYTE_ORDER == SDL_BIG_ENDIAN)
   4.242 +        Uint16 swapl = (Uint16) ((((float) SDL_Swap16(*(ptr))) *
   4.243 +                                    args->left_f) * args->distance_f);
   4.244 +        Uint16 swapr = (Uint16) (((float) SDL_Swap16(*(ptr+1))) *
   4.245 +                                    args->right_f) * args->distance_f);
   4.246 +        *(ptr++) = (Uint16) SDL_Swap16(swapl);
   4.247 +        *(ptr++) = (Uint16) SDL_Swap16(swapr);
   4.248 +#else
   4.249 +        *(ptr++) = (Uint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   4.250 +        *(ptr++) = (Uint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   4.251 +#endif
   4.252 +    }
   4.253 +}
   4.254 +
   4.255 +static void _Eff_position_s16lsb(int chan, void *stream, int len, void *udata)
   4.256 +{
   4.257 +    /* 16 signed bits (lsb) * 2 channels. */
   4.258 +    volatile position_args *args = (volatile position_args *) udata;
   4.259 +    Sint16 *ptr = (Sint16 *) stream;
   4.260 +    int i;
   4.261 +
   4.262 +    for (i = 0; i < len; i += sizeof (Sint16) * 2) {
   4.263 +#if (SDL_BYTE_ORDER == SDL_BIG_ENDIAN)
   4.264 +        Sint16 swapl = (Sint16) ((((float) SDL_Swap16(*(ptr))) *
   4.265 +                                    args->left_f) * args->distance_f);
   4.266 +        Sint16 swapr = (Sint16) (((float) SDL_Swap16(*(ptr+1))) *
   4.267 +                                    args->right_f) * args->distance_f);
   4.268 +        *(ptr++) = (Sint16) SDL_Swap16(swapl);
   4.269 +        *(ptr++) = (Sint16) SDL_Swap16(swapr);
   4.270 +#else
   4.271 +        *(ptr++) = (Sint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   4.272 +        *(ptr++) = (Sint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   4.273 +#endif
   4.274 +    }
   4.275 +}
   4.276 +
   4.277 +static void _Eff_position_u16msb(int chan, void *stream, int len, void *udata)
   4.278 +{
   4.279 +    /* 16 signed bits (lsb) * 2 channels. */
   4.280 +    volatile position_args *args = (volatile position_args *) udata;
   4.281 +    Uint16 *ptr = (Uint16 *) stream;
   4.282 +    int i;
   4.283 +
   4.284 +    for (i = 0; i < len; i += sizeof (Sint16) * 2) {
   4.285 +#if (SDL_BYTE_ORDER == SDL_LIL_ENDIAN)
   4.286 +        Uint16 swapl = (Uint16) ((((float) SDL_Swap16(*(ptr))) *
   4.287 +                                    args->left_f) * args->distance_f);
   4.288 +        Uint16 swapr = (Uint16) (((float) SDL_Swap16(*(ptr+1))) *
   4.289 +                                    args->right_f) * args->distance_f);
   4.290 +        *(ptr++) = (Uint16) SDL_Swap16(swapl);
   4.291 +        *(ptr++) = (Uint16) SDL_Swap16(swapr);
   4.292 +#else
   4.293 +        *(ptr++) = (Uint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   4.294 +        *(ptr++) = (Uint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   4.295 +#endif
   4.296 +    }
   4.297 +}
   4.298 +
   4.299 +static void _Eff_position_s16msb(int chan, void *stream, int len, void *udata)
   4.300 +{
   4.301 +    /* 16 signed bits (lsb) * 2 channels. */
   4.302 +    volatile position_args *args = (volatile position_args *) udata;
   4.303 +    Sint16 *ptr = (Sint16 *) stream;
   4.304 +    int i;
   4.305 +
   4.306 +    for (i = 0; i < len; i += sizeof (Sint16) * 2) {
   4.307 +#if (SDL_BYTE_ORDER == SDL_LIL_ENDIAN)
   4.308 +        Sint16 swapl = (Sint16) ((((float) SDL_Swap16(*(ptr))) *
   4.309 +                                    args->left_f) * args->distance_f);
   4.310 +        Sint16 swapr = (Sint16) (((float) SDL_Swap16(*(ptr+1))) *
   4.311 +                                    args->right_f) * args->distance_f);
   4.312 +        *(ptr++) = (Sint16) SDL_Swap16(swapl);
   4.313 +        *(ptr++) = (Sint16) SDL_Swap16(swapr);
   4.314 +#else
   4.315 +        *(ptr++) = (Sint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   4.316 +        *(ptr++) = (Sint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   4.317 +#endif
   4.318 +    }
   4.319 +}
   4.320 +
   4.321 +
   4.322 +static void init_position_args(position_args *args)
   4.323 +{
   4.324 +    memset(args, '\0', sizeof (position_args));
   4.325 +    args->in_use = 0;
   4.326 +    args->left_u8 = args->right_u8 = args->distance_u8 = 255;
   4.327 +    args->left_f  = args->right_f  = args->distance_f  = 1.0f;
   4.328 +    Mix_QuerySpec(NULL, NULL, (int *) &args->channels);
   4.329 +}
   4.330 +
   4.331 +
   4.332 +static position_args *get_position_arg(int channel)
   4.333 +{
   4.334 +    void *rc;
   4.335 +    int i;
   4.336 +
   4.337 +    if (channel < 0) {
   4.338 +        if (pos_args_global == NULL) {
   4.339 +            pos_args_global = malloc(sizeof (position_args));
   4.340 +            if (pos_args_global == NULL) {
   4.341 +                Mix_SetError("Out of memory");
   4.342 +                return(NULL);
   4.343 +            }
   4.344 +            init_position_args(pos_args_global);
   4.345 +        }
   4.346 +
   4.347 +        return(pos_args_global);
   4.348 +    }
   4.349 +
   4.350 +    if (channel >= position_channels) {
   4.351 +        rc = realloc(pos_args_array, (channel + 1) * sizeof (position_args *));
   4.352 +        if (rc == NULL) {
   4.353 +            Mix_SetError("Out of memory");
   4.354 +            return(NULL);
   4.355 +        }
   4.356 +        pos_args_array = (position_args **) rc;
   4.357 +        for (i = position_channels; i <= channel; i++) {
   4.358 +            pos_args_array[i] = NULL;
   4.359 +        }
   4.360 +        position_channels = channel + 1;
   4.361 +    }
   4.362 +
   4.363 +    if (pos_args_array[channel] == NULL) {
   4.364 +        pos_args_array[channel] = (position_args *)malloc(sizeof(position_args));
   4.365 +        if (pos_args_array[channel] == NULL) {
   4.366 +            Mix_SetError("Out of memory");
   4.367 +            return(NULL);
   4.368 +        }
   4.369 +        init_position_args(pos_args_array[channel]);
   4.370 +    }
   4.371 +
   4.372 +    return(pos_args_array[channel]);
   4.373 +}
   4.374 +
   4.375 +
   4.376 +static Mix_EffectFunc_t get_position_effect_func(Uint16 format)
   4.377 +{
   4.378 +    Mix_EffectFunc_t f = NULL;
   4.379 +
   4.380 +    switch (format) {
   4.381 +        case AUDIO_U8:
   4.382 +            f = (_Eff_build_volume_table_u8()) ? _Eff_position_table_u8 :
   4.383 +                                                 _Eff_position_u8;
   4.384 +            break;
   4.385 +
   4.386 +        case AUDIO_S8:
   4.387 +            f = (_Eff_build_volume_table_s8()) ? _Eff_position_table_s8 :
   4.388 +                                                 _Eff_position_s8;
   4.389 +            break;
   4.390 +
   4.391 +        case AUDIO_U16LSB:
   4.392 +            f = _Eff_position_u16lsb;
   4.393 +            break;
   4.394 +
   4.395 +        case AUDIO_S16LSB:
   4.396 +            f = _Eff_position_s16lsb;
   4.397 +            break;
   4.398 +
   4.399 +        case AUDIO_U16MSB:
   4.400 +            f = _Eff_position_u16msb;
   4.401 +            break;
   4.402 +
   4.403 +        case AUDIO_S16MSB:
   4.404 +            f = _Eff_position_s16msb;
   4.405 +            break;
   4.406 +
   4.407 +        default:
   4.408 +            Mix_SetError("Unsupported audio format");
   4.409 +    }
   4.410 +
   4.411 +    return(f);
   4.412 +}
   4.413 +
   4.414 +
   4.415 +int Mix_SetPanning(int channel, Uint8 left, Uint8 right)
   4.416 +{
   4.417 +    Mix_EffectFunc_t f = NULL;
   4.418 +    int channels;
   4.419 +    Uint16 format;
   4.420 +    position_args *args = NULL;
   4.421 +
   4.422 +    Mix_QuerySpec(NULL, &format, &channels);
   4.423 +
   4.424 +    if (channels != 2)    /* it's a no-op; we call that successful. */
   4.425 +        return(1);
   4.426 +
   4.427 +    f = get_position_effect_func(format);
   4.428 +    if (f == NULL)
   4.429 +        return(0);
   4.430 +
   4.431 +    args = get_position_arg(channel);
   4.432 +    if (!args)
   4.433 +        return(0);
   4.434 +
   4.435 +        /* it's a no-op; unregister the effect, if it's registered. */
   4.436 +    if ((args->distance_u8 == 255) && (left == 255) &&
   4.437 +        (right == 255) && (args->in_use))
   4.438 +    {
   4.439 +        return(Mix_UnregisterEffect(channel, f));
   4.440 +    }
   4.441 +
   4.442 +    args->left_u8 = left;
   4.443 +    args->right_u8 = right;
   4.444 +    args->left_f = ((float) left) / 255.0;
   4.445 +    args->right_f = ((float) right) / 255.0;
   4.446 +    if (!args->in_use) {
   4.447 +        args->in_use = 1;
   4.448 +        return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   4.449 +    }
   4.450 +
   4.451 +    return(1);
   4.452 +}
   4.453 +
   4.454 +
   4.455 +int Mix_SetDistance(int channel, Uint8 distance)
   4.456 +{
   4.457 +    Mix_EffectFunc_t f = NULL;
   4.458 +    Uint16 format;
   4.459 +    position_args *args = NULL;
   4.460 +
   4.461 +    Mix_QuerySpec(NULL, &format, NULL);
   4.462 +    f = get_position_effect_func(format);
   4.463 +    if (f == NULL)
   4.464 +        return(0);
   4.465 +
   4.466 +    args = get_position_arg(channel);
   4.467 +    if (!args)
   4.468 +        return(0);
   4.469 +
   4.470 +    distance = 255 - distance;  /* flip it to our scale. */
   4.471 +
   4.472 +        /* it's a no-op; unregister the effect, if it's registered. */
   4.473 +    if ((distance == 255) && (args->left_u8 == 255) &&
   4.474 +        (args->right_u8 == 255) && (args->in_use))
   4.475 +    {
   4.476 +        return(Mix_UnregisterEffect(channel, f));
   4.477 +    }
   4.478 +
   4.479 +    args->distance_u8 = distance;
   4.480 +    args->distance_f = ((float) distance) / 255.0;
   4.481 +    if (!args->in_use) {
   4.482 +        args->in_use = 1;
   4.483 +        return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   4.484 +    }
   4.485 +
   4.486 +    return(1);
   4.487 +}
   4.488 +
   4.489 +
   4.490 +int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance)
   4.491 +{
   4.492 +    Mix_EffectFunc_t f = NULL;
   4.493 +    Uint16 format;
   4.494 +    int channels;
   4.495 +    position_args *args = NULL;
   4.496 +    Uint8 left = 255, right = 255;
   4.497 +    int flip;
   4.498 +    float angle_f;
   4.499 +
   4.500 +    Mix_QuerySpec(NULL, &format, &channels);
   4.501 +    f = get_position_effect_func(format);
   4.502 +    if (f == NULL)
   4.503 +        return(0);
   4.504 +
   4.505 +        /* unwind the angle...it'll be between 0 and 359. */
   4.506 +    while (angle >= 360) angle -= 360;
   4.507 +    while (angle < 0) angle += 360;
   4.508 +
   4.509 +    args = get_position_arg(channel);
   4.510 +    if (!args)
   4.511 +        return(0);
   4.512 +
   4.513 +        /* it's a no-op; unregister the effect, if it's registered. */
   4.514 +    if ((!distance) && (!angle) && (args->in_use))
   4.515 +        return(Mix_UnregisterEffect(channel, f));
   4.516 +
   4.517 +    if (channels == 2)
   4.518 +    {
   4.519 +        /*
   4.520 +         * We only attenuate by position if the angle falls on the far side
   4.521 +         *  of center; That is, an angle that's due north would not attenuate
   4.522 +         *  either channel. Due west attenuates the right channel to 0.0, and
   4.523 +         *  due east attenuates the left channel to 0.0. Slightly east of
   4.524 +         *  center attenuates the left channel a little, and the right channel
   4.525 +         *  not at all. I think of this as occlusion by one's own head.  :)
   4.526 +         *
   4.527 +         *   ...so, we split our angle circle into four quadrants...
   4.528 +         */
   4.529 +        if (angle < 90) {
   4.530 +            left = 255 - ((Uint8) (255.0f * (((float) angle) / 89.0f)));
   4.531 +        } else if (angle < 180) {
   4.532 +            left = (Uint8) (255.0f * (((float) (angle - 90)) / 89.0f));
   4.533 +        } else if (angle < 270) {
   4.534 +            right = 255 - ((Uint8) (255.0f * (((float) (angle - 180)) / 89.0f)));
   4.535 +        } else {
   4.536 +            right = (Uint8) (255.0f * (((float) (angle - 270)) / 89.0f));
   4.537 +        }
   4.538 +    }
   4.539 +
   4.540 +    distance = 255 - distance;  /* flip it to scale Mix_SetDistance() uses. */
   4.541 +
   4.542 +    args->left_u8 = left;
   4.543 +    args->left_f = ((float) left) / 255.0;
   4.544 +    args->right_u8 = right;
   4.545 +    args->right_f = ((float) right) / 255.0;
   4.546 +    args->distance_u8 = distance;
   4.547 +    args->distance_f = ((float) distance) / 255.0;
   4.548 +    if (!args->in_use) {
   4.549 +        args->in_use = 1;
   4.550 +        return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   4.551 +    }
   4.552 +
   4.553 +    return(1);
   4.554 +}
   4.555 +
   4.556 +
   4.557 +/* end of effects_position.c ... */
   4.558 +
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/effect_stereoreverse.c	Tue Sep 11 19:14:36 2001 +0000
     5.3 @@ -0,0 +1,119 @@
     5.4 +/*
     5.5 +    MIXERLIB:  An audio mixer library based on the SDL library
     5.6 +    Copyright (C) 1997-1999  Sam Lantinga
     5.7 +
     5.8 +    This library is free software; you can redistribute it and/or
     5.9 +    modify it under the terms of the GNU Library General Public
    5.10 +    License as published by the Free Software Foundation; either
    5.11 +    version 2 of the License, or (at your option) any later version.
    5.12 +
    5.13 +    This library is distributed in the hope that it will be useful,
    5.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    5.16 +    Library General Public License for more details.
    5.17 +
    5.18 +    You should have received a copy of the GNU Library General Public
    5.19 +    License along with this library; if not, write to the Free
    5.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    5.21 +
    5.22 +    This file by Ryan C. Gordon (icculus@linuxgames.com)
    5.23 +
    5.24 +    These are some internally supported special effects that use SDL_mixer's
    5.25 +    effect callback API. They are meant for speed over quality.  :)
    5.26 +*/
    5.27 +
    5.28 +/* $Id$ */
    5.29 +
    5.30 +#include <stdio.h>
    5.31 +#include <stdlib.h>
    5.32 +#include <sys/time.h>
    5.33 +#include <unistd.h>
    5.34 +#include "SDL.h"
    5.35 +#include "SDL_mixer.h"
    5.36 +
    5.37 +#define __MIX_INTERNAL_EFFECT__
    5.38 +#include "effects_internal.h"
    5.39 +
    5.40 +/* profile code:
    5.41 +    #include <sys/time.h>
    5.42 +    #include <unistd.h>
    5.43 +    struct timeval tv1;
    5.44 +    struct timeval tv2;
    5.45 +    
    5.46 +    gettimeofday(&tv1, NULL);
    5.47 +
    5.48 +        ... do your thing here ...
    5.49 +
    5.50 +    gettimeofday(&tv2, NULL);
    5.51 +    printf("%ld\n", tv2.tv_usec - tv1.tv_usec);
    5.52 +*/
    5.53 +
    5.54 +
    5.55 +
    5.56 +/*
    5.57 + * Stereo reversal effect...this one's pretty straightforward...
    5.58 + */
    5.59 +
    5.60 +static void _Eff_reversestereo16(int chan, void *stream, int len, void *udata)
    5.61 +{
    5.62 +    /* 16 bits * 2 channels. */
    5.63 +    Uint32 *ptr = (Uint32 *) stream;
    5.64 +    int i;
    5.65 +
    5.66 +    for (i = 0; i < len; i += sizeof (Uint32), ptr++) {
    5.67 +        *ptr = (((*ptr) & 0xFFFF0000) >> 16) | (((*ptr) & 0x0000FFFF) << 16);
    5.68 +    }
    5.69 +}
    5.70 +
    5.71 +
    5.72 +static void _Eff_reversestereo8(int chan, void *stream, int len, void *udata)
    5.73 +{
    5.74 +    /* 8 bits * 2 channels. */
    5.75 +    Uint32 *ptr = (Uint32 *) stream;
    5.76 +    int i;
    5.77 +
    5.78 +    /* get the last two bytes if len is not divisible by four... */
    5.79 +    if (len % sizeof (Uint32) != 0) {
    5.80 +        Uint16 *p = (Uint16 *) (((Uint8 *) stream) + (len - 2));
    5.81 +        *p = (((*p) & 0xFF00) >> 8) | (((*ptr) & 0x00FF) << 8);
    5.82 +        len -= 2;
    5.83 +    }
    5.84 +
    5.85 +    for (i = 0; i < len; i += sizeof (Uint32), ptr++) {
    5.86 +        *ptr = (((*ptr) & 0x0000FF00) >> 8) | (((*ptr) & 0x000000FF) << 8) |
    5.87 +               (((*ptr) & 0xFF000000) >> 8) | (((*ptr) & 0x00FF0000) << 8);
    5.88 +    }
    5.89 +}
    5.90 +
    5.91 +
    5.92 +int Mix_SetReverseStereo(int channel, int flip)
    5.93 +{
    5.94 +    Mix_EffectFunc_t f = NULL;
    5.95 +    int channels;
    5.96 +    Uint16 format;
    5.97 +
    5.98 +    Mix_QuerySpec(NULL, &format, &channels);
    5.99 +
   5.100 +    if (channels == 2) {
   5.101 +        if ((format & 0xFF) == 16)
   5.102 +            f = _Eff_reversestereo16;
   5.103 +        else if ((format & 0xFF) == 8)
   5.104 +            f = _Eff_reversestereo8;
   5.105 +        else {
   5.106 +            Mix_SetError("Unsupported audio format");
   5.107 +            return(0);
   5.108 +        }
   5.109 +
   5.110 +        if (!flip) {
   5.111 +            return(Mix_UnregisterEffect(channel, f));
   5.112 +        } else {
   5.113 +            return(Mix_RegisterEffect(channel, f, NULL, NULL));
   5.114 +        }
   5.115 +    }
   5.116 +
   5.117 +    return(1);
   5.118 +}
   5.119 +
   5.120 +
   5.121 +/* end of effect_stereoreverse.c ... */
   5.122 +
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/effects_internal.c	Tue Sep 11 19:14:36 2001 +0000
     6.3 @@ -0,0 +1,112 @@
     6.4 +/*
     6.5 +    MIXERLIB:  An audio mixer library based on the SDL library
     6.6 +    Copyright (C) 1997-1999  Sam Lantinga
     6.7 +
     6.8 +    This library is free software; you can redistribute it and/or
     6.9 +    modify it under the terms of the GNU Library General Public
    6.10 +    License as published by the Free Software Foundation; either
    6.11 +    version 2 of the License, or (at your option) any later version.
    6.12 +
    6.13 +    This library is distributed in the hope that it will be useful,
    6.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    6.16 +    Library General Public License for more details.
    6.17 +
    6.18 +    You should have received a copy of the GNU Library General Public
    6.19 +    License along with this library; if not, write to the Free
    6.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    6.21 +
    6.22 +    This file by Ryan C. Gordon (icculus@linuxgames.com)
    6.23 +
    6.24 +    These are some helper functions for the internal mixer special effects.
    6.25 +*/
    6.26 +
    6.27 +
    6.28 +
    6.29 +     /* ------ These are used internally only. Don't touch. ------ */
    6.30 +
    6.31 +
    6.32 +
    6.33 +#include <stdio.h>
    6.34 +#include <stdlib.h>
    6.35 +#include "SDL_mixer.h"
    6.36 +
    6.37 +/* Should we favor speed over memory usage and/or quality of output? */
    6.38 +int _Mix_effects_max_speed = 0;
    6.39 +
    6.40 +
    6.41 +void _Mix_InitEffects(void)
    6.42 +{
    6.43 +    _Mix_effects_max_speed = (getenv(MIX_EFFECTSMAXSPEED) != NULL);
    6.44 +}
    6.45 +
    6.46 +
    6.47 +void *_Eff_volume_table = NULL;
    6.48 +
    6.49 +
    6.50 +/* Build the volume table for Uint8-format samples.
    6.51 + *
    6.52 + * Each column of the table is a possible sample, while each row of the
    6.53 + *  table is a volume. Volume is a Uint8, where 0 is silence and 255 is full
    6.54 + *  volume. So _Eff_volume_table[128][mysample] would be the value of
    6.55 + *  mysample, at half volume.
    6.56 + */
    6.57 +void *_Eff_build_volume_table_u8(void)
    6.58 +{
    6.59 +    int volume;
    6.60 +    int sample;
    6.61 +    Uint8 *rc;
    6.62 +
    6.63 +    if (!_Mix_effects_max_speed) {
    6.64 +        return(NULL);
    6.65 +    }
    6.66 +
    6.67 +    if (!_Eff_volume_table) {
    6.68 +        rc = malloc(256 * 256);
    6.69 +        if (rc) {
    6.70 +            _Eff_volume_table = (void *) rc;
    6.71 +            for (volume = 0; volume < 256; volume++) {
    6.72 +                for (sample = 0; sample < 256; sample ++) {
    6.73 +                    *rc = (Uint8)(((float) sample) * ((float) volume / 255.0));
    6.74 +                    rc++;
    6.75 +                }
    6.76 +            }
    6.77 +        }
    6.78 +    }
    6.79 +
    6.80 +    return(_Eff_volume_table);
    6.81 +}
    6.82 +
    6.83 +
    6.84 +/* Build the volume table for Sint8-format samples.
    6.85 + *
    6.86 + * Each column of the table is a possible sample, while each row of the
    6.87 + *  table is a volume. Volume is a Uint8, where 0 is silence and 255 is full
    6.88 + *  volume. So _Eff_volume_table[128][mysample] would be the value of
    6.89 + *  mysample, at half volume.
    6.90 + */
    6.91 +void *_Eff_build_volume_table_s8(void)
    6.92 +{
    6.93 +    int volume;
    6.94 +    int sample;
    6.95 +    Sint8 *rc;
    6.96 +
    6.97 +    if (!_Eff_volume_table) {
    6.98 +        rc = malloc(256 * 256);
    6.99 +        if (rc) {
   6.100 +            _Eff_volume_table = (void *) rc;
   6.101 +            for (volume = 0; volume < 256; volume++) {
   6.102 +                for (sample = 0; sample < 256; sample ++) {
   6.103 +                    *rc = (Sint8)(((float) sample) * ((float) volume / 255.0));
   6.104 +                    rc++;
   6.105 +                }
   6.106 +            }
   6.107 +        }
   6.108 +    }
   6.109 +
   6.110 +    return(_Eff_volume_table);
   6.111 +}
   6.112 +
   6.113 +
   6.114 +/* end of effects.c ... */
   6.115 +
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/effects_internal.h	Tue Sep 11 19:14:36 2001 +0000
     7.3 @@ -0,0 +1,27 @@
     7.4 +#ifndef _INCLUDE_EFFECTS_INTERNAL_H_
     7.5 +#define _INCLUDE_EFFECTS_INTERNAL_H_
     7.6 +
     7.7 +#ifndef __MIX_INTERNAL_EFFECT__
     7.8 +#error You should not include this file or use these functions.
     7.9 +#endif
    7.10 +
    7.11 +#include "SDL_mixer.h"
    7.12 +
    7.13 +/* Set up for C function definitions, even when using C++ */
    7.14 +#ifdef __cplusplus
    7.15 +extern "C" {
    7.16 +#endif
    7.17 +
    7.18 +extern int _Mix_effects_max_speed;
    7.19 +extern void *_Eff_volume_table;
    7.20 +void *_Eff_build_volume_table_u8(void);
    7.21 +void *_Eff_build_volume_table_s8(void);
    7.22 +
    7.23 +/* Set up for C function definitions, even when using C++ */
    7.24 +#ifdef __cplusplus
    7.25 +}
    7.26 +#endif
    7.27 +
    7.28 +
    7.29 +#endif
    7.30 +
     8.1 --- a/mixer.c	Tue Sep 11 18:49:18 2001 +0000
     8.2 +++ b/mixer.c	Tue Sep 11 19:14:36 2001 +0000
     8.3 @@ -44,6 +44,15 @@
     8.4  
     8.5  static SDL_AudioSpec mixer;
     8.6  static SDL_mutex *mixer_lock;
     8.7 +
     8.8 +typedef struct _Mix_effectinfo
     8.9 +{
    8.10 +	Mix_EffectFunc_t callback;
    8.11 +	Mix_EffectDone_t done_callback;
    8.12 +	void *udata;
    8.13 +	struct _Mix_effectinfo *next;
    8.14 +} effect_info;
    8.15 +
    8.16  static struct _Mix_Channel {
    8.17  	Mix_Chunk *chunk;
    8.18  	int playing;
    8.19 @@ -58,7 +67,11 @@
    8.20  	int fade_volume;
    8.21  	Uint32 fade_length;
    8.22  	Uint32 ticks_fade;
    8.23 +	effect_info *effects;
    8.24  } *mix_channel = NULL;
    8.25 +
    8.26 +static effect_info *posteffects = NULL;
    8.27 +
    8.28  static int num_channels;
    8.29  static int reserved_channels = 0;
    8.30  
    8.31 @@ -80,9 +93,61 @@
    8.32  static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer;
    8.33  static void *music_data = NULL;
    8.34  
    8.35 +
    8.36 +/* rcg06192001 get linked library's version. */
    8.37 +const SDL_version *Mix_Linked_Version(void)
    8.38 +{
    8.39 +	static SDL_version linked_mixver;
    8.40 +	MIX_VERSION(&linked_mixver);
    8.41 +	return(&linked_mixver);
    8.42 +}
    8.43 +
    8.44 +
    8.45 +/* rcg06122001 Cleanup effect callbacks. */
    8.46 +static void Mix_ChannelDonePlaying(int channel)
    8.47 +{
    8.48 +	effect_info *i = NULL;
    8.49 +
    8.50 +	if (channel_done_callback) {
    8.51 +	    channel_done_callback(channel);
    8.52 +	}
    8.53 +
    8.54 +	Mix_UnregisterAllEffects(channel);
    8.55 +}
    8.56 +
    8.57 +
    8.58 +static void *Mix_DoEffects(int chan, void *snd, int len)
    8.59 +{
    8.60 +	int posteffect = (chan == MIX_CHANNEL_POST);
    8.61 +	effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects);
    8.62 +	void *buf = snd;
    8.63 +
    8.64 +	if (e != NULL) {    /* are there any registered effects? */
    8.65 +		/* if this is the postmix, we can just overwrite the original. */
    8.66 +		if (!posteffect) {
    8.67 +			buf = malloc(len);
    8.68 +			if (buf == NULL) {
    8.69 +				return(snd);
    8.70 +			}
    8.71 +		    memcpy(buf, snd, len);
    8.72 +		}
    8.73 +
    8.74 +		for (; e != NULL; e = e->next) {
    8.75 +			if (e->callback != NULL) {
    8.76 +				e->callback(chan, buf, len, e->udata);
    8.77 +			}
    8.78 +		}
    8.79 +	}
    8.80 +
    8.81 +	/* be sure to free() the return value if != snd ... */
    8.82 +	return(buf);
    8.83 +}
    8.84 +
    8.85 +
    8.86  /* Mixing function */
    8.87  static void mix_channels(void *udata, Uint8 *stream, int len)
    8.88  {
    8.89 +	Uint8 *mix_input;
    8.90  	int i, mixable, volume;
    8.91  	Uint32 sdl_ticks;
    8.92  
    8.93 @@ -125,7 +190,12 @@
    8.94  				if ( mixable > len ) {
    8.95  					mixable = len;
    8.96  				}
    8.97 -				SDL_MixAudio(stream,mix_channel[i].samples,mixable,volume);
    8.98 +
    8.99 +				mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable);
   8.100 +				SDL_MixAudio(stream,mix_input,mixable,volume);
   8.101 +				if (mix_input != mix_channel[i].samples)
   8.102 +					free(mix_input);
   8.103 +
   8.104  				mix_channel[i].samples += mixable;
   8.105  				mix_channel[i].playing -= mixable;
   8.106  				/* If looping the sample and we are at its end, make sure
   8.107 @@ -136,7 +206,12 @@
   8.108  				    	if (remaining > alen) {
   8.109  						remaining = alen;
   8.110  				    	}
   8.111 -					SDL_MixAudio(stream+mixable, mix_channel[i].chunk->abuf, remaining, volume);
   8.112 +
   8.113 +					mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining);
   8.114 +					SDL_MixAudio(stream+mixable, mix_input, remaining, volume);
   8.115 +					if (mix_input != mix_channel[i].chunk->abuf)
   8.116 +						free(mix_input);
   8.117 +
   8.118  					--mix_channel[i].looping;
   8.119  					mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining;
   8.120  					mix_channel[i].playing = mix_channel[i].chunk->alen - remaining;
   8.121 @@ -150,12 +225,16 @@
   8.122  				}
   8.123  
   8.124  				/* rcg06072001 Alert app if channel is done playing. */
   8.125 -				if ( (!mix_channel[i].playing) && (channel_done_callback) ) {
   8.126 -					channel_done_callback(i);
   8.127 +				if (!mix_channel[i].playing) {
   8.128 +					Mix_ChannelDonePlaying(i);
   8.129  				}
   8.130  			}
   8.131  		}
   8.132  	}
   8.133 +
   8.134 +	/* rcg06122001 run posteffects... */
   8.135 +	Mix_DoEffects(MIX_CHANNEL_POST, stream, len);
   8.136 +
   8.137  	if ( mix_postmix ) {
   8.138  		mix_postmix(mix_postmix_data, stream, len);
   8.139  	}
   8.140 @@ -169,6 +248,9 @@
   8.141  			(fmt->channels > 1) ? "stereo" : "mono", fmt->freq);
   8.142  }
   8.143  
   8.144 +
   8.145 +void _Mix_InitEffects(void);
   8.146 +
   8.147  /* Open the mixer with a certain desired audio format */
   8.148  int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize)
   8.149  {
   8.150 @@ -225,8 +307,12 @@
   8.151  		mix_channel[i].volume = SDL_MIX_MAXVOLUME;
   8.152  		mix_channel[i].tag = -1;
   8.153  		mix_channel[i].expire = 0;
   8.154 +		mix_channel[i].effects = NULL;
   8.155  	}
   8.156  	Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
   8.157 +
   8.158 +	_Mix_InitEffects();
   8.159 +
   8.160  	audio_opened = 1;
   8.161  	SDL_PauseAudio(0);
   8.162  	return(0);
   8.163 @@ -260,6 +346,7 @@
   8.164  			mix_channel[i].volume = SDL_MIX_MAXVOLUME;
   8.165  			mix_channel[i].tag = -1;
   8.166  			mix_channel[i].expire = 0;
   8.167 +			mix_channel[i].effects = NULL;
   8.168  		}
   8.169  	}
   8.170  	num_channels = numchans;
   8.171 @@ -669,7 +756,10 @@
   8.172  		}
   8.173  	} else {
   8.174  		SDL_mutexP(mixer_lock);
   8.175 +		if (mix_channel[which].playing) {
   8.176 +			Mix_ChannelDonePlaying(which);
   8.177  		mix_channel[which].playing = 0;
   8.178 +		}
   8.179  		mix_channel[which].expire = 0;
   8.180  		if(mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */
   8.181  			mix_channel[which].volume = mix_channel[which].fade_volume;
   8.182 @@ -742,7 +832,7 @@
   8.183  }
   8.184  
   8.185  /* Check the status of a specific channel.
   8.186 -   If the specified mix_channel is -1, check all mix_channels.
   8.187 +   If the specified mix_channel is -1, check all mix channels.
   8.188  */
   8.189  int Mix_Playing(int which)
   8.190  {
   8.191 @@ -753,12 +843,16 @@
   8.192  		int i;
   8.193  
   8.194  		for ( i=0; i<num_channels; ++i ) {
   8.195 -			if ( mix_channel[i].playing > 0 ) {
   8.196 +			if ((mix_channel[i].playing > 0) ||
   8.197 +				(mix_channel[i].looping > 0))
   8.198 +			{
   8.199  				++status;
   8.200  			}
   8.201  		}
   8.202  	} else {
   8.203 -		if ( mix_channel[which].playing > 0 ) {
   8.204 +		if ((mix_channel[which].playing > 0) ||
   8.205 +			(mix_channel[which].looping > 0))
   8.206 +		{
   8.207  			++status;
   8.208  		}
   8.209  	}
   8.210 @@ -780,8 +874,14 @@
   8.211  /* Close the mixer, halting all playing audio */
   8.212  void Mix_CloseAudio(void)
   8.213  {
   8.214 +	int i;
   8.215 +
   8.216  	if ( audio_opened ) {
   8.217  		if ( audio_opened == 1 ) {
   8.218 +			for (i = 0; i < num_channels; i++) {
   8.219 +				Mix_UnregisterAllEffects(i);
   8.220 +			}
   8.221 +			Mix_UnregisterAllEffects(MIX_CHANNEL_POST);
   8.222  			close_music();
   8.223  			Mix_HaltChannel(-1);
   8.224  			SDL_CloseAudio();
   8.225 @@ -933,3 +1033,175 @@
   8.226  	}
   8.227  	return(chan);
   8.228  }
   8.229 +
   8.230 +
   8.231 +
   8.232 +/*
   8.233 + * rcg06122001 The special effects exportable API.
   8.234 + *  Please see effect_*.c for internally-implemented effects, such
   8.235 + *  as Mix_SetPanning().
   8.236 + */
   8.237 +
   8.238 +static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f,
   8.239 +											Mix_EffectDone_t d, void *arg)
   8.240 +{
   8.241 +	effect_info *new_e = malloc(sizeof (effect_info));
   8.242 +
   8.243 +	if (!e) {
   8.244 +		Mix_SetError("Internal error");
   8.245 +		return(0);
   8.246 +	}
   8.247 +
   8.248 +	if (f == NULL) {
   8.249 +		Mix_SetError("NULL effect callback");
   8.250 +		return(0);
   8.251 +	}
   8.252 +
   8.253 +	if (new_e == NULL) {
   8.254 +		Mix_SetError("Out of memory");
   8.255 +		return(0);
   8.256 +	}
   8.257 +
   8.258 +	new_e->callback = f;
   8.259 +	new_e->done_callback = d;
   8.260 +	new_e->udata = arg;
   8.261 +	new_e->next = NULL;
   8.262 +
   8.263 +	SDL_LockAudio();
   8.264 +
   8.265 +	/* add new effect to end of linked list... */
   8.266 +	if (*e == NULL) {
   8.267 +		*e = new_e;
   8.268 +	} else {
   8.269 +		effect_info *cur = *e;
   8.270 +		while (1) {
   8.271 +			if (cur->next == NULL) {
   8.272 +				cur->next = new_e;
   8.273 +				break;
   8.274 +			}
   8.275 +			cur = cur->next;
   8.276 +		}
   8.277 +	}
   8.278 +
   8.279 +	SDL_UnlockAudio();
   8.280 +	return(1);
   8.281 +}
   8.282 +
   8.283 +
   8.284 +static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f)
   8.285 +{
   8.286 +	effect_info *cur;
   8.287 +	effect_info *prev = NULL;
   8.288 +	effect_info *next = NULL;
   8.289 +
   8.290 +	if (!e) {
   8.291 +		Mix_SetError("Internal error");
   8.292 +		return(0);
   8.293 +	}
   8.294 +
   8.295 +	SDL_LockAudio();
   8.296 +	for (cur = *e; cur != NULL; cur = cur->next) {
   8.297 +		if (cur->callback == f) {
   8.298 +			next = cur->next;
   8.299 +			if (cur->done_callback != NULL) {
   8.300 +				cur->done_callback(channel, cur->udata);
   8.301 +			}
   8.302 +			free(cur);
   8.303 +
   8.304 +			if (prev == NULL) {   /* removing first item of list? */
   8.305 +				*e = next;
   8.306 +			} else {
   8.307 +				prev->next = next;
   8.308 +			}
   8.309 +			SDL_UnlockAudio();
   8.310 +			return(1);
   8.311 +		}
   8.312 +		prev = cur;
   8.313 +	}
   8.314 +
   8.315 +	SDL_UnlockAudio();
   8.316 +	Mix_SetError("No such effect registered");
   8.317 +	return(0);
   8.318 +}
   8.319 +
   8.320 +
   8.321 +static int _Mix_remove_all_effects(int channel, effect_info **e)
   8.322 +{
   8.323 +	effect_info *cur;
   8.324 +	effect_info *next;
   8.325 +
   8.326 +	if (!e) {
   8.327 +		Mix_SetError("Internal error");
   8.328 +		return(0);
   8.329 +	}
   8.330 +
   8.331 +	SDL_LockAudio();
   8.332 +	for (cur = *e; cur != NULL; cur = next) {
   8.333 +		next = cur->next;
   8.334 +		if (cur->done_callback != NULL) {
   8.335 +			cur->done_callback(channel, cur->udata);
   8.336 +		}
   8.337 +		free(cur);
   8.338 +	}
   8.339 +	*e = NULL;
   8.340 +	SDL_UnlockAudio();
   8.341 +
   8.342 +	return(1);
   8.343 +}
   8.344 +
   8.345 +
   8.346 +int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f,
   8.347 +						Mix_EffectDone_t d, void *arg)
   8.348 +{
   8.349 +	effect_info **e = NULL;
   8.350 +
   8.351 +	if (channel == MIX_CHANNEL_POST) {
   8.352 +		e = &posteffects;
   8.353 +	} else {
   8.354 +		if ((channel < 0) || (channel >= num_channels)) {
   8.355 +			Mix_SetError("Invalid channel number");
   8.356 +			return(0);
   8.357 +		}
   8.358 +		e = &mix_channel[channel].effects;
   8.359 +	}
   8.360 +
   8.361 +	return(_Mix_register_effect(e, f, d, arg));
   8.362 +}
   8.363 +
   8.364 +
   8.365 +int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f)
   8.366 +{
   8.367 +	effect_info **e = NULL;
   8.368 +
   8.369 +	if (channel == MIX_CHANNEL_POST) {
   8.370 +		e = &posteffects;
   8.371 +	} else {
   8.372 +		if ((channel < 0) || (channel >= num_channels)) {
   8.373 +			Mix_SetError("Invalid channel number");
   8.374 +			return(0);
   8.375 +		}
   8.376 +		e = &mix_channel[channel].effects;
   8.377 +	}
   8.378 +	return(_Mix_remove_effect(channel, e, f));
   8.379 +}
   8.380 +
   8.381 +
   8.382 +int Mix_UnregisterAllEffects(int channel)
   8.383 +{
   8.384 +	effect_info **e = NULL;
   8.385 +
   8.386 +	if (channel == MIX_CHANNEL_POST) {
   8.387 +		e = &posteffects;
   8.388 +	} else {
   8.389 +		if ((channel < 0) || (channel >= num_channels)) {
   8.390 +			Mix_SetError("Invalid channel number");
   8.391 +			return(0);
   8.392 +		}
   8.393 +		e = &mix_channel[channel].effects;
   8.394 +	}
   8.395 +
   8.396 +	return(_Mix_remove_all_effects(channel, e));
   8.397 +}
   8.398 +
   8.399 +/* end of mixer.c ... */
   8.400 +
     9.1 --- a/playwave.c	Tue Sep 11 18:49:18 2001 +0000
     9.2 +++ b/playwave.c	Tue Sep 11 19:14:36 2001 +0000
     9.3 @@ -34,10 +34,223 @@
     9.4  #include "SDL_mixer.h"
     9.5  
     9.6  
     9.7 +/*
     9.8 + * rcg06132001 various mixer tests. Define the ones you want.
     9.9 + */
    9.10 +#define TEST_MIX_VERSIONS
    9.11 +#define TEST_MIX_CHANNELFINISHED
    9.12 +/*#define TEST_MIX_PANNING*/
    9.13 +/*#define TEST_MIX_DISTANCE*/
    9.14 +#define TEST_MIX_POSITION
    9.15 +
    9.16 +
    9.17 +#if (defined TEST_MIX_POSITION)
    9.18 +
    9.19 +#if (defined TEST_MIX_PANNING)
    9.20 +#error TEST_MIX_POSITION interferes with TEST_MIX_PANNING.
    9.21 +#endif
    9.22 +
    9.23 +#if (defined TEST_MIX_DISTANCE)
    9.24 +#error TEST_MIX_POSITION interferes with TEST_MIX_DISTANCE.
    9.25 +#endif
    9.26 +
    9.27 +#endif
    9.28 +
    9.29 +
    9.30 +/* rcg06192001 for debugging purposes. */
    9.31 +static void output_test_warnings(void)
    9.32 +{
    9.33 +#if (defined TEST_MIX_CHANNELFINISHED)
    9.34 +	fprintf(stderr, "Warning: TEST_MIX_CHANNELFINISHED is enabled in this binary...\n");
    9.35 +#endif
    9.36 +#if (defined TEST_MIX_PANNING)
    9.37 +	fprintf(stderr, "Warning: TEST_MIX_PANNING is enabled in this binary...\n");
    9.38 +#endif
    9.39 +#if (defined TEST_MIX_VERSIONS)
    9.40 +	fprintf(stderr, "Warning: TEST_MIX_VERSIONS is enabled in this binary...\n");
    9.41 +#endif
    9.42 +#if (defined TEST_MIX_DISTANCE)
    9.43 +	fprintf(stderr, "Warning: TEST_MIX_DISTANCE is enabled in this binary...\n");
    9.44 +#endif
    9.45 +#if (defined TEST_MIX_POSITION)
    9.46 +	fprintf(stderr, "Warning: TEST_MIX_POSITION is enabled in this binary...\n");
    9.47 +#endif
    9.48 +}
    9.49 +
    9.50 +
    9.51  static int audio_open = 0;
    9.52  static Mix_Chunk *wave = NULL;
    9.53  
    9.54 -void CleanUp(void)
    9.55 +
    9.56 +/* rcg06192001 Check new Mixer version API. */
    9.57 +#if (defined TEST_MIX_VERSIONS)
    9.58 +static void output_versions(const char *libname, const SDL_version *compiled,
    9.59 +							const SDL_version *linked)
    9.60 +{
    9.61 +	fprintf(stderr,
    9.62 +			"This program was compiled against %s %d.%d.%d,\n"
    9.63 +			" and is dynamically linked to %d.%d.%d.\n", libname,
    9.64 +			compiled->major, compiled->minor, compiled->patch,
    9.65 +			linked->major, linked->minor, linked->patch);
    9.66 +}
    9.67 +
    9.68 +static void test_versions(void)
    9.69 +{
    9.70 +	SDL_version compiled;
    9.71 +	const SDL_version *linked;
    9.72 +
    9.73 +	SDL_VERSION(&compiled);
    9.74 +	linked = SDL_Linked_Version();
    9.75 +	output_versions("SDL", &compiled, linked);
    9.76 +
    9.77 +	MIX_VERSION(&compiled);
    9.78 +	linked = Mix_Linked_Version();
    9.79 +	output_versions("SDL_mixer", &compiled, linked);
    9.80 +}
    9.81 +#endif
    9.82 +
    9.83 +
    9.84 +#ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
    9.85 +static volatile int channel_is_done = 0;
    9.86 +static void channel_complete_callback(int chan)
    9.87 +{
    9.88 +	Mix_Chunk *done_chunk = Mix_GetChunk(chan);
    9.89 +	fprintf(stderr, "We were just alerted that Mixer channel #%d is done.\n", chan);
    9.90 +	fprintf(stderr, "Channel's chunk pointer is (%p).\n", done_chunk);
    9.91 +	fprintf(stderr, " Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT");
    9.92 +	channel_is_done = 1;
    9.93 +}
    9.94 +#endif
    9.95 +
    9.96 +
    9.97 +/* rcg06192001 abstract this out for testing purposes. */
    9.98 +static int still_playing(void)
    9.99 +{
   9.100 +#ifdef TEST_MIX_CHANNELFINISHED
   9.101 +	return(!channel_is_done);
   9.102 +#else
   9.103 +	return(Mix_Playing(0));
   9.104 +#endif
   9.105 +}
   9.106 +
   9.107 +
   9.108 +#if (defined TEST_MIX_PANNING)
   9.109 +static void do_panning_update(void)
   9.110 +{
   9.111 +	static Uint8 leftvol = 128;
   9.112 +	static Uint8 rightvol = 128;
   9.113 +	static Uint8 leftincr = -1;
   9.114 +	static Uint8 rightincr = 1;
   9.115 +	static int panningok = 1;
   9.116 +	static Uint32 next_panning_update = 0;
   9.117 +
   9.118 +	if ((panningok) && (SDL_GetTicks() >= next_panning_update)) {
   9.119 +		panningok = Mix_SetPanning(0, leftvol, rightvol);
   9.120 +		if (!panningok) {
   9.121 +			fprintf(stderr, "Mix_SetPanning(0, %d, %d) failed!\n",
   9.122 +					(int) leftvol, (int) rightvol);
   9.123 +			fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
   9.124 +		}
   9.125 +
   9.126 +		if ((leftvol == 255) || (leftvol == 0)) {
   9.127 +			if (leftvol == 255)
   9.128 +				printf("All the way in the left speaker.\n");
   9.129 +				leftincr *= -1;
   9.130 +		}
   9.131 +
   9.132 +		if ((rightvol == 255) || (rightvol == 0)) {
   9.133 +			if (rightvol == 255)
   9.134 +				printf("All the way in the right speaker.\n");
   9.135 +			rightincr *= -1;
   9.136 +		}
   9.137 +
   9.138 +		leftvol += leftincr;
   9.139 +		rightvol += rightincr;
   9.140 +		next_panning_update = SDL_GetTicks() + 10;
   9.141 +	}
   9.142 +}
   9.143 +#endif
   9.144 +
   9.145 +
   9.146 +#if (defined TEST_MIX_DISTANCE)
   9.147 +static void do_distance_update(void)
   9.148 +{
   9.149 +	static Uint8 distance = 1;
   9.150 +	static Uint8 distincr = 1;
   9.151 +	static int distanceok = 1;
   9.152 +	static Uint32 next_distance_update = 0;
   9.153 +
   9.154 +	if ((distanceok) && (SDL_GetTicks() >= next_distance_update)) {
   9.155 +		distanceok = Mix_SetDistance(0, distance);
   9.156 +		if (!distanceok) {
   9.157 +			fprintf(stderr, "Mix_SetDistance(0, %d) failed!\n", (int) distance);
   9.158 +			fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
   9.159 +		}
   9.160 +
   9.161 +		if (distance == 0) {
   9.162 +			printf("Distance at nearest point.\n");
   9.163 +			distincr *= -1;
   9.164 +		}
   9.165 +		else if (distance == 255) {
   9.166 +			printf("Distance at furthest point.\n");
   9.167 +			distincr *= -1;
   9.168 +		}
   9.169 +
   9.170 +		distance += distincr;
   9.171 +		next_distance_update = SDL_GetTicks() + 15;
   9.172 +	}
   9.173 +}
   9.174 +#endif
   9.175 +
   9.176 +
   9.177 +#if (defined TEST_MIX_POSITION)
   9.178 +static void do_position_update(void)
   9.179 +{
   9.180 +	static Sint16 distance = 1;
   9.181 +	static Sint8 distincr = 1;
   9.182 +	static Uint16 angle = 0;
   9.183 +	static Sint8 angleincr = 1;
   9.184 +	static int positionok = 1;
   9.185 +	static Uint32 next_position_update = 0;
   9.186 +
   9.187 +	if ((positionok) && (SDL_GetTicks() >= next_position_update)) {
   9.188 +		positionok = Mix_SetPosition(0, angle, distance);
   9.189 +		if (!positionok) {
   9.190 +			fprintf(stderr, "Mix_SetPosition(0, %d, %d) failed!\n",
   9.191 +					(int) angle, (int) distance);
   9.192 +			fprintf(stderr, "Reason: [%s].\n", Mix_GetError());
   9.193 +		}
   9.194 +
   9.195 +		if (angle == 0) {
   9.196 +			printf("Due north; now rotating clockwise...\n");
   9.197 +			angleincr = 1;
   9.198 +		}
   9.199 +
   9.200 +		else if (angle == 360) {
   9.201 +			printf("Due north; now rotating counter-clockwise...\n");
   9.202 +			angleincr = -1;
   9.203 +		}
   9.204 +
   9.205 +		distance += distincr;
   9.206 +
   9.207 +		if (distance < 0) {
   9.208 +			distance = 0;
   9.209 +			distincr = 3;
   9.210 +			printf("Distance is very, very near. Stepping away by threes...\n");
   9.211 +		} else if (distance > 255) {
   9.212 +			distance = 255;
   9.213 +			distincr = -3;
   9.214 +			printf("Distance is very, very far. Stepping towards by threes...\n");
   9.215 +		}
   9.216 +
   9.217 +		angle += angleincr;
   9.218 +		next_position_update = SDL_GetTicks() + 30;
   9.219 +	}
   9.220 +}
   9.221 +#endif
   9.222 +
   9.223 +
   9.224 +static void CleanUp(void)
   9.225  {
   9.226  	if ( wave ) {
   9.227  		Mix_FreeChunk(wave);
   9.228 @@ -50,33 +263,88 @@
   9.229  	SDL_Quit();
   9.230  }
   9.231  
   9.232 -void Usage(char *argv0)
   9.233 +
   9.234 +static void Usage(char *argv0)
   9.235  {
   9.236 -	fprintf(stderr, "Usage: %s [-8] [-r rate] [-l] [-m] <wavefile>\n", argv0);
   9.237 +	fprintf(stderr, "Usage: %s [-8] [-r rate] [-f] [-F] [-l] [-m] <wavefile>\n", argv0);
   9.238  }
   9.239  
   9.240  
   9.241 -/*#define TEST_MIX_CHANNELFINISHED*/
   9.242 -#ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
   9.243 -static volatile int channel_is_done = 0;
   9.244 -static void channel_complete_callback(int chan)
   9.245 +/*
   9.246 + * rcg06182001 This is sick, but cool.
   9.247 + *
   9.248 + *  Actually, it's meant to be an example of how to manipulate a voice
   9.249 + *  without having to use the mixer effects API. This is more processing
   9.250 + *  up front, but no extra during the mixing process. Also, in a case like
   9.251 + *  this, when you need to touch the whole sample at once, it's the only
   9.252 + *  option you've got. And, with the effects API, you are altering a copy of
   9.253 + *  the original sample for each playback, and thus, your changes aren't
   9.254 + *  permanent; here, you've got a reversed sample, and that's that until
   9.255 + *  you either reverse it again, or reload it.
   9.256 + */
   9.257 +static void flip_sample(Mix_Chunk *wave)
   9.258  {
   9.259 -	Mix_Chunk *done_chunk = Mix_GetChunk(chan);
   9.260 -	printf("We were just alerted that Mixer channel #%d is done.\n", chan);
   9.261 -	printf("Channel's chunk pointer is (%p).\n", done_chunk);
   9.262 -	printf(" Which %s correct.\n", (wave == done_chunk) ? "is" : "is NOT");
   9.263 -	channel_is_done = 1;
   9.264 +	Uint16 format;
   9.265 +	int channels, i, incr;
   9.266 +	Uint8 *start = wave->abuf;
   9.267 +	Uint8 *end = wave->abuf + wave->alen;
   9.268 +
   9.269 +	Mix_QuerySpec(NULL, &format, &channels);
   9.270 +	incr = (format & 0xFF) * channels;
   9.271 +
   9.272 +	end -= incr;
   9.273 +
   9.274 +	switch (incr) {
   9.275 +		case 8:
   9.276 +			for (i = wave->alen / 2; i >= 0; i -= 1) {
   9.277 +				Uint8 tmp = *start;
   9.278 +				*start = *end;
   9.279 +				*end = tmp;
   9.280 +				start++;
   9.281 +				end--;
   9.282 +			}
   9.283 +			break;
   9.284 +
   9.285 +		case 16:
   9.286 +			for (i = wave->alen / 2; i >= 0; i -= 2) {
   9.287 +				Uint16 tmp = *start;
   9.288 +				*((Uint16 *) start) = *((Uint16 *) end);
   9.289 +				*((Uint16 *) end) = tmp;
   9.290 +				start += 2;
   9.291 +				end -= 2;
   9.292 +			}
   9.293 +			break;
   9.294 +
   9.295 +		case 32:
   9.296 +			for (i = wave->alen / 2; i >= 0; i -= 4) {
   9.297 +				Uint32 tmp = *start;
   9.298 +				*((Uint32 *) start) = *((Uint32 *) end);
   9.299 +				*((Uint32 *) end) = tmp;
   9.300 +				start += 4;
   9.301 +				end -= 4;
   9.302 +			}
   9.303 +			break;
   9.304 +
   9.305 +		default:
   9.306 +			fprintf(stderr, "Unhandled format in sample flipping.\n");
   9.307 +			return;
   9.308 +	}
   9.309  }
   9.310 -#endif
   9.311  
   9.312  
   9.313 -main(int argc, char *argv[])
   9.314 +int main(int argc, char *argv[])
   9.315  {
   9.316  	int audio_rate;
   9.317  	Uint16 audio_format;
   9.318  	int audio_channels;
   9.319  	int loops = 0;
   9.320  	int i;
   9.321 +	int reverse_stereo = 0;
   9.322 +	int reverse_sample = 0;
   9.323 +
   9.324 +	setbuf(stdout, NULL);    /* rcg06132001 for debugging purposes. */
   9.325 +	setbuf(stderr, NULL);    /* rcg06192001 for debugging purposes, too. */
   9.326 +	output_test_warnings();
   9.327  
   9.328  	/* Initialize variables */
   9.329  	audio_rate = MIX_DEFAULT_FREQUENCY;
   9.330 @@ -97,6 +365,12 @@
   9.331  		} else
   9.332  		if ( strcmp(argv[i], "-8") == 0 ) {
   9.333  			audio_format = AUDIO_U8;
   9.334 +		} else
   9.335 +		if ( strcmp(argv[i], "-f") == 0 ) { /* rcg06122001 flip stereo */
   9.336 +			reverse_stereo = 1;
   9.337 +		} else
   9.338 +		if ( strcmp(argv[i], "-F") == 0 ) { /* rcg06172001 flip sample */
   9.339 +			reverse_sample = 1;
   9.340  		} else {
   9.341  			Usage(argv[0]);
   9.342  			return(1);
   9.343 @@ -133,6 +407,10 @@
   9.344  	}
   9.345  	audio_open = 1;
   9.346  
   9.347 +#if (defined TEST_MIX_VERSIONS)
   9.348 +    test_versions();
   9.349 +#endif
   9.350 +
   9.351  	/* Load the requested wave file */
   9.352  	wave = Mix_LoadWAV(argv[i]);
   9.353  	if ( wave == NULL ) {
   9.354 @@ -141,23 +419,44 @@
   9.355  		return(2);
   9.356  	}
   9.357  
   9.358 +	if (reverse_sample) {
   9.359 +		flip_sample(wave);
   9.360 +	}
   9.361 +
   9.362  #ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
   9.363 -	setbuf(stdout, NULL);
   9.364  	Mix_ChannelFinished(channel_complete_callback);
   9.365  #endif
   9.366  
   9.367 +	if ( (!Mix_SetReverseStereo(MIX_CHANNEL_POST, reverse_stereo)) &&
   9.368 +		 (reverse_stereo) )
   9.369 +	{
   9.370 +		printf("Failed to set up reverse stereo effect!\n");
   9.371 +		printf("Reason: [%s].\n", Mix_GetError());
   9.372 +	}
   9.373 +
   9.374  	/* Play and then exit */
   9.375  	Mix_PlayChannel(0, wave, loops);
   9.376  
   9.377 -#ifdef TEST_MIX_CHANNELFINISHED  /* rcg06072001 */
   9.378 -	while (!channel_is_done) {
   9.379 -		SDL_Delay(100);
   9.380 -	}
   9.381 -#else
   9.382 -	while ( Mix_Playing(0) ) {
   9.383 -		SDL_Delay(100);
   9.384 -	}
   9.385 +	while (still_playing()) {
   9.386 +
   9.387 +#if (defined TEST_MIX_PANNING)  /* rcg06132001 */
   9.388 +		do_panning_update();
   9.389  #endif
   9.390  
   9.391 +#if (defined TEST_MIX_DISTANCE) /* rcg06192001 */
   9.392 +		do_distance_update();
   9.393 +#endif
   9.394 +
   9.395 +#if (defined TEST_MIX_POSITION) /* rcg06202001 */
   9.396 +		do_position_update();
   9.397 +#endif
   9.398 +
   9.399 +		SDL_Delay(1);
   9.400 +
   9.401 +	} /* while still_playing() loop... */
   9.402 +
   9.403  	return(0);
   9.404  }
   9.405 +
   9.406 +/* end of playwave.c ... */
   9.407 +