Skip to content

Commit

Permalink
vgm: ym2413 tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
Federico Berti committed Oct 4, 2019
1 parent d061445 commit 685dd07
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 70 deletions.
135 changes: 101 additions & 34 deletions src/uk/co/omgdrv/simplevgm/fm/ym2413/Emu2413.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,21 @@
// Port of emu2413.c v0.61 -- YM2413 emulator written by Mitsutaka Okazaki
// zlib license

/**
* Ported by the nintaco team: https://nintaco.com
* Original C implementation: https://github.com/digital-sound-antiques/emu2413
* <p>
* ---
* 2019-10-01 Federico Berti
* - back-ported 0.63 changes: Support per-channel output
* - update 2413 instruments
* - adaptation work
*/

public final class Emu2413 {

public static short[] default_inst = {
//unused
public static final short[] vrc7_inst = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27,
0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12,
Expand All @@ -32,6 +44,42 @@ public final class Emu2413 {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

/* YM2413 TONES by Mitsutaka Okazaki
* The following patches are referred from VRC7
* - @5: Clarinet
* - @7: Trumpet
* - Drums: BD/SD/HH/TM/TC
* https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#opll_vrc7_patch_format
*/
public static final short[] ym2413_inst = {
/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */
/* 0 1 2 3 4 5 6 7 */
0x49, 0x4c, 0x4c, 0x32, 0x00, 0x00, 0x00, 0x00, //0
0x61, 0x61, 0x1E, 0x17, 0xF0, 0x7F, 0x00, 0x17, //1
0x13, 0x41, 0x17, 0x0E, 0xFF, 0xFF, 0x23, 0x13, //2
0x23, 0x01, 0x9A, 0x04, 0xA3, 0xf4, 0xF0, 0x23, //3
0x11, 0x61, 0x0e, 0x07, 0xfa, 0x64, 0x70, 0x17, //4
0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28, //5
0x21, 0x22, 0x16, 0x05, 0xf0, 0x71, 0x00, 0x18, //6
0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07, //7
0x23, 0x21, 0x2d, 0x16, 0x90, 0x90, 0x00, 0x07, //8
0x21, 0x21, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, //9
0x21, 0x21, 0x0b, 0x1a, 0x85, 0xa0, 0x70, 0x07, //A
0x23, 0x01, 0x83, 0x10, 0xff, 0xb4, 0x10, 0xf4, //B
0x97, 0xc1, 0x20, 0x07, 0xff, 0xf4, 0x22, 0x22, //C
0x61, 0x00, 0x0c, 0x05, 0xd2, 0xf6, 0x40, 0x43, //D
0x01, 0x01, 0x56, 0x03, 0xf4, 0xf0, 0x03, 0x02, //E
0x21, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x23, //F

/* drum instruments definitions */
/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */
/* 0 1 2 3 4 5 6 7 */
/* Drums dumped from the VRC7 using debug mode, these are likely also correct for ym2413(OPLL) but need verification */
0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d,/* BD */
0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68,/* HH, SD */
0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55 /* TOM, TOP CYM */
};

public static final int CHANNELS = 9;
public static final int SLOTS = CHANNELS * 2;
public static final int PATCHES = SLOTS + 1;
Expand Down Expand Up @@ -134,9 +182,9 @@ private static boolean BIT(final int s, final int b) {
}

// Input clock
private static final int clk = 3579545;
private static int fmClockHz = 3579545;
// Sampling rate
private static final int rate = 49716;
private static int fmRateHz = 49716;

// WaveTable for each envelope amp
private static final int[] fullsintable = new int[PG_WIDTH];
Expand All @@ -149,10 +197,10 @@ private static boolean BIT(final int s, final int b) {
private static final int[] amtable = new int[AM_PG_WIDTH];

// Phase delta for LFO
private static final int pm_dphase
= (int) (PM_SPEED * PM_DP_WIDTH / (clk / 72));
private static final int am_dphase
= (int) (AM_SPEED * AM_DP_WIDTH / (clk / 72));
private static int pm_dphase
= (int) (PM_SPEED * PM_DP_WIDTH / (fmClockHz / 72));
private static int am_dphase
= (int) (AM_SPEED * AM_DP_WIDTH / (fmClockHz / 72));

// dB to Liner table
private static final int[] DB2LIN_TABLE = new int[(DB_MUTE + DB_MUTE) * 2];
Expand Down Expand Up @@ -397,7 +445,7 @@ private static void OPLL_dump2patch(final short[] dump,
private static void OPLL_getDefaultPatch(int num,
OPLL_PATCH[] patch) {
short[] r = new short[8];
System.arraycopy(default_inst, num * 8, r, 0, r.length);
System.arraycopy(ym2413_inst, num * 8, r, 0, r.length);
OPLL_dump2patch(r, patch);
}

Expand Down Expand Up @@ -749,6 +797,18 @@ private static void internal_refresh() {
makeDphaseDRTable();
}

public static void OPLL_init(int clock, int rate) {
fmRateHz = rate;
if (fmClockHz != clock) {
fmClockHz = clock;
pm_dphase
= (int) (PM_SPEED * PM_DP_WIDTH / (fmClockHz / 72));
am_dphase
= (int) (AM_SPEED * AM_DP_WIDTH / (fmClockHz / 72));
}
OPLL_init();
}

public static void OPLL_init() {
makePmTable();
makeAmTable();
Expand Down Expand Up @@ -810,14 +870,12 @@ public static final void OPLL_reset(final OPLL opll) {
OPLL_writeReg(opll, i, 0);
}

opll.realstep = (int) ((1L << 31L) / rate);
opll.opllstep = (int) ((1L << 31L) / (clk / 72));
opll.realstep = (int) ((1L << 31L) / fmRateHz);
opll.opllstep = (int) ((1L << 31L) / (fmClockHz / 72));
opll.oplltime = 0;
for (int i = 0; i < 14; i++) {
opll.pan[i] = 2;
}
opll.sprev[0] = opll.sprev[1] = 0;
opll.snext[0] = opll.snext[1] = 0;
}

/*********************************************************
Expand Down Expand Up @@ -1058,10 +1116,10 @@ private static int calc_slot_hat(final OPLL_SLOT slot, final int pgout_cym,
return DB2LIN_TABLE[dbout + slot.egout];
}

private static int calc(final OPLL opll) {
static int INST_VOL_MULT = 8;
static int RHYTHM_VOL_MULT = 16;

int inst = 0;
int perc = 0;
private static void update_output(final OPLL opll) {

update_ampm(opll);
update_noise(opll);
Expand All @@ -1071,68 +1129,77 @@ private static int calc(final OPLL opll) {
calc_envelope(opll.slot[i], opll.lfo_am);
}

//CH1-6
for (int i = 0; i < 6; i++) {
if (CAR(opll, i).eg_mode != FINISH) {
inst += calc_slot_car(CAR(opll, i), calc_slot_mod(MOD(opll, i)));
opll.ch_out[i] += calc_slot_car(CAR(opll, i), calc_slot_mod(MOD(opll, i)));
}
}

// CH6
// CH7
if (opll.patch_number[6] <= 15) {
if (CAR(opll, 6).eg_mode != FINISH) {
inst += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6)));
opll.ch_out[6] += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6)));
}
} else {
if (CAR(opll, 6).eg_mode != FINISH) {
perc += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6)));
opll.ch_out[9] += calc_slot_car(CAR(opll, 6), calc_slot_mod(MOD(opll, 6)));
}
}

// CH7
// CH8
if (opll.patch_number[7] <= 15) {
if (CAR(opll, 7).eg_mode != FINISH)
inst += calc_slot_car(CAR(opll, 7), calc_slot_mod(MOD(opll, 7)));
opll.ch_out[7] += calc_slot_car(CAR(opll, 7), calc_slot_mod(MOD(opll, 7)));
} else {
if (MOD(opll, 7).eg_mode != FINISH) {
perc += calc_slot_hat(MOD(opll, 7), CAR(opll, 8).pgout,
opll.ch_out[10] += calc_slot_hat(MOD(opll, 7), CAR(opll, 8).pgout,
(opll.noise_seed & 1) != 0);
}
if (CAR(opll, 7).eg_mode != FINISH) {
perc -= calc_slot_snare(CAR(opll, 7), (opll.noise_seed & 1) != 0);
opll.ch_out[11] -= calc_slot_snare(CAR(opll, 7), (opll.noise_seed & 1) != 0);
}
}

// CH8
// CH9
if (opll.patch_number[8] <= 15) {
if (CAR(opll, 8).eg_mode != FINISH) {
inst += calc_slot_car(CAR(opll, 8), calc_slot_mod(MOD(opll, 8)));
opll.ch_out[8] += calc_slot_car(CAR(opll, 8), calc_slot_mod(MOD(opll, 8)));
}
} else {
if (MOD(opll, 8).eg_mode != FINISH) {
perc += calc_slot_tom(MOD(opll, 8));
opll.ch_out[12] += calc_slot_tom(MOD(opll, 8));
}

if (CAR(opll, 8).eg_mode != FINISH) {
perc -= calc_slot_cym(CAR(opll, 8), MOD(opll, 7).pgout);
opll.ch_out[13] -= calc_slot_cym(CAR(opll, 8), MOD(opll, 7).pgout);
}
}

return (inst + (perc << 1)) << 3;
/* Always calc average of two samples */
for (int i = 0; i < 14; i++) {
opll.ch_out[i] >>= 1;
}
}

static int mix_output(OPLL opll) {
int i;
opll.out = opll.ch_out[0];
for (i = 1; i < 14; i++) {
opll.out += opll.ch_out[i];
}
return opll.out;
}

public static int OPLL_calc(final OPLL opll) {

while (opll.realstep > opll.oplltime) {
opll.oplltime += opll.opllstep;
opll.prev = opll.next;
opll.next = calc(opll);
update_output(opll);
}

opll.oplltime -= opll.realstep;
opll.out = (int) (((double) opll.next * (opll.opllstep - opll.oplltime)
+ (double) opll.prev * opll.oplltime) / opll.opllstep);

return opll.out;
return mix_output(opll);
}

/****************************************************
Expand Down
21 changes: 17 additions & 4 deletions src/uk/co/omgdrv/simplevgm/fm/ym2413/OPLL.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

import java.io.Serializable;

// Port of emu2413.c v0.61 -- YM2413 emulator written by Mitsutaka Okazaki
// zlib license

/**
* Ported by the nintaco team: https://nintaco.com
* Original C implementation: https://github.com/digital-sound-antiques/emu2413
* <p>
* ---
* 2019-10-01 Federico Berti
* - back-ported 0.63 changes: Support per-channel output
* - update 2413 instruments
* - adaptation work
*/
public class OPLL implements Serializable {

private static final long serialVersionUID = 0;
Expand All @@ -12,10 +25,6 @@ public class OPLL implements Serializable {
public int realstep;
public int oplltime;
public int opllstep;
public int prev;
public int next;
public int[] sprev = new int[2];
public int[] snext = new int[2];
public int[] pan = new int[16];

// Register
Expand Down Expand Up @@ -44,6 +53,10 @@ public class OPLL implements Serializable {
public OPLL_PATCH[] patch = new OPLL_PATCH[19 * 2];
public int[] patch_update = new int[2]; // flag for check patch update

/* Output of each channels / 0-8:TONE, 9:BD 10:HH 11:SD, 12:TOM, 13:CYM */
int[] ch_out = new int[14];


public OPLL() {
for (int i = slot.length - 1; i >= 0; i--) {
slot[i] = new OPLL_SLOT();
Expand Down
35 changes: 3 additions & 32 deletions src/uk/co/omgdrv/simplevgm/fm/ym2413/Ym2413Provider.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,6 @@ public class Ym2413Provider implements VgmFmProvider {

public enum FmReg {ADDR_LATCH_REG, DATA_REG}

public static final short[] ym2413_inst = {
/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */
/* 0 1 2 3 4 5 6 7 */
/* These YM2413(OPLL) patch dumps are done via audio analysis (and a/b testing?) from Jarek and are known to be inaccurate */
0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00, //0
0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17, //1
0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13, //2
0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23, //3
0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27, //4
0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18, //5
0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13, //6
0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17, //7
0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07, //8
0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, //9
0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07, //A
0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22, //B
0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12, //C
0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44, //D
0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3, //E
0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13, //F

/* drum instruments definitions */
/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */
/* 0 1 2 3 4 5 6 7 */
/* Drums dumped from the VRC7 using debug mode, these are likely also correct for ym2413(OPLL) but need verification */
0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d,/* BD */
0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68,/* HH, SD */
0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55 /* TOM, TOP CYM */
};

@Override
public void reset() {
for (int i = 0x10; i < 0x40; i++) {
Expand All @@ -69,19 +39,20 @@ public void reset() {

@Override
public void init(int clock, int rate) {
Emu2413.default_inst = ym2413_inst;
Emu2413.OPLL_init();
opll = Emu2413.OPLL_new();
}


static int AUDIO_SCALE_BITS = 2;

@Override
public void update(int[] buf_lr, int offset, int samples441) {
offset <<= 1; //stereo
sampleRateCalcAcc += samples441 / rateRatio;
int total = (int) sampleRateCalcAcc + 1; //needed to match the offsets
for (int i = 0; i < total; i++) {
int res = Emu2413.OPLL_calc(opll);
int res = Emu2413.OPLL_calc(opll) << AUDIO_SCALE_BITS;
rateRatioAcc += rateRatio;
if (rateRatioAcc > 1) {
buf_lr[offset++] = res;
Expand Down

0 comments on commit 685dd07

Please sign in to comment.