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()
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 +