/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/*****************************************************************************
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: Dune Emperor **
** **
** Module: (_) **
** **
** Version: $ID$ **
** **
** File name: **
** **
** Created by: 04/05/96 TR **
** **
** Description: **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
//#include "asimp3\mss.h"
//#include "asimp3\mp3dec.h"
// 'assignment within condition expression'.
#pragma warning(disable : 4706)
DBG_DECLARE_TYPE ( AudioSample )
DBG_DECLARE_TYPE ( AudioFormat )
DBG_DECLARE_TYPE ( AudioFrame )
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
//
// Sample rate in samples/second for [MPEG25][MPEG version][value]
//
static const int sample_rate[2][2][4] =
{{
{ 22050L,24000L,16000L,22050L },
{ 44100L,48000L,32000L,44100L }
},
{
{ 11025L,12000L, 8000L,11025L },
{ 44100L,48000L,32000L,44100L }
}};
//
// Bit rate in bits/second for [MPEG version][value]
//
static const int bit_rate[2][15] =
{
{ 0L,8000L,16000L,24000L,32000L,40000L,48000L,56000L,64000L,80000L,96000L,112000L,128000L,144000L,160000L }
,
{ 0L,32000L,40000L,48000L,56000L,64000L,80000L,96000L,112000L,128000L,160000L,192000L,224000L,256000L,320000L }
};
/*****************************************************************************
** Public Data **
*****************************************************************************/
const short MSADPCM_StdCoef[7][2] = {
{ 256, 0},
{ 512,-256},
{ 0, 0},
{ 192, 64},
{ 240, 0},
{ 460,-208},
{ 392,-232}
};
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioSample* AudioCreateSample( uint bytes )
{
AudioSample *sample;
DBG_ASSERT ( bytes > 0 );
ALLOC_STRUCT ( sample, AudioSample );
AudioSampleInit ( sample );
sample->Bytes = bytes;
if ( ! ( sample->Data = ( char *) AudioMemAlloc ( sample->Bytes ) ))
{
goto error;
}
return sample;
error:
if ( sample )
{
AudioSampleDestroy ( sample );
}
return NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleDestroy ( AudioSample *sample )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
if ( sample->Data )
{
AudioMemFree ( sample->Data );
}
AudioMemFree ( sample );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleInit ( AudioSample *sample )
{
DBG_ASSERT ( sample != NULL );
DBG_SET_TYPE ( sample, AudioSample );
sample->Data = NULL;
sample->Bytes = 0;
sample->Format = NULL;
sample->Attribs = NULL;
ListInit ( &sample->Frames );
#ifdef _DEBUG
sample->name[0] = 0;
#endif
}
#ifndef IG_FINAL_RELEASE
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleSetName ( AudioSample *sample, const char *orig_name )
{
int diff;
char *buffer = NULL;
char *name;
DBG_ASSERT_TYPE ( sample, AudioSample );
buffer = (char *) AudioMemAlloc ( strlen ( orig_name ) + 1);
name = buffer;
sample->name[0] = 0;
if ( name )
{
char *ptr;
strcpy ( name, orig_name );
ptr = strrchr ( name, '.' );
if ( ptr )
{
*ptr = 0;
}
ptr = strrchr ( name, '\\' );
if ( ptr )
{
name = ptr + 1;
}
if ( ( diff = (strlen ( name ) - (sizeof(sample->name) - 1))) > 0 )
{
strcpy ( sample->name, "...");
name = name + diff + 3;
strcat ( sample->name, name );
}
else
{
strcpy ( sample->name, name );
}
}
if ( buffer )
{
AudioMemFree ( buffer );
}
}
#endif
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleAddFrame ( AudioSample *sample, AudioFrame *frame )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
DBG_ASSERT_TYPE ( frame, AudioFrame );
ListAddToTail ( &sample->Frames, &frame->nd );
sample->Bytes += frame->Bytes;
frame->sample = sample;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
AudioFrame* AudioSampleFirstFrame ( AudioSample *sample )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
return (AudioFrame *) ListNodeNext ( &sample->Frames );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioSampleDeinit ( AudioSample *sample )
{
DBG_ASSERT_TYPE ( sample, AudioSample );
DBG_INVALIDATE_TYPE ( sample );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioFormatBytes ( AudioFormat *format, TimeStamp time )
{
DBG_ASSERT_TYPE ( format, AudioFormat );
return (int) ( ((TimeStamp)format->BytesPerSecond*time)/(TimeStamp)SECONDS(1));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
TimeStamp AudioFormatTime ( AudioFormat *format, int bytes )
{
DBG_ASSERT_TYPE ( format, AudioFormat );
if ( format->BytesPerSecond )
{
return (((TimeStamp)bytes)*(TimeStamp)SECONDS(1))/(TimeStamp)format->BytesPerSecond;
}
return 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFormatInit ( AudioFormat *format )
{
DBG_ASSERT ( format != NULL );
memset ( format, 0, sizeof ( AudioFormat));
DBG_SET_TYPE ( format, AudioFormat );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFormatUpdate ( AudioFormat *format )
{
DBG_ASSERT_TYPE ( format, AudioFormat );
DBG_ASSERT ( format->Channels != 0 );
DBG_ASSERT ( format->SampleWidth >0 );
DBG_ASSERT ( format->Rate >0 );
DBG_ASSERT ( format->Compression < AUDIO_COMPRESS_MAX_ID );
if ( format->Compression != AUDIO_COMPRESS_MP3 )
{
format->BytesPerSecond = format->Channels*format->SampleWidth*format->Rate;
if ( format->Compression == AUDIO_COMPRESS_IMA_ADPCM
|| format->Compression == AUDIO_COMPRESS_MS_ADPCM )
{
format->BytesPerSecond >>= 2; // 4:1 compression
}
}
else
{
int mpeg1 = (W_BitsGet ( format->cdata.mp3.Header, 1, 19 ));
int bitrateindex = W_BitsGet ( format->cdata.mp3.Header, 4, 12 );
format->BytesPerSecond = bit_rate[mpeg1][bitrateindex] / 8;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFrameInit ( AudioFrame *frame, void *data, int bytes )
{
DBG_ASSERT_PTR ( frame );
DBG_SET_TYPE ( frame, AudioFrame );
frame->Bytes = bytes;
frame->Data = data;
ListNodeInit ( &frame->nd );
frame->sample = NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AudioFrameDeinit ( AudioFrame *frame )
{
DBG_ASSERT_TYPE ( frame, AudioFrame );
frame->sample = NULL;
DBG_INVALIDATE_TYPE ( frame );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
#define MAX_SYNC_SEARCH (10*1024) // max bytes that will be searched for the sync
int AudioFormatReadMP3File ( File *file, AudioFormat *format, int *datasize )
{
unsigned char buffer[MAX_SYNC_SEARCH+3];
int bytes;
int pos;
unsigned int header = 0;
int bitrateindex = 0; // initialize to prevent compiler warning
int sampling_frequency = 0; // initialize to prevent compiler warning
int layer;
int data_start = file->position ();
bytes = file->read ( buffer, sizeof (buffer ));
pos = 0;
while ( !header && bytes >= 4 )
{
unsigned char mask = 0xFF;
// find next sync
while ( bytes >= 3 )
{
if ( (buffer[pos]&mask) == mask )
{
if ( mask == 0xE0 )
{
pos--;
bytes++;
//header = (buffer[pos] << 24) || (buffer[pos+1] << 16) || (buffer[pos+2] << 8) || buffer[pos+3];
header = MAKEID ( buffer[pos], buffer[pos+1], buffer[pos+2], buffer[pos+3]);
break;
}
mask = 0xE0;
}
else
{
mask = 0xFF;
}
pos++;
bytes--;
}
// validate the header
bitrateindex = W_BitsGet ( header, 4, 12 );
sampling_frequency = W_BitsGet ( header, 2, 10 );
layer = (W_BitsGet ( header, 2, 17 ));
if ( bitrateindex == 0x0f || sampling_frequency == 0x03 || layer != 1)
{
header = 0;
pos++;
bytes--;
continue;
}
}
if (!header )
{
return FALSE;
}
int mpeg25 = !(W_BitsGet ( header, 1, 20 ));
int mpeg1 = (W_BitsGet ( header, 1, 19 ));
int mode = (W_BitsGet ( header, 2, 6 ));
format->Compression = AUDIO_COMPRESS_MP3;
format->SampleWidth = 2;
format->Channels = (mode == 3) ? 1 : 2;
format->BytesPerSecond = bit_rate[mpeg1][bitrateindex] / 8;
format->Rate = sample_rate[mpeg25][mpeg1][sampling_frequency];
format->Flags = mAUDIO_FORMAT_PCM;
format->cdata.mp3.Header = header;
AudioFormatUpdate ( format );
if ( datasize )
{
*datasize = file->size() - (data_start + pos) ;
}
file->seek ( data_start+pos, File::START );
return TRUE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioFormatSeekToPos ( File *file, const AudioFormat *format, int pos, int data_start )
{
if ( pos )
{
int block_size = 0;
if ( format->Compression == AUDIO_COMPRESS_MP3 )
{
AudioFormat tformat;
file->seek ( pos+data_start, File::START );
AudioFormatInit ( &tformat );
int found = FALSE;
while ( !found )
{
if ( !AudioFormatReadMP3File ( file, &tformat, NULL ) )
{
break;
}
if ( AudioFormatSame ( &tformat, format ) )
{
found = TRUE;
break;
}
file->seek ( 1, File::CURRENT );
}
if ( !found )
{
pos = 0;
}
else
{
pos = file->seek ( 0, File::CURRENT ) - data_start ;
}
}
else
{
switch ( format->Compression )
{
case AUDIO_COMPRESS_NONE:
block_size = format->Channels*format->SampleWidth;
break;
case AUDIO_COMPRESS_IMA_ADPCM:
case AUDIO_COMPRESS_MS_ADPCM:
block_size = format->cdata.adpcm.BlockSize;
break;
}
if ( block_size > 1 )
{
pos = ( pos / block_size ) *block_size;
}
}
}
file->seek ( pos + data_start, File::START );
return pos;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
int AudioFormatSame ( const AudioFormat *f1, const AudioFormat *f2 )
{
if ( (f1->Rate != f2->Rate )
|| (f1->Compression != f2->Compression )
|| (f1->SampleWidth != f2->SampleWidth )
|| (f1->Channels != f2->Channels )
|| (f1->Flags != f2->Flags ) )
{
return FALSE;
}
if ( f1->Compression == AUDIO_COMPRESS_IMA_ADPCM && (f1->cdata.adpcm.BlockSize != f2->cdata.adpcm.BlockSize))
{
return FALSE;
}
if ( f1->Compression == AUDIO_COMPRESS_MP3 && (f1->cdata.mp3.Header != f2->cdata.mp3.Header))
{
return FALSE;
}
return TRUE;
}