aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakamichi Horikawa <takamichiho@gmail.com>2017-03-19 23:26:12 +0900
committerTakamichi Horikawa <takamichiho@gmail.com>2017-03-19 23:26:12 +0900
commited25c02966bf944aad480c11fefe34c0f46a728b (patch)
tree1c4689b6a614e67dfcfb2c8af039f28ad6be5ed0
parent774eef22ca5d0351bf896e1fe9021347202ce8c1 (diff)
PMD: ADPCM support
-rw-r--r--fmdriver/fmdriver_pmd.c807
-rw-r--r--fmdriver/fmdriver_pmd.h10
-rw-r--r--gtk/main.c107
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;
diff --git a/gtk/main.c b/gtk/main.c
index 43db33e..cd8c9ab 100644
--- a/gtk/main.c
+++ b/gtk/main.c
@@ -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));