/*
** 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 .
*/
/********************************************************************* Code **
** **
** Westwood Studios Pacific. **
** **
** Confidential Information **
** Copyright (C) 1998 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: WAUDIO Library **
** **
** Module: Audio (AUD_) **
** **
** Version: $ID$ **
** **
** File name: wn32/dsound/r2/drv_dsnd.c **
** **
** Created by: 06/26/96 TR **
** **
** Description: Audio device driver for Win'95 Direct Sound **
** **
** **
** audioLoad : load system **
** audioUnload : unload system **
** audioOpen : open a unit **
** audioClose : close a unit **
** **
** audioOpenChannel : open an audio channel **
** audioCloseChannel : close an audio channel **
** audioStart : satrt a sample **
** audioStop : stop a sample **
** audioPause : pause a sample **
** audioResume : resume a sample **
** audioCheck : check if a sample is playing **
** audioUpdate : update sample attributes **
** audioQueueIt : queue up sample to play next **
** AUD_command : driver specific command **
*****************************************************************************/
#include /* Always include this header first. */
#define WIN32_LEAN_AND_MEAN
#include
#pragma warning ( disable: 4706 )
#pragma warning(disable:4201)
#include
//
// Colin: I'm taking the INITGUID stuff out here because we are now linking
// the main RTS.exe to dxguid.lib, having this defined here causes duplicate
// symbols when linking
//
// #define INITGUID // see comment above
#include
// #undef INITGUID // see comment above
#include
#include "asimp3\mss.h"
#include "asimp3\mp3dec.h"
#include
#include
#include
#include
#include
/*****************************************************************************
** Includes **
*****************************************************************************/
#include /* prototypes for driver code */
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
#define vAUD_DRV_MAX_CHANNELS 100 /* this is arbitrary */
#define vAUD_DRV_POLL_INTERVAL (SECONDS(1)/30) /* this frequency will determine the buffer sizes */
#define vAUD_DRV_OVER_SAMPLE 8
#define vAUD_DRV_FRAMES 4 // this must be at least 2
#define vAUD_DRV_LAG_FRAMES 0 // follow behind play frame by this many frames
#define vAUD_DRV_DBHALF -1000 // -10dB
/*****************************************************************************
** Private Types **
*****************************************************************************/
#define STATE_READ_BLOCK 0
#define STATE_WRITE_BLOCK 1
#define STATE_DECODE_BLOCK 2
#define STATE_INIT_BLOCK 3
typedef struct
{
int previousValue;
int index;
} IMA_DATA;
typedef struct
{
int idelta;
short iCoef[2];
} MS_DATA;
typedef struct
{
ASISTREAM *asi;
int in_pos; // start of data in in buffer
} MP3_DATA;
typedef int (CB_DECODE) ( struct AudioDriverChannelTag *);
typedef struct _transfer
{
int state; // current state
int next_state; //
uint8 *in; // input buffer
int in_bytes; // bytes to read
int in_bytes_left; // bytes left to read
int in_size; // current size of input buffer
int in_size_needed; // required size of input buffer
uint8 *out; // output buffer
int out_bytes; // bytes to write
int out_bytes_left; // bytes left to write
int out_size; // current size of output buffer
int out_size_needed; // required size of output buffer
int channels; // number of channels
int pending; // there is buffered data that still needs servicing
int block_size;
CB_DECODE *decode; // code to decode block
IMA_DATA ima[2];
MS_DATA ms[2];
MP3_DATA mp3;
} TRANSFER;
typedef int (CB_TRANSFER) ( struct AudioDriverChannelTag *, uint8 *dst, int *dst_bytes, uint8 *src , int *src_bytes );
typedef struct AudioDriverChannelTag
{
AudioChannel *chan; /* WCORE channel we belong to */
volatile uint flags;
/* play */
volatile int pcm_pos; /* pcm playback position relative to the end of the sample */
volatile uint frame; /* current frame position of play back */
volatile TimeStamp last_poll; /* time of last poll */
volatile TimeStamp poll_interval; /* time between polls */
/* source data */
volatile uint8 *src_start; /* start of source data */
volatile int src_size; /* size of source (bytes) */
/* transfer */
volatile uint8 *src_pos; /* source sample data read pos*/
volatile int src_left; /* number of bytes of source left */
volatile ulong dst_pos; /* current write posistion in buffer */
volatile ulong write_pos; /* current write posistion in buffer according to DS */
volatile int dst_left; /* bytes left to write befor wrap round */
volatile int silence_left; /* bytes of silence left to fill */
int min_fill; /* debug */
/* buffer */
AudioFormat format; /* current buffer format */
LPDIRECTSOUNDBUFFER dsbuf; /* Direct sound buffer interface */
int buf_size; /* size of active area in bytes*/
int frame_bytes; /* frame size in bytes */
CB_TRANSFER *transfer; /* code to read sample data */
TRANSFER transfer_data;
volatile int next_frame_called;
volatile int service_count;
volatile int frames_played;
volatile int original_frame;
volatile int original_dst_pos;
} AUD_DRV_CHAN;
/*****************************************************************************
** Private Data **
*****************************************************************************/
static AUD_Thread *thread = NULL;
static Lock drv_ref;
#ifdef _DEBUG
int mixer_skip = 0;
#endif
static AudioDriver _AUD_driver =
{
NULL, /* use for extended data */
audioOpenChannel,
audioCloseChannel,
audioStart,
audioStop,
audioPause,
audioResume,
audioCheck,
audioUpdate,
audioQueueIt,
audioLock,
audioUnlock
};
static AudioSystemMaster _AUD_system_master =
{
"Direct Sound Driver V2.0",
0x0, /* reserved, do not change ! */
mAUDIO_SYS_PROP_NONE,
AUDIO_SYSTEM_MASTER_INIT_STATE, // do not change this !
AUDIO_STAMP_SYSTEM_MASTER, // struct id, do not change this !
audioLoad,
audioUnload,
audioOpen,
audioClose,
&_AUD_driver
};
/* direct sound */
LPDIRECTSOUND AUD_sound_object = NULL;
LPDIRECTSOUNDBUFFER AUD_primary_buffer = NULL;
// Table was generated with the function 1000*LOG2(n/100) where n is the range 0..100
// 0 = no volume, 100 = full volume, and the range is linear ( 50 is half volume).
// LOG2() is log to the base 2
static int AUD_log_table[101] ={ //1....2.....3......4....5......6.....7....8.....9......0
-10000,-6644,-5644,-5059,-4644,-4322,-4059,-3837,-3644,-3474,
-3322,-3184,-3059,-2943,-2837,-2737,-2644,-2556,-2474,-2396,
-2322,-2252,-2184,-2120,-2059,-2000,-1943,-1889,-1837,-1786,
-1737,-1690,-1644,-1599,-1556,-1515,-1474,-1434,-1396,-1358,
-1322,-1286,-1252,-1218,-1184,-1152,-1120,-1089,-1059,-1029,
-1000, -971, -943, -916, -889, -862, -837, -811, -786, -761,
-737, -713, -690, -667, -644, -621, -599, -578, -556, -535,
-515, -494, -474, -454, -434, -415, -396, -377, -358, -340,
-322, -304, -286, -269, -252, -234, -218, -201, -184, -168,
-152, -136, -120, -105, -89, -74, -59, -44, -29, -14,
0
};
/*****************************************************************************
** Public Data **
*****************************************************************************/
AudioSystemMaster *AudioSystemDSD = &_AUD_system_master;
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
static void AUD_pcm_service ( AudioChannel *chan );
static void AUD_init_buffer ( AUD_DRV_CHAN *ci );
static int AUD_create_pcm_buffer ( AUD_DRV_CHAN *ci, AudioFormat *format );
static void AUD_destroy_pcm_buffer ( AUD_DRV_CHAN *ci );
static void AUD_set_buffer_src ( AUD_DRV_CHAN *ci );
static void AUD_new_buffer_src ( AUD_DRV_CHAN *ci );
static int AUD_fill_buffer ( AUD_DRV_CHAN *ci, int bytes );
static void AUD_update_buffer ( AUD_DRV_CHAN *ci );
static void AUD_reset_buffer ( AUD_DRV_CHAN *ci );
static void AUD_channel_stop ( AudioChannel *chan );
static uint AUD_get_current_frame ( AUD_DRV_CHAN *ci );
static void AUD_set_wave_format ( WAVEFORMATEX *pcmwf, AudioFormat *format );
static int AUD_same_format ( AudioFormat *format1, AudioFormat *format2 );
static AUD_ThreadCB AUD_service_device;
static int AUD_service_device ( AudioDevice *dev );
static int AUD_restore_primary ( void );
static int AUD_restore_buffer ( AUD_DRV_CHAN *ci );
static CB_TRANSFER PCM_transfer;
static CB_DECODE IMA_decode_block;
static CB_DECODE MS_decode_block;
//static int AUD_set_dev_format ( AudioDevice *dev, AudioFormat *format );
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
static void transfer_init ( TRANSFER *trans )
{
memset ( trans, 0, sizeof (TRANSFER));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void transfer_deinit ( TRANSFER *trans )
{
if ( trans->in )
{
free ( trans->in );
trans->in = NULL;
trans->in_size = 0;
}
if ( trans->out )
{
free ( trans->out );
trans->out = NULL;
trans->out_size = 0;
}
if ( trans->mp3.asi )
{
ASI_stream_close ( (HASISTREAM) trans->mp3.asi );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int NULL_transfer ( AUD_DRV_CHAN *, uint8 *, int *, uint8 *, int *)
{
return FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int PCM_transfer ( AUD_DRV_CHAN *, uint8 *dst, int *dst_bytes, uint8 *src, int *src_bytes )
{
int transfer;
if ( (transfer = *dst_bytes ) > *src_bytes )
{
transfer = *src_bytes;
}
if ( transfer )
{
memcpy ( dst, src, transfer );
*dst_bytes = transfer;
*src_bytes = transfer;
}
return TRUE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int ADPCM_transfer ( AUD_DRV_CHAN *ci, uint8 *dst, int *dst_bytes, uint8 *src, int *src_bytes )
{
int sbytes = 0;
int dbytes = 0;
int stransfered = 0;
int dtransfered = 0;
int bytes;
int done = FALSE;
TRANSFER *data = &ci->transfer_data;
if ( !data->out || !data->in )
{
return FALSE;
}
if ( src_bytes )
{
sbytes = *src_bytes;
}
if ( dst_bytes )
{
dbytes = *dst_bytes;
}
while ( !done )
{
switch ( data->state )
{
case STATE_READ_BLOCK:
{
if ( !data->in_bytes_left || src == NULL)
{
// have read in the src
data->state = data->next_state;
data->in_bytes_left = 0;
break;
}
// ok, read in as much as we can
if ( (bytes = data->in_bytes_left ) > sbytes )
{
bytes = sbytes;
}
if ( bytes == 0 )
{
// no more data to read at this time
done = TRUE;
break;
}
memcpy ( &data->in[ data->in_bytes - data->in_bytes_left], src, bytes );
src += bytes;
stransfered+= bytes;
sbytes -= bytes;
data->in_bytes_left -= bytes;
data->pending = TRUE;
break;
}
case STATE_WRITE_BLOCK:
{
if ( !data->out_bytes_left )
{
// have written out to dst
data->state = data->next_state;
data->pending = FALSE;
break;
}
// ok, write out as much as we can
if ( (bytes = data->out_bytes_left ) > dbytes )
{
bytes = dbytes;
}
if ( bytes == 0 )
{
// no more data to write at this time
done = TRUE;
break;
}
memcpy ( dst, &data->out[ data->out_bytes - data->out_bytes_left], bytes );
dst += bytes;
dtransfered+= bytes;
dbytes -= bytes;
data->out_bytes_left -= bytes;
break;
}
case STATE_DECODE_BLOCK:
{
if ( !data->decode ( ci ) )
{
return FALSE;
}
data->state = STATE_WRITE_BLOCK;
data->out_bytes_left = data->out_bytes;
data->next_state = STATE_INIT_BLOCK;
break;
}
case STATE_INIT_BLOCK:
{
if ( src != NULL )
{
data->in_bytes = data->block_size;
data->in_bytes_left = data->block_size;
data->state = STATE_READ_BLOCK;
data->next_state = STATE_DECODE_BLOCK;
}
else
{
done = TRUE;
}
break;
}
}
}
if ( src_bytes )
{
*src_bytes = stransfered;
}
if ( dst_bytes )
{
*dst_bytes = dtransfered;
}
return TRUE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static S32 AILCALLBACK fetch_CB ( U32 user, void FAR *dest, S32 bytes, S32 offset)
{
TRANSFER *data = (TRANSFER *) user ;
if ( bytes)
{
if ( bytes > data->in_bytes_left )
{
bytes = data->in_bytes_left;
}
memcpy ( dest, &data->in[data->mp3.in_pos], bytes);
data->mp3.in_pos += bytes;
data->in_bytes_left -= bytes;
}
return bytes;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int MP3_transfer ( AUD_DRV_CHAN *ci, uint8 *dst, int *dst_bytes, uint8 *src, int *src_bytes )
{
int sbytes = 0;
int dbytes = 0;
int stransfered = 0;
int dtransfered = 0;
int bytes;
//int done = FALSE;
TRANSFER *data = &ci->transfer_data;
if ( !data->in )
{
return FALSE;
}
if ( src_bytes )
{
sbytes = *src_bytes;
}
if ( dst_bytes )
{
dbytes = *dst_bytes;
*dst_bytes = 0;
}
// make sure existing in data is at the start of the buffer
if ( data->in_bytes_left && data->mp3.in_pos > 0 )
{
//copy the data down to the start of the buffer
memcpy ( data->in, &data->in[data->mp3.in_pos], data->in_bytes_left );
}
data->mp3.in_pos = 0;
// make sure the in buffer is full
if ( data->in_bytes_left < data->in_size_needed && src )
{
// ok, read in as much as we can
if ( (bytes = data->in_size_needed - data->in_bytes_left ) > sbytes )
{
bytes = sbytes;
}
if ( bytes == 0 )
{
// no more data to read at this time
return TRUE;
}
memcpy ( &data->in[ data->in_bytes_left], src, bytes );
src += bytes;
stransfered+= bytes;
sbytes -= bytes;
data->in_bytes_left += bytes;
data->pending = TRUE;
if ( data->in_bytes_left < data->in_size_needed )
{
return TRUE; // not enough data to continue
}
}
if ( !data->mp3.asi )
{
// start MP3 streamer
if ( ! ( data->mp3.asi = (ASISTREAM *) ASI_stream_open ( (U32) data, fetch_CB, 0 )))
{
// bad MP3 stream
return FALSE;
}
}
// decode more of the stream
if ( dbytes > 16*1024)
{
dbytes = 16*1024;
}
dtransfered = ASI_stream_process ( (HASISTREAM) data->mp3.asi, dst, dbytes );
data->pending = data->in_bytes_left || (data->mp3.asi->output_cursor < data->mp3.asi->frame_size);
if ( src_bytes )
{
*src_bytes = stransfered;
}
if ( dst_bytes )
{
*dst_bytes = dtransfered;
}
return dtransfered>0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int AUD_service_device ( AUD_Thread *thread, void *data )
{
AudioChannel *chan;
AudioSearch sh;
AudioDevice *dev = (AudioDevice*) data;
if ( !dev )
{
return TRUE;
}
#ifdef _DEBUG
if ( mixer_skip )
{
mixer_skip--;
if ( mixer_skip < 0 )
{
mixer_skip = 0;
}
return TRUE;
}
#endif
if ( NotLocked ( &dev->channelAccess) )
{
TimeStamp now = AudioGetTime () ;
AudioServicePerform ( &dev->mixerUpdate, now );
chan = AudioDeviceFirstChannel ( dev, &sh );
while ( chan )
{
if ( chan->Control.Status & mAUDIO_CTRL_PLAYING )
{
if ( audioCheck ( chan ))
{
chan->drvData->poll_interval = now - chan->drvData->last_poll;
chan->drvData->last_poll = now;
AUD_update_buffer ( chan->drvData );
}
else
{
AUD_channel_stop ( chan );
}
}
chan = AudioDeviceNextChannel ( &sh );
}
AudioDeviceService ( dev );
return TRUE;
}
return FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int AUD_same_format ( AudioFormat *f1, AudioFormat *f2 )
{
return ( f1->Channels == f2->Channels
&& f1->Rate == f2->Rate
&& f1->SampleWidth == f2->SampleWidth );
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void AUD_set_wave_format ( WAVEFORMATEX *pcmwf, AudioFormat *format )
{
memset ( pcmwf, 0, sizeof(WAVEFORMATEX) );
pcmwf->wFormatTag = WAVE_FORMAT_PCM;
pcmwf->nChannels = (unsigned short ) format->Channels;
pcmwf->nSamplesPerSec = format->Rate;
pcmwf->nBlockAlign = (unsigned short ) format->Channels*format->SampleWidth;
pcmwf->nAvgBytesPerSec = pcmwf->nSamplesPerSec * pcmwf->nBlockAlign;
pcmwf->wBitsPerSample = (unsigned short ) format->SampleWidth<<3;
pcmwf->cbSize = 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int AUD_create_pcm_buffer ( AUD_DRV_CHAN *ci, AudioFormat *format )
{
DSBUFFERDESC desc;
WAVEFORMATEX pcmwf;
DBG_Function ("DSND2:AUD_create_pcm_buffer");
AudioChannelSetFormat ( ci->chan, format );
if ( ci->dsbuf )
{
if ( AUD_same_format ( &ci->format, format ) )
{
ci->chan->drv_format_changed = FALSE;
/* we already have a buffer of the required format */
return NO_ERROR;
}
ci->chan->drv_format_changed = TRUE;
}
/* we need to recreate the buffer */
AUD_destroy_pcm_buffer ( ci );
// Set up wave format structure.
AUD_set_wave_format ( &pcmwf, format );
ci->format = *format;
// Set up DSBUFFERDESC structure.
memset ( &desc, 0, sizeof(DSBUFFERDESC) );
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = (DSBCAPS_CTRLVOLUME |DSBCAPS_CTRLPAN|DSBCAPS_CTRLFREQUENCY) ; // Need default controls vol, pan, pitch
/* now we work out the size that the buffer needs to be. This is based on the polling interval and
the number of frames.
A frame size is the that size that when played back will take X polling intervals ( over sampling). This ensures that
the polling code will not miss any frames. A frame is also the minimum update size.
frame size = ((bytes per second) * (poll interval per second) * over sample
*/
ci->frame_bytes = ((pcmwf.nAvgBytesPerSec * (vAUD_DRV_POLL_INTERVAL/10) )/(TIMER_HZ/10)) * vAUD_DRV_OVER_SAMPLE;
ci->frame_bytes = ((ci->frame_bytes+1024-1)/1024) * 1024;
// DBGPRINTF (("Sound frame size = %d bytes\n", ci->frame_bytes ));
desc.dwBufferBytes = vAUD_DRV_FRAMES * ci->frame_bytes;
desc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
// Create buffer.
if ( AUD_sound_object->CreateSoundBuffer ( &desc, &ci->dsbuf, NULL) != DS_OK)
{
ci->dsbuf = NULL ;
return ERROR_CODE_FAIL;
}
#ifdef _DEBUG
DSBCAPS caps = {0};
caps.dwSize = sizeof(caps);
if ( ci->dsbuf->GetCaps ( &caps ) != DS_OK )
{
msg_assert ( FALSE, ("Error trying to get caps of secondary buffer"));
}
msg_assert ( caps.dwBufferBytes == desc.dwBufferBytes , ("Error: Asked for buffer of %d bytes, but got %d bytes", desc.dwBufferBytes, caps.dwBufferBytes ));
#endif
ci->buf_size = desc.dwBufferBytes;
// DBGPRINTF (("Sound buffer size = %d bytes\n", ci->buf_size ));
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void AUD_destroy_pcm_buffer ( AUD_DRV_CHAN *ci )
{
if ( ci->dsbuf )
{
ci->dsbuf->Release ();
ci->dsbuf = NULL;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int AUD_restore_primary ( void )
{
if ( AUD_primary_buffer->Restore ( ) == DS_OK )
{
return TRUE;
}
return FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int AUD_restore_buffer ( AUD_DRV_CHAN *ci )
{
if ( ci->dsbuf->Restore () == DS_OK )
{
AUD_init_buffer ( ci );
return TRUE;
}
return FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static uint AUD_get_current_frame ( AUD_DRV_CHAN *ci )
{
ulong pos;
ci->dsbuf->GetCurrentPosition ( &pos, (ulong*) &ci->write_pos );
return pos / ci->frame_bytes;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void AUD_init_buffer ( AUD_DRV_CHAN *ci )
{
ci->src_left = 0;
ci->pcm_pos = 0;
ci->silence_left = ci->frame_bytes*4;
ci->dsbuf->SetCurrentPosition ( 0 );
ci->frame = AUD_get_current_frame ( ci );
ci->last_poll = AudioGetTime ( );
ci->min_fill = ci->buf_size;
ci->dsbuf->GetCurrentPosition ( (ulong*) &ci->dst_pos, (ulong*) &ci->write_pos );
ci->dst_left = ci->buf_size - ci->dst_pos;
ci->service_count = 0;
ci->original_frame = ci->frame;
ci->frames_played = 0;
ci->original_dst_pos = ci->dst_pos;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void AUD_reset_buffer ( AUD_DRV_CHAN *ci )
{
ci->dst_pos = 0;
ci->dst_left = ci->buf_size;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void AUD_set_buffer_src ( AUD_DRV_CHAN *ci )
{
ci->next_frame_called = FALSE;
ci->src_pos = (uint8 *) ci->chan->frameData;
ci->src_start = (uint8 *) ci->chan->frameData;
ci->src_left = ci->src_size = ci->chan->bytesInFrame;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void AUD_new_buffer_src ( AUD_DRV_CHAN *ci )
{
AudioSample *sample = ci->chan->sample;
AudioFormat *format;
TRANSFER *data = &ci->transfer_data;
if ( !sample )
{
return;
}
if ( !( format = ci->chan->sample->Format ) )
{
format = &ci->chan->Device->DefaultFormat;
}
#ifdef _DEBUG
msg_assert ( AUD_same_format ( &ci->format, format ), ("Sample \"%s\" has incompatible format with the previous sample.\nCannot seamlessly merge samples of differing playback parameters!!", ci->chan->sample_name));
#endif //_DEBUG
data->out_bytes_left = 0;
data->in_bytes_left = 0;
data->in_bytes = 0;
data->state = STATE_INIT_BLOCK;
data->in_size_needed = 0;
data->out_size_needed = 0;
data->channels = format->Channels;
data->pending = FALSE;
if ( data->mp3.asi )
{
ASI_stream_close ( (HASISTREAM) data->mp3.asi );
}
data->mp3.asi = NULL;
switch ( format->Compression )
{
case AUDIO_COMPRESS_NONE :
ci->transfer = PCM_transfer;
break;
case AUDIO_COMPRESS_IMA_ADPCM:
ci->transfer = ADPCM_transfer;
data->decode = IMA_decode_block;
data->block_size = format->cdata.adpcm.BlockSize;
data->in_size_needed = data->block_size;
data->out_size_needed = (data->block_size+32)*4;
break;
case AUDIO_COMPRESS_MS_ADPCM:
ci->transfer = ADPCM_transfer;
data->decode = MS_decode_block;
data->block_size = format->cdata.adpcm.BlockSize;
data->in_size_needed = data->block_size;
data->out_size_needed = (data->block_size+32)*4;
break;
case AUDIO_COMPRESS_MP3:
ci->transfer = MP3_transfer;
data->in_size_needed = STREAM_BUFSIZE + 1024;
data->mp3.in_pos = 0;
data->in_bytes = data->in_size_needed;
break;
#ifdef _DEBUG
default:
ci->transfer = NULL_transfer;
msg_assert ( FALSE , ("Unknown compression format"));
break;
#endif
}
if ( data->in_size < data->in_size_needed )
{
if ( data->in )
{
free ( data->in );
}
data->in = (uint8*) malloc ( data->in_size_needed );
msg_assert ( data->in, ("Failed to create new in buffer"));
if ( !data->in )
{
data->in_size = 0;
}
else
{
data->in_size = data->in_size_needed;
}
}
if ( data->out_size < data->out_size_needed )
{
if ( data->out )
{
free ( data->out );
}
data->out = (uint8*) malloc ( data->out_size_needed );
msg_assert ( data->out, ("Failed to create new out buffer"));
if ( !data->out )
{
data->out_size = 0;
}
else
{
data->out_size = data->out_size_needed;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int AUD_lock_buffer ( AUD_DRV_CHAN *ci, ulong pos, ulong transfer, uint8 **dst, ulong *bytes1, uint8 **dst2, ulong *bytes2 )
{
int retval;
int retry_count = 3;
retry:
if ( (retval = ci->dsbuf->Lock ( pos, transfer, (void **) dst, bytes1, (void **)dst2, bytes2, 0)) != DS_OK )
{
if ( retval = DSERR_BUFFERLOST )
{
if ( !AUD_restore_buffer ( ci ) )
{
DBGPRINTF (("Failed to restore buffer\n"));
return FALSE;
}
DBGPRINTF (("Restored buffer\n"));
if ( retry_count-- )
{
goto retry;
}
}
DBGPRINTF (("Failed to lock buffer\n"));
return FALSE;
}
return TRUE;
}
static int AUD_fill_buffer ( AUD_DRV_CHAN *ci, int total_bytes )
{
ulong transfer, bytes_transfer;
ulong bytes;
uint8 *lpos1 = NULL,*lpos2=NULL;
ulong lbytes1 = 0,lbytes2 = 0;
uint8 *dst;
int locked =FALSE;
int result = FALSE;
DBG_Function ("DSND2:AUD_fill_buffer");
while ( total_bytes > 0 )
{
/* check for buffer wrap roun */
if ( ci->dst_left <= 0)
{
AUD_reset_buffer ( ci );
}
bytes_transfer = 0;
/* check for buffer overflow and adjust transfer accordingly */
if ( (bytes = (ulong) total_bytes) > (ulong ) ci->dst_left )
{
bytes = ci->dst_left;
}
if ( locked )
{
ci->dsbuf->Unlock ( lpos1, lbytes1, lpos2, lbytes2 );
locked = FALSE;
}
if ( !AUD_lock_buffer ( ci, ci->dst_pos, bytes, &lpos1, &lbytes1, &lpos2, &lbytes2) )
{
goto done;
}
locked = TRUE;
if ( (lpos2 != NULL) || (lbytes1 != bytes) || (lbytes2 != 0))
{
DBGPRINTF (("Buffer management out by %d bytes\n", lbytes2));
goto done;
}
dst = lpos1;
while ( bytes )
{
transfer = bytes;
/* is there any source data left */
if ( ci->src_left == 0 )
{
/* call up to higher level to see if more source is going to be supplied */
ci->src_left = -1; /* unless the call back adds new source we assume no more data */
if ( !ci->next_frame_called )
{
ci->chan->drvCBNextFrame ( ci->chan );
ci->next_frame_called = TRUE;
}
if ( !ci->chan->bytesInFrame )
{
// no more data for this sample
if ( !ci->transfer_data.pending)
{
ci->chan->drvCBNextSample ( ci->chan );
if ( ci->chan->bytesInFrame )
{
// we have a new sample
AUD_new_buffer_src ( ci );
}
}
}
if ( ci->chan->bytesInFrame )
{
AUD_set_buffer_src ( ci );
}
if ( ci->src_left == 0 )
{
ci->src_left = -1;
}
}
if ( ci->src_left <= 0 && !ci->transfer_data.pending )
{
if ( ci->chan->current_format.SampleWidth == 2)
{
memset ( dst, 0x0000, transfer );
}
else
{
memset ( dst, 0x80, transfer );
}
ci->silence_left -= transfer ;
}
else
{
int src_bytes;
if ( (src_bytes = ci->src_left) <= 0 )
{
// flush buffered transfer
if ( !ci->transfer ( ci, dst, (int*) &transfer, NULL, NULL ) )
{
goto done;
}
src_bytes = 0;
ci->src_left = 0; // try to read more source
}
else
{
if ( !ci->transfer ( ci, dst, (int*) &transfer, (uint8 *)ci->src_pos, &src_bytes ) )
{
goto done;
}
}
ci->src_pos+= src_bytes;
ci->src_left -= src_bytes;
ci->pcm_pos += transfer;
}
dst += transfer;
ci->dst_pos += transfer;
ci->dst_left -= transfer;
bytes -= transfer;
bytes_transfer += transfer;
}
total_bytes -= bytes_transfer;
}
result = TRUE;
done:
if ( locked )
{
ci->dsbuf->Unlock ( lpos1, lbytes1, lpos2, lbytes2 );
locked = FALSE;
}
return result;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void AUD_update_buffer ( AUD_DRV_CHAN *ci )
{
uint frame;
int dif;
int filled;
frame = AUD_get_current_frame ( ci );
if ( ( dif = (frame - ci->frame )))
{
/* pcm address has moved on */
if ( dif < 0 )
{
/* pcm has wraped round to start of buffer */
dif = frame + (vAUD_DRV_FRAMES - ci->frame);
}
if ( (dif -= vAUD_DRV_LAG_FRAMES ) > 0 )
{
if ( (ci->frame += dif) >= vAUD_DRV_FRAMES)
{
ci->frame %= vAUD_DRV_FRAMES;
}
ci->frames_played += dif;
dif = dif * ci->frame_bytes;
filled = AUD_fill_buffer ( ci, dif );
if ( ci->min_fill > dif )
{
ci->min_fill = dif;
}
if ( (ci->pcm_pos -= dif) <=0 || !filled )
{
ci->dsbuf->Stop();
}
}
}
ci->service_count++;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void AUD_channel_stop ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci;
ci = (AUD_DRV_CHAN *) chan->drvData;
/* put stop call here */
if ( ci->dsbuf )
{
ci->dsbuf->Stop();
}
if ( chan->drvCBSampleDone )
{
chan->drvCBSampleDone ( chan );
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioLoad ( AudioSystem *system )
{
int result;
ASI_startup();
result = DirectSoundCreate(NULL, &AUD_sound_object, NULL);
if (result != DS_OK)
{
//Print_Sound_Error(Fetch_String(TXT_DSOUND_NO_COOP) );
return ERROR_CODE_FAIL;
}
assert ( AUD_sound_object );
result = AUD_sound_object->SetCooperativeLevel( AudioGetWindowsHandle () , DSSCL_PRIORITY);
system->numUnits = 1; /* tell WCORE that there is only 1 audio unit */
if ( !thread )
{
LockInit ( &drv_ref );
if (!(thread = AUD_ThreadCreate ( "Audio Service Thread", AUD_THREAD_PRI_REALTIME, AUD_service_device )) )
{
return ERROR_CODE_FAIL;
}
LockAcquire ( &drv_ref );
AUD_ThreadSetInterval ( thread, vAUD_DRV_POLL_INTERVAL );
}
else
{
LockAcquire ( &drv_ref );
}
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void audioUnload ( AudioSystem * )
{
/* put in code to uninitialize audio system */
if ( AUD_sound_object )
{
AUD_sound_object->Release();
AUD_sound_object = NULL ;
}
if ( thread )
{
LockRelease ( &drv_ref );
if ( NotLocked ( &drv_ref ))
{
AUD_ThreadDestroy ( thread );
thread = NULL;
}
}
ASI_shutdown ();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioOpen ( AudioDevice *dev )
{
/* put in code to instance a device */
dev->maxChannels = vAUD_DRV_MAX_CHANNELS;
AudioServiceSetInterval ( &dev->mixerUpdate, vAUD_DRV_POLL_INTERVAL );
AudioServiceSetMustServiceInterval ( &dev->mixerUpdate, vAUD_DRV_POLL_INTERVAL * vAUD_DRV_OVER_SAMPLE * vAUD_DRV_FRAMES );
AudioServiceSetResetInterval ( &dev->mixerUpdate, vAUD_DRV_POLL_INTERVAL * vAUD_DRV_OVER_SAMPLE * vAUD_DRV_FRAMES * 3 );
AUD_ThreadSetData ( thread, dev);
/* start the primary buffer playing */
{
DSBUFFERDESC dsbdesc;
WAVEFORMATEX pcmwf;
AUD_set_wave_format ( &pcmwf, &dev->DefaultFormat );
memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); // Zero it out.
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbdesc.dwBufferBytes = 0;
if ( AUD_sound_object->CreateSoundBuffer ( &dsbdesc, &AUD_primary_buffer, NULL) != DS_OK )
{
AUD_primary_buffer = NULL;
return ERROR_CODE_FAIL;
}
if ( AUD_primary_buffer->SetFormat ( &pcmwf ) != DS_OK )
{
DBGPRINTF (("Unable to set the desired primary format\n"));
}
DSCAPS caps = {0};
caps.dwSize = sizeof( caps );
AUD_sound_object->GetCaps ( &caps );
if (caps.dwFlags & DSCAPS_EMULDRIVER)
{
//msg_assert ( FALSE, ("Audio Emulation"));
}
AUD_primary_buffer->Play ( 0, 0, DSBPLAY_LOOPING );
}
dev->frames = vAUD_DRV_FRAMES;
dev->over_sample = vAUD_DRV_OVER_SAMPLE;
dev->frame_lag = vAUD_DRV_LAG_FRAMES;
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void audioClose ( AudioDevice * )
{
/* put in code to close up a device */
AUD_ThreadSetData ( thread, NULL );
if ( AUD_primary_buffer )
{
AUD_primary_buffer->Stop ();
AUD_primary_buffer->Release ();
AUD_primary_buffer = NULL;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioOpenChannel ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci;
DBG_Function ("DSND2:audioOpenChannel");
/* put in code to instance a channel */
MEM_ALLOC_STRUCT ( ci, AUD_DRV_CHAN, vMEM_ANY|mMEM_INTERNAL );
chan->drvData = ci;
ci->chan = chan;
chan->time_min_frame = vAUD_DRV_POLL_INTERVAL * vAUD_DRV_OVER_SAMPLE;
chan->time_buffer = vAUD_DRV_FRAMES * chan->time_min_frame;
transfer_init ( &ci->transfer_data );
AudioChannelSetFormat ( ci->chan, &ci->chan->Device->DefaultFormat ); /* initially create sound buffer with default format */
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioCloseChannel ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci;
DBG_Function ("DSND2:audioCloseChannel");
/* put in code to close a channel*/
if ( (ci = chan->drvData) )
{
transfer_deinit ( &ci->transfer_data );
AUD_destroy_pcm_buffer ( ci );
MEM_Free ( ci );
chan->drvData = NULL;
}
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioStart ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci;
AudioFormat *format;
AudioSample *sample;
DBG_Function ("DSND2:audioStart");
DBG_ASSERT ( !audioCheck ( chan ));
ci = chan->drvData;
sample = chan->sample;
if ( !( format = sample->Format ) )
{
format = &chan->Device->DefaultFormat;
}
/* recreate buffer if format changed */
if ( AUD_create_pcm_buffer ( ci, format ) != vNO_ERROR)
{
DBGPRINTF (("Failed to create playback buffer\n"));
return ERROR_CODE_FAIL;
}
AUD_init_buffer ( ci ); /* initialise buffer for play back */
AUD_new_buffer_src ( ci );
AUD_set_buffer_src ( ci ); /* attach source to buffer */
if ( !AUD_fill_buffer ( ci, ci->buf_size )) /* pre-fill buffer to max */
{
DBGPRINTF (( "Failed to prefill buffer\n"));
return ERROR_CODE_FAIL;
}
/* update buffer attributes */
audioUpdate ( chan );
/* start playing */
if ( ci->dsbuf->Play ( 0, 0, DSBPLAY_LOOPING ) != DS_OK) /* must do looping or things will go horribly wrong */
{
DBGPRINTF (("Failed to start playback\n"));
return ERROR_CODE_FAIL;
}
DBG_ASSERT ( audioCheck ( chan ));
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioStop ( AudioChannel *chan )
{
if ( chan->Control.Status & (mAUDIO_CTRL_ACTIVE) )
{
AUD_channel_stop ( chan );
}
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioPause ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci = chan->drvData;
if ( ci->dsbuf )
{
ci->dsbuf->Stop ();
}
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioResume ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci = chan->drvData;
if ( ci->dsbuf )
{
if ( ci->dsbuf->Play ( 0, 0, DSBPLAY_LOOPING ) == DS_OK) /* must do looping or things will go horribly wrong */
{
return vNO_ERROR;
}
}
return ERROR_CODE_FAIL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioCheck ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci = chan->drvData;
DWORD status;
if ( ci->dsbuf && (ci->dsbuf->GetStatus ( &status ) == DS_OK) )
{
if ( status & (DSBSTATUS_PLAYING|DSBSTATUS_LOOPING) == (DSBSTATUS_PLAYING|DSBSTATUS_LOOPING) )
{
return TRUE;
}
else
{
return FALSE;
}
}
return FALSE;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioUpdate ( AudioChannel *chan )
{
AUD_DRV_CHAN *ci;
int vol;
int pitch;
HRESULT stat;
ci = chan->drvData;
vol = AudioLevelApply ( &chan->attribs.VolumeLevel, 100 );
ci->dsbuf->SetVolume ( AUD_log_table[vol]);
#if 1
{
int pos;
pos = AudioLevelApply ( &chan->attribs.PanPosition, 200 ) - 100;
if ( pos < 0 )
{
stat = ci->dsbuf->SetPan ( AUD_log_table[100+pos]);
}
else
{
stat = ci->dsbuf->SetPan ( -AUD_log_table[100-pos]);
}
}
#endif
pitch = AudioAttribsCalcPitch ( &chan->attribs, ci->chan->current_format.Rate );
ci->dsbuf->SetFrequency ( pitch);
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioQueueIt ( AudioChannel *)
{
return ERROR_CODE_FAIL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioLock ( AudioChannel *)
{
AUD_ThreadBeginCriticalSection ( thread );
return vNO_ERROR;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int audioUnlock ( AudioChannel *)
{
AUD_ThreadEndCriticalSection ( thread );
return vNO_ERROR;
}
/* command functions */
#if 0
static int AUD_set_dev_format ( AudioDevice *, AudioFormat *format )
{
WAVEFORMATEX wf;
int err = ERROR_CODE_FAIL;
if ( AUD_primary_buffer )
{
// Set up wave format structure.
AUD_primary_buffer->Stop ();
AUD_set_wave_format ( &wf, format );
if ( AUD_primary_buffer->SetFormat ( &wf ) == DS_OK )
{
err = vNO_ERROR;
}
AUD_primary_buffer->Play ( 0, 0, DSBPLAY_LOOPING );
}
return err;
}
#endif
/*****************************************************************************
** Public Functions **
*****************************************************************************/
/* :NOTICE:
There are no public functions in a driver. If you have any then you are doing
something wrong. -Tr
*/
void *GetDirectSoundObject ( void )
{
return (void *) AUD_sound_object;
}
void *GetPrimaryBuffer ( void )
{
return (void *) AUD_primary_buffer ;
}
void AudioLoseFocus ( void )
{
if ( AUD_primary_buffer )
{
AUD_primary_buffer->Stop ();
}
}
void AudioRegainFocus ( void )
{
if ( AUD_primary_buffer )
{
DWORD status;
AUD_primary_buffer->GetStatus ( &status );
if ( (status & DSBSTATUS_BUFFERLOST) )
{
AUD_restore_primary ( );
}
if ( !(status & DSBSTATUS_PLAYING) )
{
AUD_primary_buffer->Play ( 0, 0, DSBPLAY_LOOPING );
}
}
}
/* debug function. Not part of API */
#ifdef _DEBUG
void AudioCauseMixerDelay ( void )
{
mixer_skip = (vAUD_DRV_OVER_SAMPLE * vAUD_DRV_FRAMES -1) * 2;
}
#include
void AudioDeviceDumpDXSNDDriver ( AudioDevice *dev, void (*print) ( const char *) )
{
char string[200];
AudioChannel *chan;
AudioSearch sh;
DBG_Function ("AudioDeviceDumpChannels");
DBG_ASSERT_TYPE ( dev, AudioDevice );
LockAcquire ( &dev->channelAccess);
sprintf ( string, " \nDXSND Channels\n");
print ( string );
chan = AudioDeviceFirstChannel ( dev, &sh );
while (chan)
{
sprintf ( string, "Channel 0x%0x, F=%6d, P=%d, I=%04d, T=%6d, B=%6d\n",
chan,
chan->drvData->frame_bytes,
chan->drvData->frame,
chan->drvData->poll_interval,
chan->drvData->min_fill,
chan->drvData->buf_size
);
print ( string );
chan = AudioDeviceNextChannel( &sh );
}
LockRelease ( &dev->channelAccess);
}
#endif
// IMA ADPCM Support
/*
*
* Lookup tables for IMA ADPCM format
*
*/
static int imaIndexAdjustTable[16] = {
-1, -1, -1, -1, /* +0 - +3, decrease the step size */
2, 4, 6, 8, /* +4 - +7, increase the step size */
-1, -1, -1, -1, /* -0 - -3, decrease the step size */
2, 4, 6, 8, /* -4 - -7, increase the step size */
};
static int imaStepSizeTable[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34,
37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494,
544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552,
1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026,
4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623,
27086, 29794, 32767
};
/******************************************************************/
/* */
/* */
/******************************************************************/
/****************************************************************************/
/* IMA ADPCM Support Functions Section */
/****************************************************************************/
/*
*
* MsAdpcmDecode - Decode a given sample and update state tables
*
*/
static short ImaAdpcmDecode( uint8 deltaCode, IMA_DATA *state )
{
/* Get the current step size */
int step;
int difference;
step = imaStepSizeTable[state->index];
/* Construct the difference by scaling the current step size */
/* This is approximately: difference = (deltaCode+.5)*step/4 */
difference = step>>3;
if ( deltaCode & 1 ) difference += step>>2;
if ( deltaCode & 2 ) difference += step>>1;
if ( deltaCode & 4 ) difference += step;
if ( deltaCode & 8 ) difference = -difference;
/* Build the new sample */
state->previousValue += difference;
if (state->previousValue > 32767) state->previousValue = 32767;
else if (state->previousValue < -32768) state->previousValue = -32768;
/* Update the step for the next sample */
state->index += imaIndexAdjustTable[deltaCode];
if (state->index < 0) state->index = 0;
else if (state->index > 88) state->index = 88;
return (short) state->previousValue;
}
static int IMA_decode_block ( AUD_DRV_CHAN *ci )
{
int i,j;
TRANSFER *data = &ci->transfer_data;
int bytes = data->in_bytes - data->in_bytes_left;
uint8 *in = data->in;
// set up output
data->out_bytes = 0;
ushort *out = (ushort *) data->out;
if ( bytes < data->channels*4 )
{
return FALSE;
}
for ( i=0; i < data->channels; i++)
{
data->ima[i].previousValue = (int) (in[1] <<8) + (int) in[0];
if (data->ima[i].previousValue & 0x8000)
data->ima[i].previousValue -= 0x10000;
if ((data->ima[i].index = in[2]) > 88)
{
msg_assert(FALSE, ("IMA ADPCM Format Error (bad index value) in wav file %s",ci->chan->sample_name));
return FALSE;
}
if (in[3])
{
msg_assert (FALSE, ("IMA ADPCM Format Error (synchronization error) in wav file %s ", ci->chan->sample_name));
return FALSE;
}
in+=4;
bytes -=4;
*out++ = (unsigned short ) data->ima[i].previousValue;
data->out_bytes += 2;
}
if ( data->channels == 1 )
{
// 99.99% of the samples being played are mono
while ( bytes > 0 )
{
ushort *out = (ushort *) &data->out[data->out_bytes];
j = 4;
while ( j-- )
{
uint8 b = *in++;
*out++ = (ushort ) ImaAdpcmDecode(b & 0x0f,&data->ima[0]);
*out++ = (ushort ) ImaAdpcmDecode((b>>4) & 0x0f,&data->ima[0]);
}
data->out_bytes += 16;
bytes -= 4;
}
}
else
{
while ( bytes > 0 )
{
for ( i = 0; i < data->channels ; i++)
{
short *out = (short *) &data->out[data->out_bytes+(i<<1)];
for (j=0;j<4;j++)
{
uint8 b = *in++;
*out = ImaAdpcmDecode(b & 0x0f,&data->ima[i]);
out += data->channels;
*out = ImaAdpcmDecode((b>>4) & 0x0f,&data->ima[i]);
out += data->channels;
}
}
data->out_bytes += data->channels<<4;
bytes -= data->channels<<2;
}
}
if ( bytes != 0 )
{
msg_assert ( FALSE, ("bad block"));
return FALSE;
}
return TRUE;
}
/****************************************************************************/
/* Microsoft ADPCM Support Functions Section */
/****************************************************************************/
static const int adaptionTable[] = {
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
static short MSAdpcmDecode( unsigned char nibble, MS_DATA *ms, int sample1, int sample2)
{
int newsample;
int predsample;
predsample = ((sample1 * ms->iCoef[0]) + ( sample2 * ms->iCoef[1]))>>8;
newsample = predsample + (ms->idelta * (nibble - ((nibble&0x08) << 1)));
ms->idelta = (ms->idelta * adaptionTable[nibble])>>8;
if ( ms->idelta < 16 )
{
ms->idelta = 16;
}
if (newsample > 0x7fff)
{
newsample = 0x7fff;
}
else if (newsample < -0x8000)
{
newsample = -0x8000;
}
return (short) newsample;
}
static int MS_decode_block ( AUD_DRV_CHAN *ci )
{
int i;
TRANSFER *data = &ci->transfer_data;
int bytes = data->in_bytes - data->in_bytes_left;
uint8 *in = data->in;
unsigned char bpredictor;
int hsize = data->channels*(1+2+2+2);
int channels = data->channels;
int hi,lo;
// set up output
data->out_bytes = 0;
short *out = (short *) data->out;
if ( (bytes -= hsize) < 0 )
{
return FALSE;
}
for ( i=0; i < channels; i++)
{
bpredictor = *in++;
if (bpredictor >= 7) {
msg_assert(FALSE, ("MS-ADPCM bpredictor out of range"));
return FALSE;
}
data->ms[i].iCoef[0] = MSADPCM_StdCoef[bpredictor][0];
data->ms[i].iCoef[1] = MSADPCM_StdCoef[bpredictor][1];
}
for ( i=0; i < channels; i++)
{
lo = *in++;
hi = *in++;
data->ms[i].idelta = (hi<<8) | lo;
}
for ( i=0; i < channels; i++)
{
lo = *in++;
hi = *in++;
out[channels+i] = (hi<<8) | lo;
}
for ( i=0; i < channels; i++)
{
lo = *in++;
hi = *in++;
out[i] = (hi<<8) | lo;
}
/* already have 1st 2 samples from block-header */
out += 2*data->channels;
data->out_bytes += 4*data->channels;
if ( channels == 1 )
{
unsigned char byte;
while ( bytes )
{
byte = *in++;
*out++ = MSAdpcmDecode(byte >> 4, &data->ms[0], out[-1], out[-2]);
*out++ = MSAdpcmDecode(byte&0x0f, &data->ms[0], out[-1], out[-2]);
bytes--;
data->out_bytes += 4;
}
}
else
{
int chan = 0;
unsigned char byte;
int channels2 = 2*channels;
int out_bytes = 4*channels;
while ( bytes )
{
byte = *in++;
*out++ = MSAdpcmDecode(byte >> 4, &data->ms[chan], out[-channels], out[-channels2]);
if (++chan == channels )
{
chan = 0;
}
*out++ = MSAdpcmDecode(byte&0x0f, &data->ms[chan], out[-channels], out[-channels2]);
if (++chan == channels)
{
chan = 0;
}
bytes--;
data->out_bytes += out_bytes;;
}
}
return TRUE;
}
// Stubs
DXDEC void FAR * AILCALL AIL_mem_alloc_lock(U32 size)
{
return AudioMemAlloc ( size );
}
DXDEC void AILCALL AIL_mem_free_lock (void FAR *ptr)
{
AudioMemFree ( ptr );
}
DXDEC U32 AILCALL AIL_MMX_available (void)
{
return 0;
}