diff options
author | Takamichi Horikawa <takamichiho@gmail.com> | 2016-11-26 20:57:57 +0900 |
---|---|---|
committer | Takamichi Horikawa <takamichiho@gmail.com> | 2016-11-26 20:57:57 +0900 |
commit | 6fd10cdacb5cbe47a4fc339c20a733d4a9a384a1 (patch) | |
tree | c7f479a70c350dca0b73d76078e46db41d9c4133 /libopna/opnaadpcm.c |
initial
Diffstat (limited to 'libopna/opnaadpcm.c')
-rw-r--r-- | libopna/opnaadpcm.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/libopna/opnaadpcm.c b/libopna/opnaadpcm.c new file mode 100644 index 0000000..1157b56 --- /dev/null +++ b/libopna/opnaadpcm.c @@ -0,0 +1,208 @@ +#include "opnaadpcm.h" + +enum { + C1_START = 0x80, + C1_REC = 0x40, + C1_MEMEXT = 0x20, + C1_REPEAT = 0x10, + C1_RESET = 0x01, + C1_MASK = 0xf9, +}; + +enum { + C2_L = 0x80, + C2_R = 0x40, + C2_8BIT = 0x02, + C2_MASK = 0xcf, +}; + +static const uint8_t adpcm_table[8] = { + 57, 57, 57, 57, 77, 102, 128, 153, +}; + +void opna_adpcm_reset(struct opna_adpcm *adpcm) { + adpcm->control1 = 0; + adpcm->control2 = 0; + adpcm->vol = 0; + adpcm->delta = 0; + adpcm->start = 0; + adpcm->end = 0; + adpcm->limit = 0; + adpcm->ramptr = 0; + adpcm->step = 0; + adpcm->ram = 0; + adpcm->acc = 0; + adpcm->prev_acc = 0; + adpcm->adpcmd = 127; + adpcm->out = 0; +} + +static uint32_t addr_conv(const struct opna_adpcm *adpcm, uint16_t a) { + uint32_t a32 = a; + return (adpcm->control2 & C2_8BIT) ? (a32<<6) : (a32<<3); +} +static uint32_t addr_conv_e(const struct opna_adpcm *adpcm, uint16_t a) { + uint32_t a32 = a+1; + uint32_t ret = (adpcm->control2 & C2_8BIT) ? (a32<<6) : (a32<<3); + return ret-1; +} + +static void adpcm_calc(struct opna_adpcm *adpcm) { + uint32_t step = (uint32_t)adpcm->step + (uint32_t)adpcm->delta; + adpcm->step = step & 0xffff; + if (step >> 16) { + if (adpcm->ramptr == addr_conv(adpcm, adpcm->limit)) { + adpcm->ramptr = 0; + } + if (adpcm->ramptr == addr_conv_e(adpcm, adpcm->end)) { + if (adpcm->control1 & C1_REPEAT) { + adpcm->ramptr = addr_conv(adpcm, adpcm->start); + adpcm->acc = 0; + adpcm->adpcmd = 127; + adpcm->prev_acc = 0; + } else { + // TODO: set EOS + adpcm->control1 = 0; + adpcm->out = 0; + adpcm->prev_acc = 0; + } + } + uint8_t data = 0; + if (adpcm->ram) { + data = adpcm->ram[(adpcm->ramptr>>1)&(OPNA_ADPCM_RAM_SIZE-1)]; + } + if (adpcm->ramptr&1) { + data &= 0x0f; + } else { + data >>= 4; + } + adpcm->ramptr++; + adpcm->ramptr &= (1<<(24+1))-1; + + adpcm->prev_acc = adpcm->acc; + int32_t acc_d = (((data&7)<<1)|1); + if (data&8) acc_d = -acc_d; + int32_t acc = adpcm->acc + (acc_d * adpcm->adpcmd / 8); + if (acc < -32768) acc = -32768; + if (acc > 32767) acc = 32767; + adpcm->acc = acc; + + uint32_t adpcmd = (adpcm->adpcmd * adpcm_table[data&7] / 64); + if (adpcmd < 127) adpcmd = 127; + if (adpcmd > 24576) adpcmd = 24576; + adpcm->adpcmd = adpcmd; + } + int32_t out = (int32_t)adpcm->prev_acc * (0x10000-adpcm->step); + out += (int32_t)adpcm->acc * adpcm->step; + out >>= 16; + out *= adpcm->vol; + out >>= 8; + if (out < -32768) out = -32768; + if (out > 32767) out = 32767; + adpcm->out = out; +} + +void opna_adpcm_writereg(struct opna_adpcm *adpcm, unsigned reg, unsigned val) { + val &= 0xff; + if (reg < 0x100) return; + if (reg >= 0x111) return; + reg &= 0xff; + switch (reg) { + case 0x00: + adpcm->control1 = val & C1_MASK; + if (adpcm->control1 & C1_START) { + adpcm->step = 0; + adpcm->acc = 0; + adpcm->prev_acc = 0; + adpcm->out = 0; + adpcm->adpcmd = 127; + } + if (adpcm->control1 & C1_MEMEXT) { + adpcm->ramptr = addr_conv(adpcm, adpcm->start); + } + if (adpcm->control1 & C1_RESET) { + adpcm->control1 = 0; + // TODO: set BRDY + } + break; + case 0x01: + adpcm->control2 = val & C2_MASK; + break; + case 0x02: + adpcm->start &= 0xff00; + adpcm->start |= val; + break; + case 0x03: + adpcm->start &= 0x00ff; + adpcm->start |= (val<<8); + break; + case 0x04: + adpcm->end &= 0xff00; + adpcm->end |= val; + break; + case 0x05: + adpcm->end &= 0x00ff; + adpcm->end |= (val<<8); + break; + case 0x08: + // data write + if ((adpcm->control1 & (C1_START|C1_REC|C1_MEMEXT)) == (C1_REC|C1_MEMEXT)) { + // external memory write + if (adpcm->ramptr != addr_conv_e(adpcm, adpcm->end)) { + if (adpcm->ram) { + adpcm->ram[(adpcm->ramptr>>1)&(OPNA_ADPCM_RAM_SIZE-1)] = val; + } + adpcm->ramptr += 2; + } else { + // TODO: set EOS + } + } + break; + case 0x09: + adpcm->delta &= 0xff00; + adpcm->delta |= val; + break; + case 0x0a: + adpcm->delta &= 0x00ff; + adpcm->delta |= (val<<8); + break; + case 0x0b: + adpcm->vol = val; + break; + case 0x0c: + adpcm->limit &= 0xff00; + adpcm->limit |= val; + break; + case 0x0d: + adpcm->limit &= 0x00ff; + adpcm->limit |= (val<<8); + break; + } +} + +void opna_adpcm_mix(struct opna_adpcm *adpcm, int16_t *buf, unsigned samples) { + if (!adpcm->ram) return; + if (!(adpcm->control1 & C1_START)) return; + for (unsigned i = 0; i < samples; i++) { + adpcm_calc(adpcm); + int32_t lo = buf[i*2+0]; + int32_t ro = buf[i*2+1]; + if (adpcm->control2 & C2_L) lo += (adpcm->out>>1); + if (adpcm->control2 & C2_R) ro += (adpcm->out>>1); + if (lo < INT16_MIN) lo = INT16_MIN; + if (lo > INT16_MAX) lo = INT16_MAX; + if (ro < INT16_MIN) ro = INT16_MIN; + if (ro > INT16_MAX) ro = INT16_MAX; + buf[i*2+0] = lo; + buf[i*2+1] = ro; + if (!(adpcm->control1 & C1_START)) return; + } +} + +void opna_adpcm_set_ram_256k(struct opna_adpcm *adpcm, void *ram) { + adpcm->ram = ram; +} + +void *opna_adpcm_get_ram(struct opna_adpcm *adpcm) { + return adpcm->ram; +} |