diff options
author | Takamichi Horikawa <takamichiho@gmail.com> | 2017-03-27 23:33:40 +0900 |
---|---|---|
committer | Takamichi Horikawa <takamichiho@gmail.com> | 2017-03-27 23:33:40 +0900 |
commit | 30c59a00956142aafda87c0bdc71c46d1a2218ff (patch) | |
tree | 0bf1c81767dc8edb86ade2a4e224392e2ac5280b /libopna | |
parent | 0073f2b8befc6163f2970cb7a01e75fffc95994e (diff) |
add oscilloscope view
Diffstat (limited to 'libopna')
-rw-r--r-- | libopna/opna.c | 18 | ||||
-rw-r--r-- | libopna/opna.h | 6 | ||||
-rw-r--r-- | libopna/opnafm.c | 43 | ||||
-rw-r--r-- | libopna/opnafm.h | 3 | ||||
-rw-r--r-- | libopna/opnassg.c | 67 | ||||
-rw-r--r-- | libopna/opnassg.h | 7 | ||||
-rw-r--r-- | libopna/opnatimer.c | 10 | ||||
-rw-r--r-- | libopna/opnatimer.h | 2 |
8 files changed, 130 insertions, 26 deletions
diff --git a/libopna/opna.c b/libopna/opna.c index 10155d0..1b0aa6c 100644 --- a/libopna/opna.c +++ b/libopna/opna.c @@ -1,4 +1,6 @@ #include "opna.h" +#include "oscillo/oscillo.h" +#include <string.h> void opna_reset(struct opna *opna) { opna_fm_reset(&opna->fm); @@ -23,8 +25,20 @@ unsigned opna_readreg(const struct opna *opna, unsigned reg) { } 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_mix_oscillo(opna, buf, samples, 0); +} + +void opna_mix_oscillo(struct opna *opna, int16_t *buf, unsigned samples, struct oscillodata *oscillo) { + if (oscillo) { + for (int i = 0; i < LIBOPNA_OSCILLO_TRACK_COUNT; i++) { + memmove(&oscillo[i].buf[0], + &oscillo[i].buf[samples], + (OSCILLO_SAMPLE_COUNT - samples)*sizeof(oscillo[i].buf[0])); + } + } + unsigned offset = OSCILLO_SAMPLE_COUNT - samples; + opna_fm_mix(&opna->fm, buf, samples, &oscillo[0], offset); + opna_ssg_mix_55466(&opna->ssg, &opna->resampler, buf, samples, &oscillo[6], offset); opna_drum_mix(&opna->drum, buf, samples); opna_adpcm_mix(&opna->adpcm, buf, samples); } diff --git a/libopna/opna.h b/libopna/opna.h index 7d7d722..2ebca0d 100644 --- a/libopna/opna.h +++ b/libopna/opna.h @@ -30,6 +30,10 @@ enum { LIBOPNA_CHAN_ADPCM = 0x8000, }; +enum { + LIBOPNA_OSCILLO_TRACK_COUNT = 11 +}; + struct opna { struct opna_fm fm; struct opna_ssg ssg; @@ -43,6 +47,8 @@ void opna_reset(struct opna *opna); void opna_writereg(struct opna *opna, unsigned reg, unsigned val); unsigned opna_readreg(const struct opna *opna, unsigned reg); void opna_mix(struct opna *opna, int16_t *buf, unsigned samples); +struct oscillodata; +void opna_mix_oscillo(struct opna *opna, int16_t *buf, unsigned samples, struct oscillodata *oscillo); unsigned opna_get_mask(const struct opna *opna); void opna_set_mask(struct opna *opna, unsigned mask); diff --git a/libopna/opnafm.c b/libopna/opnafm.c index 3f5367f..abc36fa 100644 --- a/libopna/opnafm.c +++ b/libopna/opnafm.c @@ -1,4 +1,5 @@ #include "opnafm.h" +#include "oscillo/oscillo.h" #include "opnatables.h" @@ -539,7 +540,46 @@ void opna_fm_chan_env(struct opna_fm_channel *chan) { } } -void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples) { +static int gcd(int a, int b) { + if (a < b) { + int t = a; + a = b; + b = t; + } + for (;;) { + int r = a % b; + if (!r) break; + a = b; + b = r; + } + return b; +} + + + +void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples, + struct oscillodata *oscillo, unsigned offset) { + if (oscillo) { + for (unsigned c = 0; c < 6; c++) { + const struct opna_fm_channel *ch = &fm->channel[c]; + unsigned freq = blkfnum2freq(ch->blk, ch->fnum); + int mul[4]; + for (int i = 0; i < 4; i++) { + mul[i] = ch->slot[i].mul << 1; + if (!mul[i]) mul[i] = 1; + } + freq *= gcd(gcd(gcd(mul[0], mul[1]), mul[2]), mul[3]); + freq /= 2; + unsigned period = 0; + if (freq) period = (1u<<(20+OSCILLO_OFFSET_SHIFT)) / freq; + if (period) { + oscillo[c].offset += (samples << OSCILLO_OFFSET_SHIFT); + oscillo[c].offset %= period; + } else { + oscillo[c].offset = 0; + } + } + } for (unsigned i = 0; i < samples; i++) { if (!fm->env_div3) { for (int c = 0; c < 6; c++) { @@ -554,6 +594,7 @@ void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples) { for (int c = 0; c < 6; c++) { int16_t o = opna_fm_chanout(&fm->channel[c]); + if (oscillo) oscillo[c].buf[offset+i] = o*2; // TODO: CSM if (c == 2 && fm->ch3.mode != CH3_MODE_NORMAL) { opna_fm_chan_phase_se(&fm->channel[c], fm); diff --git a/libopna/opnafm.h b/libopna/opnafm.h index 1a7f55a..b62556e 100644 --- a/libopna/opnafm.h +++ b/libopna/opnafm.h @@ -88,7 +88,8 @@ struct opna_fm { }; void opna_fm_reset(struct opna_fm *fm); -void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples); +struct oscillodata; +void opna_fm_mix(struct opna_fm *fm, int16_t *buf, unsigned samples, struct oscillodata *oscillo, unsigned offset); void opna_fm_writereg(struct opna_fm *fm, unsigned reg, unsigned val); // diff --git a/libopna/opnassg.c b/libopna/opnassg.c index 223758c..ec03437 100644 --- a/libopna/opnassg.c +++ b/libopna/opnassg.c @@ -1,4 +1,5 @@ #include "opnassg.h" +#include "oscillo/oscillo.h" /* static const float voltable[32] = { 0.0f, 0.0f, 0x1.ae89f9p-8f, 0x1.000000p-7f, @@ -126,10 +127,12 @@ static bool opna_ssg_tone_out(const struct opna_ssg *ssg, int chan) { return (ssg->ch[chan].out || (reg & 0x1)) && ((ssg->lfsr & 1) || (reg & 0x8)); } +#if 0 static bool opna_ssg_tone_silent(const struct opna_ssg *ssg, int chan) { unsigned reg = ssg->regs[0x7] >> chan; return (reg & 0x1) && (reg & 0x8); } +#endif static int opna_ssg_noise_period(const struct opna_ssg *ssg) { return ssg->regs[0x6] & 0x1f; @@ -149,6 +152,10 @@ int opna_ssg_channel_level(const struct opna_ssg *ssg, int ch) { : (opna_ssg_tone_volume(ssg, ch) << 1) + 1; } +#define COEFF 0x3fff +#define COEFFSH 14 + +// 3 samples per frame 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)) { @@ -173,31 +180,38 @@ void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples) { } } - int16_t out = 0; + //int16_t out = 0; for (int ch = 0; ch < 3; ch++) { + buf[i*3+ch] = 0; 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 (ssg->mask & (1<<ch)) continue; #if 1 - // may output DC offset // YMF288 seems to disable output when 0 <= Tp < 8 + int32_t previntmp = 0; 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]; + //out += voltable[level]; + previntmp = voltable[level]/2; } + previntmp *= COEFF; + ssg->prevout[ch] = previntmp - ssg->previn[ch] + ((((int64_t)COEFF)*ssg->prevout[ch]) >> COEFFSH); + ssg->previn[ch] = previntmp; + buf[i*3+ch] = ssg->prevout[ch] >> COEFFSH; + //buf[i*3+ch] = voltable[level]/2; #else 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]) / 2; + //out += (opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]) / 2; + buf[i*3+ch] = (opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]) / 4; } #endif } - buf[i] = out / 2; + //buf[i] = out / 2; } } @@ -205,27 +219,46 @@ void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples) { void opna_ssg_mix_55466( struct opna_ssg *ssg, struct opna_ssg_resampler *resampler, - int16_t *buf, int samples) { + int16_t *buf, int samples, + struct oscillodata *oscillo, unsigned offset +) { + if (oscillo) { + for (unsigned c = 0; c < 3; c++) { + unsigned period = (opna_ssg_tone_period(ssg, c) << OSCILLO_OFFSET_SHIFT) * 2 * 32 / 144; + if (period) { + oscillo[c].offset += (samples << OSCILLO_OFFSET_SHIFT); + oscillo[c].offset %= period; + } else { + oscillo[c].offset = 0; + } + } + } for (int i = 0; i < samples; i++) { { int ssg_samples = ((resampler->index + 9)>>1) - ((resampler->index)>>1); - int16_t ssgbuf[5]; + int16_t ssgbuf[15]; opna_ssg_generate_raw(ssg, ssgbuf, ssg_samples); for (int j = 0; j < ssg_samples; j++) { - resampler->buf[BUFINDEX(j)] = ssgbuf[j]; + resampler->buf[BUFINDEX(j)*3+0] = ssgbuf[j*3+0]; + resampler->buf[BUFINDEX(j)*3+1] = ssgbuf[j*3+1]; + resampler->buf[BUFINDEX(j)*3+2] = ssgbuf[j*3+2]; } 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; + for (int ch = 0; ch < 3; ch++) { + int32_t chsample = 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); + chsample += (resampler->buf[BUFINDEX(j)*3+ch] * sinctable[sincindex])>>2; + } + if (oscillo) oscillo[ch].buf[offset+i] = chsample >> 13; + if (!(ssg->mask & (1<<ch))) sample += chsample; } - sample >>= 16; sample *= 13000; sample >>= 14; diff --git a/libopna/opnassg.h b/libopna/opnassg.h index 8a59d91..0321163 100644 --- a/libopna/opnassg.h +++ b/libopna/opnassg.h @@ -25,10 +25,12 @@ struct opna_ssg { bool env_hld; bool env_holding; unsigned mask; + int32_t previn[3]; + int32_t prevout[3]; }; struct opna_ssg_resampler { - int16_t buf[(1<<7)]; + int16_t buf[(1<<7)*3]; unsigned index; }; @@ -46,9 +48,10 @@ void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples); // call to buffer written with OPNA output // samplerate: 7987200/144 Hz // (55466.66..) Hz +struct oscillodata; void opna_ssg_mix_55466( struct opna_ssg *ssg, struct opna_ssg_resampler *resampler, - int16_t *buf, int samples); + int16_t *buf, int samples, struct oscillodata *oscillo, unsigned offset); void opna_ssg_writereg(struct opna_ssg *ssg, unsigned reg, unsigned val); unsigned opna_ssg_readreg(const struct opna_ssg *ssg, unsigned reg); // channel level (0 - 31) diff --git a/libopna/opnatimer.c b/libopna/opnatimer.c index 789c28e..142176f 100644 --- a/libopna/opnatimer.c +++ b/libopna/opnatimer.c @@ -1,5 +1,6 @@ #include "opnatimer.h" #include "opna.h" +#include "oscillo/oscillo.h" enum { TIMERA_BITS = 10, @@ -65,9 +66,12 @@ void opna_timer_writereg(struct opna_timer *timer, unsigned reg, unsigned val) { } } } -#include <stdio.h> -#include <stdlib.h> + void opna_timer_mix(struct opna_timer *timer, int16_t *buf, unsigned samples) { + opna_timer_mix_oscillo(timer, buf, samples, 0); +} + +void opna_timer_mix_oscillo(struct opna_timer *timer, int16_t *buf, unsigned samples, struct oscillodata *oscillo) { do { unsigned generate_samples = samples; if (timer->timerb_enable && timer->timerb_load) { @@ -82,7 +86,7 @@ void opna_timer_mix(struct opna_timer *timer, int16_t *buf, unsigned samples) { generate_samples = timera_samples; } } - opna_mix(timer->opna, buf, generate_samples); + opna_mix_oscillo(timer->opna, buf, generate_samples, oscillo); if (timer->mix_cb) { timer->mix_cb(timer->mix_userptr, buf, generate_samples); } diff --git a/libopna/opnatimer.h b/libopna/opnatimer.h index ad0fe0b..c7b9511 100644 --- a/libopna/opnatimer.h +++ b/libopna/opnatimer.h @@ -37,6 +37,8 @@ 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); +struct oscillodata; +void opna_timer_mix_oscillo(struct opna_timer *timer, int16_t *buf, unsigned samples, struct oscillodata *oscillo); #ifdef __cplusplus } |