File: client\snd_mem.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 // snd_mem.c: sound caching
   21 
   22 #include "client.h"
   23 #include "snd_loc.h"
   24 
   25 int                     cache_full_cycle;
   26 
   27 byte *S_Alloc (int size);
   28 
   29 /*
   30 ================
   31 ResampleSfx
   32 ================
   33 */
   34 void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
   35 {
   36         int             outcount;
   37         int             srcsample;
   38         float   stepscale;
   39         int             i;
   40         int             sample, samplefrac, fracstep;
   41         sfxcache_t      *sc;
   42         
   43         sc = sfx->cache;
   44         if (!sc)
   45                 return;
   46 
   47         stepscale = (float)inrate / dma.speed;  // this is usually 0.5, 1, or 2
   48 
   49         outcount = sc->length / stepscale;
   50         sc->length = outcount;
   51         if (sc->loopstart != -1)
   52                 sc->loopstart = sc->loopstart / stepscale;
   53 
   54         sc->speed = dma.speed;
   55         if (s_loadas8bit->value)
   56                 sc->width = 1;
   57         else
   58                 sc->width = inwidth;
   59         sc->stereo = 0;
   60 
   61 // resample / decimate to the current source rate
   62 
   63         if (stepscale == 1 && inwidth == 1 && sc->width == 1)
   64         {
   65 // fast special case
   66                 for (i=0 ; i<outcount ; i++)
   67                         ((signed char *)sc->data)[i]
   68                         = (int)( (unsigned char)(data[i]) - 128);
   69         }
   70         else
   71         {
   72 // general case
   73                 samplefrac = 0;
   74                 fracstep = stepscale*256;
   75                 for (i=0 ; i<outcount ; i++)
   76                 {
   77                         srcsample = samplefrac >> 8;
   78                         samplefrac += fracstep;
   79                         if (inwidth == 2)
   80                                 sample = LittleShort ( ((short *)data)[srcsample] );
   81                         else
   82                                 sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
   83                         if (sc->width == 2)
   84                                 ((short *)sc->data)[i] = sample;
   85                         else
   86                                 ((signed char *)sc->data)[i] = sample >> 8;
   87                 }
   88         }
   89 }
   90 
   91 //=============================================================================
   92 
   93 /*
   94 ==============
   95 S_LoadSound
   96 ==============
   97 */
   98 sfxcache_t *S_LoadSound (sfx_t *s)
   99 {
  100     char        namebuffer[MAX_QPATH];
  101         byte    *data;
  102         wavinfo_t       info;
  103         int             len;
  104         float   stepscale;
  105         sfxcache_t      *sc;
  106         int             size;
  107         char    *name;
  108 
  109         if (s->name[0] == '*')
  110                 return NULL;
  111 
  112 // see if still in memory
  113         sc = s->cache;
  114         if (sc)
  115                 return sc;
  116 
  117 //Com_Printf ("S_LoadSound: %x\n", (int)stackbuf);
  118 // load it in
  119         if (s->truename)
  120                 name = s->truename;
  121         else
  122                 name = s->name;
  123 
  124         if (name[0] == '#')
  125                 strcpy(namebuffer, &name[1]);
  126         else
  127                 Com_sprintf (namebuffer, sizeof(namebuffer), "sound/%s", name);
  128 
  129 //      Com_Printf ("loading %s\n",namebuffer);
  130 
  131         size = FS_LoadFile (namebuffer, (void **)&data);
  132 
  133         if (!data)
  134         {
  135                 Com_DPrintf ("Couldn't load %s\n", namebuffer);
  136                 return NULL;
  137         }
  138 
  139         info = GetWavinfo (s->name, data, size);
  140         if (info.channels != 1)
  141         {
  142                 Com_Printf ("%s is a stereo sample\n",s->name);
  143                 FS_FreeFile (data);
  144                 return NULL;
  145         }
  146 
  147         stepscale = (float)info.rate / dma.speed;       
  148         len = info.samples / stepscale;
  149 
  150         len = len * info.width * info.channels;
  151 
  152         sc = s->cache = Z_Malloc (len + sizeof(sfxcache_t));
  153         if (!sc)
  154         {
  155                 FS_FreeFile (data);
  156                 return NULL;
  157         }
  158         
  159         sc->length = info.samples;
  160         sc->loopstart = info.loopstart;
  161         sc->speed = info.rate;
  162         sc->width = info.width;
  163         sc->stereo = info.channels;
  164 
  165         ResampleSfx (s, sc->speed, sc->width, data + info.dataofs);
  166 
  167         FS_FreeFile (data);
  168 
  169         return sc;
  170 }
  171 
  172 
  173 
  174 /*
  175 ===============================================================================
  176 
  177 WAV loading
  178 
  179 ===============================================================================
  180 */
  181 
  182 
  183 byte    *data_p;
  184 byte    *iff_end;
  185 byte    *last_chunk;
  186 byte    *iff_data;
  187 int     iff_chunk_len;
  188 
  189 
  190 short GetLittleShort(void)
  191 {
  192         short val = 0;
  193         val = *data_p;
  194         val = val + (*(data_p+1)<<8);
  195         data_p += 2;
  196         return val;
  197 }
  198 
  199 int GetLittleLong(void)
  200 {
  201         int val = 0;
  202         val = *data_p;
  203         val = val + (*(data_p+1)<<8);
  204         val = val + (*(data_p+2)<<16);
  205         val = val + (*(data_p+3)<<24);
  206         data_p += 4;
  207         return val;
  208 }
  209 
  210 void FindNextChunk(char *name)
  211 {
  212         while (1)
  213         {
  214                 data_p=last_chunk;
  215 
  216                 if (data_p >= iff_end)
  217                 {       // didn't find the chunk
  218                         data_p = NULL;
  219                         return;
  220                 }
  221                 
  222                 data_p += 4;
  223                 iff_chunk_len = GetLittleLong();
  224                 if (iff_chunk_len < 0)
  225                 {
  226                         data_p = NULL;
  227                         return;
  228                 }
  229 //              if (iff_chunk_len > 1024*1024)
  230 //                      Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
  231                 data_p -= 8;
  232                 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
  233                 if (!strncmp(data_p, name, 4))
  234                         return;
  235         }
  236 }
  237 
  238 void FindChunk(char *name)
  239 {
  240         last_chunk = iff_data;
  241         FindNextChunk (name);
  242 }
  243 
  244 
  245 void DumpChunks(void)
  246 {
  247         char    str[5];
  248         
  249         str[4] = 0;
  250         data_p=iff_data;
  251         do
  252         {
  253                 memcpy (str, data_p, 4);
  254                 data_p += 4;
  255                 iff_chunk_len = GetLittleLong();
  256                 Com_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
  257                 data_p += (iff_chunk_len + 1) & ~1;
  258         } while (data_p < iff_end);
  259 }
  260 
  261 /*
  262 ============
  263 GetWavinfo
  264 ============
  265 */
  266 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
  267 {
  268         wavinfo_t       info;
  269         int     i;
  270         int     format;
  271         int             samples;
  272 
  273         memset (&info, 0, sizeof(info));
  274 
  275         if (!wav)
  276                 return info;
  277                 
  278         iff_data = wav;
  279         iff_end = wav + wavlength;
  280 
  281 // find "RIFF" chunk
  282         FindChunk("RIFF");
  283         if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
  284         {
  285                 Com_Printf("Missing RIFF/WAVE chunks\n");
  286                 return info;
  287         }
  288 
  289 // get "fmt " chunk
  290         iff_data = data_p + 12;
  291 // DumpChunks ();
  292 
  293         FindChunk("fmt ");
  294         if (!data_p)
  295         {
  296                 Com_Printf("Missing fmt chunk\n");
  297                 return info;
  298         }
  299         data_p += 8;
  300         format = GetLittleShort();
  301         if (format != 1)
  302         {
  303                 Com_Printf("Microsoft PCM format only\n");
  304                 return info;
  305         }
  306 
  307         info.channels = GetLittleShort();
  308         info.rate = GetLittleLong();
  309         data_p += 4+2;
  310         info.width = GetLittleShort() / 8;
  311 
  312 // get cue chunk
  313         FindChunk("cue ");
  314         if (data_p)
  315         {
  316                 data_p += 32;
  317                 info.loopstart = GetLittleLong();
  318 //              Com_Printf("loopstart=%d\n", sfx->loopstart);
  319 
  320         // if the next chunk is a LIST chunk, look for a cue length marker
  321                 FindNextChunk ("LIST");
  322                 if (data_p)
  323                 {
  324                         if (!strncmp (data_p + 28, "mark", 4))
  325                         {       // this is not a proper parse, but it works with cooledit...
  326                                 data_p += 24;
  327                                 i = GetLittleLong ();   // samples in loop
  328                                 info.samples = info.loopstart + i;
  329 //                              Com_Printf("looped length: %i\n", i);
  330                         }
  331                 }
  332         }
  333         else
  334                 info.loopstart = -1;
  335 
  336 // find data chunk
  337         FindChunk("data");
  338         if (!data_p)
  339         {
  340                 Com_Printf("Missing data chunk\n");
  341                 return info;
  342         }
  343 
  344         data_p += 4;
  345         samples = GetLittleLong () / info.width;
  346 
  347         if (info.samples)
  348         {
  349                 if (samples < info.samples)
  350                         Com_Error (ERR_DROP, "Sound %s has a bad loop length", name);
  351         }
  352         else
  353                 info.samples = samples;
  354 
  355         info.dataofs = data_p - wav;
  356         
  357         return info;
  358 }
  359 
  360