From 6fd10cdacb5cbe47a4fc339c20a733d4a9a384a1 Mon Sep 17 00:00:00 2001 From: Takamichi Horikawa Date: Sat, 26 Nov 2016 20:57:57 +0900 Subject: initial --- libopna/opnassg.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 libopna/opnassg.c (limited to 'libopna/opnassg.c') 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<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 -- cgit v1.2.3