diff options
| -rw-r--r-- | fmdriver/fmdriver_pmd.c | 807 | ||||
| -rw-r--r-- | fmdriver/fmdriver_pmd.h | 10 | ||||
| -rw-r--r-- | gtk/main.c | 107 | 
3 files changed, 848 insertions, 76 deletions
| diff --git a/fmdriver/fmdriver_pmd.c b/fmdriver/fmdriver_pmd.c index 19f7e21..2a093c2 100644 --- a/fmdriver/fmdriver_pmd.c +++ b/fmdriver/fmdriver_pmd.c @@ -42,6 +42,11 @@ enum {    LFO_WF_ONESHOT  }; +// 0790: PPZ8 + + +// 33f7: write standard +// 341f: write extended  // 3447  static void pmd_reg_write(struct fmdriver_work *work,                            struct driver_pmd *pmd, @@ -163,7 +168,7 @@ static void pmd_reset_state(struct driver_pmd *pmd) {    pmd->tonemask_fb_alg = false;    pmd->adpcm_start = 0;    pmd->adpcm_stop = 0; -  // TODO: pmd->42c1 = 0x8000; +  pmd->adpcm_release = 0x8000;    pmd->ssgrhythm = 0;    pmd->opnarhythm = 0;    pmd->rand = 0; @@ -211,7 +216,7 @@ static bool pmd_data_init(struct driver_pmd *pmd) {      p->curr_note = 0xff;      if (pi <= PMD_PART_FM_6) {        p->vol = 108; -      p->fm_pan_ams_pms = 0xc0; +      p->pan = 0xc0;        p->fm_slotmask = 0xf0;        p->fm_tone_slotmask = 0xff;      } else if (pi <= PMD_PART_SSG_3) { @@ -220,7 +225,7 @@ static bool pmd_data_init(struct driver_pmd *pmd) {        p->ssg_env_state_old = SSG_ENV_STATE_OLD_OFF;      } else if (pi == PMD_PART_ADPCM) {        p->vol = 128; -      p->fm_pan_ams_pms = 0xc0; +      p->pan = 0xc0;      } else if (pi == PMD_PART_RHYTHM) {        p->vol = 15;      } @@ -595,7 +600,7 @@ static void pmd_lfo_tick_if_needed_hlfo(  ) {    part->hlfo_delay = part->hlfo_delay_set;    if (part->hlfo_delay) { -    pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, part->fm_pan_ams_pms & 0xc0); +    pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, part->pan & 0xc0);    }    // 30c0    part->slot_delay_cnt = part->slot_delay; @@ -1273,6 +1278,55 @@ static void pmd_note_freq_ssg(    part->actual_freq = tonefreq;  } +// 0671 +static void pmd_note_freq_adpcm( +  struct pmd_part *part, +  uint8_t note +) { +  if ((note & 0xf) == 0xf) { +    // 2798 +    part->actual_note = 0xff; +    if (!part->lfof.freq && !part->lfof_b.freq) { +      part->actual_freq = 0; +    } +    return; +  } +  part->actual_note = note; +  static const uint16_t adpcm_tonetable[0x10] = { +    // 0788 +    // ??? +    0x6264, +    0x6840, +    0x6e74, +    0x7506, +    0x7bfc, +    0x835e, +    0x8b2e, +    0x9376, +    0x9c3c, +    0xa588, +    0xaf62, +    0xb9d0, +    0, 0, 0, 0 +  }; +  uint8_t octave = note >> 4; +  int cl = 5 - octave; +  if (cl < 0) cl = 0; +  uint16_t tonefreq = adpcm_tonetable[note & 0x0f]; +  if (octave <= 5) { +    tonefreq >>= (5 - octave); +  } else { +    // 06a8 +    octave = 5; +    if (!(tonefreq & 0x8000)) { +      tonefreq <<= 1; +      octave++; +    } +    part->actual_note = (part->actual_note & 0xf) | (octave << 4); +  } +  part->actual_freq = tonefreq; +} +  // 14a0  static void pmd_part_calc_gate(    struct driver_pmd *pmd, @@ -1464,7 +1518,7 @@ static void pmd_ssg_vol_out(          if (part->lfof.vol) vol += part->lfo_diff;          if (part->lfof_b.vol) vol += part->lfo_diff_b;          lfovol += vol; -        vol = u8s8(lfovol); +        vol = lfovol;          if (lfovol < 0) {            vol = 0;          } else if (lfovol > 0xf) { @@ -1477,6 +1531,69 @@ static void pmd_ssg_vol_out(    work->opna_writereg(work, 0x07+pmd->proc_ch, vol);  } +// 049a +static void pmd_adpcm_vol_out( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +) { +  uint8_t vol = part->volume_save; +  if (!vol) vol = part->vol; +   +  // 04a4 +  if (pmd->adpcm_voldown) { +    uint8_t voldown = -pmd->adpcm_voldown; +    vol = vol * voldown >> 8; +  } +  // 04b3 +  if (pmd->fadeout_vol) { +    uint8_t fadeout = -pmd->fadeout_vol; +    fadeout = ((uint16_t)fadeout * fadeout) >> 8; +    vol = vol * fadeout >> 8; +  } +  if (vol) { +    if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { +      // 04d0 +      uint8_t envvol = part->ssg_env_vol; +      if (!envvol) { +        // 04fd +        vol = 0; +        // -> 053f +      } else { +        vol = (vol * (envvol+1)) >> 3; +        if (vol & 1) { +          vol >>= 1; +          vol++; +        } else { +          vol >>= 1; +        } +      } +    } else { +      // 04e8 +      int newvol = vol + (part->ssg_env_vol << 4); +      if (newvol > 0xff) newvol = 0xff; +      if (newvol < 0) newvol = 0; +      vol = newvol; +    } +    if (vol) { +      if (part->lfof.vol || part->lfof_b.vol) { +        int32_t lfovol = 0; +        if (part->lfof.vol) vol += part->lfo_diff; +        if (part->lfof_b.vol) vol += part->lfo_diff_b; +        lfovol += vol; +        vol = lfovol; +        if (lfovol < 0) { +          vol = 0; +        } else if (lfovol > 0xff) { +          vol = 0xff; +        } +      } +    } +  } +  // 053f +  work->opna_writereg(work, 0x10b, vol); +} +  // 2985  static void pmd_ssg_freq_out(    struct fmdriver_work *work, @@ -1532,6 +1649,32 @@ static void pmd_ssg_freq_out(    work->opna_writereg(work, (pmd->proc_ch-1)*2+1, freq>>8);  } +// 0620 +static void pmd_adpcm_freq_out( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +) { +  uint16_t freq = part->actual_freq; +  if (!freq) return; +  freq += (unsigned)part->portamento_diff; +  uint32_t det = 0; +  if (part->lfof.freq || part->lfof_b.freq) { +    // 29db +    if (part->lfof.freq) det += part->lfo_diff; +    if (part->lfof_b.freq) det += part->lfo_diff_b; +    det <<= 2; +  } +  // 0649 +  det += part->detune; +  int32_t newfreq = freq + u16s16(det); +  if (newfreq > 0xffff) newfreq = 0xffff; +  if (newfreq < 0) newfreq = 0; +  freq = newfreq; +  work->opna_writereg(work, 0x109, freq); +  work->opna_writereg(work, 0x10a, freq >> 8); +} +  // 2c9f  static uint16_t pmd_part_ssg_readout(    struct fmdriver_work *work, @@ -1587,6 +1730,32 @@ static void pmd_part_fm_keyon(    work->opna_writereg(work, 0x28, *slotkey | (pmd->proc_ch-1) | (pmd->opna_a1<<2));  } +// 0547 +static void pmd_part_adpcm_keyon( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +) { +  if (part->actual_note == 0xff) return; +  work->opna_writereg(work, 0x101, 0x02); +  work->opna_writereg(work, 0x100, 0x21); +  work->opna_writereg(work, 0x102, pmd->adpcm_start); +  work->opna_writereg(work, 0x103, pmd->adpcm_start >> 8); +  work->opna_writereg(work, 0x104, pmd->adpcm_stop); +  work->opna_writereg(work, 0x105, pmd->adpcm_stop >> 8); +  if (!pmd->adpcm_start_loop && !pmd->adpcm_stop_loop) { +    work->opna_writereg(work, 0x100, 0xa0); +    work->opna_writereg(work, 0x101, part->pan | 0x02); +  } else { +    work->opna_writereg(work, 0x100, 0xb0); +    work->opna_writereg(work, 0x101, part->pan | 0x02); +    work->opna_writereg(work, 0x102, pmd->adpcm_start_loop); +    work->opna_writereg(work, 0x103, pmd->adpcm_start_loop >> 8); +    work->opna_writereg(work, 0x104, pmd->adpcm_stop_loop); +    work->opna_writereg(work, 0x105, pmd->adpcm_stop_loop >> 8); +  } +} +  // 2942  static uint32_t pmd_blkfnum_normalize(    uint16_t blk, @@ -1733,6 +1902,36 @@ static void pmd_part_ssg_out(    pmd_part_loop_check(pmd, part);  } +// 01b1 +static void pmd_part_adpcm_out( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +) { +  if (part->volume_save && part->actual_note != 0xff) { +    if (!pmd->volume_saved) { +      part->volume_save = 0; +    } +    pmd->volume_saved = false; +  } +  pmd_adpcm_vol_out(work, pmd, part); +  pmd_adpcm_freq_out(work, pmd, part); +  if (part->keystatus.off) { +    pmd_part_adpcm_keyon(work, pmd, part); +  } +  part->note_proc++; +  pmd->no_keyoff = false; +  pmd->volume_saved = false; +  part->keystatus.off = false; +  part->keystatus.off_mask = false; +  if (pmd->datalen > (part->ptr + 1)) { +    if (pmd->data[part->ptr] == 0xfb) { +      part->keystatus.off_mask = true; +    } +  } +  pmd_part_loop_check(pmd, part); +} +  // none  static bool pmd_part_masked(    const struct pmd_part *part @@ -1838,6 +2037,36 @@ static void pmd_cmdff_tonenum(    }  } +// 046c +static void pmd_cmdff_tonenum_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  uint8_t tonenum = pmd_part_cmdload(pmd, part); +  part->tonenum = tonenum; +  pmd->adpcm_start = pmd->adpcm_addr[tonenum][0]; +  pmd->adpcm_stop = pmd->adpcm_addr[tonenum][1]; +  pmd->adpcm_start_loop = 0; +  pmd->adpcm_stop_loop = 0; +  pmd->adpcm_release = 0x8000; +} + +// 0a3c +static void pmd() { +  // 0790 +} + +// 0afd +static void pmd_cmdff_tonenum_ppz8( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  part->tonenum = pmd_part_cmdload(pmd, part); +  // 0a3c +} +  // 22b3  static void pmd_cmdfe_gate_abs(    struct fmdriver_work *work, @@ -2025,6 +2254,17 @@ static void pmd_cmdf4_volinc_ssg(    if (part->vol < 0xf) part->vol++;  } +// 0421 +static void pmd_cmdf4_volinc_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  int newvol = part->vol + 0x10; +  if (newvol > 0xff) newvol = 0xff; +  part->vol = newvol; +} +  // 241c  static void pmd_cmdf3_voldec_fm(    struct fmdriver_work *work, @@ -2045,6 +2285,17 @@ static void pmd_cmdf3_voldec_ssg(    if (part->vol) part->vol--;  } +// 0434 +static void pmd_cmdf3_voldec_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  int newvol = part->vol - 0x10; +  if (newvol < 0) newvol = 0; +  part->vol = newvol; +} +  // 24ea  static void pmd_cmdf2_lfo(    struct fmdriver_work *work, @@ -2163,14 +2414,14 @@ static void pmd_part_set_pan(    uint8_t pan  ){    uint8_t pan_ams_pms = (pan & 3) << 6; -  pan_ams_pms |= part->fm_pan_ams_pms & 0x3f; -  part->fm_pan_ams_pms = pan_ams_pms; +  pan_ams_pms |= part->pan & 0x3f; +  part->pan = pan_ams_pms;    // 258e    if (pmd->proc_ch == 3 && !pmd->opna_a1) { -    pmd->parts[PMD_PART_FM_3].fm_pan_ams_pms = pan_ams_pms; -    pmd->parts[PMD_PART_FM_3B].fm_pan_ams_pms = pan_ams_pms; -    pmd->parts[PMD_PART_FM_3C].fm_pan_ams_pms = pan_ams_pms; -    pmd->parts[PMD_PART_FM_3D].fm_pan_ams_pms = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3].pan = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3B].pan = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3C].pan = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3D].pan = pan_ams_pms;    }    // 25b6    if (pmd_part_masked(part)) return; @@ -2187,6 +2438,15 @@ static void pmd_cmdec_pan(    pmd_part_set_pan(work, pmd, part, pmd_part_cmdload(pmd, part));  } +// 044d +static void pmd_cmdec_pan_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  part->pan = pmd_part_cmdload(pmd, part) << 6; +} +  // 265d  static void pmd_opnarhythm_inc(uint8_t *incdata, uint8_t val) {    for (int i = 0; i < 6; i++) { @@ -2354,8 +2614,20 @@ static void pmd_cmde3_vol_add_ssg(    part->vol = vol;  } -// FM:  2427 -// SSG: 2440 +// 042e +static void pmd_cmde3_vol_add_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  int vol = part->vol + pmd_part_cmdload(pmd, part); +  if (vol > 0xff) vol = 0xff; +  part->vol = vol; +} + +// FM:    2427 +// SSG:   2440 +// ADPCM: 043f  static void pmd_cmde2_vol_sub(    struct fmdriver_work *work,    struct driver_pmd *pmd, @@ -2373,13 +2645,13 @@ static void pmd_cmde1_ams_pms(    struct pmd_part *part  ){    uint8_t pan_ams_pms = pmd_part_cmdload(pmd, part); -  pan_ams_pms |= part->fm_pan_ams_pms & 0xc0; -  part->fm_pan_ams_pms = pan_ams_pms; +  pan_ams_pms |= part->pan & 0xc0; +  part->pan = pan_ams_pms;    if (pmd->proc_ch == 3 && !pmd->opna_a1) { -    pmd->parts[PMD_PART_FM_3].fm_pan_ams_pms = pan_ams_pms; -    pmd->parts[PMD_PART_FM_3B].fm_pan_ams_pms = pan_ams_pms; -    pmd->parts[PMD_PART_FM_3C].fm_pan_ams_pms = pan_ams_pms; -    pmd->parts[PMD_PART_FM_3D].fm_pan_ams_pms = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3].pan = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3B].pan = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3C].pan = pan_ams_pms; +    pmd->parts[PMD_PART_FM_3D].pan = pan_ams_pms;    }    if (pmd_part_masked(part)) return;    pan_ams_pms = pmd_hlfo_delay_check(part, pan_ams_pms); @@ -2430,6 +2702,18 @@ static void pmd_cmdde_echo_init_add_ssg(    pmd->volume_saved = true;  } +// 21ed +static void pmd_cmdde_echo_init_add_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  int vol = part->vol + pmd_part_cmdload(pmd, part); +  if (vol > 0xfe) vol = 0xfe; +  part->volume_save = vol+1; +  pmd->volume_saved = true; +} +  // 21fb  static void pmd_cmddd_echo_init_sub(    struct fmdriver_work *work, @@ -2539,6 +2823,41 @@ static void pmd_cmdda_portamento_ssg(    pmd_part_ssg_out(work, pmd, part);  } +// 03d6 +static void pmd_cmdda_portamento_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  uint8_t note = pmd_part_cmdload(pmd, part); +  if (pmd_part_masked(part)) return; +  pmd_part_lfo_init_ssg(work, pmd, part, note); +  note = pmd_part_note_transpose(part, note); +  pmd_note_freq_adpcm(part, note); +  uint16_t f1 = part->actual_freq; +  uint8_t n1 = part->actual_note; +  note = pmd_part_cmdload(pmd, part); +  note = pmd_part_note_transpose(part, note); +  pmd_note_freq_fm(part, note); +  uint16_t f2 = part->actual_freq; +  part->actual_freq = f1; +  part->actual_note = n1; +  int freqdiff = f2 - f1; +  uint8_t clocks = pmd_part_cmdload(pmd, part); +  part->len = part->len_cnt = clocks; +  pmd_part_calc_gate(pmd, part); +  int16_t p_add = 0; +  int16_t p_rem = 0; +  if (clocks) { +    p_add = freqdiff / clocks; +    p_rem = freqdiff % clocks; +  } +  part->portamento_add = p_add; +  part->portamento_rem = p_rem; +  part->lfof.portamento = true; +  pmd_part_adpcm_out(work, pmd, part); +} +  // 20bf  static void pmd_cmdd6_md(    struct fmdriver_work *work, @@ -2666,6 +2985,31 @@ static void pmd_cmdcf_slotmask(    part->proc_masked = pmd_part_masked(part);  } +// 039a +static void pmd_cmdce_adpcm_loop( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  uint16_t diff = pmd_part_cmdload(pmd, part); +  diff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; +  if (!(diff & 0x80)) diff += pmd->adpcm_start; +  else diff += pmd->adpcm_stop; +  pmd->adpcm_start_loop = diff; +  diff = pmd_part_cmdload(pmd, part); +  diff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; +  if (diff && !(diff & 0x80)) diff += pmd->adpcm_start; +  else diff += pmd->adpcm_stop; +  pmd->adpcm_stop_loop = diff; +  diff = pmd_part_cmdload(pmd, part); +  diff |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; +  if (diff != 0x8000) { +    if (!(diff & 0x80)) diff += pmd->adpcm_start; +    else diff += pmd->adpcm_stop; +  } +  pmd->adpcm_release = diff; +} +  // 1e1b  static void pmd_cmdcd_env_new(    struct fmdriver_work *work, @@ -2793,7 +3137,7 @@ static void pmd_fm3ex_init(    part->actual_note = 0xff;    part->curr_note = 0xff;    part->vol = 0x6c; -  part->fm_pan_ams_pms = pmd->parts[PMD_PART_FM_3].fm_pan_ams_pms; +  part->pan = pmd->parts[PMD_PART_FM_3].pan;    part->mask.slot = true;  } @@ -2832,27 +3176,6 @@ static void pmd_cmdc5_lfo_slotmask(    pmd_fm3ex_mode_update_check(work, pmd, part);  } -// 1d61 -static void pmd_cmdba_lfo2_slotmask( -  struct fmdriver_work *work, -  struct driver_pmd *pmd, -  struct pmd_part *part -){ -  uint8_t mask = pmd_part_cmdload(pmd, part); -  mask &= 0x0f; -  if (mask) { -    // 1d4a -    mask <<= 4; -    mask |= 0x0f; -    part->vol_lfo_slotmask_b = mask; -  } else { -    // 1d59 -    part->vol_lfo_slotmask_b = part->fm_slotout; -  } -  // 1d7b -  pmd_fm3ex_mode_update_check(work, pmd, part); -} -  // 22c6  static void pmd_cmdc4_gate_rel(    struct fmdriver_work *work, @@ -2877,6 +3200,21 @@ static void pmd_cmdc3_pan_ex(    pmd_part_set_pan(work, pmd, part, pan);  } +// 0458 +static void pmd_cmdc3_pan_ex_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  uint8_t data = pmd_part_cmdload(pmd, part); +  pmd_part_cmdload(pmd, part); +  uint8_t pan; +  if (!data) pan = 0xc0; +  else if (data & 0x80) pan = 0x40; +  else pan = 0x80; +  part->pan = pan; +} +  // 2509  static void pmd_cmdc2_lfo_delay(    struct fmdriver_work *work, @@ -3102,7 +3440,7 @@ static void pmd_part_fm_unmask(      }    }    // 1d23 -  pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, pmd_hlfo_delay_check(part, part->fm_pan_ams_pms)); +  pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, pmd_hlfo_delay_check(part, part->pan));  }  // 1c50 @@ -3167,6 +3505,33 @@ static void pmd_cmdc0_mml_mask_ssg(    }  } +// 036a +static void pmd_cmdc0_mml_mask_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  uint8_t data = pmd_part_cmdload(pmd, part); +  if (data >= 2) { +    pmd_cmdc0_extended(work, pmd, part, data); +    return; +  } else if (data == 1) { +    // 0376 +    part->mask.mml = false; +    bool masked = pmd_part_masked(part); +    part->mask.mml = true; +    if (!masked) { +      work->opna_writereg(work, 0x101, 0x02); +      work->opna_writereg(work, 0x100, 0x01); +    } +    part->proc_masked = true; +  } else { +    // 0390 +    part->mask.mml = false; +    part->proc_masked = pmd_part_masked(part); +  } +} +  // 1caa  static void pmd_cmdc0_mml_mask_rhythm(    struct fmdriver_work *work, @@ -3253,6 +3618,27 @@ static void pmd_cmdbb_lfo2_ext(    pmd_part_lfo_flip(part);  } +// 1d61 +static void pmd_cmdba_lfo2_slotmask( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  uint8_t mask = pmd_part_cmdload(pmd, part); +  mask &= 0x0f; +  if (mask) { +    // 1d4a +    mask <<= 4; +    mask |= 0x0f; +    part->vol_lfo_slotmask_b = mask; +  } else { +    // 1d59 +    part->vol_lfo_slotmask_b = part->fm_slotout; +  } +  // 1d7b +  pmd_fm3ex_mode_update_check(work, pmd, part); +} +  // 246e  static void pmd_cmdb9_lfo2_delay(    struct fmdriver_work *work, @@ -3380,6 +3766,31 @@ static void pmd_cmdb5_slot_delay(    part->slot_delay_cnt = part->slot_delay;  } +// 099f +static void pmd_cmdb4_ppz8_init( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +){ +  for (int i = 0; i < 8; i++) { +    uint16_t ptr = pmd_part_cmdload(pmd, part); +    ptr |= (uint16_t)pmd_part_cmdload(pmd, part) << 8; +    if (!ptr) continue; +    struct pmd_part *ppzpart = &pmd->parts[PMD_PART_PPZ_1+i]; +    ppzpart->ptr = ptr; +    ppzpart->len = ppzpart->len_cnt = 1; +    ppzpart->keystatus.off = true; +    ppzpart->keystatus.off_mask = true; +    ppzpart->md_cnt = 0xff; +    ppzpart->md_cnt_set = 0xff; +    ppzpart->md_cnt_b = 0xff; +    ppzpart->md_cnt_set_b = 0xff; +    ppzpart->actual_note = 0xff; +    ppzpart->vol = 0x80; +    ppzpart->pan = 5; +  } +} +  // 22bc  static void pmd_cmdb3_gate_min(    struct fmdriver_work *work, @@ -3657,6 +4068,92 @@ static const pmd_cmd_func pmd_cmd_table_rhythm[PMD_CMD_CNT] = {    pmd_cmd_null_1  }; +static const pmd_cmd_func pmd_cmd_table_adpcm[PMD_CMD_CNT] = { +  pmd_cmdff_tonenum_adpcm, +  pmd_cmdfe_gate_abs, +  pmd_cmdfd_vol, +  pmd_cmdfc_tempo, +  pmd_cmdfb_tie, +  pmd_cmdfa_d5_det, +  pmd_cmdf9_repeat_reset, +  pmd_cmdf8_repeat, +  pmd_cmdf7_repeat_exit, +  pmd_cmdf6_set_loop, +  pmd_cmdf5_transpose, +  pmd_cmdf4_volinc_adpcm, +  pmd_cmdf3_voldec_adpcm, +  pmd_cmdf2_lfo, +  pmd_cmdf1_lfo_switch, +  pmd_cmdf0_env_old, +  pmd_cmdef_poke, +  pmd_cmd_null_1, +  pmd_cmd_null_1, +  pmd_cmdec_pan_adpcm, +  pmd_cmdeb_opnarhythm, +  pmd_cmdea_opnarhythm_il, +  pmd_cmde9_opnarhythm_pan, +  pmd_cmde8_opnarhythm_tl, +  pmd_cmde7_transpose_rel, +  pmd_cmde6_opnarhythm_tl_rel, +  pmd_cmde5_opnarhythm_il_rel, +  pmd_cmd_null_1, +  pmd_cmde3_vol_add_adpcm, +  pmd_cmde2_vol_sub, +  pmd_cmde1_ams_pms, +  pmd_cmde0_hlfo, +  pmd_cmddf_meas_len, +  pmd_cmdde_echo_init_add_adpcm, +  pmd_cmddd_echo_init_sub, +  pmd_cmddc_status1, +  pmd_cmddb_status1_add, +  pmd_cmdda_portamento_adpcm, +  pmd_cmd_null_1, +  pmd_cmd_null_1, +  pmd_cmd_null_1, +  pmd_cmdd6_md, +  pmd_cmdfa_d5_det, +  pmd_cmdd4_ssgeff, +  pmd_cmdd3_fmeff, +  pmd_cmdd2_fadeout, +  pmd_cmd_null_1, +  pmd_cmd_null_1, +  pmd_cmd_null_1, +  pmd_cmdce_adpcm_loop, +  pmd_cmdcd_env_new, +  pmd_cmd_null_1, +  pmd_cmdcb_lfo_waveform, +  pmd_cmdca_lfo_ext, +  pmd_cmdc9_env_ext, +  pmd_cmd_null_3, +  pmd_cmd_null_3, +  pmd_cmd_null_6, +  pmd_cmd_null_1, +  pmd_cmdc4_gate_rel, +  pmd_cmdc3_pan_ex_adpcm, +  pmd_cmdc2_lfo_delay, +  pmd_cmd_null_0, +  pmd_cmdc0_mml_mask_adpcm, +  pmd_cmdbf_lfo2, +  pmd_cmdbe_lfo2_switch, +  pmd_cmdbd_lfo2_md, +  pmd_cmdbc_lfo2_waveform, +  pmd_cmdbb_lfo2_ext, +  pmd_cmdba_lfo2_slotmask, // slotmask on ADPCM?? +  pmd_cmdb9_lfo2_delay, +  pmd_cmd_null_2, +  pmd_cmdb7_lfo_md_cnt, +  pmd_cmd_null_1, +  pmd_cmd_null_2, +  pmd_cmdb4_ppz8_init, +  pmd_cmdb3_gate_min, +  pmd_cmdb2_transpose_master, +  pmd_cmdb1_gate_rand_range +}; + +static const pmd_cmd_func pmd_cmd_table_ppz8[PMD_CMD_CNT] = { +   +}; +  // 1857  static void pmd_part_cmd_ssg(    struct fmdriver_work *work, @@ -3699,6 +4196,20 @@ static void pmd_part_cmd_rhythm(    pmd_cmd_table_rhythm[cmd^0xff](work, pmd, part);  } +// 02c6 +static void pmd_part_cmd_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part, +  uint8_t cmd +) { +  if (cmd < 0xb1) { +    part->ptr = 0; +    return; +  } +  pmd_cmd_table_adpcm[cmd^0xff](work, pmd, part); +} +  // 20e8  static void pmd_portamento_tick(    struct pmd_part *part @@ -3823,6 +4334,47 @@ static void pmd_part_proc_ssg_lfoenv(    pmd_part_loop_check(pmd, part);  } +// 01fa +static void pmd_part_proc_adpcm_lfoenv( +  struct fmdriver_work *work, +  struct driver_pmd *pmd, +  struct pmd_part *part +) { +  static const struct pmd_part_lfo_flags lfof_z; +  pmd->lfoprocf = lfof_z; +  pmd->lfoprocf_b = lfof_z; +  pmd->lfoprocf.portamento = part->lfof.portamento; +  if (part->lfof.freq || part->lfof.vol || part->lfof.sync || part->lfof.portamento || +      part->lfof_b.freq || part->lfof_b.vol || part->lfof_b.sync || part->lfof_b.portamento) { +    if (part->lfof.freq || part->lfof.vol) { +      if (pmd_lfo_tick(pmd, part)) { +        pmd->lfoprocf.freq = part->lfof.freq; +        pmd->lfoprocf.vol = part->lfof.vol; +      } +    } +    if (part->lfof_b.freq || part->lfof_b.vol) { +      pmd_part_lfo_flip(part); +      if (pmd_lfo_tick(pmd, part)) { +        pmd->lfoprocf_b.freq = part->lfof_b.freq; +        pmd->lfoprocf_b.vol = part->lfof_b.vol; +      } +      pmd_part_lfo_flip(part); +    } +    if (pmd->lfoprocf.freq || pmd->lfoprocf.portamento || pmd->lfoprocf_b.freq) { +      if (pmd->lfoprocf.portamento) { +        pmd_portamento_tick(part); +      } +      pmd_adpcm_freq_out(work, pmd, part); +    } +  } +  // 0250 +  if (pmd_ssg_env_proc(pmd, part) || pmd->lfoprocf.vol || pmd->lfoprocf_b.vol || pmd->fadeout_speed) { +    // 049a +    pmd_adpcm_vol_out(work, pmd, part); +  } +  pmd_part_loop_check(pmd, part); +} +  // 16d9  static bool pmd_part_ssg_next_masked(    struct fmdriver_work *work, @@ -3889,7 +4441,7 @@ static void pmd_part_proc_fm_lfo(  ) {    if (part->hlfo_delay) {      if (!--part->hlfo_delay) { -      pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, part->fm_pan_ams_pms); +      pmd_reg_write(work, pmd, 0xb3+pmd->proc_ch, part->pan);      }    }    // 1410 @@ -3992,9 +4544,9 @@ static void pmd_part_proc_fm(          part->actual_note = 0xff;          if (!part->loop_ptr) {            if (part->proc_masked) { -            pmd_part_proc_fm_lfo(work, pmd, part); -          } else {              pmd_part_loop_check_masked(pmd, part); +          } else { +            pmd_part_proc_fm_lfo(work, pmd, part);            }            return;          } @@ -4026,7 +4578,16 @@ static void pmd_part_proc_ssg(    struct driver_pmd *pmd,    struct pmd_part *part  ) { -  if (!part->ptr) return; +  if (!part->ptr) { +    // original +    if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { +      part->ssg_env_state_new = SSG_ENV_STATE_NEW_RR; +    } else { +      part->ssg_env_state_old = SSG_ENV_STATE_OLD_RR; +    } +    // original end +    return; +  }    part->proc_masked = pmd_part_masked(part);    part->len_cnt--; @@ -4070,9 +4631,9 @@ static void pmd_part_proc_ssg(          part->actual_note = 0xff;          if (!part->loop_ptr) {            if (part->proc_masked) { -            pmd_part_proc_ssg_lfoenv(work, pmd, part); -          } else {              pmd_part_loop_check_masked(pmd, part); +          } else { +            pmd_part_proc_ssg_lfoenv(work, pmd, part);            }            return;          } @@ -4266,6 +4827,8 @@ static void pmd_part_proc_opnarhythm(  // 05cf  static void pmd_part_off_adpcm( +  struct fmdriver_work *work, +  struct driver_pmd *pmd,    struct pmd_part *part  ) {    if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_NEW) { @@ -4274,7 +4837,26 @@ static void pmd_part_off_adpcm(      if (part->ssg_env_state_old == SSG_ENV_STATE_OLD_RR) return;    }    // 05e2 -  // XXX +  if (pmd->adpcm_release != 0x8000) { +    work->opna_writereg(work, 0x100, 0x21); +    work->opna_writereg(work, 0x102, pmd->adpcm_release); +    work->opna_writereg(work, 0x103, pmd->adpcm_release>>8); +    work->opna_writereg(work, 0x104, pmd->adpcm_stop); +    work->opna_writereg(work, 0x105, pmd->adpcm_stop>>8); +    work->opna_writereg(work, 0x100, 0xa0); +  } +  pmd_part_off_ssg(part); +} + +// 0c0e +static void pmd_part_off_ppz8( +  struct pmd_part *part +) { +  if (part->ssg_env_state_old != SSG_ENV_STATE_OLD_NEW) { +    if (part->ssg_env_state_old != SSG_ENV_STATE_OLD_RR) pmd_part_off_ssg(part); +  } else { +    if (part->ssg_env_state_new != SSG_ENV_STATE_NEW_RR) pmd_part_off_ssg(part); +  }  }  // 0149 / 026c @@ -4283,26 +4865,109 @@ static void pmd_part_proc_adpcm(    struct driver_pmd *pmd,    struct pmd_part *part  ) { -  /*    if (!part->ptr) return;    part->proc_masked = pmd_part_masked(part);    part->len_cnt--; -  if (!part->keystatus.off && !part->keystatus.off_mask) { +  if (part->proc_masked) { +    part->keystatus.off = true; +    part->keystatus.off_mask = true; +  } else if (!part->keystatus.off && !part->keystatus.off_mask) { +    //0164      if (part->len_cnt <= part->gate) {        part->keystatus.off = true;        part->keystatus.off_mask = true; -      // TODO: 05cf; +      pmd_part_off_adpcm(work, pmd, part); +    } +  } +  // 0170 / 0273 +  if (part->len_cnt) { +    if (part->proc_masked) { +      pmd_part_loop_check(pmd, part); +    } else { +      pmd_part_proc_adpcm_lfoenv(work, pmd, part); +    } +    return; +  } +  // PCM effect not implemented +  // 0177 +  part->lfof.portamento = false; +  for (;;) { +    // 017b / 029a +    uint8_t cmd = pmd_part_cmdload(pmd, part); +    if (cmd & 0x80) { +      if (cmd != 0x80) { +        pmd_part_cmd_adpcm(work, pmd, part, cmd); +        if (cmd == 0xda && !pmd_part_masked(part)) return; +      } else { +        // 0187 +        part->ptr = 0; +        part->loop.looped = true; +        part->loop.ended = true; +        part->actual_note = 0xff; +        if (!part->loop_ptr) { +          if (part->proc_masked) { +            pmd_part_loop_check_masked(pmd, part); +          } else { +            pmd_part_proc_adpcm_lfoenv(work, pmd, part); +          } +          return; +        } +        part->ptr = part->loop_ptr; +        part->loop.ended = false; +      } +    } else { +      // 01a1 / 02a1 +      if (part->proc_masked) { +        pmd_part_proc_note_masked(work, pmd, part); +      } else { +        pmd_part_lfo_init_ssg(work, pmd, part, cmd); +        cmd = pmd_part_note_transpose(part, cmd); +        pmd_note_freq_adpcm(part, cmd); +        part->len = part->len_cnt = pmd_part_cmdload(pmd, part); +        pmd_part_calc_gate(pmd, part); +        pmd_part_adpcm_out(work, pmd, part); +      } +      return;      }    } -  */  } -// 079b +// 079b / 08be  static void pmd_part_proc_ppz8(    struct fmdriver_work *work,    struct driver_pmd *pmd,    struct pmd_part *part  ) { +  /* +  if (!part->ptr) return; +  part->proc_masked = pmd_part_masked(part); +  part->len_cnt--; +  if (!part->keystatus.off && !part->keystatus.off_mask) { +    // 07b6 +    if (part->len_cnt <= part->gate) { +      part->keystatus.off = true; +      part->keystatus.off_mask = true; +      pmd_part_off_ppz8(work, pmd, part); +    } +  } +  // 07c2 +  if (part->len_cnt) { +    // 084c +  } +  part->lfof.portamento = false; +  for (;;) { +    // 07cd +    uint8_t cmd = pmd_part_cmdload(pmd, part); +    if (cmd & 0x80) { +      if (cmd != 0x80) { +        pmd_part_cmd_adpcm(work, pmd, part, cmd); +        if (cmd == 0xda && !pmd_part_masked(part)) return; +      } else { +        // 0187 +      } +    } +  } +  */  }  // 11fd @@ -4533,7 +5198,8 @@ static void pmd_work_status_update(        }        break;      case PART_TYPE_ADPCM: -      track->playing = false; +      //track->playing = false; +      break;      }    }    work->ssg_noise_freq = pmd->ssg_noise_freq_wrote; @@ -4643,6 +5309,10 @@ const char *pmd_get_memo(  void pmd_filenamecopy(char *dest, const char *src) {    int i;    for (i = 0; i < (PMD_FILENAMELEN+1); i++) { +    if (src[i] == '.') { +      dest[i] = 0; +      return; +    }      dest[i] = src[i];      if (!src[i]) return;    } @@ -4693,10 +5363,37 @@ bool pmd_ppc_load(    struct fmdriver_work *work,    uint8_t *data, size_t datalen  ) { +  struct driver_pmd *pmd = (struct driver_pmd *)work->driver;    if (datalen < PPC_HEADER_SIZE) return false;    const char *header = "ADPCM DATA for  PMD ver.4.4-  ";    for (int i = 0; i < 30; i++) {      if (data[i] != (uint8_t)header[i]) return false;    } +  for (int i = 0; i < 256; i++) { +    pmd->adpcm_addr[i][0] = read16le(&data[32+4*i+0]); +    pmd->adpcm_addr[i][1] = read16le(&data[32+4*i+2]); +  } +  work->opna_writereg(work, 0x100, 0x01); +  work->opna_writereg(work, 0x110, 0x13); +  work->opna_writereg(work, 0x110, 0x80); +  work->opna_writereg(work, 0x100, 0x60); +  work->opna_writereg(work, 0x101, 0x02); +  work->opna_writereg(work, 0x102, 0x00); +  work->opna_writereg(work, 0x103, 0x00); +  work->opna_writereg(work, 0x104, 0xff); +  work->opna_writereg(work, 0x105, 0xff); +  work->opna_writereg(work, 0x10c, 0xff); +  work->opna_writereg(work, 0x10d, 0xff); + +  for (int i = 0; i < 0x4c0; i++) { +    work->opna_writereg(work, 0x108, 0); +  } +   +  for (size_t i = PPC_HEADER_SIZE; i < datalen; i++) { +    work->opna_writereg(work, 0x108, data[i]); +  } + +  work->opna_writereg(work, 0x110, 0x0c); +  work->opna_writereg(work, 0x100, 0x01);  } diff --git a/fmdriver/fmdriver_pmd.h b/fmdriver/fmdriver_pmd.h index 61b2cbe..ba03cda 100644 --- a/fmdriver/fmdriver_pmd.h +++ b/fmdriver/fmdriver_pmd.h @@ -116,7 +116,7 @@ struct pmd_part {      bool env;    } flagext, flagext_b;    // 002f -  uint8_t fm_pan_ams_pms; +  uint8_t pan;    // 0030    uint8_t ssg_mix;    // 0031 @@ -310,7 +310,12 @@ struct driver_pmd {    } loop;    // 42ba    bool ppsdrv_enabled; +  // 42bd +  uint16_t adpcm_start_loop; +  // 42bf +  uint16_t adpcm_stop_loop;    // 42c1 +  uint16_t adpcm_release;    // 42c3    // timera_cnt sampled at timerb    uint8_t timera_cnt_b; @@ -442,6 +447,9 @@ struct driver_pmd {    // 43ce    struct pmd_part parts[PMD_PART_NUM]; +  // 4c9e +  uint16_t adpcm_addr[256][2]; +    bool ssgeff_tone_mix;    bool ssgeff_noise_mix;    bool ssgeff_on; @@ -131,35 +131,50 @@ static uint8_t opna_status_libopna(struct fmdriver_work *work, bool a1) {    return status;  } -static GFileInputStream *pvisearch(GFile *dir, const char *pvibase) { -  // TODO: not SJIS aware -  char pviname[8+3+2] = {0}; -  char pviname_l[8+3+2] = {0}; -  strcpy(pviname, pvibase); -  strcat(pviname, ".PVI"); -  strcpy(pviname_l, pviname); -  for (char *c = pviname_l; *c; c++) { -    if (('A' <= *c) && (*c <= 'Z')) { -      *c += ('a' - 'A'); +static GFileInputStream *pcmfilesearch(GFile *dir, const char *name) { +  char *name_l = malloc(strlen(name)); +  if (name_l) { +    strcpy(name_l, name); +    // TODO: not SJIS aware +    for (char *c = name_l; *c; c++) { +      if (('A' <= *c) && (*c <= 'Z')) { +        *c += ('a' - 'A'); +      }      }    } -  GFile *pvifile = g_file_get_child(dir, pviname); -  GFileInputStream *pvistream = g_file_read(pvifile, 0, 0); -  g_object_unref(G_OBJECT(pvifile)); -  if (pvistream) return pvistream; -  pvifile = g_file_get_child(dir, pviname_l); -  pvistream = g_file_read(pvifile, 0, 0); -  g_object_unref(G_OBJECT(pvifile)); -  if (pvistream) return pvistream; +  GFile *file = g_file_get_child(dir, name); +  GFileInputStream *stream = g_file_read(file, 0, 0); +  g_object_unref(G_OBJECT(file)); +  if (stream) { +    free(name_l); +    return stream; +  } +  if (name_l) { +    file = g_file_get_child(dir, name_l); +    free(name_l); +    stream = g_file_read(file, 0, 0); +    g_object_unref(G_OBJECT(file)); +    if (stream) return stream; +  }    return 0;  } +static GFileInputStream *extsearch(GFile *dir, const char *base, const char *ext) { +  char *name = malloc(strlen(base) + strlen(ext) + 1); +  if (!name) return 0; +  strcpy(name, base); +  strcat(name, ext); +  GFileInputStream *ret = pcmfilesearch(dir, name); +  free(name); +  return ret; +} +  static bool loadpvi(struct fmdriver_work *work,                      struct driver_fmp *fmp,                      GFile *dir) {    // no need to load, always success    if(strlen(fmp->pvi_name) == 0) return true; -  GFileInputStream *pvistream = pvisearch(dir, fmp->pvi_name); +  GFileInputStream *pvistream = extsearch(dir, fmp->pvi_name, ".PVI");    if (!pvistream) goto err;    void *data = malloc(OPNA_ADPCM_RAM_SIZE);    if (!data) goto err_stream; @@ -184,7 +199,7 @@ static bool loadppzpvi(struct fmdriver_work *work,                      GFile *dir) {    // no need to load, always success    if(strlen(fmp->ppz_name) == 0) return true; -  GFileInputStream *pvistream = pvisearch(dir, fmp->ppz_name); +  GFileInputStream *pvistream = extsearch(dir, fmp->ppz_name, ".PVI");    if (!pvistream) goto err;    GFileInfo *pviinfo = g_file_input_stream_query_info(      pvistream, G_FILE_ATTRIBUTE_STANDARD_SIZE, @@ -223,6 +238,46 @@ err:    return false;  } +static bool loadppc(struct fmdriver_work *work, +                    struct driver_pmd *pmd, +                    GFile *dir) { +  // no need to load, always success +  if(strlen(pmd->ppcfile) == 0) return true; +  fprintf(stderr, "PPC: %s\n", pmd->ppcfile); +  GFileInputStream *stream = extsearch(dir, pmd->ppcfile,  ".PPC"); +  if (!stream) goto err; +  GFileInfo *info = g_file_input_stream_query_info( +    stream, G_FILE_ATTRIBUTE_STANDARD_SIZE, +    0, 0); +  if (!info) goto err_stream; +  gsize fsize; +  { +    goffset sfsize = g_file_info_get_size(info); +    if (sfsize < 0) goto err_info; +    fsize = sfsize; +  } +  void *data = malloc(fsize); +  if (!data) goto err_info; +  gsize read; +  if (!g_input_stream_read_all( +    G_INPUT_STREAM(stream), data, fsize, &read, 0, 0 +  )) goto err_data; +  if (read != fsize) goto err_data; +  if (!pmd_ppc_load(work, data, fsize)) goto err_data; +  free(data); +  g_object_unref(G_OBJECT(info)); +  g_object_unref(G_OBJECT(stream)); +  return true; +err_data: +  free(data); +err_info: +  g_object_unref(G_OBJECT(info)); +err_stream: +  g_object_unref(G_OBJECT(stream)); +err: +  return false; +} +  static void load_drumrom(void) {    const char *path = "ym2608_adpcm_rom.bin";    const char *home = getenv("HOME"); @@ -383,6 +438,13 @@ static bool openfile(const char *uri) {    g.work.opna = &g.opna_timer;    g.work.ppz8 = &g.ppz8;    g.work.ppz8_functbl = &ppz8_functbl; +  char *disppath = g_filename_from_uri(uri, 0, 0); +  if (disppath) { +    strncpy(g.work.filename, disppath, sizeof(g.work.filename)-1); +    g_free(disppath); +  } else { +    strncpy(g.work.filename, uri, sizeof(g.work.filename)-1); +  }    opna_timer_set_int_callback(&g.opna_timer, opna_int_cb, &g.work);    opna_timer_set_mix_callback(&g.opna_timer, opna_mix_cb, &g.ppz8);    if (driver_type == DRIVER_FMP) { @@ -395,6 +457,11 @@ static bool openfile(const char *uri) {      }    } else {      pmd_init(&g.work, &g.driver->pmd); +    GFile *dir = g_file_get_parent(fmfile); +    if (dir) { +      loadppc(&g.work, &g.driver->pmd, dir); +      g_object_unref(G_OBJECT(dir)); +    }    }    fmdsp_vram_init(&g.fmdsp, &g.work, g.vram);    g_object_unref(G_OBJECT(fmstream)); | 
