256 lines
5.1 KiB
C
256 lines
5.1 KiB
C
/*
|
|
snd_wav.c - wav format load & save
|
|
Copyright (C) 2010 Uncle Mike
|
|
Copyright (C) 2023 FTEQW developers
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include "nanoquake.h"
|
|
|
|
static const byte *iff_data;
|
|
static const byte *iff_dataPtr;
|
|
static const byte *iff_end;
|
|
static const byte *iff_lastChunk;
|
|
static int iff_chunkLen;
|
|
|
|
/*
|
|
=================
|
|
GetLittleShort
|
|
=================
|
|
*/
|
|
static short GetLittleShort( void )
|
|
{
|
|
short val = 0;
|
|
|
|
val += (*(iff_dataPtr+0) << 0);
|
|
val += (*(iff_dataPtr+1) << 8);
|
|
iff_dataPtr += 2;
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
GetLittleLong
|
|
=================
|
|
*/
|
|
static int GetLittleLong( void )
|
|
{
|
|
int val = 0;
|
|
|
|
val += (*(iff_dataPtr+0) << 0);
|
|
val += (*(iff_dataPtr+1) << 8);
|
|
val += (*(iff_dataPtr+2) <<16);
|
|
val += (*(iff_dataPtr+3) <<24);
|
|
iff_dataPtr += 4;
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
FindNextChunk
|
|
=================
|
|
*/
|
|
static void FindNextChunk( const char *filename, const char *name )
|
|
{
|
|
while( 1 )
|
|
{
|
|
ptrdiff_t remaining = iff_end - iff_lastChunk;
|
|
|
|
if( remaining < 8 )
|
|
{
|
|
iff_dataPtr = NULL;
|
|
return;
|
|
}
|
|
|
|
iff_dataPtr = iff_lastChunk + 4;
|
|
remaining -= 8;
|
|
|
|
iff_chunkLen = GetLittleLong();
|
|
if( iff_chunkLen < 0 )
|
|
{
|
|
iff_dataPtr = NULL;
|
|
return;
|
|
}
|
|
|
|
if( iff_chunkLen > remaining )
|
|
{
|
|
Con_DPrintf( "%s: '%s' truncated by %i bytes\n", __func__, filename, iff_chunkLen - remaining );
|
|
iff_chunkLen = remaining;
|
|
}
|
|
|
|
remaining -= iff_chunkLen;
|
|
iff_dataPtr -= 8;
|
|
|
|
iff_lastChunk = iff_dataPtr + 8 + iff_chunkLen;
|
|
if ((iff_chunkLen&1) && remaining)
|
|
iff_lastChunk++;
|
|
if (!Q_strncmp(iff_dataPtr, name, 4))
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
FindChunk
|
|
=================
|
|
*/
|
|
static void FindChunk( const char *filename, const char *name )
|
|
{
|
|
iff_lastChunk = iff_data;
|
|
FindNextChunk( filename, name );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
Sound_LoadWAV
|
|
=============
|
|
*/
|
|
qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesize )
|
|
{
|
|
int samples, fmt;
|
|
|
|
if( !buffer || filesize <= 0 )
|
|
return false;
|
|
|
|
iff_data = buffer;
|
|
iff_end = buffer + filesize;
|
|
|
|
// find "RIFF" chunk
|
|
FindChunk( name, "RIFF" );
|
|
|
|
if( !( iff_dataPtr && !Q_strncmp( (const char *)iff_dataPtr + 8, "WAVE", 4 )))
|
|
{
|
|
Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'RIFF/WAVE' chunks\n", name );
|
|
return false;
|
|
}
|
|
|
|
// get "fmt " chunk
|
|
iff_data = iff_dataPtr + 12;
|
|
FindChunk( name, "fmt " );
|
|
|
|
if( !iff_dataPtr )
|
|
{
|
|
Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'fmt ' chunk\n", name );
|
|
return false;
|
|
}
|
|
|
|
iff_dataPtr += 8;
|
|
fmt = GetLittleShort();
|
|
|
|
if( fmt != 1 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Sound_LoadWAV: %s not a microsoft PCM format\n", name );
|
|
return false;
|
|
}
|
|
|
|
sound.channels = GetLittleShort();
|
|
if( sound.channels != 1 && sound.channels != 2 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Sound_LoadWAV: only mono and stereo WAV files supported (%s)\n", name );
|
|
return false;
|
|
}
|
|
|
|
sound.rate = GetLittleLong();
|
|
iff_dataPtr += 6;
|
|
|
|
sound.width = GetLittleShort() / 8;
|
|
|
|
if( sound.width != 1 && sound.width != 2 )
|
|
{
|
|
Con_DPrintf( S_ERROR "Sound_LoadWAV: only 8 and 16 bit WAV files supported (%s)\n", name );
|
|
return false;
|
|
}
|
|
|
|
// get cue chunk
|
|
FindChunk( name, "cue " );
|
|
|
|
if( iff_dataPtr )
|
|
{
|
|
iff_dataPtr += 32;
|
|
sound.loopstart = GetLittleLong();
|
|
FindNextChunk( name, "LIST" ); // if the next chunk is a LIST chunk, look for a cue length marker
|
|
|
|
if( iff_dataPtr )
|
|
{
|
|
if( !Q_strncmp( (const char *)iff_dataPtr + 28, "mark", 4 ))
|
|
{
|
|
// this is not a proper parse, but it works with CoolEdit...
|
|
iff_dataPtr += 24;
|
|
sound.samples = sound.loopstart + GetLittleLong(); // samples in loop
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sound.loopstart = -1;
|
|
sound.samples = 0;
|
|
}
|
|
|
|
// find data chunk
|
|
FindChunk( name, "data" );
|
|
|
|
if( !iff_dataPtr )
|
|
{
|
|
Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'data' chunk\n", name );
|
|
return false;
|
|
}
|
|
|
|
iff_dataPtr += 4;
|
|
samples = GetLittleLong() / sound.width;
|
|
|
|
if( sound.samples )
|
|
{
|
|
if( samples < sound.samples )
|
|
{
|
|
Con_DPrintf( S_ERROR "Sound_LoadWAV: %s has a bad loop length\n", name );
|
|
return false;
|
|
}
|
|
}
|
|
else sound.samples = samples;
|
|
|
|
if( sound.samples <= 0 )
|
|
{
|
|
Con_Reportf( S_ERROR "Sound_LoadWAV: file with %i samples (%s)\n", sound.samples, name );
|
|
return false;
|
|
}
|
|
|
|
sound.type = WF_PCMDATA;
|
|
sound.samples /= sound.channels;
|
|
|
|
// Load the data
|
|
sound.size = sound.samples * sound.width * sound.channels;
|
|
sound.wav = Mem_Malloc( host.soundpool, sound.size );
|
|
|
|
memcpy( sound.wav, buffer + (iff_dataPtr - buffer), sound.size );
|
|
|
|
// now convert 8-bit sounds to signed
|
|
if( sound.width == 1 )
|
|
{
|
|
int i, j;
|
|
signed char *pData = (signed char *)sound.wav;
|
|
|
|
for( i = 0; i < sound.samples; i++ )
|
|
{
|
|
for( j = 0; j < sound.channels; j++ )
|
|
{
|
|
*pData = (byte)((int)((byte)*pData) - 128 );
|
|
pData++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|