effect_position.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 21 Feb 2003 17:11:56 +0000
changeset 218 a21e382190a8
parent 201 e55fb6b45559
child 238 2eae977be9d0
permissions -rw-r--r--
Fixed Ryan's naughty coding. :)
     1 /*
     2     SDL_mixer:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
     4 
     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.
     9 
    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.
    14 
    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
    18 
    19     This file by Ryan C. Gordon (icculus@linuxgames.com)
    20 
    21     These are some internally supported special effects that use SDL_mixer's
    22     effect callback API. They are meant for speed over quality.  :)
    23 */
    24 
    25 /* $Id$ */
    26 
    27 #include <stdio.h>
    28 #include <stdlib.h>
    29 #include <string.h>
    30 
    31 #include "SDL.h"
    32 #include "SDL_mixer.h"
    33 #include "SDL_endian.h"
    34 
    35 #define __MIX_INTERNAL_EFFECT__
    36 #include "effects_internal.h"
    37 
    38 /* profile code:
    39     #include <sys/time.h>
    40     #include <unistd.h>
    41     struct timeval tv1;
    42     struct timeval tv2;
    43     
    44     gettimeofday(&tv1, NULL);
    45 
    46         ... do your thing here ...
    47 
    48     gettimeofday(&tv2, NULL);
    49     printf("%ld\n", tv2.tv_usec - tv1.tv_usec);
    50 */
    51 
    52 
    53 /*
    54  * Positional effects...panning, distance attenuation, etc.
    55  */
    56 
    57 typedef struct _Eff_positionargs
    58 {
    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;
    65     volatile int in_use;
    66     volatile int channels;
    67 } position_args;
    68 
    69 static position_args **pos_args_array = NULL;
    70 static position_args *pos_args_global = NULL;
    71 static int position_channels = 0;
    72 
    73 /* This just frees up the callback-specific data. */
    74 static void _Eff_PositionDone(int channel, void *udata)
    75 {
    76     if (channel < 0) {
    77         if (pos_args_global != NULL) {
    78             free(pos_args_global);
    79             pos_args_global = NULL;
    80         }
    81     }
    82 
    83     else if (pos_args_array[channel] != NULL) {
    84         free(pos_args_array[channel]);
    85         pos_args_array[channel] = NULL;
    86     }
    87 }
    88 
    89 
    90 static void _Eff_position_u8(int chan, void *stream, int len, void *udata)
    91 {
    92     volatile position_args *args = (volatile position_args *) udata;
    93     Uint8 *ptr = (Uint8 *) stream;
    94     int i;
    95 
    96         /*
    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.
   100          */
   101     if (len % sizeof (Uint16) != 0) {
   102         *ptr = (Uint8) (((float) *ptr) * args->distance_f);
   103         ptr++;
   104         len--;
   105     }
   106 
   107     for (i = 0; i < len; i += sizeof (Uint8) * 2) {
   108         /* must adjust the sample so that 0 is the center */
   109         *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) 
   110             * args->left_f) * args->distance_f) + 128);
   111         ptr++;
   112         *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) 
   113             * args->right_f) * args->distance_f) + 128);
   114         ptr++;
   115     }
   116 }
   117 
   118 
   119 /*
   120  * This one runs about 10.1 times faster than the non-table version, with
   121  *  no loss in quality. It does, however, require 64k of memory for the
   122  *  lookup table. Also, this will only update position information once per
   123  *  call; the non-table version always checks the arguments for each sample,
   124  *  in case the user has called Mix_SetPanning() or whatnot again while this
   125  *  callback is running.
   126  */
   127 static void _Eff_position_table_u8(int chan, void *stream, int len, void *udata)
   128 {
   129     volatile position_args *args = (volatile position_args *) udata;
   130     Uint8 *ptr = (Uint8 *) stream;
   131     Uint32 *p;
   132     int i;
   133     Uint8 *l = ((Uint8 *) _Eff_volume_table) + (256 * args->left_u8);
   134     Uint8 *r = ((Uint8 *) _Eff_volume_table) + (256 * args->right_u8);
   135     Uint8 *d = ((Uint8 *) _Eff_volume_table) + (256 * args->distance_u8);
   136 
   137         /*
   138          * if there's only a mono channnel, then l[] and r[] are always
   139          *  volume 255, and are therefore throwaways. Still, we have to
   140          *  be sure not to overrun the audio buffer...
   141          */
   142     while (len % sizeof (Uint32) != 0) {
   143         *ptr = d[l[*ptr]];
   144         ptr++;
   145         if (args->channels > 1) {
   146             *ptr = d[r[*ptr]];
   147             ptr++;
   148         }
   149         len -= args->channels;
   150     }
   151 
   152     p = (Uint32 *) ptr;
   153 
   154     for (i = 0; i < len; i += sizeof (Uint32)) {
   155 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
   156         *(p++) = (d[l[(*p & 0xFF000000) >> 24]] << 24) |
   157                  (d[r[(*p & 0x00FF0000) >> 16]] << 16) |
   158                  (d[l[(*p & 0x0000FF00) >>  8]] <<  8) |
   159                  (d[r[(*p & 0x000000FF)      ]]      ) ;
   160 #else
   161         *(p++) = (d[r[(*p & 0xFF000000) >> 24]] << 24) |
   162                  (d[l[(*p & 0x00FF0000) >> 16]] << 16) |
   163                  (d[r[(*p & 0x0000FF00) >>  8]] <<  8) |
   164                  (d[l[(*p & 0x000000FF)      ]]      ) ;
   165 #endif
   166     }
   167 }
   168 
   169 
   170 static void _Eff_position_s8(int chan, void *stream, int len, void *udata)
   171 {
   172     volatile position_args *args = (volatile position_args *) udata;
   173     Sint8 *ptr = (Sint8 *) stream;
   174     int i;
   175 
   176         /*
   177          * if there's only a mono channnel (the only way we wouldn't have
   178          *  a len divisible by 2 here), then left_f and right_f are always
   179          *  1.0, and are therefore throwaways.
   180          */
   181     if (len % sizeof (Sint16) != 0) {
   182         *ptr = (Sint8) (((float) *ptr) * args->distance_f);
   183         ptr++;
   184         len--;
   185     }
   186 
   187     for (i = 0; i < len; i += sizeof (Sint8) * 2) {
   188         *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f);
   189         ptr++;
   190         *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f);
   191         ptr++;
   192     }
   193 }
   194 
   195 
   196 /*
   197  * This one runs about 10.1 times faster than the non-table version, with
   198  *  no loss in quality. It does, however, require 64k of memory for the
   199  *  lookup table. Also, this will only update position information once per
   200  *  call; the non-table version always checks the arguments for each sample,
   201  *  in case the user has called Mix_SetPanning() or whatnot again while this
   202  *  callback is running.
   203  */
   204 static void _Eff_position_table_s8(int chan, void *stream, int len, void *udata)
   205 {
   206     volatile position_args *args = (volatile position_args *) udata;
   207     Sint8 *ptr = (Sint8 *) stream;
   208     Uint32 *p;
   209     int i;
   210     Sint8 *l = ((Sint8 *) _Eff_volume_table) + (256 * args->left_u8);
   211     Sint8 *r = ((Sint8 *) _Eff_volume_table) + (256 * args->right_u8);
   212     Sint8 *d = ((Sint8 *) _Eff_volume_table) + (256 * args->distance_u8);
   213 
   214 
   215     while (len % sizeof (Uint32) != 0) {
   216         *ptr = d[l[*ptr]];
   217         ptr++;
   218         if (args->channels > 1) {
   219             *ptr = d[r[*ptr]];
   220             ptr++;
   221         }
   222         len -= args->channels;
   223     }
   224 
   225     p = (Uint32 *) ptr;
   226 
   227     for (i = 0; i < len; i += sizeof (Uint32)) {
   228 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
   229         *(p++) = (d[l[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) |
   230                  (d[r[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) |
   231                  (d[l[((Sint16)(Sint8)((*p & 0x0000FF00) >>  8))+128]] <<  8) |
   232                  (d[r[((Sint16)(Sint8)((*p & 0x000000FF)      ))+128]]      ) ;
   233 #else
   234         *(p++) = (d[r[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) |
   235                  (d[l[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) |
   236                  (d[r[((Sint16)(Sint8)((*p & 0x0000FF00) >>  8))+128]] <<  8) |
   237                  (d[l[((Sint16)(Sint8)((*p & 0x000000FF)      ))+128]]      ) ;
   238 #endif
   239     }
   240 
   241 
   242 }
   243 
   244 
   245 /* !!! FIXME : Optimize the code for 16-bit samples? */
   246 
   247 static void _Eff_position_u16lsb(int chan, void *stream, int len, void *udata)
   248 {
   249     volatile position_args *args = (volatile position_args *) udata;
   250     Uint16 *ptr = (Uint16 *) stream;
   251     int i;
   252 
   253     for (i = 0; i < len; i += sizeof (Uint16) * 2) {
   254         Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768);
   255         Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768);
   256         
   257         Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f)
   258                                     * args->distance_f) + 32768);
   259         Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f)
   260                                     * args->distance_f) + 32768);
   261 
   262         *(ptr++) = (Uint16) SDL_SwapLE16(swapl);
   263         *(ptr++) = (Uint16) SDL_SwapLE16(swapr);
   264     }
   265 }
   266 
   267 static void _Eff_position_s16lsb(int chan, void *stream, int len, void *udata)
   268 {
   269     /* 16 signed bits (lsb) * 2 channels. */
   270     volatile position_args *args = (volatile position_args *) udata;
   271     Sint16 *ptr = (Sint16 *) stream;
   272     int i;
   273 
   274     for (i = 0; i < len; i += sizeof (Sint16) * 2) {
   275         Sint16 swapl = (Sint16) ((((float) SDL_SwapLE16(*(ptr+0))) *
   276                                     args->left_f) * args->distance_f);
   277         Sint16 swapr = (Sint16) ((((float) SDL_SwapLE16(*(ptr+1))) *
   278                                     args->right_f) * args->distance_f);
   279         *(ptr++) = (Sint16) SDL_SwapLE16(swapl);
   280         *(ptr++) = (Sint16) SDL_SwapLE16(swapr);
   281     }
   282 }
   283 
   284 static void _Eff_position_u16msb(int chan, void *stream, int len, void *udata)
   285 {
   286     /* 16 signed bits (lsb) * 2 channels. */
   287     volatile position_args *args = (volatile position_args *) udata;
   288     Uint16 *ptr = (Uint16 *) stream;
   289     int i;
   290 
   291     for (i = 0; i < len; i += sizeof (Sint16) * 2) {
   292         Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768);
   293         Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768);
   294         
   295         Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f)
   296                                     * args->distance_f) + 32768);
   297         Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f)
   298                                     * args->distance_f) + 32768);
   299 
   300         *(ptr++) = (Uint16) SDL_SwapBE16(swapl);
   301         *(ptr++) = (Uint16) SDL_SwapBE16(swapr);
   302     }
   303 }
   304 
   305 static void _Eff_position_s16msb(int chan, void *stream, int len, void *udata)
   306 {
   307     /* 16 signed bits (lsb) * 2 channels. */
   308     volatile position_args *args = (volatile position_args *) udata;
   309     Sint16 *ptr = (Sint16 *) stream;
   310     int i;
   311 
   312     for (i = 0; i < len; i += sizeof (Sint16) * 2) {
   313         Sint16 swapl = (Sint16) ((((float) SDL_SwapBE16(*(ptr+0))) *
   314                                     args->left_f) * args->distance_f);
   315         Sint16 swapr = (Sint16) ((((float) SDL_SwapBE16(*(ptr+1))) *
   316                                     args->right_f) * args->distance_f);
   317         *(ptr++) = (Sint16) SDL_SwapBE16(swapl);
   318         *(ptr++) = (Sint16) SDL_SwapBE16(swapr);
   319     }
   320 }
   321 
   322 
   323 static void init_position_args(position_args *args)
   324 {
   325     memset(args, '\0', sizeof (position_args));
   326     args->in_use = 0;
   327     args->left_u8 = args->right_u8 = args->distance_u8 = 255;
   328     args->left_f  = args->right_f  = args->distance_f  = 1.0f;
   329     Mix_QuerySpec(NULL, NULL, (int *) &args->channels);
   330 }
   331 
   332 
   333 static position_args *get_position_arg(int channel)
   334 {
   335     void *rc;
   336     int i;
   337 
   338     if (channel < 0) {
   339         if (pos_args_global == NULL) {
   340             pos_args_global = malloc(sizeof (position_args));
   341             if (pos_args_global == NULL) {
   342                 Mix_SetError("Out of memory");
   343                 return(NULL);
   344             }
   345             init_position_args(pos_args_global);
   346         }
   347 
   348         return(pos_args_global);
   349     }
   350 
   351     if (channel >= position_channels) {
   352         rc = realloc(pos_args_array, (channel + 1) * sizeof (position_args *));
   353         if (rc == NULL) {
   354             Mix_SetError("Out of memory");
   355             return(NULL);
   356         }
   357         pos_args_array = (position_args **) rc;
   358         for (i = position_channels; i <= channel; i++) {
   359             pos_args_array[i] = NULL;
   360         }
   361         position_channels = channel + 1;
   362     }
   363 
   364     if (pos_args_array[channel] == NULL) {
   365         pos_args_array[channel] = (position_args *)malloc(sizeof(position_args));
   366         if (pos_args_array[channel] == NULL) {
   367             Mix_SetError("Out of memory");
   368             return(NULL);
   369         }
   370         init_position_args(pos_args_array[channel]);
   371     }
   372 
   373     return(pos_args_array[channel]);
   374 }
   375 
   376 
   377 static Mix_EffectFunc_t get_position_effect_func(Uint16 format)
   378 {
   379     Mix_EffectFunc_t f = NULL;
   380 
   381     switch (format) {
   382         case AUDIO_U8:
   383             f = (_Eff_build_volume_table_u8()) ? _Eff_position_table_u8 :
   384                                                  _Eff_position_u8;
   385             break;
   386 
   387         case AUDIO_S8:
   388             f = (_Eff_build_volume_table_s8()) ? _Eff_position_table_s8 :
   389                                                  _Eff_position_s8;
   390             break;
   391 
   392         case AUDIO_U16LSB:
   393             f = _Eff_position_u16lsb;
   394             break;
   395 
   396         case AUDIO_S16LSB:
   397             f = _Eff_position_s16lsb;
   398             break;
   399 
   400         case AUDIO_U16MSB:
   401             f = _Eff_position_u16msb;
   402             break;
   403 
   404         case AUDIO_S16MSB:
   405             f = _Eff_position_s16msb;
   406             break;
   407 
   408         default:
   409             Mix_SetError("Unsupported audio format");
   410     }
   411 
   412     return(f);
   413 }
   414 
   415 
   416 int Mix_SetPanning(int channel, Uint8 left, Uint8 right)
   417 {
   418     Mix_EffectFunc_t f = NULL;
   419     int channels;
   420     Uint16 format;
   421     position_args *args = NULL;
   422 
   423     Mix_QuerySpec(NULL, &format, &channels);
   424 
   425     if (channels != 2)    /* it's a no-op; we call that successful. */
   426         return(1);
   427 
   428     f = get_position_effect_func(format);
   429     if (f == NULL)
   430         return(0);
   431 
   432     args = get_position_arg(channel);
   433     if (!args)
   434         return(0);
   435 
   436         /* it's a no-op; unregister the effect, if it's registered. */
   437     if ((args->distance_u8 == 255) && (left == 255) &&
   438         (right == 255) && (args->in_use))
   439     {
   440         return(Mix_UnregisterEffect(channel, f));
   441     }
   442 
   443     args->left_u8 = left;
   444     args->right_u8 = right;
   445     args->left_f = ((float) left) / 255.0f;
   446     args->right_f = ((float) right) / 255.0f;
   447     if (!args->in_use) {
   448         args->in_use = 1;
   449         return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   450     }
   451 
   452     return(1);
   453 }
   454 
   455 
   456 int Mix_SetDistance(int channel, Uint8 distance)
   457 {
   458     Mix_EffectFunc_t f = NULL;
   459     Uint16 format;
   460     position_args *args = NULL;
   461 
   462     Mix_QuerySpec(NULL, &format, NULL);
   463     f = get_position_effect_func(format);
   464     if (f == NULL)
   465         return(0);
   466 
   467     args = get_position_arg(channel);
   468     if (!args)
   469         return(0);
   470 
   471     distance = 255 - distance;  /* flip it to our scale. */
   472 
   473         /* it's a no-op; unregister the effect, if it's registered. */
   474     if ((distance == 255) && (args->left_u8 == 255) &&
   475         (args->right_u8 == 255) && (args->in_use))
   476     {
   477         return(Mix_UnregisterEffect(channel, f));
   478     }
   479 
   480     args->distance_u8 = distance;
   481     args->distance_f = ((float) distance) / 255.0f;
   482     if (!args->in_use) {
   483         args->in_use = 1;
   484         return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   485     }
   486 
   487     return(1);
   488 }
   489 
   490 
   491 int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance)
   492 {
   493     Mix_EffectFunc_t f = NULL;
   494     Uint16 format;
   495     int channels;
   496     position_args *args = NULL;
   497     Uint8 left = 255, right = 255;
   498 
   499     Mix_QuerySpec(NULL, &format, &channels);
   500     f = get_position_effect_func(format);
   501     if (f == NULL)
   502         return(0);
   503 
   504         /* unwind the angle...it'll be between 0 and 359. */
   505     while (angle >= 360) angle -= 360;
   506     while (angle < 0) angle += 360;
   507 
   508     args = get_position_arg(channel);
   509     if (!args)
   510         return(0);
   511 
   512         /* it's a no-op; unregister the effect, if it's registered. */
   513     if ((!distance) && (!angle) && (args->in_use))
   514         return(Mix_UnregisterEffect(channel, f));
   515 
   516     if (channels == 2)
   517     {
   518         /*
   519          * We only attenuate by position if the angle falls on the far side
   520          *  of center; That is, an angle that's due north would not attenuate
   521          *  either channel. Due west attenuates the right channel to 0.0, and
   522          *  due east attenuates the left channel to 0.0. Slightly east of
   523          *  center attenuates the left channel a little, and the right channel
   524          *  not at all. I think of this as occlusion by one's own head.  :)
   525          *
   526          *   ...so, we split our angle circle into four quadrants...
   527          */
   528         if (angle < 90) {
   529             left = 255 - ((Uint8) (255.0f * (((float) angle) / 89.0f)));
   530         } else if (angle < 180) {
   531             left = (Uint8) (255.0f * (((float) (angle - 90)) / 89.0f));
   532         } else if (angle < 270) {
   533             right = 255 - ((Uint8) (255.0f * (((float) (angle - 180)) / 89.0f)));
   534         } else {
   535             right = (Uint8) (255.0f * (((float) (angle - 270)) / 89.0f));
   536         }
   537     }
   538 
   539     distance = 255 - distance;  /* flip it to scale Mix_SetDistance() uses. */
   540 
   541     args->left_u8 = left;
   542     args->left_f = ((float) left) / 255.0f;
   543     args->right_u8 = right;
   544     args->right_f = ((float) right) / 255.0f;
   545     args->distance_u8 = distance;
   546     args->distance_f = ((float) distance) / 255.0f;
   547     if (!args->in_use) {
   548         args->in_use = 1;
   549         return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   550     }
   551 
   552     return(1);
   553 }
   554 
   555 
   556 /* end of effects_position.c ... */
   557