effect_position.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 07 Feb 2002 17:40:36 +0000
changeset 159 01490534f9fe
parent 138 4d0dc6b4985d
child 201 e55fb6b45559
permissions -rw-r--r--
*** empty log message ***
     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         len--;
   104     }
   105 
   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);
   109     }
   110 }
   111 
   112 
   113 /*
   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.
   120  */
   121 static void _Eff_position_table_u8(int chan, void *stream, int len, void *udata)
   122 {
   123     volatile position_args *args = (volatile position_args *) udata;
   124     Uint8 *ptr = (Uint8 *) stream;
   125     Uint32 *p;
   126     int i;
   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);
   130 
   131         /*
   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...
   135          */
   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;
   141     }
   142 
   143     p = (Uint32 *) ptr;
   144 
   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)      ]]      ) ;
   151 #else
   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)      ]]      ) ;
   156 #endif
   157     }
   158 }
   159 
   160 
   161 static void _Eff_position_s8(int chan, void *stream, int len, void *udata)
   162 {
   163     volatile position_args *args = (volatile position_args *) udata;
   164     Sint8 *ptr = (Sint8 *) stream;
   165     int i;
   166 
   167         /*
   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.
   171          */
   172     if (len % sizeof (Sint16) != 0) {
   173         *(ptr++) = (Sint8) (((float) *ptr) * args->distance_f);
   174         len--;
   175     }
   176 
   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);
   180     }
   181 }
   182 
   183 
   184 /*
   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.
   191  */
   192 static void _Eff_position_table_s8(int chan, void *stream, int len, void *udata)
   193 {
   194     volatile position_args *args = (volatile position_args *) udata;
   195     Sint8 *ptr = (Sint8 *) stream;
   196     Uint32 *p;
   197     int i;
   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);
   201 
   202 
   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;
   208     }
   209 
   210     p = (Uint32 *) ptr;
   211 
   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]]      ) ;
   218 #else
   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]]      ) ;
   223 #endif
   224     }
   225 
   226 
   227 }
   228 
   229 
   230 /* !!! FIXME : Optimize the code for 16-bit samples? */
   231 
   232 static void _Eff_position_u16lsb(int chan, void *stream, int len, void *udata)
   233 {
   234     volatile position_args *args = (volatile position_args *) udata;
   235     Uint16 *ptr = (Uint16 *) stream;
   236     int i;
   237 
   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);
   246 #else
   247         *(ptr++) = (Uint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   248         *(ptr++) = (Uint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   249 #endif
   250     }
   251 }
   252 
   253 static void _Eff_position_s16lsb(int chan, void *stream, int len, void *udata)
   254 {
   255     /* 16 signed bits (lsb) * 2 channels. */
   256     volatile position_args *args = (volatile position_args *) udata;
   257     Sint16 *ptr = (Sint16 *) stream;
   258     int i;
   259 
   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);
   268 #else
   269         *(ptr++) = (Sint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   270         *(ptr++) = (Sint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   271 #endif
   272     }
   273 }
   274 
   275 static void _Eff_position_u16msb(int chan, void *stream, int len, void *udata)
   276 {
   277     /* 16 signed bits (lsb) * 2 channels. */
   278     volatile position_args *args = (volatile position_args *) udata;
   279     Uint16 *ptr = (Uint16 *) stream;
   280     int i;
   281 
   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);
   290 #else
   291         *(ptr++) = (Uint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   292         *(ptr++) = (Uint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   293 #endif
   294     }
   295 }
   296 
   297 static void _Eff_position_s16msb(int chan, void *stream, int len, void *udata)
   298 {
   299     /* 16 signed bits (lsb) * 2 channels. */
   300     volatile position_args *args = (volatile position_args *) udata;
   301     Sint16 *ptr = (Sint16 *) stream;
   302     int i;
   303 
   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);
   312 #else
   313         *(ptr++) = (Sint16) ((((float) *ptr)*args->left_f)*args->distance_f);
   314         *(ptr++) = (Sint16) ((((float) *ptr)*args->right_f)*args->distance_f);
   315 #endif
   316     }
   317 }
   318 
   319 
   320 static void init_position_args(position_args *args)
   321 {
   322     memset(args, '\0', sizeof (position_args));
   323     args->in_use = 0;
   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);
   327 }
   328 
   329 
   330 static position_args *get_position_arg(int channel)
   331 {
   332     void *rc;
   333     int i;
   334 
   335     if (channel < 0) {
   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");
   340                 return(NULL);
   341             }
   342             init_position_args(pos_args_global);
   343         }
   344 
   345         return(pos_args_global);
   346     }
   347 
   348     if (channel >= position_channels) {
   349         rc = realloc(pos_args_array, (channel + 1) * sizeof (position_args *));
   350         if (rc == NULL) {
   351             Mix_SetError("Out of memory");
   352             return(NULL);
   353         }
   354         pos_args_array = (position_args **) rc;
   355         for (i = position_channels; i <= channel; i++) {
   356             pos_args_array[i] = NULL;
   357         }
   358         position_channels = channel + 1;
   359     }
   360 
   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");
   365             return(NULL);
   366         }
   367         init_position_args(pos_args_array[channel]);
   368     }
   369 
   370     return(pos_args_array[channel]);
   371 }
   372 
   373 
   374 static Mix_EffectFunc_t get_position_effect_func(Uint16 format)
   375 {
   376     Mix_EffectFunc_t f = NULL;
   377 
   378     switch (format) {
   379         case AUDIO_U8:
   380             f = (_Eff_build_volume_table_u8()) ? _Eff_position_table_u8 :
   381                                                  _Eff_position_u8;
   382             break;
   383 
   384         case AUDIO_S8:
   385             f = (_Eff_build_volume_table_s8()) ? _Eff_position_table_s8 :
   386                                                  _Eff_position_s8;
   387             break;
   388 
   389         case AUDIO_U16LSB:
   390             f = _Eff_position_u16lsb;
   391             break;
   392 
   393         case AUDIO_S16LSB:
   394             f = _Eff_position_s16lsb;
   395             break;
   396 
   397         case AUDIO_U16MSB:
   398             f = _Eff_position_u16msb;
   399             break;
   400 
   401         case AUDIO_S16MSB:
   402             f = _Eff_position_s16msb;
   403             break;
   404 
   405         default:
   406             Mix_SetError("Unsupported audio format");
   407     }
   408 
   409     return(f);
   410 }
   411 
   412 
   413 int Mix_SetPanning(int channel, Uint8 left, Uint8 right)
   414 {
   415     Mix_EffectFunc_t f = NULL;
   416     int channels;
   417     Uint16 format;
   418     position_args *args = NULL;
   419 
   420     Mix_QuerySpec(NULL, &format, &channels);
   421 
   422     if (channels != 2)    /* it's a no-op; we call that successful. */
   423         return(1);
   424 
   425     f = get_position_effect_func(format);
   426     if (f == NULL)
   427         return(0);
   428 
   429     args = get_position_arg(channel);
   430     if (!args)
   431         return(0);
   432 
   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))
   436     {
   437         return(Mix_UnregisterEffect(channel, f));
   438     }
   439 
   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;
   444     if (!args->in_use) {
   445         args->in_use = 1;
   446         return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   447     }
   448 
   449     return(1);
   450 }
   451 
   452 
   453 int Mix_SetDistance(int channel, Uint8 distance)
   454 {
   455     Mix_EffectFunc_t f = NULL;
   456     Uint16 format;
   457     position_args *args = NULL;
   458 
   459     Mix_QuerySpec(NULL, &format, NULL);
   460     f = get_position_effect_func(format);
   461     if (f == NULL)
   462         return(0);
   463 
   464     args = get_position_arg(channel);
   465     if (!args)
   466         return(0);
   467 
   468     distance = 255 - distance;  /* flip it to our scale. */
   469 
   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))
   473     {
   474         return(Mix_UnregisterEffect(channel, f));
   475     }
   476 
   477     args->distance_u8 = distance;
   478     args->distance_f = ((float) distance) / 255.0f;
   479     if (!args->in_use) {
   480         args->in_use = 1;
   481         return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   482     }
   483 
   484     return(1);
   485 }
   486 
   487 
   488 int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance)
   489 {
   490     Mix_EffectFunc_t f = NULL;
   491     Uint16 format;
   492     int channels;
   493     position_args *args = NULL;
   494     Uint8 left = 255, right = 255;
   495 
   496     Mix_QuerySpec(NULL, &format, &channels);
   497     f = get_position_effect_func(format);
   498     if (f == NULL)
   499         return(0);
   500 
   501         /* unwind the angle...it'll be between 0 and 359. */
   502     while (angle >= 360) angle -= 360;
   503     while (angle < 0) angle += 360;
   504 
   505     args = get_position_arg(channel);
   506     if (!args)
   507         return(0);
   508 
   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));
   512 
   513     if (channels == 2)
   514     {
   515         /*
   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.  :)
   522          *
   523          *   ...so, we split our angle circle into four quadrants...
   524          */
   525         if (angle < 90) {
   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)));
   531         } else {
   532             right = (Uint8) (255.0f * (((float) (angle - 270)) / 89.0f));
   533         }
   534     }
   535 
   536     distance = 255 - distance;  /* flip it to scale Mix_SetDistance() uses. */
   537 
   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;
   544     if (!args->in_use) {
   545         args->in_use = 1;
   546         return(Mix_RegisterEffect(channel, f, _Eff_PositionDone, (void *) args));
   547     }
   548 
   549     return(1);
   550 }
   551 
   552 
   553 /* end of effects_position.c ... */
   554