diff options
| author | Takamichi Horikawa <takamichiho@gmail.com> | 2017-08-23 21:15:14 +0900 | 
|---|---|---|
| committer | Takamichi Horikawa <takamichiho@gmail.com> | 2017-08-23 21:15:14 +0900 | 
| commit | 499d7dcf4912ba9b224a68439520407c25f8c9ae (patch) | |
| tree | ba01fe33daf3e0dfe533171df8444494ecd0f1d2 /libopna | |
| parent | dfb3f4f10cc66a72027ae5869318cc10baf30a2e (diff) | |
LIBOPNA: ssg: added YMF288 mode
Diffstat (limited to 'libopna')
| -rw-r--r-- | libopna/opnassg.c | 158 | ||||
| -rw-r--r-- | libopna/opnassg.h | 18 | 
2 files changed, 115 insertions, 61 deletions
| diff --git a/libopna/opnassg.c b/libopna/opnassg.c index d92a8b8..a40e6c8 100644 --- a/libopna/opnassg.c +++ b/libopna/opnassg.c @@ -1,22 +1,9 @@  #include "opnassg.h"  #include "oscillo/oscillo.h" -#include <string.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, @@ -27,6 +14,50 @@ static const int16_t voltable[32] = {     3247,  3862,  4592,  5461,     6494,  7723,  9185, 10922  }; +*/ + +// captured from YMF288 +static const uint16_t voltable[32] = { +      0,     0,     0,     0, +      4,     8,    12,    16, +     20,    24,    28,    32, +     36,    44,    52,    64, +     76,    92,   108,   128, +    152,   180,   216,   256, +    304,   360,   428,   512, +    608,   720,   856,  1020, +}; + +/* + * on YMF288: + * when TP >= 8: + * + *          | + *  +voltab +     -----     -----     -- + *          | + *        0 +--------------------------- + *          | + *  -voltab +          -----     ----- + *          | + * + * when TP < 8: + *          | + *  +voltab +     ---------------------- + *          | + *        0 +--------------------------- + *          | + *  -voltab + + *          | + * + * when /TONE=1 && /NOISE=1 (both diabled) + *          | + * +2voltab +     ---------------------- + *          | + *          + + *          | + *        0 +--------------------------- + *          | + */  // GNU Octave  // Fc = 7987200 @@ -111,22 +142,10 @@ const int16_t opna_ssg_sinctable[OPNA_SSG_SINCTABLELEN*2] = {  opna_ssg_sinc_calc_func_type opna_ssg_sinc_calc_func = opna_ssg_sinc_calc_c;  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; -  ssg->mask = 0; +  *ssg = (struct opna_ssg) { +    .mix = 0x10000, +    .ymf288 = true, +  };  }  void opna_ssg_resampler_reset(struct opna_ssg_resampler *resampler) { @@ -182,12 +201,17 @@ 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_out_ymf288(const struct opna_ssg *ssg, int chan) { +  unsigned reg = ssg->regs[0x7] >> chan; +  bool toneout =  +      opna_ssg_tone_period(ssg, chan) < 8 ? true : ssg->ch[chan].out; +  return (toneout || (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);  } -#endif  static int opna_ssg_noise_period(const struct opna_ssg *ssg) {    return ssg->regs[0x6] & 0x1f; @@ -243,28 +267,26 @@ void opna_ssg_generate_raw(struct opna_ssg *ssg, int16_t *buf, int samples) {          ssg->ch[ch].tone_counter = 0;          ssg->ch[ch].out = !ssg->ch[ch].out;        } -#if 1 -      // 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]; -        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*4+ch] = ssg->prevout[ch] >> COEFFSH; -      //buf[i*4+ch] = voltable[level]/2; -#else -      if (!opna_ssg_tone_silent(ssg, ch)) { +      if (!ssg->ymf288) { +        // OPNA output level + HPF +        int32_t previntmp = 0; +        if (opna_ssg_tone_out(ssg, ch)) { +          int level = opna_ssg_channel_level(ssg, ch); +          previntmp = voltable[level]*5; +        } +        previntmp *= COEFF; +        ssg->prevout[ch] = previntmp - ssg->previn[ch] + ((((int64_t)COEFF)*ssg->prevout[ch]) >> COEFFSH); +        ssg->previn[ch] = previntmp; +        buf[i*4+ch] = ssg->prevout[ch] >> COEFFSH; +      } else { +        // YMF288          int level = opna_ssg_channel_level(ssg, ch); -        //out += (opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]) / 2; -        buf[i*4+ch] = (opna_ssg_tone_out(ssg, ch) ? voltable[level] : -voltable[level]) / 4; +        if (!opna_ssg_tone_silent(ssg, ch)) { +          buf[i*4+ch] = (opna_ssg_tone_out_ymf288(ssg, ch) ? voltable[level] : -voltable[level]); +        } else { +          buf[i*4+ch] = voltable[level]*2; +        }        } -#endif      }      //buf[i] = out / 2; @@ -306,20 +328,34 @@ void opna_ssg_mix_55466(      resampler->index &= (1u<<(OPNA_SSG_SINCTABLEBIT+1))-1;      memcpy(resampler->buf + OPNA_SSG_SINCTABLELEN*4, resampler->buf, OPNA_SSG_SINCTABLELEN*4*sizeof(*resampler->buf));      int32_t outbuf[3]; -    opna_ssg_sinc_calc_func(resampler->index, resampler->buf, outbuf); +    if (!ssg->ymf288) { +      // OPNA analog: bandlimited sinc resample +      opna_ssg_sinc_calc_func(resampler->index, resampler->buf, outbuf); +      for (int ch = 0; ch < 3; ch++) { +        outbuf[ch] >>= 16; +        outbuf[ch] *= 13000; +        outbuf[ch] >>= 16; +        outbuf[ch] *= ssg->mix; +        outbuf[ch] >>= 16; +      } +    } else { +      // YMF288: average of the samples (equivalent to FIR with rectangular function +      for (int ch = 0; ch < 3; ch++) { +        int ind = (resampler->index & 1) ? BUFINDEX(5) : BUFINDEX(0); +        outbuf[ch] = resampler->buf[ind*4+ch]; +        for (int s = 0; s < 4; s++) { +          outbuf[ch] += resampler->buf[BUFINDEX(s+1)*4+ch] * 2; +        } +        outbuf[ch] /= 9; +      } +    }      for (int ch = 0; ch < 3; ch++) { -      if (oscillo) oscillo[ch].buf[offset+i] = outbuf[ch] >> 15; +      if (oscillo) oscillo[ch].buf[offset+i] = outbuf[ch] << 1;        int32_t nlevel = outbuf[ch]; -      nlevel >>= 16; -      nlevel *= 13000; -      nlevel >>= 14;        if (nlevel < 0) nlevel = -nlevel;        if (((unsigned)nlevel) > level[ch]) level[ch] = nlevel; -      if (!(ssg->mask & (1<<ch))) sample += outbuf[ch] >> 2; +      if (!(ssg->mask & (1<<ch))) sample += outbuf[ch];      } -    sample >>= 16; -    sample *= 13000; -    sample >>= 14;      int32_t lo = buf[i*2+0];      int32_t ro = buf[i*2+1]; diff --git a/libopna/opnassg.h b/libopna/opnassg.h index 3077d08..6b1d0cd 100644 --- a/libopna/opnassg.h +++ b/libopna/opnassg.h @@ -3,6 +3,7 @@  #include <stdint.h>  #include <stdbool.h> +#include <string.h>  #include "leveldata/leveldata.h"  #ifdef __cplusplus @@ -31,6 +32,8 @@ struct opna_ssg {    unsigned mask;    int32_t previn[3];    int32_t prevout[3]; +  uint32_t mix; +  bool ymf288;  };  struct opna_ssg_resampler { @@ -63,6 +66,21 @@ unsigned opna_ssg_readreg(const struct opna_ssg *ssg, unsigned reg);  int opna_ssg_channel_level(const struct opna_ssg *ssg, int ch);  unsigned opna_ssg_tone_period(const struct opna_ssg *ssg, int ch); +static inline void opna_ssg_set_ymf288( +    struct opna_ssg *ssg, struct opna_ssg_resampler *resampler, bool ymf288) { +  // enable bit-perfect with YMF288/OPN3-L mode +  if (ssg->ymf288 != ymf288) { +    ssg->ymf288 = ymf288; +    memset(resampler->buf, 0, sizeof(resampler->buf)); +  } +} + +static inline void opna_ssg_set_mix(struct opna_ssg *ssg, uint32_t mix) { +  // 0x10000: default (PC-9801-86 equivalent) +  // only valid when !ymf288 +  ssg->mix = mix; +} +  typedef void (*opna_ssg_sinc_calc_func_type)(unsigned resampler_index,                                               const int16_t *inbuf, int32_t *outbuf);  extern opna_ssg_sinc_calc_func_type opna_ssg_sinc_calc_func; | 
