📄 adpcm.c
字号:
/*
* streaming ADPCM driver
* by Aaron Giles
*
* Library to transcode from an ADPCM source to raw PCM.
* Written by Buffoni Mirko in 08/06/97
* References: various sources and documents.
*
* HJB 08/31/98
* modified to use an automatically selected oversampling factor
* for the current Machine->sample_rate
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "driver.h"
#include "adpcm.h"
#define OVERSAMPLING 0 /* breaks 10 Yard Fight */
/* signed/unsigned 8-bit conversion macros */
#ifdef SIGNED_SAMPLES
#define AUDIO_CONV(A) ((A))
#else
#define AUDIO_CONV(A) ((A)+0x80)
#endif
/* struct describing a single playing ADPCM voice */
struct ADPCMVoice
{
int playing; /* 1 if we are actively playing */
int channel; /* which channel are we playing on? */
unsigned char *base; /* pointer to the base memory location */
unsigned char *stream; /* input stream data (if any) */
void *buffer; /* output buffer (could be 8 or 16 bits) */
int bufpos; /* current position in the buffer */
int mask; /* mask to keep us within the buffer */
int sample; /* current sample number */
int count; /* total samples to play */
int signal; /* current ADPCM signal */
int step; /* current ADPCM step */
int volume; /* output volume */
};
/* global pointer to the current interface */
static struct ADPCMinterface *adpcm_intf;
/* global pointer to the current array of samples */
static struct ADPCMsample *sample_list;
/* array of ADPCM voices */
static struct ADPCMVoice adpcm[MAX_ADPCM];
/* sound channel info */
static int channel;
/* global buffer length and emulation output rate */
static int buffer_len;
static int emulation_rate;
#if OVERSAMPLING
static int oversampling;
#endif
/* step size index shift table */
static int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
/* lookup table for the precomputed difference */
static int diff_lookup[49*16];
/*
* Compute the difference table
*/
static void ComputeTables (void)
{
/* nibble to bit map */
static int nbl2bit[16][4] =
{
{ 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
{ 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
{-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
{-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
};
int step, nib;
/* loop over all possible steps */
for (step = 0; step <= 48; step++)
{
/* compute the step value */
int stepval = floor (16.0 * pow (11.0 / 10.0, (double)step));
/* loop over all nibbles and compute the difference */
for (nib = 0; nib < 16; nib++)
{
diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
(stepval * nbl2bit[nib][1] +
stepval/2 * nbl2bit[nib][2] +
stepval/4 * nbl2bit[nib][3] +
stepval/8);
}
}
}
/*
* Start emulation of several ADPCM output streams
*/
int ADPCM_sh_start (struct ADPCMinterface *intf)
{
int i;
/* compute the difference tables */
ComputeTables ();
/* copy the interface pointer to a global */
adpcm_intf = intf;
/* set the default sample table */
sample_list = (struct ADPCMsample *)Machine->gamedrv->sound_prom;
/* if there's an init function, call it now to generate the table */
if (intf->init)
{
sample_list = malloc (257 * sizeof (struct ADPCMsample));
if (!sample_list)
return 1;
memset (sample_list, 0, 257 * sizeof (struct ADPCMsample));
(*intf->init) (intf, sample_list, 256);
}
/* reserve sound channels */
channel = get_play_channels (intf->num);
/* compute the emulation rate and buffer size */
buffer_len = intf->frequency / Machine->drv->frames_per_second;
emulation_rate = buffer_len * Machine->drv->frames_per_second;
/* initialize the voices */
memset (adpcm, 0, sizeof (adpcm));
for (i = 0; i < intf->num; i++)
{
adpcm[i].channel = channel + i;
adpcm[i].mask = 0xffffffff;
adpcm[i].signal = -2;
adpcm[i].volume = intf->volume[i];
/* allocate an output buffer */
adpcm[i].buffer = malloc (buffer_len * Machine->sample_bits / 8);
if (!adpcm[i].buffer)
return 1;
}
/* success */
return 0;
}
/*
* Stop emulation of several ADPCM output streams
*/
void ADPCM_sh_stop (void)
{
int i;
/* free the temporary table if we created it */
if (sample_list && sample_list != (struct ADPCMsample *)Machine->gamedrv->sound_prom)
free (sample_list);
sample_list = 0;
/* free any output and streaming buffers */
for (i = 0; i < adpcm_intf->num; i++)
{
if (adpcm[i].stream)
free (adpcm[i].stream);
if (adpcm[i].buffer)
free (adpcm[i].buffer);
}
}
/*
* Update emulation of an ADPCM output stream
*/
static void ADPCM_update (struct ADPCMVoice *voice, int finalpos)
{
int left = finalpos - voice->bufpos;
/* see if there's actually any need to generate samples */
if (left > 0)
{
/* if this voice is active */
if (voice->playing)
{
unsigned char *base = voice->base;
int sample = voice->sample;
int signal = voice->signal;
int count = voice->count;
int step = voice->step;
int mask = voice->mask;
int val;
#if OVERSAMPLING
int oldsignal = signal;
int delta, i;
#endif
/* 16-bit case */
if (Machine->sample_bits == 16)
{
short *buffer = (short *)voice->buffer + voice->bufpos;
while (left)
{
/* compute the new amplitude and update the current step */
val = base[(sample / 2) & mask] >> (((sample & 1) << 2) ^ 4);
signal += diff_lookup[step * 16 + (val & 15)];
if (signal > 2047) signal = 2047;
else if (signal < -2048) signal = -2048;
step += index_shift[val & 7];
if (step > 48) step = 48;
else if (step < 0) step = 0;
#if OVERSAMPLING
/* antialiasing samples */
delta = signal - oldsignal;
for (i = 1; left && i <= oversampling; left--, i++)
*buffer++ = (oldsignal + delta * i / oversampling) * 16;
oldsignal = signal;
#else
*buffer++ = signal * 16;
left--;
#endif
/* next! */
if (++sample > count)
{
/* if we're not streaming, fill with silence and stop */
if (voice->base != voice->stream)
{
while (left--)
*buffer++ = 0;
voice->playing = 0;
}
/* if we are streaming, pad with the last sample */
else
{
short last = buffer[-1];
while (left--)
*buffer++ = last;
}
break;
}
}
}
/* 8-bit case */
else
{
unsigned char *buffer = (unsigned char *)voice->buffer + voice->bufpos;
while (left)
{
/* compute the new amplitude and update the current step */
val = base[(sample / 2) & mask] >> (((sample & 1) << 2) ^ 4);
signal += diff_lookup[step * 16 + (val & 15)];
if (signal > 2047) signal = 2047;
else if (signal < -2048) signal = -2048;
step += index_shift[val & 7];
if (step > 48) step = 48;
else if (step < 0) step = 0;
#if OVERSAMPLING
delta = signal - oldsignal;
for (i = 1; left && i <= oversampling; left--, i++)
*buffer++ = AUDIO_CONV((oldsignal + delta * i / oversampling) / 16);
oldsignal = signal;
#else
*buffer++ = AUDIO_CONV(signal / 16);
left--;
#endif
/* next! */
if (++sample > count)
{
/* if we're not streaming, fill with silence and stop */
if (voice->base != voice->stream)
{
while (left--)
*buffer++ = AUDIO_CONV (0);
voice->playing = 0;
}
/* if we are streaming, pad with the last sample */
else
{
unsigned char last = buffer[-1];
while (left--)
*buffer++ = last;
}
break;
}
}
}
/* update the parameters */
voice->sample = sample;
voice->signal = signal;
voice->step = step;
}
/* voice is not playing */
else
{
if (Machine->sample_bits == 16)
memset ((short *)voice->buffer + voice->bufpos, 0, left * 2);
else
memset ((unsigned char *)voice->buffer + voice->bufpos, AUDIO_CONV (0), left);
}
/* update the final buffer position */
voice->bufpos = finalpos;
}
}
/*
* Update emulation of several ADPCM output streams
*/
void ADPCM_sh_update (void)
{
int i;
/* bail if we're not emulating sound */
if (Machine->sample_rate == 0)
return;
/* loop over voices */
for (i = 0; i < adpcm_intf->num; i++)
{
struct ADPCMVoice *voice = adpcm + i;
/* update to the end of buffer */
ADPCM_update (voice, buffer_len);
/* play the result */
if (Machine->sample_bits == 16)
osd_play_streamed_sample_16 (voice->channel, voice->buffer, 2*buffer_len, emulation_rate, voice->volume,OSD_PAN_CENTER);
else
osd_play_streamed_sample (voice->channel, voice->buffer, buffer_len, emulation_rate, voice->volume,OSD_PAN_CENTER);
/* reset the buffer position */
voice->bufpos = 0;
}
}
/*
* Handle a write to the ADPCM data stream
*/
void ADPCM_trigger (int num, int which)
{
struct ADPCMVoice *voice = adpcm + num;
struct ADPCMsample *sample;
/* bail if we're not playing anything */
if (Machine->sample_rate == 0)
return;
/* range check the numbers */
if (num >= adpcm_intf->num)
{
return;
}
/* find a match */
for (sample = sample_list; sample->length > 0; sample++)
{
if (sample->num == which)
{
/* update the ADPCM voice */
ADPCM_update (voice, cpu_scalebyfcount (buffer_len));
/* set up the voice to play this sample */
voice->playing = 1;
voice->base = &Machine->memory_region[adpcm_intf->region][sample->offset];
voice->sample = 0;
voice->count = sample->length;
/* also reset the ADPCM parameters */
voice->signal = -2;
voice->step = 0;
return;
}
}
}
void ADPCM_play (int num, int offset, int length)
{
struct ADPCMVoice *voice = adpcm + num;
/* bail if we're not playing anything */
if (Machine->sample_rate == 0)
return;
/* range check the numbers */
if (num >= adpcm_intf->num)
{
return;
}
/* update the ADPCM voice */
ADPCM_update (voice, cpu_scalebyfcount (buffer_len));
/* set up the voice to play this sample */
voice->playing = 1;
voice->base = &Machine->memory_region[adpcm_intf->region][offset];
voice->sample = 0;
voice->count = length;
/* also reset the ADPCM parameters */
voice->signal = -2;
voice->step = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -