📄 adpcm.c
字号:
/*
* Stop playback on an ADPCM data channel
*/
void ADPCM_stop (int num)
{
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));
/* stop playback */
voice->playing = 0;
}
/*
* Change volume on an ADPCM data channel
*/
void ADPCM_setvol (int num, int vol)
{
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;
}
voice->volume = vol;
/* update the ADPCM voice */
ADPCM_update(voice, cpu_scalebyfcount (buffer_len));
}
/*
* Stop playback on an ADPCM data channel
*/
int ADPCM_playing (int num)
{
struct ADPCMVoice *voice = adpcm + num;
/* bail if we're not playing anything */
if (Machine->sample_rate == 0)
return 0;
/* range check the numbers */
if (num >= adpcm_intf->num)
{
return 0;
}
/* update the ADPCM voice */
ADPCM_update (voice, cpu_scalebyfcount (buffer_len));
/* return true if we're playing */
return voice->playing;
}
/*
*
* OKIM 6295 ADPCM chip:
*
* Command bytes are sent:
*
* 1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample number to trigger
* abcd ???? = second half of command; one of the abcd bits is set to indicate which voice
* the ???? bits may be a volume or an extension of the xxxxxxx bits above
*
* 0abc d000 = stop playing; one or more of the abcd bits is set to indicate which voice(s)
*
* Status is read:
*
* ???? abcd = one bit per voice, set to 0 if nothing is playing, or 1 if it is active
*
*/
static struct OKIM6295interface *okim6295_interface;
static int okim6295_command[MAX_OKIM6295];
/*
* Start emulation of an OKIM6295-compatible chip
*/
int OKIM6295_sh_start (struct OKIM6295interface *intf)
{
static struct ADPCMinterface generic_interface;
int i;
/* save a global pointer to our interface */
okim6295_interface = intf;
/* create an interface for the generic system here */
generic_interface.num = 4 * intf->num;
generic_interface.frequency = intf->frequency;
generic_interface.region = intf->region;
generic_interface.init = 0;
for (i = 0; i < intf->num; i++)
generic_interface.volume[i*4+0] =
generic_interface.volume[i*4+1] =
generic_interface.volume[i*4+2] =
generic_interface.volume[i*4+3] = intf->volume[i];
/* reset the parameters */
for (i = 0; i < intf->num; i++)
okim6295_command[i] = -1;
/* initialize it in the standard fashion */
return ADPCM_sh_start (&generic_interface);
}
/*
* Stop emulation of an OKIM6295-compatible chip
*/
void OKIM6295_sh_stop (void)
{
/* standard stop */
ADPCM_sh_stop ();
}
/*
* Update emulation of an OKIM6295-compatible chip
*/
void OKIM6295_sh_update (void)
{
/* standard update */
ADPCM_sh_update ();
}
/*
* Read the status port of an OKIM6295-compatible chip
*/
int OKIM6295_status_r (int num)
{
int i, result, buffer_end;
/* range check the numbers */
if (num >= okim6295_interface->num)
{
return 0x0f;
}
/* precompute the end of buffer */
buffer_end = cpu_scalebyfcount (buffer_len);
/* set the bit to 1 if something is playing on a given channel */
result = 0;
for (i = 0; i < 4; i++)
{
struct ADPCMVoice *voice = adpcm + num * 4 + i;
/* update the ADPCM voice */
ADPCM_update (voice, buffer_end);
/* set the bit if it's playing */
if (voice->playing)
result |= 1 << i;
}
return result;
}
/*
* Write to the data port of an OKIM6295-compatible chip
*/
void OKIM6295_data_w (int num, int data)
{
/* range check the numbers */
if (num >= okim6295_interface->num)
{
return;
}
/* if a command is pending, process the second half */
if (okim6295_command[num] != -1)
{
int temp = data >> 4, i, start, stop, buffer_end;
unsigned char *base;
/* determine the start/stop positions */
base = &Machine->memory_region[okim6295_interface->region][okim6295_command[num] * 8];
start = (base[0] << 16) + (base[1] << 8) + base[2];
stop = (base[3] << 16) + (base[4] << 8) + base[5];
/* precompute the end of buffer */
buffer_end = cpu_scalebyfcount (buffer_len);
/* determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte */
for (i = 0; i < 4; i++)
{
if (temp & 1)
{
struct ADPCMVoice *voice = adpcm + num * 4 + i;
/* update the ADPCM voice */
ADPCM_update (voice, buffer_end);
/* set up the voice to play this sample */
voice->playing = 1;
voice->base = &Machine->memory_region[okim6295_interface->region][start];
voice->sample = 0;
voice->count = 2 * (stop - start + 1);
/* also reset the ADPCM parameters */
voice->signal = -2;
voice->step = 0;
}
temp >>= 1;
}
/* reset the command (there may be additional information in the low 4 bits (volume?) */
okim6295_command[num] = -1;
}
/* if this is the start of a command, remember the sample number for next time */
else if (data & 0x80)
{
okim6295_command[num] = data & 0x7f;
}
/* otherwise, see if this is a silence command */
else
{
int temp = data >> 3, i, buffer_end;
/* precompute the end of buffer */
buffer_end = cpu_scalebyfcount (buffer_len);
/* determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command */
for (i = 0; i < 4; i++)
{
if (temp & 1)
{
struct ADPCMVoice *voice = adpcm + num * 4 + i;
/* update the ADPCM voice */
ADPCM_update (voice, buffer_end);
/* stop this guy from playing */
voice->playing = 0;
}
temp >>= 1;
}
}
}
/*
*
* MSM 5205 ADPCM chip:
*
* Data is streamed from a CPU by means of a clock generated on the chip.
*
* A reset signal is set high or low to determine whether playback (and interrupts) are occuring
*
*/
static struct MSM5205interface *msm5205_interface;
/*
* Start emulation of an MSM5205-compatible chip
*/
int MSM5205_sh_start (struct MSM5205interface *intf)
{
static struct ADPCMinterface generic_interface;
int i, stream_size, result;
/* save a global pointer to our interface */
msm5205_interface = intf;
/* if there's an interrupt function to be called, set it up */
if (msm5205_interface->interrupt)
timer_pulse (TIME_IN_HZ (msm5205_interface->frequency), 0, msm5205_interface->interrupt);
/* create an interface for the generic system here */
generic_interface.num = intf->num;
generic_interface.frequency = intf->frequency;
generic_interface.region = 0;
generic_interface.init = 0;
for (i = 0; i < intf->num; i++)
generic_interface.volume[i] = intf->volume[i];
/* initialize it in the standard fashion */
result = ADPCM_sh_start (&generic_interface);
/* if we succeeded, create streams for everyone */
if (!result)
{
/* stream size is the number of samples per second rounded to the next power of 2 */
stream_size = 1;
while (stream_size < msm5205_interface->frequency)
stream_size <<= 1;
/* allocate streams for everyone */
for (i = 0; i < intf->num; i++)
{
struct ADPCMVoice *voice = adpcm + i;
/* create the stream memory */
voice->stream = malloc (stream_size);
if (!voice->stream)
return 1;
/* set the mask value */
voice->mask = stream_size - 1;
}
}
/* return the result */
return result;
}
/*
* Stop emulation of an MSM5205-compatible chip
*/
void MSM5205_sh_stop (void)
{
/* standard stop */
ADPCM_sh_stop ();
}
/*
* Update emulation of an MSM5205-compatible chip
*/
void MSM5205_sh_update (void)
{
/* standard update */
ADPCM_sh_update ();
}
/*
* Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF)
*/
void MSM5205_reset_w (int num, int reset)
{
struct ADPCMVoice *voice = adpcm + num;
/* range check the numbers */
if (num >= msm5205_interface->num)
{
return;
}
/* see if this is turning on playback */
if (!voice->playing && !reset)
{
/* update the ADPCM voice */
ADPCM_update (voice, cpu_scalebyfcount (buffer_len));
/* mark this voice as playing */
voice->playing = 1;
voice->base = voice->stream;
voice->sample = 0;
voice->count = 0;
/* also reset the ADPCM parameters */
voice->signal = -2;
voice->step = 0;
}
/* see if this is turning off playback */
else if (voice->playing && reset)
{
/* update the ADPCM voice */
ADPCM_update (voice, cpu_scalebyfcount (buffer_len));
/* mark this voice as not playing */
voice->playing = 0;
}
}
/*
* Handle an update of the data to the chip
*/
void MSM5205_data_w (int num, int data)
{
struct ADPCMVoice *voice = adpcm + num;
/* only do this if we're playing */
if (voice->playing)
{
int count = voice->count;
/* set the upper or lower half */
if (!(count & 1))
voice->stream[(count / 2) & voice->mask] = data << 4;
else
voice->stream[(count / 2) & voice->mask] |= data & 0x0f;
/* update the count */
voice->count = count + 1;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -