NP2kai/sound/adpcmg.c
2018-01-02 23:24:45 +09:00

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;
}