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