mirror of
https://github.com/libretro/NP2kai.git
synced 2024-10-07 14:53:33 +00:00
361 lines
6.6 KiB
C
361 lines
6.6 KiB
C
/**
|
|
* @file adpcmg.c
|
|
* @brief Implementation of the OPNA ADPCM
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
#include "adpcm.h"
|
|
|
|
#define ADPCM_NBR 0x80000000
|
|
|
|
static const UINT adpcmdeltatable[8] = {
|
|
// 0.89, 0.89, 0.89, 0.89, 1.2, 1.6, 2.0, 2.4
|
|
228, 228, 228, 228, 308, 408, 512, 612};
|
|
|
|
|
|
REG8 SOUNDCALL adpcm_readsample(ADPCM ad) {
|
|
|
|
UINT32 pos;
|
|
REG8 data;
|
|
REG8 ret;
|
|
|
|
if ((ad->reg.ctrl1 & 0x60) == 0x20) {
|
|
pos = ad->pos & 0x1fffff;
|
|
if (!(ad->reg.ctrl2 & 2)) {
|
|
data = ad->buf[pos >> 3];
|
|
pos += 8;
|
|
}
|
|
else {
|
|
const UINT8 *ptr;
|
|
REG8 bit;
|
|
UINT tmp;
|
|
ptr = ad->buf + ((pos >> 3) & 0x7fff);
|
|
bit = 1 << (pos & 7);
|
|
tmp = (ptr[0x00000] & bit);
|
|
tmp += (ptr[0x08000] & bit) << 1;
|
|
tmp += (ptr[0x10000] & bit) << 2;
|
|
tmp += (ptr[0x18000] & bit) << 3;
|
|
tmp += (ptr[0x20000] & bit) << 4;
|
|
tmp += (ptr[0x28000] & bit) << 5;
|
|
tmp += (ptr[0x30000] & bit) << 6;
|
|
tmp += (ptr[0x38000] & bit) << 7;
|
|
data = (REG8)(tmp >> (pos & 7));
|
|
pos++;
|
|
}
|
|
if (pos != ad->stop) {
|
|
pos &= 0x1fffff;
|
|
ad->status |= 4;
|
|
}
|
|
if (pos >= ad->limit) {
|
|
pos = 0;
|
|
}
|
|
ad->pos = pos;
|
|
}
|
|
else {
|
|
data = 0;
|
|
}
|
|
pos = ad->fifopos;
|
|
ret = ad->fifo[ad->fifopos];
|
|
ad->fifo[ad->fifopos] = data;
|
|
ad->fifopos ^= 1;
|
|
return(ret);
|
|
}
|
|
|
|
void SOUNDCALL adpcm_datawrite(ADPCM ad, REG8 data) {
|
|
|
|
UINT32 pos;
|
|
|
|
pos = ad->pos & 0x1fffff;
|
|
if (!(ad->reg.ctrl2 & 2)) {
|
|
ad->buf[pos >> 3] = data;
|
|
pos += 8;
|
|
}
|
|
else {
|
|
UINT8 *ptr;
|
|
UINT8 bit;
|
|
UINT8 mask;
|
|
ptr = ad->buf + ((pos >> 3) & 0x7fff);
|
|
bit = 1 << (pos & 7);
|
|
mask = ~bit;
|
|
ptr[0x00000] &= mask;
|
|
if (data & 0x01) {
|
|
ptr[0x00000] |= bit;
|
|
}
|
|
ptr[0x08000] &= mask;
|
|
if (data & 0x02) {
|
|
ptr[0x08000] |= bit;
|
|
}
|
|
ptr[0x10000] &= mask;
|
|
if (data & 0x04) {
|
|
ptr[0x10000] |= bit;
|
|
}
|
|
ptr[0x18000] &= mask;
|
|
if (data & 0x08) {
|
|
ptr[0x18000] |= bit;
|
|
}
|
|
ptr[0x20000] &= mask;
|
|
if (data & 0x10) {
|
|
ptr[0x20000] |= bit;
|
|
}
|
|
ptr[0x28000] &= mask;
|
|
if (data & 0x20) {
|
|
ptr[0x28000] |= bit;
|
|
}
|
|
ptr[0x30000] &= mask;
|
|
if (data & 0x40) {
|
|
ptr[0x30000] |= bit;
|
|
}
|
|
ptr[0x38000] &= mask;
|
|
if (data & 0x80) {
|
|
ptr[0x38000] |= bit;
|
|
}
|
|
pos++;
|
|
}
|
|
if (pos == ad->stop) {
|
|
pos &= 0x1fffff;
|
|
ad->status |= 4;
|
|
}
|
|
if (pos >= ad->limit) {
|
|
pos = 0;
|
|
}
|
|
ad->pos = pos;
|
|
}
|
|
|
|
static void SOUNDCALL getadpcmdata(ADPCM ad) {
|
|
|
|
UINT32 pos;
|
|
UINT data;
|
|
UINT dir;
|
|
SINT32 dlt;
|
|
SINT32 samp;
|
|
|
|
pos = ad->pos;
|
|
if (!(ad->reg.ctrl2 & 2)) {
|
|
data = ad->buf[(pos >> 3) & 0x3ffff];
|
|
if (!(pos & ADPCM_NBR)) {
|
|
data >>= 4;
|
|
}
|
|
pos += ADPCM_NBR + 4;
|
|
}
|
|
else {
|
|
const UINT8 *ptr;
|
|
REG8 bit;
|
|
UINT tmp;
|
|
ptr = ad->buf + ((pos >> 3) & 0x7fff);
|
|
bit = 1 << (pos & 7);
|
|
if (!(pos & ADPCM_NBR)) {
|
|
tmp = (ptr[0x20000] & bit);
|
|
tmp += (ptr[0x28000] & bit) << 1;
|
|
tmp += (ptr[0x30000] & bit) << 2;
|
|
tmp += (ptr[0x38000] & bit) << 3;
|
|
data = tmp >> (pos & 7);
|
|
pos += ADPCM_NBR;
|
|
}
|
|
else {
|
|
tmp = (ptr[0x00000] & bit);
|
|
tmp += (ptr[0x08000] & bit) << 1;
|
|
tmp += (ptr[0x10000] & bit) << 2;
|
|
tmp += (ptr[0x18000] & bit) << 3;
|
|
data = tmp >> (pos & 7);
|
|
pos += ADPCM_NBR + 1;
|
|
}
|
|
}
|
|
dir = data & 8;
|
|
data &= 7;
|
|
dlt = adpcmdeltatable[data] * ad->delta;
|
|
dlt >>= 8;
|
|
if (dlt < 127) {
|
|
dlt = 127;
|
|
}
|
|
else if (dlt > 24000) {
|
|
dlt = 24000;
|
|
}
|
|
samp = ad->delta;
|
|
ad->delta = dlt;
|
|
samp *= ((data * 2) + 1);
|
|
samp >>= ADPCM_SHIFT;
|
|
if (!dir) {
|
|
samp += ad->samp;
|
|
if (samp > 32767) {
|
|
samp = 32767;
|
|
}
|
|
}
|
|
else {
|
|
samp = ad->samp - samp;
|
|
if (samp < -32767) {
|
|
samp = -32767;
|
|
}
|
|
}
|
|
ad->samp = samp;
|
|
|
|
if (!(pos & ADPCM_NBR)) {
|
|
if (pos == ad->stop) {
|
|
if (ad->reg.ctrl1 & 0x10) {
|
|
pos = ad->start;
|
|
ad->samp = 0;
|
|
ad->delta = 127;
|
|
}
|
|
else {
|
|
pos &= 0x1fffff;
|
|
ad->status |= 4;
|
|
ad->play = 0;
|
|
}
|
|
}
|
|
else if (pos >= ad->limit) {
|
|
pos = 0;
|
|
}
|
|
}
|
|
ad->pos = pos;
|
|
samp *= ad->level;
|
|
samp >>= (10 + 1);
|
|
ad->out0 = ad->out1;
|
|
ad->out1 = samp + ad->fb;
|
|
ad->fb = samp >> 1;
|
|
}
|
|
|
|
void SOUNDCALL adpcm_getpcm(ADPCM ad, SINT32 *pcm, UINT count) {
|
|
|
|
SINT32 remain;
|
|
SINT32 samp;
|
|
|
|
if ((count == 0) || (ad->play == 0)) {
|
|
return;
|
|
}
|
|
remain = ad->remain;
|
|
if (ad->step <= ADTIMING) {
|
|
do {
|
|
if (remain < 0) {
|
|
remain += ADTIMING;
|
|
getadpcmdata(ad);
|
|
if (ad->play == 0) {
|
|
if (remain > 0) {
|
|
do {
|
|
samp = (ad->out0 * remain) >> ADTIMING_BIT;
|
|
if (ad->reg.ctrl2 & 0x80) {
|
|
pcm[0] += samp;
|
|
}
|
|
if (ad->reg.ctrl2 & 0x40) {
|
|
pcm[1] += samp;
|
|
}
|
|
pcm += 2;
|
|
remain -= ad->step;
|
|
} while((remain > 0) && (--count));
|
|
}
|
|
goto adpcmstop;
|
|
}
|
|
}
|
|
samp = (ad->out0 * remain) + (ad->out1 * (ADTIMING - remain));
|
|
samp >>= ADTIMING_BIT;
|
|
if (ad->reg.ctrl2 & 0x80) {
|
|
pcm[0] += samp;
|
|
}
|
|
if (ad->reg.ctrl2 & 0x40) {
|
|
pcm[1] += samp;
|
|
}
|
|
pcm += 2;
|
|
remain -= ad->step;
|
|
} while(--count);
|
|
}
|
|
else {
|
|
do {
|
|
if (remain > 0) {
|
|
samp = ad->out0 * (ADTIMING - remain);
|
|
do {
|
|
getadpcmdata(ad);
|
|
if (ad->play == 0) {
|
|
goto adpcmstop;
|
|
}
|
|
samp += ad->out0 * np2min(remain, ad->pertim);
|
|
remain -= ad->pertim;
|
|
} while(remain > 0);
|
|
}
|
|
else {
|
|
samp = ad->out0 * ADTIMING;
|
|
}
|
|
remain += ADTIMING;
|
|
samp >>= ADTIMING_BIT;
|
|
if (ad->reg.ctrl2 & 0x80) {
|
|
pcm[0] += samp;
|
|
}
|
|
if (ad->reg.ctrl2 & 0x40) {
|
|
pcm[1] += samp;
|
|
}
|
|
pcm += 2;
|
|
} while(--count);
|
|
}
|
|
ad->remain = remain;
|
|
return;
|
|
|
|
adpcmstop:
|
|
ad->out0 = 0;
|
|
ad->out1 = 0;
|
|
ad->fb = 0;
|
|
ad->remain = 0;
|
|
}
|
|
|
|
/**
|
|
* Step adpcm
|
|
* @param[in] ad An instance of ADPCM
|
|
* @param[out] pcm A pointer to a buffer
|
|
* @param[in] count The size of the buffer
|
|
*/
|
|
void SOUNDCALL adpcm_getpcm_dummy(ADPCM ad, SINT32 *pcm, UINT count)
|
|
{
|
|
SINT32 remain;
|
|
|
|
if ((count == 0) || (ad->play == 0))
|
|
{
|
|
return;
|
|
}
|
|
remain = ad->remain;
|
|
if (ad->step <= ADTIMING)
|
|
{
|
|
do
|
|
{
|
|
if (remain < 0)
|
|
{
|
|
remain += ADTIMING;
|
|
getadpcmdata(ad);
|
|
if (ad->play == 0)
|
|
{
|
|
if (remain > 0)
|
|
{
|
|
do
|
|
{
|
|
remain -= ad->step;
|
|
} while ((remain > 0) && (--count));
|
|
}
|
|
goto adpcmstop;
|
|
}
|
|
}
|
|
remain -= ad->step;
|
|
} while(--count);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (remain > 0)
|
|
{
|
|
do {
|
|
getadpcmdata(ad);
|
|
if (ad->play == 0)
|
|
{
|
|
goto adpcmstop;
|
|
}
|
|
remain -= ad->pertim;
|
|
} while (remain > 0);
|
|
}
|
|
remain += ADTIMING;
|
|
} while (--count);
|
|
}
|
|
ad->remain = remain;
|
|
return;
|
|
|
|
adpcmstop:
|
|
ad->out0 = 0;
|
|
ad->out1 = 0;
|
|
ad->fb = 0;
|
|
ad->remain = 0;
|
|
}
|