diff options
Diffstat (limited to 'libopna')
-rw-r--r-- | libopna/opna.c | 24 | ||||
-rw-r--r-- | libopna/opna.h | 30 | ||||
-rw-r--r-- | libopna/opnaadpcm.c | 208 | ||||
-rw-r--r-- | libopna/opnaadpcm.h | 43 | ||||
-rw-r--r-- | libopna/opnadrum.c | 141 | ||||
-rw-r--r-- | libopna/opnadrum.h | 57 | ||||
-rw-r--r-- | libopna/opnafm.c | 511 | ||||
-rw-r--r-- | libopna/opnafm.h | 111 | ||||
-rw-r--r-- | libopna/opnassg.c | 233 | ||||
-rw-r--r-- | libopna/opnassg.h | 60 | ||||
-rw-r--r-- | libopna/opnatables.h | 242 | ||||
-rw-r--r-- | libopna/opnatimer.c | 79 | ||||
-rw-r--r-- | libopna/opnatimer.h | 42 | ||||
-rw-r--r-- | libopna/s98gen.c | 141 | ||||
-rw-r--r-- | libopna/s98gen.h | 34 |
15 files changed, 1956 insertions, 0 deletions
diff --git a/libopna/opna.c b/libopna/opna.c new file mode 100644 index 0000000..4e198a0 --- /dev/null +++ b/libopna/opna.c @@ -0,0 +1,24 @@ +#include "opna.h" + +void opna_reset(struct opna *opna) { + opna_fm_reset(&opna->fm); + opna_ssg_reset(&opna->ssg); + opna_ssg_resampler_reset(&opna->resampler); + opna_drum_reset(&opna->drum); + opna_adpcm_reset(&opna->adpcm); +} + +void opna_writereg(struct opna *opna, unsigned reg, unsigned val) { + val &= 0xff; + opna_fm_writereg(&opna->fm, reg, val); + opna_ssg_writereg(&opna->ssg, reg, val); + opna_drum_writereg(&opna->drum, reg, val); + opna_adpcm_writereg(&opna->adpcm, reg, val); +} + +void opna_mix(struct opna *opna, int16_t *buf, unsigned samples) { + opna_fm_mix(&opna->fm, buf, samples); + opna_ssg_mix_55466(&opna->ssg, &opna->resampler, buf, samples); + opna_drum_mix(&opna->drum, buf, samples); + opna_adpcm_mix(&opna->adpcm, buf, samples); +} diff --git a/libopna/opna.h b/libopna/opna.h new file mode 100644 index 0000000..a056156 --- /dev/null +++ b/libopna/opna.h @@ -0,0 +1,30 @@ +#ifndef LIBOPNA_OPNA_H_INCLUDED +#define LIBOPNA_OPNA_H_INCLUDED + +#include "opnafm.h" +#include "opnassg.h" +#include "opnadrum.h" +#include "opnaadpcm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct opna { + struct opna_fm fm; + struct opna_ssg ssg; + struct opna_drum drum; + struct opna_adpcm adpcm; + struct opna_ssg_resampler resampler; + +}; + +void opna_reset(struct opna *opna); +void opna_writereg(struct opna *opna, unsigned reg, unsigned val); +void opna_mix(struct opna *opna, int16_t *buf, unsigned samples); + +#ifdef __cplusplus +} +#endif + +#endif // LIBOPNA_OPNA_H_INCLUDED 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; +} diff --git a/libopna/opnaadpcm.h b/libopna/opnaadpcm.h new file mode 100644 index 0000000..27a1349 --- /dev/null +++ b/libopna/opnaadpcm.h @@ -0,0 +1,43 @@ +#ifndef LIBOPNA_OPNAADPCM_H_INCLUDED +#define LIBOPNA_OPNAADPCM_H_INCLUDED + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct opna_adpcm { + uint8_t control1; + uint8_t control2; + uint8_t vol; + uint16_t delta; + uint16_t start; + uint16_t end; + uint16_t limit; + uint32_t ramptr; + uint16_t step; + uint8_t *ram; + int16_t acc; + int16_t prev_acc; + uint16_t adpcmd; + int16_t out; +}; + +void opna_adpcm_reset(struct opna_adpcm *adpcm); +void opna_adpcm_mix(struct opna_adpcm *adpcm, int16_t *buf, unsigned samples); +void opna_adpcm_writereg(struct opna_adpcm *adpcm, unsigned reg, unsigned val); + +enum { + OPNA_ADPCM_RAM_SIZE = (1<<18) +}; + +void opna_adpcm_set_ram_256k(struct opna_adpcm *adpcm, void *ram); +void *opna_adpcm_get_ram(struct opna_adpcm *adpcm); + +#ifdef __cplusplus +} +#endif + +#endif // LIBOPNA_OPNAADPCM_H_INCLUDED diff --git a/libopna/opnadrum.c b/libopna/opnadrum.c new file mode 100644 index 0000000..2527c1a --- /dev/null +++ b/libopna/opnadrum.c @@ -0,0 +1,141 @@ +#include "opnadrum.h" + +static const uint16_t steps[49] = { + 16, 17, 19, 21, 23, 25, 28, + 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, + 118, 130, 143, 157, 173, 190, 209, + 230, 253, 279, 307, 337, 371, 408, + 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552 +}; + +static const int8_t step_inc[8] = { + -1, -1, -1, -1, 2, 5, 7, 9 +}; + +void opna_drum_reset(struct opna_drum *drum) { + for (int d = 0; d < 6; d++) { + drum->drums[d].data = 0; + drum->drums[d].playing = false; + drum->drums[d].index = 0; + drum->drums[d].len = 0; + drum->drums[d].level = 0; + drum->drums[d].left = false; + drum->drums[d].right = false; + } + drum->total_level = 0; +} + +void opna_drum_set_rom(struct opna_drum *drum, void *romptr) { + uint8_t *rom = (uint8_t *)romptr; + static const struct { + unsigned start; + unsigned end; + int div; + } part[6] = { + {OPNA_ROM_BD_START, OPNA_ROM_SD_START-1, 3}, + {OPNA_ROM_SD_START, OPNA_ROM_TOP_START-1, 3}, + {OPNA_ROM_TOP_START, OPNA_ROM_HH_START-1, 3}, + {OPNA_ROM_HH_START, OPNA_ROM_TOM_START-1, 3}, + {OPNA_ROM_TOM_START, OPNA_ROM_RIM_START-1, 6}, + {OPNA_ROM_RIM_START, OPNA_ROM_SIZE-1, 6}, + }; + drum->drums[0].data = drum->rom_bd; + drum->drums[1].data = drum->rom_sd; + drum->drums[2].data = drum->rom_top; + drum->drums[3].data = drum->rom_hh; + drum->drums[4].data = drum->rom_tom; + drum->drums[5].data = drum->rom_rim; + for (int p = 0; p < 6; p++) { + drum->drums[p].playing = false; + drum->drums[p].index = 0; + unsigned addr = part[p].start << 1; + int step = 0; + unsigned acc = 0; + int outindex = 0; + for (;;) { + if ((addr>>1) == part[p].end) break; + unsigned data = rom[addr>>1]; + if (!(addr&1)) data >>= 4; + data &= ((1<<4)-1); + int acc_diff = ((((data&7)<<1)|1) * steps[step]) >> 3; + if (data&8) acc_diff = -acc_diff; + acc += acc_diff; + step += step_inc[data&7]; + if (step < 0) step = 0; + if (step > 48) step = 48; + addr++; + + int out = acc & ((1u<<12)-1); + if (out >= (1<<11)) out -= (1<<12); + int16_t out16 = out << 4; + for (int i = 0; i < part[p].div; i++) { + drum->drums[p].data[outindex] = out16; + outindex++; + } + } + drum->drums[p].len = outindex; + } +} + +void opna_drum_mix(struct opna_drum *drum, int16_t *buf, int samples) { + for (int i = 0; i < samples; i++) { + int32_t lo = buf[i*2+0]; + int32_t ro = buf[i*2+1]; + for (int d = 0; d < 6; d++) { + if (drum->drums[d].playing && drum->drums[d].data) { + int co = drum->drums[d].data[drum->drums[d].index]; + co >>= 4; + unsigned level = (drum->drums[d].level^0x1f) + (drum->total_level^0x3f); + co *= 15 - (level&7); + co >>= 1+(level>>3); + if (drum->drums[d].left) lo += co; + if (drum->drums[d].right) ro += co; + drum->drums[d].index++; + if (drum->drums[d].index == drum->drums[d].len) { + drum->drums[d].index = 0; + drum->drums[d].playing = false; + } + } + } + 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; + } +} + +void opna_drum_writereg(struct opna_drum *drum, unsigned reg, unsigned val) { + val &= 0xff; + switch (reg) { + case 0x10: + for (int d = 0; d < 6; d++) { + if (val & (1<<d)) { + drum->drums[d].playing = !(val & 0x80); + drum->drums[d].index = 0; + } + } + break; + case 0x11: + drum->total_level = val & 0x3f; + break; + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + { + int d = reg - 0x18; + drum->drums[d].left = val & 0x80; + drum->drums[d].right = val & 0x40; + drum->drums[d].level = val & 0x1f; + } + break; + default: + break; + } +} diff --git a/libopna/opnadrum.h b/libopna/opnadrum.h new file mode 100644 index 0000000..24bcd48 --- /dev/null +++ b/libopna/opnadrum.h @@ -0,0 +1,57 @@ +#ifndef LIBOPNA_OPNADRUM_H_INCLUDED +#define LIBOPNA_OPNADRUM_H_INCLUDED + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPNA_ROM_BD_START 0x0000 +#define OPNA_ROM_SD_START 0x01c0 +#define OPNA_ROM_TOP_START 0x0440 +#define OPNA_ROM_HH_START 0x1b80 +#define OPNA_ROM_TOM_START 0x1d00 +#define OPNA_ROM_RIM_START 0x1f80 +#define OPNA_ROM_SIZE 0x2000 + +#define OPNA_ROM_BD_SIZE ((OPNA_ROM_SD_START-OPNA_ROM_BD_START)*2*3) +#define OPNA_ROM_SD_SIZE ((OPNA_ROM_TOP_START-OPNA_ROM_SD_START)*2*3) +#define OPNA_ROM_TOP_SIZE ((OPNA_ROM_HH_START-OPNA_ROM_TOP_START)*2*3) +#define OPNA_ROM_HH_SIZE ((OPNA_ROM_TOM_START-OPNA_ROM_HH_START)*2*3) +#define OPNA_ROM_TOM_SIZE ((OPNA_ROM_RIM_START-OPNA_ROM_TOM_START)*2*6) +#define OPNA_ROM_RIM_SIZE ((OPNA_ROM_SIZE-OPNA_ROM_RIM_START)*2*6) + +struct opna_drum { + struct { + int16_t *data; + bool playing; + unsigned index; + unsigned len; + unsigned level; + bool left; + bool right; + } drums[6]; + unsigned total_level; + int16_t rom_bd[OPNA_ROM_BD_SIZE]; + int16_t rom_sd[OPNA_ROM_SD_SIZE]; + int16_t rom_top[OPNA_ROM_TOP_SIZE]; + int16_t rom_hh[OPNA_ROM_HH_SIZE]; + int16_t rom_tom[OPNA_ROM_TOM_SIZE]; + int16_t rom_rim[OPNA_ROM_RIM_SIZE]; +}; + +void opna_drum_reset(struct opna_drum *drum); +// set rom data, size: 0x2000 (8192) bytes +void opna_drum_set_rom(struct opna_drum *drum, void *rom); + +void opna_drum_mix(struct opna_drum *drum, int16_t *buf, int samples); + +void opna_drum_writereg(struct opna_drum *drum, unsigned reg, unsigned val); + +#ifdef __cplusplus +} +#endif + +#endif // LIBOPNA_OPNADRUM_H_INCLUDED diff --git a/libopna/opnafm.c b/libopna/opnafm.c new file mode 100644 index 0000000..c7512a8 --- /dev/null +++ b/libopna/opnafm.c @@ -0,0 +1,511 @@ +#include "opnafm.h" + +#include "opnatables.h" + +enum { + CH3_MODE_NORMAL = 0, + CH3_MODE_CSM = 1, + CH3_MODE_SE = 2 +}; + +static void opna_fm_slot_reset(struct opna_fm_slot *slot) { + slot->phase = 0; + slot->env = 1023; + slot->env_count = 0; + slot->env_state = ENV_RELEASE; + slot->rate_shifter = 0; + slot->rate_selector = 0; + slot->rate_mul = 0; + slot->tl = 0; + slot->sl = 0; + slot->ar = 0; + slot->dr = 0; + slot->sr = 0; + slot->rr = 0; + slot->mul = 0; + slot->det = 0; + slot->ks = 0; + slot->keyon = false; +} + + +void opna_fm_chan_reset(struct opna_fm_channel *chan) { + for (int i = 0; i < 4; i++) { + opna_fm_slot_reset(&chan->slot[i]); + } + + chan->fbmem1 = 0; + chan->fbmem2 = 0; + chan->alg_mem = 0; + + chan->alg = 0; + chan->fb = 0; + chan->fnum = 0; + chan->blk = 0; +} + +void opna_fm_reset(struct opna_fm *fm) { + for (int i = 0; i < 6; i++) { + opna_fm_chan_reset(&fm->channel[i]); + fm->lselect[i] = true; + fm->rselect[i] = true; + } + fm->blkfnum_h = 0; + fm->env_div3 = 0; + + fm->ch3.mode = CH3_MODE_NORMAL; + for (int i = 0; i < 3; i++) { + fm->ch3.fnum[i] = 0; + fm->ch3.blk[i] = 0; + } +} +#define LIBOPNA_ENABLE_HIRES +// maximum output: 2042<<2 = 8168 +static int16_t opna_fm_slotout(struct opna_fm_slot *slot, int16_t modulation) { + unsigned pind = (slot->phase >> 10); + pind += modulation >> 1; + bool minus = pind & (1<<(LOGSINTABLEBIT+1)); + bool reverse = pind & (1<<LOGSINTABLEBIT); + if (reverse) pind = ~pind; + pind &= (1<<LOGSINTABLEBIT)-1; + +#ifdef LIBOPNA_ENABLE_HIRES + unsigned pind_hires = (slot->phase >> 8); + pind_hires += modulation << 1; + minus = pind_hires & (1<<(LOGSINTABLEHIRESBIT+1)); + reverse = pind_hires & (1<<LOGSINTABLEHIRESBIT); + if (reverse) pind_hires = ~pind_hires; + pind_hires &= (1<<LOGSINTABLEHIRESBIT)-1; + + int logout = logsintable_hires[pind_hires] + (slot->env << 2) + (slot->tl << 5); +#else + int logout = logsintable[pind] + (slot->env << 2) + (slot->tl << 5); +#endif // LIBOPNA_ENABLE_HIRES + + int selector = logout & ((1<<EXPTABLEBIT)-1); + int shifter = logout >> EXPTABLEBIT; + if (shifter > 13) shifter = 13; + + int16_t out = (exptable[selector] << 2) >> shifter; + if (minus) out = -out; + return out; +} + +static unsigned blkfnum2freq(unsigned blk, unsigned fnum) { + return (fnum << blk) >> 1; +} + +#define F(n) (!!(fnum & (1 << ((n)-1)))) + +static unsigned blkfnum2keycode(unsigned blk, unsigned fnum) { + unsigned keycode = blk<<2; + keycode |= F(11) << 1; + keycode |= (F(11) && (F(10)||F(9)||F(8))) || ((!F(11))&&F(10)&&F(9)&&F(8)); + return keycode; +} + +#undef F + +static void opna_fm_slot_phase(struct opna_fm_slot *slot, unsigned freq) { +// TODO: detune +// freq += slot->dt; + unsigned det = dettable[slot->det & 0x3][slot->keycode]; + if (slot->det & 0x4) det = -det; + freq += det; + freq &= (1U<<17)-1; + int mul = slot->mul << 1; + if (!mul) mul = 1; + slot->phase += ((freq * mul)>>1); +} + +void opna_fm_chan_phase(struct opna_fm_channel *chan) { + unsigned freq = blkfnum2freq(chan->blk, chan->fnum); + for (int i = 0; i < 4; i++) { + opna_fm_slot_phase(&chan->slot[i], freq); + } +} + +static void opna_fm_chan_phase_se(struct opna_fm_channel *chan, struct opna_fm *fm) { + unsigned freq; + freq = blkfnum2freq(fm->ch3.blk[2], fm->ch3.fnum[1]); + opna_fm_slot_phase(&chan->slot[0], freq); + freq = blkfnum2freq(fm->ch3.blk[0], fm->ch3.fnum[0]); + opna_fm_slot_phase(&chan->slot[2], freq); + freq = blkfnum2freq(fm->ch3.blk[1], fm->ch3.fnum[2]); + opna_fm_slot_phase(&chan->slot[1], freq); + freq = blkfnum2freq(chan->blk, chan->fnum); + opna_fm_slot_phase(&chan->slot[3], freq); +} + +int16_t opna_fm_chanout(struct opna_fm_channel *chan) { + int16_t fb = chan->fbmem1 + chan->fbmem2; + int16_t slot0 = chan->fbmem1; + chan->fbmem1 = chan->fbmem2; + if (!chan->fb) fb = 0; + chan->fbmem2 = opna_fm_slotout(&chan->slot[0], fb >> (9 - chan->fb)); + + int16_t slot2; + int16_t out = 0; + + switch (chan->alg) { + case 0: + slot2 = opna_fm_slotout(&chan->slot[2], chan->alg_mem); + chan->alg_mem = opna_fm_slotout(&chan->slot[1], slot0); + out = opna_fm_slotout(&chan->slot[3], slot2); + break; + case 1: + slot2 = opna_fm_slotout(&chan->slot[2], chan->alg_mem); + chan->alg_mem = slot0; + chan->alg_mem += opna_fm_slotout(&chan->slot[1], 0); + out = opna_fm_slotout(&chan->slot[3], slot2); + break; + case 2: + slot2 = opna_fm_slotout(&chan->slot[2], chan->alg_mem); + chan->alg_mem = opna_fm_slotout(&chan->slot[1], 0); + out = opna_fm_slotout(&chan->slot[3], slot0 + slot2); + break; + case 3: + slot2 = opna_fm_slotout(&chan->slot[2], 0); + out = opna_fm_slotout(&chan->slot[3], slot2 + chan->alg_mem); + chan->alg_mem = opna_fm_slotout(&chan->slot[1], slot0); + break; + case 4: + out = opna_fm_slotout(&chan->slot[1], slot0); + slot2 = opna_fm_slotout(&chan->slot[2], 0); + out += opna_fm_slotout(&chan->slot[3], slot2); + break; + case 5: + out = opna_fm_slotout(&chan->slot[2], chan->alg_mem); + chan->alg_mem = slot0; + out += opna_fm_slotout(&chan->slot[1], slot0); + out += opna_fm_slotout(&chan->slot[3], slot0); + break; + case 6: + out = opna_fm_slotout(&chan->slot[1], slot0); + out += opna_fm_slotout(&chan->slot[2], 0); + out += opna_fm_slotout(&chan->slot[3], 0); + break; + case 7: + out = slot0; + out += opna_fm_slotout(&chan->slot[1], 0); + out += opna_fm_slotout(&chan->slot[2], 0); + out += opna_fm_slotout(&chan->slot[3], 0); + break; + } + + return out; +} + +static void opna_fm_slot_setrate(struct opna_fm_slot *slot, int status) { + int r; + switch (status) { + case ENV_ATTACK: + r = slot->ar; + break; + case ENV_DECAY: + r = slot->dr; + break; + case ENV_SUSTAIN: + r = slot->sr; + break; + case ENV_RELEASE: + r = (slot->rr*2+1); + break; + default: + return; + } + + if (!r) { + slot->rate_selector = 0; + slot->rate_mul = 0; + slot->rate_shifter = 0; + return; + } + + int rate = 2*r + (slot->keycode >> (3 - slot->ks)); + + if (rate > 63) rate = 63; + int rate_shifter = 11 - (rate >> 2); + if (rate_shifter < 0) { + slot->rate_selector = (rate & ((1<<2)-1)) + 4; + slot->rate_mul = 1<<(-rate_shifter-1); + slot->rate_shifter = 0; + } else { + slot->rate_selector = rate & ((1<<2)-1); + slot->rate_mul = 1; + slot->rate_shifter = rate_shifter; + } +} + +static void opna_fm_slot_env(struct opna_fm_slot *slot) { + slot->env_count++; + if (!(slot->env_count & ((1<<slot->rate_shifter)-1))) { + int rate_index = (slot->env_count >> slot->rate_shifter) & 7; + int env_inc = rateinctable[slot->rate_selector][rate_index]; + env_inc *= slot->rate_mul; + + switch (slot->env_state) { + int newenv; + int sl; + case ENV_ATTACK: + newenv = slot->env + (((-slot->env-1) * env_inc) >> 4); + if (newenv <= 0) { + slot->env = 0; + slot->env_state = ENV_DECAY; + opna_fm_slot_setrate(slot, ENV_DECAY); + } else { + slot->env = newenv; + } + break; + case ENV_DECAY: + slot->env += env_inc; + sl = slot->sl; + if (sl == 0xf) sl = 0x1f; + if (slot->env >= (sl << 5)) { + slot->env_state = ENV_SUSTAIN; + opna_fm_slot_setrate(slot, ENV_SUSTAIN); + } + break; + case ENV_SUSTAIN: + slot->env += env_inc; + if (slot->env >= 1023) slot->env = 1023; + break; + case ENV_RELEASE: + slot->env += env_inc; + if (slot->env >= 1023) { + slot->env = 1023; + slot->env_state = ENV_OFF; + } + break; + } + } +} + +void opna_fm_slot_key(struct opna_fm_channel *chan, int slotnum, bool keyon) { + struct opna_fm_slot *slot = &chan->slot[slotnum]; + if (keyon) { + if (!slot->keyon) { + slot->keyon = true; + slot->env_state = ENV_ATTACK; + slot->env_count = 0; + slot->phase = 0; + opna_fm_slot_setrate(slot, ENV_ATTACK); + } + } else { + if ((slot->env_state != ENV_OFF) && slot->keyon) { + slot->keyon = false; + slot->env_state = ENV_RELEASE; + opna_fm_slot_setrate(slot, ENV_RELEASE); + } + } +} + +void opna_fm_slot_set_det(struct opna_fm_slot *slot, unsigned det) { + det &= 0x7; + slot->det = det; +} + +void opna_fm_slot_set_mul(struct opna_fm_slot *slot, unsigned mul) { + mul &= 0xf; + slot->mul = mul; +} + +void opna_fm_slot_set_tl(struct opna_fm_slot *slot, unsigned tl) { + tl &= 0x7f; + slot->tl = tl; +} + +void opna_fm_slot_set_ks(struct opna_fm_slot *slot, unsigned ks) { + ks &= 0x3; + slot->ks = ks; +} + +void opna_fm_slot_set_ar(struct opna_fm_slot *slot, unsigned ar) { + ar &= 0x1f; + slot->ar = ar; + if (slot->env_state == ENV_ATTACK) { + opna_fm_slot_setrate(slot, ENV_ATTACK); + } +} + +void opna_fm_slot_set_dr(struct opna_fm_slot *slot, unsigned dr) { + dr &= 0x1f; + slot->dr = dr; + if (slot->env_state == ENV_DECAY) { + opna_fm_slot_setrate(slot, ENV_DECAY); + } +} + +void opna_fm_slot_set_sr(struct opna_fm_slot *slot, unsigned sr) { + sr &= 0x1f; + slot->sr = sr; + if (slot->env_state == ENV_SUSTAIN) { + opna_fm_slot_setrate(slot, ENV_SUSTAIN); + } +} + +void opna_fm_slot_set_sl(struct opna_fm_slot *slot, unsigned sl) { + sl &= 0xf; + slot->sl = sl; +} + +void opna_fm_slot_set_rr(struct opna_fm_slot *slot, unsigned rr) { + rr &= 0xf; + slot->rr = rr; + if (slot->env_state == ENV_RELEASE) { + opna_fm_slot_setrate(slot, ENV_RELEASE); + } +} + +void opna_fm_chan_set_blkfnum(struct opna_fm_channel *chan, unsigned blk, unsigned fnum) { + blk &= 0x7; + fnum &= 0x7ff; + chan->blk = blk; + chan->fnum = fnum; + for (int i = 0; i < 4; i++) { + chan->slot[i].keycode = blkfnum2keycode(chan->blk, chan->fnum); + opna_fm_slot_setrate(&chan->slot[i], chan->slot[i].env_state); + } +} + +void opna_fm_chan_set_alg(struct opna_fm_channel *chan, unsigned alg) { + alg &= 0x7; + chan->alg = alg; +} + +void opna_fm_chan_set_fb(struct opna_fm_channel *chan, unsigned fb) { + fb &= 0x7; + chan->fb = fb; +} +//#include <stdio.h> +void opna_fm_writereg(struct opna_fm *fm, unsigned reg, unsigned val) { + val &= (1<<8)-1; + + if (reg > 0x1ff) return; + + switch (reg) { + case 0x27: + { + unsigned mode = val >> 6; + if (mode != fm->ch3.mode) { +// printf("0x27\n"); +// printf(" mode = %d\n", mode); + fm->ch3.mode = mode; + } + } + return; + case 0x28: + { + int c = val & 0x3; + if (c == 3) return; + if (val & 0x4) c += 3; + for (int i = 0; i < 4; i++) { + opna_fm_slot_key(&fm->channel[c], i, (val & (1<<(4+i)))); + } + } + return; + } + + int c = reg & 0x3; + if (c == 3) return; + if (reg & (1<<8)) c += 3; + int s = ((reg & (1<<3)) >> 3) | ((reg & (1<<2)) >> 1); + struct opna_fm_channel *chan = &fm->channel[c]; + struct opna_fm_slot *slot = &chan->slot[s]; + switch (reg & 0xf0) { + case 0x30: + opna_fm_slot_set_det(slot, (val >> 4) & 0x7); + opna_fm_slot_set_mul(slot, val & 0xf); + break; + case 0x40: + opna_fm_slot_set_tl(slot, val & 0x7f); + break; + case 0x50: + opna_fm_slot_set_ks(slot, (val >> 6) & 0x3); + opna_fm_slot_set_ar(slot, val & 0x1f); + break; + case 0x60: + opna_fm_slot_set_dr(slot, val & 0x1f); + break; + case 0x70: + opna_fm_slot_set_sr(slot, val & 0x1f); + break; + case 0x80: + opna_fm_slot_set_sl(slot, (val >> 4) & 0xf); + opna_fm_slot_set_rr(slot, val & 0xf); + break; + case 0xa0: + { + unsigned blk = (fm->blkfnum_h >> 3) & 0x7; + unsigned fnum = ((fm->blkfnum_h & 0x7) << 8) | (val & 0xff); + switch (reg & 0xc) { + case 0x0: + opna_fm_chan_set_blkfnum(chan, blk, fnum); + break; + case 0x8: + c %= 3; + fm->ch3.blk[c] = blk; + fm->ch3.fnum[c] = fnum; + break; + case 0x4: + case 0xc: + fm->blkfnum_h = val & 0x3f; + break; + } + } + break; + case 0xb0: + switch (reg & 0xc) { + case 0x0: + opna_fm_chan_set_alg(chan, val & 0x7); + opna_fm_chan_set_fb(chan, (val >> 3) & 0x7); + break; + case 0x4: + fm->lselect[c] = val & 0x80; + fm->rselect[c] = val & 0x40; + break; + } + break; + } +} + +void opna_fm_chan_env(struct opna_fm_channel *chan) { + for (int i = 0; i < 4; i++) { + opna_fm_slot_env(&chan->slot[i]); + } +} + +void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples) { + for (unsigned i = 0; i < samples; i++) { + if (!fm->env_div3) { + for (int c = 0; c < 6; c++) { + opna_fm_chan_env(&fm->channel[c]); + } + fm->env_div3 = 3; + } + fm->env_div3--; + + int32_t lo = buf[i*2+0]; + int32_t ro = buf[i*2+1]; + + for (int c = 0; c < 6; c++) { + int16_t o = opna_fm_chanout(&fm->channel[c]); + // TODO: CSM + if (c == 2 && fm->ch3.mode != CH3_MODE_NORMAL) { + opna_fm_chan_phase_se(&fm->channel[c], fm); + } else { + opna_fm_chan_phase(&fm->channel[c]); + } + o >>= 1; + if (fm->lselect[c]) lo += o; + if (fm->rselect[c]) ro += o; + } + + 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; + } +} diff --git a/libopna/opnafm.h b/libopna/opnafm.h new file mode 100644 index 0000000..acd673f --- /dev/null +++ b/libopna/opnafm.h @@ -0,0 +1,111 @@ +#ifndef LIBOPNA_OPNAFM_H_INCLUDED +#define LIBOPNA_OPNAFM_H_INCLUDED + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + ENV_ATTACK, + ENV_DECAY, + ENV_SUSTAIN, + ENV_RELEASE, + ENV_OFF, +}; + +struct opna_fm_slot { + // 20bits, upper 10 bits will be the index to sine table + uint32_t phase; + // 10 bits + uint16_t env; + uint16_t env_count; + uint8_t env_state; + uint8_t rate_shifter; + uint8_t rate_selector; + uint8_t rate_mul; + + uint8_t tl; + uint8_t sl; + + uint8_t ar; + uint8_t dr; + uint8_t sr; + uint8_t rr; + + uint8_t mul; + uint8_t det; + uint8_t ks; + + uint8_t keycode; + + bool keyon; +}; + +struct opna_fm_channel { + struct opna_fm_slot slot[4]; + + // save 2 samples for slot 1 feedback + uint16_t fbmem1; + uint16_t fbmem2; + // save sample for long (>2) chain of slots + uint16_t alg_mem; + + uint8_t alg; + uint8_t fb; + uint16_t fnum; + uint8_t blk; +}; + +struct opna_fm { + struct opna_fm_channel channel[6]; + + // remember here what was written on higher byte, + // actually write when lower byte written + uint8_t blkfnum_h; + // channel 3 blk, fnum + struct { + uint16_t fnum[3]; + uint8_t blk[3]; + uint8_t mode; + } ch3; + + // do envelope once per 3 samples + uint8_t env_div3; + + // pan + bool lselect[6]; + bool rselect[6]; +}; + +void opna_fm_reset(struct opna_fm *fm); +void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples); +void opna_fm_writereg(struct opna_fm *fm, unsigned reg, unsigned val); + +// +void opna_fm_chan_reset(struct opna_fm_channel *chan); +void opna_fm_chan_phase(struct opna_fm_channel *chan); +void opna_fm_chan_env(struct opna_fm_channel *chan); +void opna_fm_chan_set_blkfnum(struct opna_fm_channel *chan, unsigned blk, unsigned fnum); +int16_t opna_fm_chanout(struct opna_fm_channel *chan); +void opna_fm_slot_key(struct opna_fm_channel *chan, int slotnum, bool keyon); + +void opna_fm_chan_set_alg(struct opna_fm_channel *chan, unsigned alg); +void opna_fm_chan_set_fb(struct opna_fm_channel *chan, unsigned fb); +void opna_fm_slot_set_ar(struct opna_fm_slot *slot, unsigned ar); +void opna_fm_slot_set_dr(struct opna_fm_slot *slot, unsigned dr); +void opna_fm_slot_set_sr(struct opna_fm_slot *slot, unsigned sr); +void opna_fm_slot_set_rr(struct opna_fm_slot *slot, unsigned rr); +void opna_fm_slot_set_sl(struct opna_fm_slot *slot, unsigned sl); +void opna_fm_slot_set_tl(struct opna_fm_slot *slot, unsigned tl); +void opna_fm_slot_set_ks(struct opna_fm_slot *slot, unsigned ks); +void opna_fm_slot_set_mul(struct opna_fm_slot *slot, unsigned mul); +void opna_fm_slot_set_det(struct opna_fm_slot *slot, unsigned det); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBOPNA_OPNAFM_H_INCLUDED */ diff --git a/libopna/opnassg.c b/libopna/opnassg.c new file mode 100644 index 0000000..d87dc91 --- /dev/null +++ b/libopna/opnassg.c @@ -0,0 +1,233 @@ +#include "opnassg.h" +/* +static const float voltable[32] = { + 0.0f, 0.0f, 0x1.ae89f9p-8f, 0x1.000000p-7f, + 0x1.306fe0p-7f, 0x1.6a09e6p-7f, 0x1.ae89f9p-7f, 0x1.000000p-6f, + 0x1.306fe0p-6f, 0x1.6a09e6p-6f, 0x1.ae89f9p-6f, 0x1.000000p-5f, + 0x1.306fe0p-5f, 0x1.6a09e6p-5f, 0x1.ae89f9p-5f, 0x1.000000p-4f, + 0x1.306fe0p-4f, 0x1.6a09e6p-4f, 0x1.ae89f9p-4f, 0x1.000000p-3f, + 0x1.306fe0p-3f, 0x1.6a09e6p-3f, 0x1.ae89f9p-3f, 0x1.000000p-2f, + 0x1.306fe0p-2f, 0x1.6a09e6p-2f, 0x1.ae89f9p-2f, 0x1.000000p-1f, + 0x1.306fe0p-1f, 0x1.6a09e6p-1f, 0x1.ae89f9p-1f, 0x1.000000p-0f +}; +*/ + +// if (i < 2) voltable[i] = 0; +// else voltable[i] = round((0x7fff / 3.0) * pow(2.0, (i - 31)/4.0)); + +static const int16_t voltable[32] = { + 0, 0, 72, 85, + 101, 121, 144, 171, + 203, 241, 287, 341, + 406, 483, 574, 683, + 812, 965, 1148, 1365, + 1624, 1931, 2296, 2731, + 3247, 3862, 4592, 5461, + 6494, 7723, 9185, 10922 +}; + +#define SINCTABLEBIT 7 +#define SINCTABLELEN (1<<SINCTABLEBIT) + +// GNU Octave +// Fc = 7987200 +// Ff = Fc/144 +// Fs = Fc/32 +// Fe = 20000 +// O = (((Ff/2)-Fe)*2)/(Fs/2) +// B = 128 * O / 2 +// FILTER=sinc(linspace(-127.5,127.5,256)*2/9/2).*rotdim(kaiser(256,B)) +// FILTERI=round(FILTER(1:128).*32768) +static const int16_t sinctable[SINCTABLELEN] = { + 1, 0, -1, -2, -3, -5, -6, -6, + -6, -5, -2, 2, 7, 11, 16, 19, + 20, 18, 13, 5, -5, -17, -29, -38, + -44, -45, -40, -29, -11, 12, 36, 60, + 79, 90, 91, 80, 56, 21, -22, -68, + -112, -146, -166, -166, -144, -100, -37, 39, + 119, 193, 251, 282, 280, 241, 166, 61, + -64, -195, -315, -406, -455, -450, -385, -264, + -96, 101, 306, 491, 632, 705, 694, 593, + 405, 147, -154, -464, -744, -954, -1062, -1043, + -889, -607, -220, 230, 692, 1108, 1421, 1580, + 1552, 1322, 902, 328, -343, -1032, -1655, -2125, + -2369, -2333, -1994, -1365, -498, 523, 1585, 2557, + 3306, 3714, 3690, 3185, 2206, 815, -868, -2673, + -4391, -5798, -6670, -6809, -6067, -4359, -1681, 1886, + 6178, 10957, 15928, 20765, 25133, 28724, 31275, 32600, +}; + +void opna_ssg_reset(struct opna_ssg *ssg) { + for (int i = 0; i < 3; i++) { + ssg->ch[i].tone_counter = 0; + ssg->ch[i].out = false; + } + for (int i = 0; i < 0x10; i++) { + ssg->regs[i] = 0; + } + ssg->noise_counter = 0; + ssg->lfsr = 0; + ssg->env_counter = 0; + ssg->env_level = 0; + ssg->env_att = false; + ssg->env_alt = false; + ssg->env_hld = false; + ssg->env_holding = false; +} + +void opna_ssg_resampler_reset(struct opna_ssg_resampler *resampler) { + for (int i = 0; i < SINCTABLELEN; i++) { + resampler->buf[i] = 0; + } + resampler->index = 0; +} + +void opna_ssg_writereg(struct opna_ssg *ssg, unsigned reg, unsigned val) { + if (reg > 0xfu) return; + val &= 0xff; + ssg->regs[reg] = val; + + if (reg == 0xd) { + ssg->env_att = ssg->regs[0xd] & 0x4; + if (ssg->regs[0xd] & 0x8) { + ssg->env_alt = ssg->regs[0xd] & 0x2; + ssg->env_hld = ssg->regs[0xd] & 0x1; + } else { + ssg->env_alt = ssg->env_att; + ssg->env_hld = true; + } + ssg->env_holding = false; + ssg->env_level = 0; + ssg->env_counter = 0; + } +} + +static int opna_ssg_tone_period(const struct opna_ssg *ssg, int chan) { + return ssg->regs[0+chan*2] | ((ssg->regs[1+chan*2] & 0xf) << 8); +} + +static bool opna_ssg_chan_env(const struct opna_ssg *ssg, int chan) { + return ssg->regs[0x8+chan] & 0x10; +} +static int opna_ssg_tone_volume(const struct opna_ssg *ssg, int chan) { + return ssg->regs[0x8+chan] & 0xf; +} + +static bool opna_ssg_tone_out(const struct opna_ssg *ssg, int chan) { + unsigned reg = ssg->regs[0x7] >> chan; + return (ssg->ch[chan].out || (reg & 0x1)) && ((ssg->lfsr & 1) || (reg & 0x8)); +} + +static bool opna_ssg_tone_silent(const struct opna_ssg *ssg, int chan) { + unsigned reg = ssg->regs[0x7] >> chan; + return (reg & 0x1) && (reg & 0x8); +} + +static int opna_ssg_noise_period(const struct opna_ssg *ssg) { + return ssg->regs[0x6] & 0x1f; +} + +static int opna_ssg_env_period(const struct opna_ssg *ssg) { + return (ssg->regs[0xc] << 8) | ssg->regs[0xb]; +} + +static int opna_ssg_env_level(const struct opna_ssg *ssg) { + return ssg->env_att ? ssg->env_level : 31-ssg->env_level; +} + +int opna_ssg_channel_level(const struct opna_ssg *ssg, int ch) { + return opna_ssg_chan_env(ssg, ch) + ? opna_ssg_env_level(ssg) + : (opna_ssg_tone_volume(ssg, ch) << 1) + 1; +} + +void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples) { + for (int i = 0; i < samples; i++) { + if (((++ssg->noise_counter) >> 1) >= opna_ssg_noise_period(ssg)) { + ssg->noise_counter = 0; + ssg->lfsr |= (!((ssg->lfsr & 1) ^ ((ssg->lfsr >> 3) & 1))) << 17; + ssg->lfsr >>= 1; + } + if (!ssg->env_holding) { + if (++ssg->env_counter >= opna_ssg_env_period(ssg)) { + ssg->env_counter = 0; + ssg->env_level++; + if (ssg->env_level == 0x20) { + ssg->env_level = 0; + if (ssg->env_alt) { + ssg->env_att = !ssg->env_att; + } + if (ssg->env_hld) { + ssg->env_level = 0x1f; + ssg->env_holding = true; + } + } + } + } + + int16_t out = 0; + for (int ch = 0; ch < 3; ch++) { + if (++ssg->ch[ch].tone_counter >= opna_ssg_tone_period(ssg, ch)) { + ssg->ch[ch].tone_counter = 0; + ssg->ch[ch].out = !ssg->ch[ch].out; + } + /* + if (opna_ssg_tone_out(ssg, ch)) { + int level = opna_ssg_chan_env(ssg, ch) + ? opna_ssg_env_level(ssg) + : (opna_ssg_tone_volume(ssg, ch) << 1) + 1; + out += voltable[level]; + } + */ + if (!opna_ssg_tone_silent(ssg, ch)) { + int level = opna_ssg_channel_level(ssg, ch); + out += opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]; + } + + } + buf[i] = out / 4; + } +} + +#define BUFINDEX(n) ((((resampler->index)>>1)+n)&(SINCTABLELEN-1)) + +void opna_ssg_mix_55466( + struct opna_ssg *ssg, struct opna_ssg_resampler *resampler, + int16_t *buf, int samples) { + for (int i = 0; i < samples; i++) { + { + int ssg_samples = ((resampler->index + 9)>>1) - ((resampler->index)>>1); + int16_t ssgbuf[5]; + opna_ssg_generate_raw(ssg, ssgbuf, ssg_samples); + for (int j = 0; j < ssg_samples; j++) { + resampler->buf[BUFINDEX(j)] = ssgbuf[j]; + } + resampler->index += 9; + } + int32_t sample = 0; + for (int j = 0; j < SINCTABLELEN; j++) { + unsigned sincindex = j*2; + if (!(resampler->index&1)) sincindex++; + bool sincsign = sincindex & (1<<(SINCTABLEBIT)); + unsigned sincmask = ((1<<(SINCTABLEBIT))-1); + sincindex = (sincindex & sincmask) ^ (sincsign ? sincmask : 0); + sample += (resampler->buf[BUFINDEX(j)] * sinctable[sincindex])>>2; + } + + sample >>= 16; + sample *= 13000; + sample >>= 14; + + int32_t lo = buf[i*2+0]; + int32_t ro = buf[i*2+1]; + lo += sample; + ro += sample; + 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; + } +} +#undef BUFINDEX diff --git a/libopna/opnassg.h b/libopna/opnassg.h new file mode 100644 index 0000000..5928c49 --- /dev/null +++ b/libopna/opnassg.h @@ -0,0 +1,60 @@ +#ifndef LIBOPNA_OPNASSG_H_INCLUDED +#define LIBOPNA_OPNASSG_H_INCLUDED + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct opna_ssg_ch { + uint16_t tone_counter; + bool out; +}; + +struct opna_ssg { + uint8_t regs[0x10]; + struct opna_ssg_ch ch[3]; + uint8_t noise_counter; + uint32_t lfsr; + uint16_t env_counter; + uint8_t env_level; + bool env_att; + bool env_alt; + bool env_hld; + bool env_holding; +}; + +struct opna_ssg_resampler { + int16_t buf[(1<<7)]; + unsigned index; +}; + +void opna_ssg_reset(struct opna_ssg *ssg); +void opna_ssg_resampler_reset(struct opna_ssg_resampler *resampler); +// generate raw data +// Monoral +// Output level: [-32766, 32766] +// Samplerate: clock / 8 +// (on opna: masterclock / 32 +// 7987200 / 32 = 249600) +void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples); + +// mix samplerate converted data for mixing with OPNA output +// call to buffer written with OPNA output +// samplerate: 7987200/144 Hz +// (55466.66..) Hz +void opna_ssg_mix_55466( + struct opna_ssg *ssg, struct opna_ssg_resampler *resampler, + int16_t *buf, int samples); +void opna_ssg_writereg(struct opna_ssg *ssg, unsigned reg, unsigned val); + +// channel level (0 - 31) +int opna_ssg_channel_level(const struct opna_ssg *ssg, int ch); + +#ifdef __cplusplus +} +#endif + +#endif // LIBOPNA_OPNASSG_H_INCLUDED diff --git a/libopna/opnatables.h b/libopna/opnatables.h new file mode 100644 index 0000000..30140bf --- /dev/null +++ b/libopna/opnatables.h @@ -0,0 +1,242 @@ +#define LOGSINTABLEBIT 8 +#define LOGSINTABLELEN (1<<LOGSINTABLEBIT) +// round(-256.0*log2((sin((2*i+1)*PI/1024.0)))) +static const uint16_t logsintable[LOGSINTABLELEN] = { + 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, + 1091, 1050, 1013, 979, 949, 920, 894, 869, + 846, 825, 804, 785, 767, 749, 732, 717, + 701, 687, 672, 659, 646, 633, 621, 609, + 598, 587, 576, 566, 556, 546, 536, 527, + 518, 509, 501, 492, 484, 476, 468, 461, + 453, 446, 439, 432, 425, 418, 411, 405, + 399, 392, 386, 380, 375, 369, 363, 358, + 352, 347, 341, 336, 331, 326, 321, 316, + 311, 307, 302, 297, 293, 289, 284, 280, + 276, 271, 267, 263, 259, 255, 251, 248, + 244, 240, 236, 233, 229, 226, 222, 219, + 215, 212, 209, 205, 202, 199, 196, 193, + 190, 187, 184, 181, 178, 175, 172, 169, + 167, 164, 161, 159, 156, 153, 151, 148, + 146, 143, 141, 138, 136, 134, 131, 129, + 127, 125, 122, 120, 118, 116, 114, 112, + 110, 108, 106, 104, 102, 100, 98, 96, + 94, 92, 91, 89, 87, 85, 83, 82, + 80, 78, 77, 75, 74, 72, 70, 69, + 67, 66, 64, 63, 62, 60, 59, 57, + 56, 55, 53, 52, 51, 49, 48, 47, + 46, 45, 43, 42, 41, 40, 39, 38, + 37, 36, 35, 34, 33, 32, 31, 30, + 29, 28, 27, 26, 25, 24, 23, 23, + 22, 21, 20, 20, 19, 18, 17, 17, + 16, 15, 15, 14, 13, 13, 12, 12, + 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 5, 5, 5, 4, + 4, 4, 3, 3, 3, 2, 2, 2, + 2, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +#define LOGSINTABLEHIRESBIT 10 +#define LOGSINTABLEHIRESLEN (1<<LOGSINTABLEHIRESBIT) +static const uint16_t logsintable_hires[LOGSINTABLEHIRESLEN] = { + 2649, 2243, 2055, 1931, 1838, 1764, 1702, 1649, + 1603, 1562, 1525, 1491, 1460, 1432, 1406, 1381, + 1358, 1336, 1316, 1296, 1278, 1260, 1243, 1227, + 1212, 1197, 1183, 1169, 1156, 1143, 1131, 1119, + 1108, 1096, 1086, 1075, 1065, 1055, 1045, 1036, + 1026, 1017, 1009, 1000, 992, 984, 976, 968, + 960, 952, 945, 938, 931, 924, 917, 910, + 904, 897, 891, 885, 879, 872, 867, 861, + 855, 849, 844, 838, 833, 827, 822, 817, + 812, 807, 802, 797, 792, 787, 783, 778, + 773, 769, 764, 760, 756, 751, 747, 743, + 739, 735, 730, 726, 722, 718, 715, 711, + 707, 703, 699, 696, 692, 688, 685, 681, + 678, 674, 671, 667, 664, 661, 657, 654, + 651, 647, 644, 641, 638, 635, 632, 629, + 626, 623, 620, 617, 614, 611, 608, 605, + 602, 599, 597, 594, 591, 588, 586, 583, + 580, 578, 575, 572, 570, 567, 565, 562, + 559, 557, 554, 552, 550, 547, 545, 542, + 540, 538, 535, 533, 531, 528, 526, 524, + 521, 519, 517, 515, 512, 510, 508, 506, + 504, 502, 500, 497, 495, 493, 491, 489, + 487, 485, 483, 481, 479, 477, 475, 473, + 471, 469, 467, 465, 463, 462, 460, 458, + 456, 454, 452, 450, 449, 447, 445, 443, + 441, 440, 438, 436, 434, 433, 431, 429, + 427, 426, 424, 422, 421, 419, 417, 416, + 414, 412, 411, 409, 407, 406, 404, 403, + 401, 399, 398, 396, 395, 393, 392, 390, + 389, 387, 386, 384, 383, 381, 380, 378, + 377, 375, 374, 372, 371, 369, 368, 367, + 365, 364, 362, 361, 360, 358, 357, 355, + 354, 353, 351, 350, 349, 347, 346, 345, + 343, 342, 341, 339, 338, 337, 336, 334, + 333, 332, 330, 329, 328, 327, 325, 324, + 323, 322, 320, 319, 318, 317, 316, 314, + 313, 312, 311, 310, 308, 307, 306, 305, + 304, 303, 301, 300, 299, 298, 297, 296, + 295, 294, 292, 291, 290, 289, 288, 287, + 286, 285, 284, 283, 281, 280, 279, 278, + 277, 276, 275, 274, 273, 272, 271, 270, + 269, 268, 267, 266, 265, 264, 263, 262, + 261, 260, 259, 258, 257, 256, 255, 254, + 253, 252, 251, 250, 249, 248, 247, 246, + 245, 244, 243, 242, 242, 241, 240, 239, + 238, 237, 236, 235, 234, 233, 232, 231, + 231, 230, 229, 228, 227, 226, 225, 224, + 224, 223, 222, 221, 220, 219, 218, 218, + 217, 216, 215, 214, 213, 212, 212, 211, + 210, 209, 208, 208, 207, 206, 205, 204, + 203, 203, 202, 201, 200, 199, 199, 198, + 197, 196, 196, 195, 194, 193, 192, 192, + 191, 190, 189, 189, 188, 187, 186, 186, + 185, 184, 183, 183, 182, 181, 180, 180, + 179, 178, 178, 177, 176, 175, 175, 174, + 173, 173, 172, 171, 171, 170, 169, 168, + 168, 167, 166, 166, 165, 164, 164, 163, + 162, 162, 161, 160, 160, 159, 158, 158, + 157, 156, 156, 155, 154, 154, 153, 152, + 152, 151, 151, 150, 149, 149, 148, 147, + 147, 146, 145, 145, 144, 144, 143, 142, + 142, 141, 141, 140, 139, 139, 138, 138, + 137, 136, 136, 135, 135, 134, 133, 133, + 132, 132, 131, 131, 130, 129, 129, 128, + 128, 127, 127, 126, 125, 125, 124, 124, + 123, 123, 122, 122, 121, 121, 120, 119, + 119, 118, 118, 117, 117, 116, 116, 115, + 115, 114, 114, 113, 113, 112, 112, 111, + 110, 110, 109, 109, 108, 108, 107, 107, + 106, 106, 105, 105, 104, 104, 103, 103, + 102, 102, 101, 101, 101, 100, 100, 99, + 99, 98, 98, 97, 97, 96, 96, 95, + 95, 94, 94, 93, 93, 93, 92, 92, + 91, 91, 90, 90, 89, 89, 88, 88, + 88, 87, 87, 86, 86, 85, 85, 85, + 84, 84, 83, 83, 82, 82, 82, 81, + 81, 80, 80, 79, 79, 79, 78, 78, + 77, 77, 77, 76, 76, 75, 75, 75, + 74, 74, 73, 73, 73, 72, 72, 71, + 71, 71, 70, 70, 69, 69, 69, 68, + 68, 68, 67, 67, 66, 66, 66, 65, + 65, 65, 64, 64, 64, 63, 63, 62, + 62, 62, 61, 61, 61, 60, 60, 60, + 59, 59, 59, 58, 58, 58, 57, 57, + 57, 56, 56, 55, 55, 55, 54, 54, + 54, 54, 53, 53, 53, 52, 52, 52, + 51, 51, 51, 50, 50, 50, 49, 49, + 49, 48, 48, 48, 47, 47, 47, 47, + 46, 46, 46, 45, 45, 45, 44, 44, + 44, 44, 43, 43, 43, 42, 42, 42, + 42, 41, 41, 41, 40, 40, 40, 40, + 39, 39, 39, 38, 38, 38, 38, 37, + 37, 37, 37, 36, 36, 36, 36, 35, + 35, 35, 35, 34, 34, 34, 34, 33, + 33, 33, 33, 32, 32, 32, 32, 31, + 31, 31, 31, 30, 30, 30, 30, 29, + 29, 29, 29, 28, 28, 28, 28, 28, + 27, 27, 27, 27, 26, 26, 26, 26, + 26, 25, 25, 25, 25, 24, 24, 24, + 24, 24, 23, 23, 23, 23, 23, 22, + 22, 22, 22, 22, 21, 21, 21, 21, + 21, 20, 20, 20, 20, 20, 19, 19, + 19, 19, 19, 18, 18, 18, 18, 18, + 18, 17, 17, 17, 17, 17, 17, 16, + 16, 16, 16, 16, 15, 15, 15, 15, + 15, 15, 15, 14, 14, 14, 14, 14, + 14, 13, 13, 13, 13, 13, 13, 12, + 12, 12, 12, 12, 12, 12, 11, 11, + 11, 11, 11, 11, 11, 10, 10, 10, + 10, 10, 10, 10, 10, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#define EXPTABLEBIT 8 +#define EXPTABLELEN (1<<EXPTABLEBIT) +// round((1<<11) / pow(2.0, (i+1.0)/256.0)) +static const uint16_t exptable[EXPTABLELEN] = { + 2042, 2037, 2031, 2026, 2020, 2015, 2010, 2004, + 1999, 1993, 1988, 1983, 1977, 1972, 1966, 1961, + 1956, 1951, 1945, 1940, 1935, 1930, 1924, 1919, + 1914, 1909, 1904, 1898, 1893, 1888, 1883, 1878, + 1873, 1868, 1863, 1858, 1853, 1848, 1843, 1838, + 1833, 1828, 1823, 1818, 1813, 1808, 1803, 1798, + 1794, 1789, 1784, 1779, 1774, 1769, 1765, 1760, + 1755, 1750, 1746, 1741, 1736, 1732, 1727, 1722, + 1717, 1713, 1708, 1704, 1699, 1694, 1690, 1685, + 1681, 1676, 1672, 1667, 1663, 1658, 1654, 1649, + 1645, 1640, 1636, 1631, 1627, 1623, 1618, 1614, + 1609, 1605, 1601, 1596, 1592, 1588, 1584, 1579, + 1575, 1571, 1566, 1562, 1558, 1554, 1550, 1545, + 1541, 1537, 1533, 1529, 1525, 1520, 1516, 1512, + 1508, 1504, 1500, 1496, 1492, 1488, 1484, 1480, + 1476, 1472, 1468, 1464, 1460, 1456, 1452, 1448, + 1444, 1440, 1436, 1433, 1429, 1425, 1421, 1417, + 1413, 1409, 1406, 1402, 1398, 1394, 1391, 1387, + 1383, 1379, 1376, 1372, 1368, 1364, 1361, 1357, + 1353, 1350, 1346, 1342, 1339, 1335, 1332, 1328, + 1324, 1321, 1317, 1314, 1310, 1307, 1303, 1300, + 1296, 1292, 1289, 1286, 1282, 1279, 1275, 1272, + 1268, 1265, 1261, 1258, 1255, 1251, 1248, 1244, + 1241, 1238, 1234, 1231, 1228, 1224, 1221, 1218, + 1214, 1211, 1208, 1205, 1201, 1198, 1195, 1192, + 1188, 1185, 1182, 1179, 1176, 1172, 1169, 1166, + 1163, 1160, 1157, 1154, 1150, 1147, 1144, 1141, + 1138, 1135, 1132, 1129, 1126, 1123, 1120, 1117, + 1114, 1111, 1108, 1105, 1102, 1099, 1096, 1093, + 1090, 1087, 1084, 1081, 1078, 1075, 1072, 1069, + 1066, 1064, 1061, 1058, 1055, 1052, 1049, 1046, + 1044, 1041, 1038, 1035, 1032, 1030, 1027, 1024, +}; + +static const uint8_t rateinctable[4*2][8] = { +// rates 0 - 47 + {0, 1, 0, 1, 0, 1, 0, 1}, + {0, 1, 0, 1, 1, 1, 0, 1}, + {0, 1, 1, 1, 0, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1}, + +// rates 48 - + {1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 2}, + {1, 2, 1, 2, 1, 2, 1, 2}, + {1, 2, 2, 2, 1, 2, 2, 2}, +}; + +// datasheet 0.053Hz = 1 +// [FD][keycode] +static const uint8_t dettable[4][32]={ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + { + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + }, + { + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, + }, + { + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8, 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22, + }, +}; diff --git a/libopna/opnatimer.c b/libopna/opnatimer.c new file mode 100644 index 0000000..195e426 --- /dev/null +++ b/libopna/opnatimer.c @@ -0,0 +1,79 @@ +#include "opnatimer.h" +#include "opna.h" + +enum { + TIMERB_SHIFT = 4, + TIMERB_BITS = 8 + TIMERB_SHIFT, +}; + +void opna_timer_reset(struct opna_timer *timer, struct opna *opna) { + timer->opna = opna; + timer->status = 0; + timer->interrupt_cb = 0; + timer->interrupt_userptr = 0; + timer->mix_cb = 0; + timer->mix_userptr = 0; + timer->timerb = 0; + timer->timerb_load = false; + timer->timerb_enable = false; + timer->timerb_cnt = 0; +} + +uint8_t opna_timer_status(const struct opna_timer *timer) { + return timer->status; +} + +void opna_timer_set_int_callback(struct opna_timer *timer, opna_timer_int_cb_t func, void *userptr) { + timer->interrupt_cb = func; + timer->interrupt_userptr = userptr; +} + +void opna_timer_set_mix_callback(struct opna_timer *timer, opna_timer_mix_cb_t func, void *userptr) { + timer->mix_cb = func; + timer->mix_userptr = userptr; +} + +void opna_timer_writereg(struct opna_timer *timer, unsigned reg, unsigned val) { + val &= 0xff; + opna_writereg(timer->opna, reg, val); + switch (reg) { + case 0x26: + timer->timerb = val; + timer->timerb_cnt = timer->timerb << TIMERB_SHIFT; + break; + case 0x27: + timer->timerb_load = val & (1<<1); + timer->timerb_enable = val & (1<<3); + if (val & (1<<5)) { + //timer->timerb_cnt = timer->timerb << TIMERB_SHIFT; + timer->status &= ~(1<<1); + } + } +} + +void opna_timer_mix(struct opna_timer *timer, int16_t *buf, unsigned samples) { + do { + unsigned generate_samples = samples; + if (timer->timerb_enable) { + unsigned timerb_samples = (1<<TIMERB_BITS) - timer->timerb_cnt; + if (timerb_samples < generate_samples) { + generate_samples = timerb_samples; + } + } + opna_mix(timer->opna, buf, generate_samples); + if (timer->mix_cb) { + timer->mix_cb(timer->mix_userptr, buf, generate_samples); + } + buf += generate_samples*2; + samples -= generate_samples; + if (timer->timerb_load) { + timer->timerb_cnt = (timer->timerb_cnt + generate_samples) & ((1<<TIMERB_BITS)-1); + if (!timer->timerb_cnt && timer->timerb_enable) { + if (!(timer->status & (1<<1))) { + timer->status |= (1<<1); + timer->interrupt_cb(timer->interrupt_userptr); + } + } + } + } while (samples); +} diff --git a/libopna/opnatimer.h b/libopna/opnatimer.h new file mode 100644 index 0000000..f3094ba --- /dev/null +++ b/libopna/opnatimer.h @@ -0,0 +1,42 @@ +#ifndef LIBOPNA_OPNA_TIMER_H_INCLUDED +#define LIBOPNA_OPNA_TIMER_H_INCLUDED + +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*opna_timer_int_cb_t)(void *ptr); +typedef void (*opna_timer_mix_cb_t)(void *ptr, int16_t *buf, unsigned samples); + +struct opna; + +struct opna_timer { + struct opna *opna; + uint8_t status; + opna_timer_int_cb_t interrupt_cb; + void *interrupt_userptr; + opna_timer_mix_cb_t mix_cb; + void *mix_userptr; + uint8_t timerb; + bool timerb_load; + bool timerb_enable; + uint16_t timerb_cnt; +}; + +void opna_timer_reset(struct opna_timer *timer, struct opna *opna); +uint8_t opna_timer_status(const struct opna_timer *timer); +void opna_timer_set_int_callback(struct opna_timer *timer, + opna_timer_int_cb_t func, void *userptr); +void opna_timer_set_mix_callback(struct opna_timer *timer, + opna_timer_mix_cb_t func, void *userptr); +void opna_timer_writereg(struct opna_timer *timer, unsigned reg, unsigned val); +void opna_timer_mix(struct opna_timer *timer, int16_t *buf, unsigned samples); + +#ifdef __cplusplus +} +#endif + +#endif // LIBOPNA_OPNA_TIMER_H_INCLUDED diff --git a/libopna/s98gen.c b/libopna/s98gen.c new file mode 100644 index 0000000..cc2b529 --- /dev/null +++ b/libopna/s98gen.c @@ -0,0 +1,141 @@ +#include "s98gen.h" + +static uint32_t read32le(const void *dataptr, size_t offset) { + const uint8_t *data = (const uint8_t *)dataptr; + return ((uint32_t)data[offset+0]) | (((uint32_t)data[offset+1])<<8) | + (((uint32_t)data[offset+2])<<16) | (((uint32_t)data[offset+3])<<24); +} + +bool s98gen_init(struct s98gen *s98, void *s98dataptr, size_t s98data_size) { + uint8_t *s98data = (uint8_t *)s98dataptr; + if (s98data_size < 0x20) { + // size of s98 header + return false; + } + if (s98data[0] != 'S' || s98data[1] != '9' || + s98data[2] != '8') { + // file magic / version check + return false; + } + if ((s98data[3] != '1') && (s98data[3] != '3')) { + return false; + } + if (s98data[0xc] != 0) { + // COMPRESSING flag not zero + return false; + } + uint32_t clock = 7987200; + if ((s98data[3] == '3') && s98data[0x1c]) { + if (s98data_size < 0x30) { + // size of s98 header + device info + return false; + } + uint32_t devicetype = read32le(s98data, 0x20); + if (devicetype != 0x04 && devicetype != 0x02) { + // OPN / OPNA only + return false; + } + clock = read32le(s98data, 0x24); + // convert OPN clock to OPNA clock + if (devicetype == 2) clock *= 2; + } + s98->s98data = s98data; + s98->s98data_size = s98data_size; + s98->current_offset = read32le(s98data, 0x14); + s98->opnaclock = clock; + s98->samples_to_generate = 0; + s98->samples_to_generate_frac = 0; + s98->timer_numerator = read32le(s98data, 0x04); + s98->timer_denominator = read32le(s98data, 0x08); + if (!s98->timer_numerator) s98->timer_numerator = 10; + if (!s98->timer_denominator) s98->timer_denominator = 1000; + opna_reset(&s98->opna); + return true; +} + +// returns 0 when failed +static size_t s98gen_getvv(struct s98gen *s98) { + unsigned shift = 0; + size_t value = 0; + do { + if (s98->s98data_size < (s98->current_offset+1)) return 0; + value |= (s98->s98data[s98->current_offset] & 0x7f) << ((shift++)*7); + } while (s98->s98data[s98->current_offset++] & 0x80); + return value + 2; +} + +static void s98gen_set_samples_to_generate(struct s98gen *s98, size_t ticks) { + uint64_t s = s98->opnaclock * s98->timer_numerator * ticks; + s <<= 16; + s /= 144 * s98->timer_denominator; + s += s98->samples_to_generate_frac; + s98->samples_to_generate = s >> 16; + s98->samples_to_generate_frac = s & ((((size_t)1)<<16)-1); +} + +static bool s98gen_parse_s98(struct s98gen *s98) { + for (;;) { + switch (s98->s98data[s98->current_offset]) { + case 0x00: + if (s98->s98data_size < (s98->current_offset + 3)) return false; + opna_writereg(&s98->opna, + s98->s98data[s98->current_offset+1], + s98->s98data[s98->current_offset+2]); + s98->current_offset += 3; + break; + case 0x01: + if (s98->s98data_size < (s98->current_offset + 3)) return false; + opna_writereg(&s98->opna, + s98->s98data[s98->current_offset+1] | 0x100, + s98->s98data[s98->current_offset+2]); + s98->current_offset += 3; + break; + case 0xfe: + if (s98->s98data_size < (s98->current_offset+1)) return false; + s98->current_offset++; + { + size_t vv = s98gen_getvv(s98); + if (!vv) return false; + s98gen_set_samples_to_generate(s98, vv); + } + return true; + case 0xff: + if (s98->s98data_size < (s98->current_offset + 1)) return false; + s98->current_offset++; + s98gen_set_samples_to_generate(s98, 1); + return true; + default: + return false; + } + } +} + +bool s98gen_generate(struct s98gen *s98, int16_t *buf, size_t samples) { + for (size_t i = 0; i < samples; i++) { + buf[i*2+0] = 0; + buf[i*2+1] = 0; + } + if (samples <= s98->samples_to_generate) { + opna_mix(&s98->opna, buf, samples); + s98->samples_to_generate -= samples; + } else { + opna_mix(&s98->opna, buf, s98->samples_to_generate); + buf += s98->samples_to_generate * 2; + samples -= s98->samples_to_generate; + s98->samples_to_generate = 0; + while (samples) { + if (!s98gen_parse_s98(s98)) return false; + if (s98->samples_to_generate >= samples) { + opna_mix(&s98->opna, buf, samples); + s98->samples_to_generate -= samples; + samples = 0; + } else { + opna_mix(&s98->opna, buf, s98->samples_to_generate); + buf += s98->samples_to_generate*2; + samples -= s98->samples_to_generate; + s98->samples_to_generate = 0; + } + } + } + return true; +} diff --git a/libopna/s98gen.h b/libopna/s98gen.h new file mode 100644 index 0000000..8fa458d --- /dev/null +++ b/libopna/s98gen.h @@ -0,0 +1,34 @@ +#ifndef MYON_S98GEN_H_INCLUDED +#define MYON_S98GEN_H_INCLUDED + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include "opna.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct s98gen { + struct opna opna; + uint8_t *s98data; + size_t s98data_size; + size_t current_offset; + uint32_t opnaclock; + uint32_t samples_to_generate; + uint16_t samples_to_generate_frac; + uint32_t timer_numerator; + uint32_t timer_denominator; +}; + +// returns true if initialization succeeded +// returns false without touching *s98 when initialization failed (invalid data) +bool s98gen_init(struct s98gen *s98, void *s98data, size_t s98data_size); +bool s98gen_generate(struct s98gen *s98, int16_t *buf, size_t samples); + +#ifdef __cplusplus +} +#endif + +#endif // MYON_S98GEN_H_INCLUDED |