2
SDL_mixer: An audio mixer library based on the SDL library
3
Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License as published by the Free Software Foundation; either
8
version 2 of the License, or (at your option) any later version.
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Library General Public License for more details.
15
You should have received a copy of the GNU Library General Public
16
License along with this library; if not, write to the Free
17
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
This file by Ryan C. Gordon (icculus@linuxgames.com)
21
These are some internally supported special effects that use SDL_mixer's
22
effect callback API. They are meant for speed over quality. :)
25
/* $Id: effect_position.c,v 1.4 2001/12/14 12:56:55 slouken Exp $ */
32
#include "SDL_mixer.h"
33
#include "SDL_endian.h"
35
#define __MIX_INTERNAL_EFFECT__
36
#include "effects_internal.h"
44
gettimeofday(&tv1, NULL);
46
... do your thing here ...
48
gettimeofday(&tv2, NULL);
49
printf("%ld\n", tv2.tv_usec - tv1.tv_usec);
54
* Positional effects...panning, distance attenuation, etc.
57
typedef struct _Eff_positionargs
59
volatile float left_f;
60
volatile float right_f;
61
volatile Uint8 left_u8;
62
volatile Uint8 right_u8;
63
volatile float distance_f;
64
volatile Uint8 distance_u8;
66
volatile int channels;
69
static position_args **pos_args_array = NULL;
70
static position_args *pos_args_global = NULL;
71
static int position_channels = 0;
73
/* This just frees up the callback-specific data. */
74
static void _Eff_PositionDone(int channel, void *udata)
77
if (pos_args_global != NULL) {
78
free(pos_args_global);
79
pos_args_global = NULL;
83
else if (pos_args_array[channel] != NULL) {
84
free(pos_args_array[channel]);
85
pos_args_array[channel] = NULL;
90
static void _Eff_position_u8(int chan, void *stream, int len, void *udata)
92
volatile position_args *args = (volatile position_args *) udata;
93
Uint8 *ptr = (Uint8 *) stream;
97
* if there's only a mono channnel (the only way we wouldn't have
98
* a len divisible by 2 here), then left_f and right_f are always
99
* 1.0, and are therefore throwaways.
101
if (len % sizeof (Uint16) != 0) {
102
*(ptr++) = (Uint8) (((float) *ptr) * args->distance_f);
106
for (i = 0; i < len; i += sizeof (Uint8) * 2) {
107
*(ptr++) = (Uint8)((((float) *ptr) * args->left_f) * args->distance_f);
108
*(ptr++) = (Uint8)((((float) *ptr) * args->right_f) * args->distance_f);
114
* This one runs about 10.1 times faster than the non-table version, with
115
* no loss in quality. It does, however, require 64k of memory for the
116
* lookup table. Also, this will only update position information once per
117
* call; the non-table version always checks the arguments for each sample,
118
* in case the user has called Mix_SetPanning() or whatnot again while this
119
* callback is running.
121
static void _Eff_position_table_u8(int chan, void *stream, int len, void *udata)
123
volatile position_args *args = (volatile position_args *) udata;
124
Uint8 *ptr = (Uint8 *) stream;
127
Uint8 *l = ((Uint8 *) _Eff_volume_table) + (256 * args->left_u8);
128
Uint8 *r = ((Uint8 *) _Eff_volume_table) + (256 * args->right_u8);
129
Uint8 *d = ((Uint8 *) _Eff_volume_table) + (256 * args->distance_u8);
132
* if there's only a mono channnel, then l[] and r[] are always
133
* volume 255, and are therefore throwaways. Still, we have to
134
* be sure not to overrun the audio buffer...
136
while (len % sizeof (Uint32) != 0) {
137
*(ptr++) = d[l[*ptr]];
138
if (args->channels == 2)
139
*(ptr++) = d[r[*ptr]];
140
len -= args->channels;
145
for (i = 0; i < len; i += sizeof (Uint32)) {
146
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
147
*(p++) = (d[l[(*p & 0xFF000000) >> 24]] << 24) |
148
(d[r[(*p & 0x00FF0000) >> 16]] << 16) |
149
(d[l[(*p & 0x0000FF00) >> 8]] << 8) |
150
(d[r[(*p & 0x000000FF) ]] ) ;
152
*(p++) = (d[r[(*p & 0xFF000000) >> 24]] << 24) |
153
(d[l[(*p & 0x00FF0000) >> 16]] << 16) |
154
(d[r[(*p & 0x0000FF00) >> 8]] << 8) |
155
(d[l[(*p & 0x000000FF) ]] ) ;
161
static void _Eff_position_s8(int chan, void *stream, int len, void *udata)
163
volatile position_args *args = (volatile position_args *) udata;
164
Sint8 *ptr = (Sint8 *) stream;
168
* if there's only a mono channnel (the only way we wouldn't have
169
* a len divisible by 2 here), then left_f and right_f are always
170
* 1.0, and are therefore throwaways.
172
if (len % sizeof (Sint16) != 0) {
173
*(ptr++) = (Sint8) (((float) *ptr) * args->distance_f);
177
for (i = 0; i < len; i += sizeof (Sint8) * 2) {
178
*(ptr++) = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f);
179
*(ptr++) = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f);
185
* This one runs about 10.1 times faster than the non-table version, with
186
* no loss in quality. It does, however, require 64k of memory for the
187
* lookup table. Also, this will only update position information once per
188
* call; the non-table version always checks the arguments for each sample,
189
* in case the user has called Mix_SetPanning() or whatnot again while this
190
* callback is running.
192
static void _Eff_position_table_s8(int chan, void *stream, int len, void *udata)
194
volatile position_args *args = (volatile position_args *) udata;
195
Sint8 *ptr = (Sint8 *) stream;
198
Sint8 *l = ((Sint8 *) _Eff_volume_table) + (256 * args->left_u8);
199
Sint8 *r = ((Sint8 *) _Eff_volume_table) + (256 * args->right_u8);
200
Sint8 *d = ((Sint8 *) _Eff_volume_table) + (256 * args->distance_u8);
203
while (len % sizeof (Uint32) != 0) {
204
*(ptr++) = d[l[*ptr]];
205
if (args->channels > 1)
206
*(ptr++) = d[r[*ptr]];
207
len -= args->channels;
212
for (i = 0; i < len; i += sizeof (Uint32)) {
213
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
214
*(p++) = (d[l[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) |
215
(d[r[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) |
216
(d[l[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) |
217
(d[r[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ;
219
*(p++) = (d[r[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) |
220
(d[l[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) |
221
(d[r[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) |
222
(d[l[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ;
230
/* !!! FIXME : Optimize the code for 16-bit samples? */
232
static void _Eff_position_u16lsb(int chan, void *stream, int len, void *udata)
234
volatile position_args *args = (volatile position_args *) udata;
235
Uint16 *ptr = (Uint16 *) stream;
238
for (i = 0; i < len; i += sizeof (Uint16) * 2) {
239
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
240
Uint16 swapl = (Uint16) ((((float) SDL_Swap16(*(ptr))) *
241
args->left_f) * args->distance_f);
242
Uint16 swapr = (Uint16) ((((float) SDL_Swap16(*(ptr+1))) *
243
args->right_f) * args->distance_f);
244
*(ptr++) = (Uint16) SDL_Swap16(swapl);
245
*(ptr++) = (Uint16) SDL_Swap16(swapr);
247
*(ptr++) = (Uint16) ((((float) *ptr)*args->left_f)*args->distance_f);
248
*(ptr++) = (Uint16) ((((float) *ptr)*args->right_f)*args->distance_f);
253
static void _Eff_position_s16lsb(int chan, void *stream, int len, void *udata)
255
/* 16 signed bits (lsb) * 2 channels. */
256
volatile position_args *args = (volatile position_args *) udata;
257
Sint16 *ptr = (Sint16 *) stream;
260
for (i = 0; i < len; i += sizeof (Sint16) * 2) {
261
#if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
262
Sint16 swapl = (Sint16) ((((float) SDL_Swap16(*(ptr))) *
263
args->left_f) * args->distance_f);
264
Sint16 swapr = (Sint16) ((((float) SDL_Swap16(*(ptr+1))) *
265
args->right_f) * args->distance_f);
266
*(ptr++) = (Sint16) SDL_Swap16(swapl);
267
*(ptr++) = (Sint16) SDL_Swap16(swapr);
269
*(ptr++) = (Sint16) ((((float) *ptr)*args->left_f)*args->distance_f);
270
*(ptr++) = (Sint16) ((((float) *ptr)*args->right_f)*args->distance_f);
275
static void _Eff_position_u16msb(int chan, void *stream, int len, void *udata)
277
/* 16 signed bits (lsb) * 2 channels. */
278
volatile position_args *args = (volatile position_args *) udata;
279
Uint16 *ptr = (Uint16 *) stream;
282
for (i = 0; i < len; i += sizeof (Sint16) * 2) {
283
#if (SDL_BYTEORDER == SDL_LIL_ENDIAN)
284
Uint16 swapl = (Uint16) ((((float) SDL_Swap16(*(ptr))) *
285
args->left_f) * args->distance_f);
286
Uint16 swapr = (Uint16) ((((float) SDL_Swap16(*(ptr+1))) *
287
args->right_f) * args->distance_f);
288
*(ptr++) = (Uint16) SDL_Swap16(swapl);
289
*(ptr++) = (Uint16) SDL_Swap16(swapr);
291
*(ptr++) = (Uint16) ((((float) *ptr)*args->left_f)*args->distance_f);
292
*(ptr++) = (Uint16) ((((float) *ptr)*args->right_f)*args->distance_f);
297
static void _Eff_position_s16msb(int chan, void *stream, int len, void *udata)
299
/* 16 signed bits (lsb) * 2 channels. */
300
volatile position_args *args = (volatile position_args *) udata;
301
Sint16 *ptr = (Sint16 *) stream;
304
for (i = 0; i < len; i += sizeof (Sint16) * 2) {
305
#if (SDL_BYTEORDER == SDL_LIL_ENDIAN)
306
Sint16 swapl = (Sint16) ((((float) SDL_Swap16(*(ptr))) *
307
args->left_f) * args->distance_f);
308
Sint16 swapr = (Sint16) ((((float) SDL_Swap16(*(ptr+1))) *
309
args->right_f) * args->distance_f);
310
*(ptr++) = (Sint16) SDL_Swap16(swapl);
311
*(ptr++) = (Sint16) SDL_Swap16(swapr);
313
*(ptr++) = (Sint16) ((((float) *ptr)*args->left_f)*args->distance_f);
314
*(ptr++) = (Sint16) ((((float) *ptr)*args->right_f)*args->distance_f);
320
static void init_position_args(position_args *args)
322
memset(args, '\0', sizeof (position_args));
324
args->left_u8 = args->right_u8 = args->distance_u8 = 255;
325
args->left_f = args->right_f = args->distance_f = 1.0f;
326
Mix_QuerySpec(NULL, NULL, (int *) &args->channels);
330
static position_args *get_position_arg(int channel)
336
if (pos_args_global == NULL) {
337
pos_args_global = malloc(sizeof (position_args));
338
if (pos_args_global == NULL) {
339
Mix_SetError("Out of memory");
342
init_position_args(pos_args_global);
345
return(pos_args_global);
348
if (channel >= position_channels) {
349
rc = realloc(pos_args_array, (channel + 1) * sizeof (position_args *));
351
Mix_SetError("Out of memory");
354
pos_args_array = (position_args **) rc;
355
for (i = position_channels; i <= channel; i++) {
356
pos_args_array[i] = NULL;
358
position_channels = channel + 1;
361
if (pos_args_array[channel] == NULL) {
362
pos_args_array[channel] = (position_args *)malloc(sizeof(position_args));
363
if (pos_args_array[channel] == NULL) {
364
Mix_SetError("Out of memory");
367
init_position_args(pos_args_array[channel]);
370
return(pos_args_array[channel]);
374
static Mix_EffectFunc_t get_position_effect_func(Uint16 format)
376
Mix_EffectFunc_t f = NULL;
380
f = (_Eff_build_volume_table_u8()) ? _Eff_position_table_u8 :
385
f = (_Eff_build_volume_table_s8()) ? _Eff_position_table_s8 :
390
f = _Eff_position_u16lsb;
394
f = _Eff_position_s16lsb;
398
f = _Eff_position_u16msb;
402
f = _Eff_position_s16msb;
406
Mix_SetError("Unsupported audio format");
413
int Mix_SetPanning(int channel, Uint8 left, Uint8 right)
415
Mix_EffectFunc_t f = NULL;
418
position_args *args = NULL;
420
Mix_QuerySpec(NULL, &format, &channels);
422
if (channels != 2) /* it's a no-op; we call that successful. */
425
f = get_position_effect_func(format);
429
args = get_position_arg(channel);
433
/* it's a no-op; unregister the effect, if it's registered. */
434
if ((args->distance_u8 == 255) && (left == 255) &&
435
(right == 255) && (args->in_use))
437
return(Mix_UnregisterEffect(channel, f));
440
args->left_u8 = left;
441
args->right_u8 = right;
442
args->left_f = ((float) left) / 255.0f;
443
args->right_f = ((float) right) / 255.0f;
446
return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
453
int Mix_SetDistance(int channel, Uint8 distance)
455
Mix_EffectFunc_t f = NULL;
457
position_args *args = NULL;
459
Mix_QuerySpec(NULL, &format, NULL);
460
f = get_position_effect_func(format);
464
args = get_position_arg(channel);
468
distance = 255 - distance; /* flip it to our scale. */
470
/* it's a no-op; unregister the effect, if it's registered. */
471
if ((distance == 255) && (args->left_u8 == 255) &&
472
(args->right_u8 == 255) && (args->in_use))
474
return(Mix_UnregisterEffect(channel, f));
477
args->distance_u8 = distance;
478
args->distance_f = ((float) distance) / 255.0f;
481
return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
488
int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance)
490
Mix_EffectFunc_t f = NULL;
493
position_args *args = NULL;
494
Uint8 left = 255, right = 255;
496
Mix_QuerySpec(NULL, &format, &channels);
497
f = get_position_effect_func(format);
501
/* unwind the angle...it'll be between 0 and 359. */
502
while (angle >= 360) angle -= 360;
503
while (angle < 0) angle += 360;
505
args = get_position_arg(channel);
509
/* it's a no-op; unregister the effect, if it's registered. */
510
if ((!distance) && (!angle) && (args->in_use))
511
return(Mix_UnregisterEffect(channel, f));
516
* We only attenuate by position if the angle falls on the far side
517
* of center; That is, an angle that's due north would not attenuate
518
* either channel. Due west attenuates the right channel to 0.0, and
519
* due east attenuates the left channel to 0.0. Slightly east of
520
* center attenuates the left channel a little, and the right channel
521
* not at all. I think of this as occlusion by one's own head. :)
523
* ...so, we split our angle circle into four quadrants...
526
left = 255 - ((Uint8) (255.0f * (((float) angle) / 89.0f)));
527
} else if (angle < 180) {
528
left = (Uint8) (255.0f * (((float) (angle - 90)) / 89.0f));
529
} else if (angle < 270) {
530
right = 255 - ((Uint8) (255.0f * (((float) (angle - 180)) / 89.0f)));
532
right = (Uint8) (255.0f * (((float) (angle - 270)) / 89.0f));
536
distance = 255 - distance; /* flip it to scale Mix_SetDistance() uses. */
538
args->left_u8 = left;
539
args->left_f = ((float) left) / 255.0f;
540
args->right_u8 = right;
541
args->right_f = ((float) right) / 255.0f;
542
args->distance_u8 = distance;
543
args->distance_f = ((float) distance) / 255.0f;
546
return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
553
/* end of effects_position.c ... */