openmohaa/code/client/snd_mem_new.cpp

544 lines
11 KiB
C++
Raw Normal View History

2024-01-08 19:43:47 +01:00
/*
===========================================================================
Copyright (C) 2024 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code 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 2 of the License,
or (at your option) any later version.
OpenMoHAA source code 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.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#if USE_SOUND_NEW
#include "snd_local.h"
#include "cl_ui.h"
2024-01-08 19:43:47 +01:00
byte *data_p;
byte *iff_end;
byte *last_chunk;
byte *iff_data;
int iff_chunk_len;
qboolean S_LoadMP3(const char *fileName, sfx_t *sfx);
2024-01-08 23:15:12 +01:00
/*
==============
GetLittleShort
==============
*/
2024-01-08 19:43:47 +01:00
short int GetLittleShort()
{
union {
short value;
byte bytes[2];
} val;
# ifdef Q3_LITTLE_ENDIAN
val.bytes[0] = data_p[0];
val.bytes[1] = data_p[1];
2024-01-16 20:27:32 +01:00
# else
val.bytes[0] = data_p[1];
val.bytes[1] = data_p[0];
2024-01-08 19:43:47 +01:00
# endif
data_p += sizeof(short);
return val.value;
}
2024-01-08 23:15:12 +01:00
/*
==============
GetLittleLong
==============
*/
2024-01-08 19:43:47 +01:00
int GetLittleLong()
{
union {
int value;
byte bytes[4];
} val;
# ifdef Q3_LITTLE_ENDIAN
val.bytes[0] = data_p[0];
val.bytes[1] = data_p[1];
val.bytes[2] = data_p[2];
val.bytes[3] = data_p[3];
2024-01-16 20:27:32 +01:00
# else
val.bytes[0] = data_p[3];
val.bytes[1] = data_p[2];
val.bytes[2] = data_p[1];
val.bytes[3] = data_p[0];
2024-01-08 19:43:47 +01:00
# endif
data_p += sizeof(int);
return val.value;
}
2024-01-08 23:15:12 +01:00
/*
==============
SetLittleShort
==============
*/
2024-01-08 19:43:47 +01:00
void SetLittleShort(int i)
{
union {
short value;
byte bytes[2];
} val;
val.value = i;
# ifdef Q3_LITTLE_ENDIAN
data_p[0] = val.bytes[0];
data_p[1] = val.bytes[1];
2024-01-16 20:27:32 +01:00
# else
data_p[0] = val.bytes[1];
data_p[1] = val.bytes[0];
2024-01-08 19:43:47 +01:00
# endif
data_p += sizeof(short);
}
2024-01-08 23:15:12 +01:00
/*
==============
SetLittleLong
==============
*/
2024-01-08 19:43:47 +01:00
void SetLittleLong(int i)
{
union {
int value;
byte bytes[2];
} val;
val.value = i;
# ifdef Q3_LITTLE_ENDIAN
data_p[0] = val.bytes[0];
data_p[1] = val.bytes[1];
data_p[2] = val.bytes[2];
data_p[3] = val.bytes[3];
2024-01-16 20:27:32 +01:00
# else
data_p[0] = val.bytes[3];
data_p[1] = val.bytes[2];
data_p[2] = val.bytes[1];
data_p[3] = val.bytes[0];
2024-01-08 19:43:47 +01:00
# endif
data_p += sizeof(int);
}
2024-01-08 23:15:12 +01:00
/*
==============
FindNextChunk
==============
*/
2024-01-08 19:43:47 +01:00
void FindNextChunk(const char *name)
{
2024-01-08 23:15:12 +01:00
int value;
while (1) {
data_p = last_chunk;
if (last_chunk >= (byte *)iff_end) {
break;
}
data_p = last_chunk + 4;
value = GetLittleLong();
iff_chunk_len = value;
if (value < 0) {
break;
}
data_p -= 8;
value++;
value &= ~1;
2024-01-16 20:27:32 +01:00
last_chunk = data_p + value + 8;
2024-01-08 23:15:12 +01:00
if (!strncmp((const char *)data_p, name, 4u)) {
return;
}
}
data_p = NULL;
2024-01-08 19:43:47 +01:00
}
2024-01-08 23:15:12 +01:00
/*
==============
FindChunk
==============
*/
2024-01-08 19:43:47 +01:00
void FindChunk(const char *name)
{
2024-01-08 23:15:12 +01:00
last_chunk = iff_data;
FindNextChunk(name);
2024-01-08 19:43:47 +01:00
}
2024-01-08 23:15:12 +01:00
/*
==============
DumpChunks
==============
*/
2024-01-08 19:43:47 +01:00
void DumpChunks()
{
2024-01-08 23:15:12 +01:00
char str[5];
str[4] = 0;
data_p = iff_data;
do {
memcpy(str, data_p, 4);
data_p += 4;
iff_chunk_len = GetLittleLong();
Com_Printf("0x%x : %s (%d)\n", data_p - 4, str, iff_chunk_len);
data_p += (iff_chunk_len + 1) & ~1;
} while (data_p < (byte *)iff_end);
2024-01-08 19:43:47 +01:00
}
2024-01-08 23:15:12 +01:00
/*
==============
GetWavinfo
==============
*/
2024-01-08 19:43:47 +01:00
wavinfo_t GetWavinfo(const char *name, byte *wav, int wavlength)
{
wavinfo_t info;
int samples;
memset(&info, 0, sizeof(wavinfo_t));
2024-01-08 23:15:12 +01:00
if (!wav) {
return info;
}
iff_data = wav;
iff_end = &wav[wavlength];
FindChunk("RIFF");
if (!data_p || strncmp((const char *)data_p + 8, "WAVE", 4u)) {
Com_Printf("Missing RIFF/WAVE chunks\n");
return info;
}
iff_data = data_p + 12;
FindChunk("fmt ");
if (!data_p) {
Com_Printf("Missing fmt chunk\n");
return info;
}
data_p += 8;
info.format = GetLittleShort();
if (info.format == 17) {
info.channels = GetLittleShort();
info.rate = (float)GetLittleLong();
data_p += 6;
info.width = (float)GetLittleShort() / 8.f;
data_p += 2;
FindChunk("data");
if (!data_p) {
Com_Printf("Missing data chunk\n");
return info;
}
data_p += 4;
if (info.width >= 1.0) {
samples = (int)((float)GetLittleLong() / info.width);
} else {
samples = (int)((float)GetLittleLong() * info.width);
}
if (!info.samples) {
info.samples = samples;
} else if (samples < info.samples) {
Com_Error(ERR_DROP, "Sound %s has a bad loop length", name);
}
info.dataofs = 0;
} else if (info.format == 1) {
info.channels = GetLittleShort();
info.rate = (float)GetLittleLong();
data_p += 6;
info.width = (float)(GetLittleShort() / 8);
FindChunk("data");
if (!data_p) {
Com_Printf("Missing data chunk\n");
return info;
}
data_p += 4;
samples = (float)GetLittleLong() / info.width;
if (!info.samples) {
info.samples = samples;
} else if (samples < info.samples) {
Com_Error(ERR_DROP, "Sound %s has a bad loop length", name);
}
info.dataofs = data_p - wav;
} else {
Com_Printf("Microsoft PCM format only\n");
return info;
}
info.datasize = iff_chunk_len;
2024-01-08 19:43:47 +01:00
return info;
}
2024-01-08 23:15:12 +01:00
/*
==============
DownSampleWav
==============
*/
qboolean DownSampleWav(wavinfo_t *info, byte *wav, int wavlength, int newkhz, byte **newdata)
2024-01-08 19:43:47 +01:00
{
2024-01-08 23:15:12 +01:00
int newdatasize;
byte* datap;
int i;
int ii;
int error;
int width;
int oldsamples;
int oldrate;
newdatasize = 0;
datap = &wav[info->dataofs];
if (info->channels > 1)
{
Com_DPrintf("Could not downsample WAV file. Stereo WAVs not supported!\n");
return 0;
}
if (info->format != 1 || !info->dataofs)
{
Com_DPrintf("Could not downsample WAV file. Not PCM format!\n");
return 0;
}
if (info->rate <= newkhz) {
return 0;
}
error = 0;
width = info->width;
for (i = 0; i < info->samples; i++) {
error += newkhz;
while (error > info->rate) {
error -= info->rate;
newdatasize += width;
}
}
oldsamples = info->samples;
oldrate = info->rate;
info->samples = newdatasize / width;
info->rate = (float)newkhz;
newdatasize += info->dataofs;
*newdata = (byte*)Z_TagMalloc(newdatasize, TAG_SOUND);
2024-01-08 23:15:12 +01:00
memcpy(*newdata, wav, info->dataofs);
iff_data = *newdata;
iff_end = *newdata + newdatasize;
FindChunk("RIFF");
if (!data_p || strncmp((const char*)data_p + 8, "WAVE", 4u))
{
Com_DPrintf("Missing RIFF/WAVE chunks\n");
return 0;
}
iff_data = data_p + 12;
FindChunk("fmt ");
if (!data_p)
{
Com_DPrintf("Missing fmt chunk\n");
return 0;
}
data_p += 12;
SetLittleShort((int)info->rate);
data_p += 8;
FindChunk("data");
if (!data_p)
{
Com_DPrintf("Missing data chunk\n");
return 0;
}
data_p += 4;
SetLittleLong((int)(info->samples * info->width));
error = 0;
for (i = 0; i < oldsamples; i++) {
error += newkhz;
while (error > oldrate) {
error -= oldrate;
for (ii = 0; ii < width; i++) {
data_p[ii] = datap[ii];
}
data_p += width;
}
datap += width;
}
return newdatasize;
2024-01-08 19:43:47 +01:00
}
2024-01-08 23:15:12 +01:00
/*
==============
DownSampleWav_MILES
==============
*/
2024-01-08 19:43:47 +01:00
int DownSampleWav_MILES(wavinfo_t *info, byte *wav, int wavlength, int newkhz, byte **newdata)
{
2024-01-08 23:15:12 +01:00
STUB_DESC("sound stuff");
2024-01-08 19:43:47 +01:00
return 0;
}
2024-01-08 23:15:12 +01:00
/*
==============
S_LoadSound
==============
*/
2024-01-08 19:43:47 +01:00
qboolean S_LoadSound(const char *fileName, sfx_t *sfx, int streamed, qboolean force_load)
{
2024-01-08 23:15:12 +01:00
int size;
fileHandle_t file_handle;
char tempName[MAX_RES_NAME + 1];
int realKhz;
2024-01-17 23:15:38 +01:00
sfx->buffer = 0;
2024-01-08 23:15:12 +01:00
if (fileName[0] == '*') {
return qfalse;
}
if (strstr(fileName, ".mp3")) {
return S_LoadMP3(fileName, sfx);
}
size = FS_FOpenFileRead(fileName, &file_handle, qfalse, qtrue);
if (size <= 0)
{
if (file_handle) {
FS_FCloseFile(file_handle);
}
return qfalse;
}
sfx->data = (byte*)Z_TagMalloc(size, TAG_SOUND);
2024-01-08 23:15:12 +01:00
FS_Read(sfx->data, size, file_handle);
FS_FCloseFile(file_handle);
sfx->info = GetWavinfo(fileName, sfx->data, size);
if (sfx->info.channels != 1)
{
Com_Printf("%s is a stereo wav file\n", fileName);
Z_Free(sfx->data);
sfx->data = NULL;
return qfalse;
}
if (!sfx->info.dataofs) {
sfx->iFlags |= SFX_FLAG_NO_OFFSET;
}
realKhz = 11025;
if (s_khz->integer != 11)
{
realKhz = 22050;
if (s_khz->integer == 44) {
realKhz = 44100;
}
}
if (!(sfx->iFlags & SFX_FLAG_STREAMED) && realKhz < sfx->info.rate) {
2024-01-08 23:15:12 +01:00
byte* newdata;
int newdatasize;
newdata = NULL;
if (sfx->iFlags & SFX_FLAG_NO_OFFSET) {
newdatasize = DownSampleWav_MILES(&sfx->info, sfx->data, size, realKhz, &newdata);
} else {
newdatasize = DownSampleWav(&sfx->info, sfx->data, size, realKhz, &newdata);
}
if (newdatasize) {
Z_Free(sfx->data);
sfx->data = newdata;
}
}
sfx->length = sfx->info.samples;
sfx->width = sfx->info.width;
sfx->time_length = sfx->info.samples / sfx->info.rate * 1000.f;
if (sfx->iFlags & SFX_FLAG_STREAMED) {
2024-01-08 23:15:12 +01:00
Z_Free(sfx->data);
sfx->data = NULL;
}
sprintf(tempName, "k%s", fileName);
UI_LoadResource(tempName);
return qtrue;
2024-01-08 19:43:47 +01:00
}
2024-01-08 23:15:12 +01:00
/*
==============
S_LoadMP3
==============
*/
2024-01-08 19:43:47 +01:00
qboolean S_LoadMP3(const char *fileName, sfx_t *sfx)
{
2024-01-08 23:15:12 +01:00
int length;
fileHandle_t file_handle;
length = FS_FOpenFileRead(fileName, &file_handle, 0, 1);
if (length <= 0) {
if (file_handle) {
FS_FCloseFile(file_handle);
}
return qfalse;
}
memset(&sfx->info, 0, sizeof(sfx->info));
sfx->data = (byte*)Z_TagMalloc(length, TAG_SOUND);
2024-01-08 23:15:12 +01:00
sfx->length = length;
sfx->width = 1;
FS_Read(sfx->data, length, file_handle);
FS_FCloseFile(file_handle);
sfx->iFlags |= SFX_FLAG_MP3;
return qtrue;
2024-01-08 19:43:47 +01:00
}
#endif