ref: 0b7b60511d71e3c80deb8d1900e1ec778d964c0c
author: qwx <[email protected]>
date: Mon May 27 19:51:52 EDT 2019
initial import
--- /dev/null
+++ b/README
@@ -1,0 +1,6 @@
+based on opl3emu2 version 1.1.1 by Joel Yliluoma, itself based on fmopl.c
+version 0.72 from MAME, which is:
+Copyright (C) 2002,2003 Jarek Burczynski (bujar at mame dot net)
+Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+
+this is a... work in progress. use at your own peril.
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,443 @@
+/*
+ * The OPL-3 mode is switched on by writing 0x01, to the offset 5
+ * of the right side.
+ *
+ * Another special register at the right side is at offset 4. It contains
+ * a bit mask defining which voices are used as 4 OP voices.
+ *
+ * The percussive mode is implemented in the left side only.
+ *
+ * With the above exceptions the both sides can be operated independently.
+ *
+ * A 4 OP voice can be created by setting the corresponding
+ * bit at offset 4 of the right side.
+ *
+ * For example setting the rightmost bit (0x01) changes the
+ * first voice on the right side to the 4 OP mode. The fourth
+ * voice is made inaccessible.
+ *
+ * If a voice is set to the 2 OP mode, it works like 2 OP modes
+ * of the original YM3812 (AdLib). In addition the voice can
+ * be connected the left, right or both stereo channels. It can
+ * even be left unconnected. This works with 4 OP voices also.
+ *
+ * The stereo connection bits are located in the FEEDBACK_CONNECTION
+ * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
+ * in the second half of the voice.
+ */
+
+/*
+ * Register numbers for the global registers
+ */
+
+#define TEST_REGISTER 0x01
+#define ENABLE_WAVE_SELECT 0x20
+
+#define TIMER1_REGISTER 0x02
+#define TIMER2_REGISTER 0x03
+#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
+#define IRQ_RESET 0x80
+#define TIMER1_MASK 0x40
+#define TIMER2_MASK 0x20
+#define TIMER1_START 0x01
+#define TIMER2_START 0x02
+
+#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
+#define RIGHT_4OP_0 0x01
+#define RIGHT_4OP_1 0x02
+#define RIGHT_4OP_2 0x04
+#define LEFT_4OP_0 0x08
+#define LEFT_4OP_1 0x10
+#define LEFT_4OP_2 0x20
+
+#define OPL3_MODE_REGISTER 0x05 /* Right side */
+#define OPL3_ENABLE 0x01
+#define OPL4_ENABLE 0x02
+
+#define KBD_SPLIT_REGISTER 0x08 /* Left side */
+#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
+#define KEYBOARD_SPLIT 0x40
+
+#define PERCOSSION_REGISTER 0xbd /* Left side only */
+#define TREMOLO_DEPTH 0x80
+#define VIBRATO_DEPTH 0x40
+#define PERCOSSION_ENABLE 0x20
+#define BASSDRUM_ON 0x10
+#define SNAREDRUM_ON 0x08
+#define TOMTOM_ON 0x04
+#define CYMBAL_ON 0x02
+#define HIHAT_ON 0x01
+
+/*
+ * Offsets to the register banks for operators. To get the
+ * register number just add the operator offset to the bank offset
+ *
+ * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
+ */
+enum{
+ AM_VIB = 0x20,
+ TREMOLO_ON = 0x80,
+ VIBRATO_ON = 0x40,
+ SUSTAIN_ON = 0x20,
+ KSR = 0x10, /* Key scaling rate */
+ MULTIPLE_MASK = 0x0f /* Frequency multiplier */
+};
+
+ /*
+ * KSL/Total level (0x40 to 0x55)
+ */
+#define KSL_LEVEL 0x40
+#define KSL_MASK 0xc0 /* Envelope scaling bits */
+#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
+
+/*
+ * Attack / Decay rate (0x60 to 0x75)
+ */
+#define ATTACK_DECAY 0x60
+#define ATTACK_MASK 0xf0
+#define DECAY_MASK 0x0f
+
+/*
+ * Sustain level / Release rate (0x80 to 0x95)
+ */
+#define SUSTAIN_RELEASE 0x80
+#define SUSTAIN_MASK 0xf0
+#define RELEASE_MASK 0x0f
+
+/*
+ * Wave select (0xE0 to 0xF5)
+ */
+#define WAVE_SELECT 0xe0
+
+/*
+ * Offsets to the register banks for voices. Just add to the
+ * voice number to get the register number.
+ *
+ * F-Number low bits (0xA0 to 0xA8).
+ */
+#define FNUM_LOW 0xa0
+
+/*
+ * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
+ */
+#define KEYON_BLOCK 0xb0
+#define KEYON_BIT 0x20
+#define BLOCKNUM_MASK 0x1c
+#define FNUM_HIGH_MASK 0x03
+
+/*
+ * Feedback / Connection (0xc0 to 0xc8)
+ *
+ * These registers have two new bits when the OPL-3 mode
+ * is selected. These bits controls connecting the voice
+ * to the stereo channels. For 4 OP voices this bit is
+ * defined in the second half of the voice (add 3 to the
+ * register offset).
+ *
+ * For 4 OP voices the connection bit is used in the
+ * both halves (gives 4 ways to connect the operators).
+ */
+#define FEEDBACK_CONNECTION 0xc0
+#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
+#define CONNECTION_BIT 0x01
+/*
+ * In the 4 OP mode there is four possible configurations how the
+ * operators can be connected together (in 2 OP modes there is just
+ * AM or FM). The 4 OP connection mode is defined by the rightmost
+ * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
+ *
+ * First half Second half Mode
+ *
+ * +---+
+ * v |
+ * 0 0 >+-1-+--2--3--4-->
+ *
+ *
+ *
+ * +---+
+ * | |
+ * 0 1 >+-1-+--2-+
+ * |->
+ * >--3----4-+
+ *
+ * +---+
+ * | |
+ * 1 0 >+-1-+-----+
+ * |->
+ * >--2--3--4-+
+ *
+ * +---+
+ * | |
+ * 1 1 >+-1-+--+
+ * |
+ * >--2--3-+->
+ * |
+ * >--4----+
+ */
+#define STEREO_BITS 0x30 /* OPL-3 only */
+#define VOICE_TO_LEFT 0x10
+#define VOICE_TO_RIGHT 0x20
+
+enum{
+ MAXINST = 100, /* FIXME: what the fuck is this? */
+ MAXPATN = 100,
+ MAXCHN = 15 /* Remember that the percussion channel is reserved! */
+};
+
+typedef struct MusPlayerPos MusPlayerPos;
+typedef struct Pattern Pattern;
+typedef struct InternalHdr InternalHdr;
+typedef struct InternalSample InternalSample;
+typedef struct MusData MusData;
+
+struct MusPlayerPos{
+ int Ord;
+ int Pat;
+ int Row;
+ char *Pos;
+ int Wait;
+};
+
+struct Pattern{
+ ushort Len;
+ char *Ptr;
+};
+
+struct InternalHdr{
+ ushort OrdNum; /* Number of orders, is even */
+ /* If bit 8 (&256) is set, there exist no */
+ /* notes between 126..253 and the 7th bit */
+ /* of note value can be used to tell if */
+ /* there is instrument number. */
+ ushort InsNum; /* Number of instruments */
+ ushort PatNum; /* Number of patterns */
+ /* If bit 9 set, all instruments have */
+ /* the insname[] filled with AsciiZ name. */
+ ushort Ins2Num; /* Number of instruments - for checking! */
+ uchar InSpeed; /* Initial speed in 7 lowermost bits */
+ /* If highest bit (&128) set, no logaritmic */
+ /* adlib volume conversion needs to be done */
+ /* when playing with midi device. */
+ uchar InTempo; /* Initial tempo */
+ uchar ChanCount[]; /* ChanCount[0] present if PatNum bit 9 set */
+}; /* Size: 2+2+2+2+1+1 = 8+2 = 10 */
+
+struct InternalSample{
+ /* unused */
+ char FT; /*finetune value 1 */
+ /* signed seminotes. */
+ uchar D[11]; /*Identificators 11 */
+ /* - 12nd byte not needed. */
+ /* six high bits of D[8] */
+ /* define the midi volume */
+ /* scaling level */
+ /* - default is 63. */
+ /* D[9] bits 5..2 have */
+ /* the automatical SDx */
+ /* adjust value. */
+ /* - default is 0. */
+ /* D[9] bits 6-7 are free. */
+ char Volume; /*0..63 1 */
+ /* If bit 6 set, */
+ /* the instrument will be */
+ /* played simultaneously */
+ /* on two channels when */
+ /* playing with a midi device. */
+ /* To gain more volume. */
+ /* If bit 7 set, */
+ /* the finetune value */
+ /* affects also FM. */
+ /* (SMP->AME conversion) */
+ ushort C2Spd; /*8363 = normal..? 2 */
+ uchar GM; /*GM program number 1 */
+ uchar Bank; /*Bank number, 0=normal. 1 */
+ /*Highest bit (&128) is free. */
+ char insname[];
+ /*Only if PatNum&512 set */
+}; /* total:17 = 0x11 */
+
+struct MusData{
+ int PlayNext;
+ /* Set to 1 every time next line is played.
+ Use it to syncronize screen with music. */
+
+ long RowTimer;
+ /* You can delay the music with this.
+ Or fasten. Units: Rate */
+
+ char *Playing; /* Currently playing music from this buffer. */
+ int Paused; /* If paused, interrupts normally but does not play. *
+ * Read only. There's PauseS3M() for this. */
+ InternalHdr *Hdr;
+ /* FIXME: >127 for percussion; was set to MAXINST before, causing bad reads; is
+ this used correctly now? why is it 100? what the fuck is it for??? */
+ InternalSample *Instr[2*MAXINST+1];
+
+ int LinearMidiVol;
+ int GlobalVolume;
+
+ int Speed;
+ int Tempo;
+
+ /* Data from song */
+ char *Orders;
+ Pattern Patn[MAXPATN];
+
+ /* Player: For SBx */
+ MusPlayerPos posi;
+ MusPlayerPos saved;
+ int LoopCount;
+ int PendingSB0;
+
+ long CurVol[MAXCHN];
+ uchar NNum[MAXCHN];
+ uchar CurPuh[MAXCHN];
+ int NoteCut[MAXCHN];
+ int NoteDelay[MAXCHN];
+ uchar CurInst[MAXCHN];
+ uchar InstNum[MAXCHN]; /* For the instrument number optimizer */
+ uchar PatternDelay;
+
+ char FineVolSlide[MAXCHN];
+ char FineFrqSlide[MAXCHN];
+ char VolSlide[MAXCHN];
+ char FreqSlide[MAXCHN];
+ uchar VibDepth[MAXCHN];
+ uchar VibSpd [MAXCHN];
+ uchar VibPos [MAXCHN];
+ uchar ArpegSpd;
+ uchar Arpeggio[MAXCHN];
+ uchar ArpegCnt[MAXCHN];
+ uchar Retrig[MAXCHN];
+
+ char Active[MAXCHN];
+ char Doubles[MAXCHN];
+
+ uchar DxxDefault[MAXCHN];
+ uchar ExxDefault[MAXCHN];
+ uchar HxxDefault[MAXCHN];
+ uchar MxxDefault[MAXCHN];
+ uchar SxxDefault[MAXCHN];
+
+ ulong Herz[MAXCHN]; /* K�ytt�� m_opl */
+};
+#define FxxDefault ExxDefault /* They really are combined in ST3 */
+
+/* fmopl */
+enum{
+ OPL_SAMPLE_BITS = 16
+};
+
+typedef s16int OPLSAMPLE; //INT32
+typedef void (*OPL_TIMERHANDLER)(void *param, int timer, double interval_sec);
+typedef void (*OPL_IRQHANDLER)(void *param, int irq);
+typedef void (*OPL_UPDATEHANDLER)(void *param, int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(void *param, uchar data);
+typedef uchar (*OPL_PORTHANDLER_R)(void *param);
+
+typedef struct OPL_SLOT OPL_SLOT;
+typedef struct OPL_CH OPL_CH;
+typedef struct FM_OPL FM_OPL;
+
+struct OPL_SLOT{
+ u32int ar; /* attack rate: AR<<2 */
+ u32int dr; /* decay rate: DR<<2 */
+ u32int rr; /* release rate:RR<<2 */
+ u8int KSR; /* key scale rate */
+ u8int ksl; /* keyscale level */
+ u8int ksr; /* key scale rate: kcode>>KSR */
+ u8int mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ u32int Cnt; /* frequency counter */
+ u32int Incr; /* frequency counter step */
+ u8int FB; /* feedback shift value */
+ s32int *connect1; /* slot1 output pointer */
+ s32int op1_out[2]; /* slot1 output for feedback */
+ u8int CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ u8int eg_type; /* percussive/non-percussive mode */
+ u8int state; /* phase type */
+ u32int TL; /* total level: TL << 2 */
+ s32int TLL; /* adjusted now TL */
+ s32int volume; /* envelope counter */
+ u32int sl; /* sustain level: sl_tab[SL] */
+ u8int eg_sh_ar; /* (attack state) */
+ u8int eg_sel_ar; /* (attack state) */
+ u8int eg_sh_dr; /* (decay state) */
+ u8int eg_sel_dr; /* (decay state) */
+ u8int eg_sh_rr; /* (release state) */
+ u8int eg_sel_rr; /* (release state) */
+ u32int key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ u32int AMmask; /* LFO Amplitude Modulation enable mask */
+ u8int vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ u16int wavetable;
+};
+struct OPL_CH{
+ OPL_SLOT SLOT[2];
+ /* phase generator state */
+ u32int block_fnum; /* block+fnum */
+ u32int fc; /* Freq. Increment base */
+ u32int ksl_base; /* KeyScaleLevel Base step */
+ u8int kcode; /* key code (for key scaling) */
+};
+struct FM_OPL{
+ /* FM channel slots */
+ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/
+
+ u32int eg_cnt; /* global envelope generator counter */
+ u32int eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ u32int eg_timer_add; /* step of eg_timer */
+ u32int eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ u8int rhythm; /* Rhythm mode */
+
+ u32int fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ u32int LFO_AM;
+ s32int LFO_PM;
+
+ u8int lfo_am_depth;
+ u8int lfo_pm_depth_range;
+ u32int lfo_am_cnt;
+ u32int lfo_am_inc;
+ u32int lfo_pm_cnt;
+ u32int lfo_pm_inc;
+
+ u32int noise_rng; /* 23 bit noise shift register */
+ u32int noise_p; /* current noise 'phase' */
+ u32int noise_f; /* current noise period */
+
+ u8int wavesel; /* waveform select enable flag */
+
+ u32int T[2]; /* timer counters */
+ u8int st[2]; /* timer enable */
+
+ /* external event callback handlers */
+ OPL_TIMERHANDLER timer_handler; /* TIMER handler */
+ void *TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ void *IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */
+ void *UpdateParam; /* stream update parameter */
+
+ u8int type; /* chip type */
+ u8int address; /* address register */
+ u8int status; /* status flag */
+ u8int statusmask; /* status mask */
+ u8int mode; /* Reg.08 : CSM,notesel,etc. */
+
+ u32int clock; /* master clock (Hz) */
+ u32int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time)*/
+
+ s32int phase_modulation; /* phase modulation input (SLOT 2) */
+ s32int output[1];
+};
--- /dev/null
+++ b/fmopl.c
@@ -1,0 +1,1676 @@
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include "dat.h"
+#include "fns.h"
+
+/* output final shift */
+#define FINAL_SH (0)
+#define MAXOUT (+32767)
+#define MINOUT (-32768)
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+int acc_calc(signed int value)
+{
+ if (value>=0)
+ {
+ if (value < 0x0200)
+ return (value & ~0);
+ if (value < 0x0400)
+ return (value & ~1);
+ if (value < 0x0800)
+ return (value & ~3);
+ if (value < 0x1000)
+ return (value & ~7);
+ if (value < 0x2000)
+ return (value & ~15);
+ if (value < 0x4000)
+ return (value & ~31);
+ return (value & ~63);
+ }
+ if (value > -0x0200)
+ return (~abs(value) & ~0);
+ if (value > -0x0400)
+ return (~abs(value) & ~1);
+ if (value > -0x0800)
+ return (~abs(value) & ~3);
+ if (value > -0x1000)
+ return (~abs(value) & ~7);
+ if (value > -0x2000)
+ return (~abs(value) & ~15);
+ if (value > -0x4000)
+ return (~abs(value) & ~31);
+ return (~abs(value) & ~63);
+}
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+
+/* mapping of register number (offset) to slot number used by the emulator */
+static int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/2.0)
+static u32int ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */
+static u32int ksl_shift[4] = { 31, 1, 2, 0 };
+
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (u32int) ( db * (2.0/ENV_STEP) )
+static u32int sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static uchar eg_inc[15*RATE_STEPS]={
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+static uchar eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static uchar eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static u8int mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ ML/2, 1*ML, 2*ML, 3*ML, 4*ML, 5*ML, 6*ML, 7*ML,
+ 8*ML, 9*ML,10*ML,10*ML,12*ML,12*ML,15*ML,15*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+* 12 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (12*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>4)
+
+/* sin waveform table in 'decibel' scale */
+/* four waveforms on OPL2 type chips */
+static unsigned int sin_tab[SIN_LEN * 4];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (losing precision is important)
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210U
+
+static u8int lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+static s8int lfo_pm_table[8*8*2] = {
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+static int num_lock = 0;
+
+
+#define SLOT7_1 (&OPL->P_CH[7].SLOT[SLOT1])
+#define SLOT7_2 (&OPL->P_CH[7].SLOT[SLOT2])
+#define SLOT8_1 (&OPL->P_CH[8].SLOT[SLOT1])
+#define SLOT8_2 (&OPL->P_CH[8].SLOT[SLOT2])
+
+
+
+
+static int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* status set and IRQ handling */
+static void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+static void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+static void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+
+/* advance LFO to next sample */
+static void advance_lfo(FM_OPL *OPL)
+{
+ u8int tmp;
+
+ /* LFO */
+ OPL->lfo_am_cnt += OPL->lfo_am_inc;
+ if (OPL->lfo_am_cnt >= ((u32int)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ OPL->lfo_am_cnt -= ((u32int)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ];
+
+ if (OPL->lfo_am_depth)
+ OPL->LFO_AM = tmp;
+ else
+ OPL->LFO_AM = tmp>>2;
+
+ OPL->lfo_pm_cnt += OPL->lfo_pm_inc;
+ OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;
+}
+
+/* advance to next sample */
+static void advance(FM_OPL *OPL)
+{
+ OPL_CH *CH;
+ OPL_SLOT *op;
+ int i;
+
+ OPL->eg_timer += OPL->eg_timer_add;
+
+ while (OPL->eg_timer >= OPL->eg_timer_overflow)
+ {
+ OPL->eg_timer -= OPL->eg_timer_overflow;
+
+ OPL->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Envelope Generator */
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ {
+ /* we want sign extension here */
+ op->volume += (s32int)(~op->volume *
+ (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ u8int block;
+ unsigned int block_fnum = CH->block_fnum;
+
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ OPL->noise_p += OPL->noise_f;
+ i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ OPL->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ u32int j;
+ j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1;
+ OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302;
+ OPL->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+static signed int op_calc(u32int phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ u32int p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+static signed int op_calc1(u32int phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ u32int p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((u32int)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+static void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )
+{
+ OPL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ OPL->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ *SLOT->connect1 += SLOT->op1_out[0];
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+static void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )
+{
+ OPL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ OPL->phase_modulation = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ OPL->phase_modulation = SLOT->op1_out[0];
+ /* else ignore output of operator 1 */
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */
+ /* SD (16) channel 7->slot 1 */
+ /* TOM (14) channel 8->slot 1 */
+ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on: */
+ /* HH channel 7->slot1 */
+ /* SD channel 7->slot2 */
+ /* TOM channel 8->slot1 */
+ /* TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ u32int phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ u32int phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(SLOT8_1);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ u32int phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;
+ }
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<12; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /* waveform 2: __ __ __ __ */
+ /* / \/ \/ \/ \*/
+ /* abs(sin) */
+
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+ }
+
+ return 1;
+}
+
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int i;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0;
+/*
+ OPL->rate = (double)OPL->clock / 72.0;
+ OPL->freqbase = 1.0;
+*/
+
+ /* Timer base time */
+ OPL->TimerBase = 1.0 / ((double)OPL->clock / 72.0);
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ OPL->fn_tab[i] = (u32int)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ }
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * OPL->freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * OPL->freqbase;
+
+ /* Noise generator: a step takes 1 sample */
+ OPL->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * OPL->freqbase;
+
+ OPL->eg_timer_add = (1<<EG_SH) * OPL->freqbase;
+ OPL->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+}
+
+static void FM_KEYON(OPL_SLOT *SLOT, u32int key_set)
+{
+ if( !SLOT->key )
+ {
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+ SLOT->key |= key_set;
+}
+
+static void FM_KEYOFF(OPL_SLOT *SLOT, u32int key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+static void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+ }
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+static void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+static void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ksl = ksl_shift[v >> 6];
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set attack rate & decay rate */
+static void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+static void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+
+/* write a value v to register r on OPL chip */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* waveform select enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ /* do not change the waveform previously selected */
+ }
+ break;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ break;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ u8int st1 = v&1;
+ u8int st2 = (v>>1)&1;
+
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL, v & (0x78-0x08) );
+ OPL_STATUSMASK_SET(OPL, (~v) & 0x78 );
+
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ double period = st2 ? (OPL->TimerBase * OPL->T[1]) : 0;
+ OPL->st[1] = st2;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ double period = st1 ? (OPL->TimerBase * OPL->T[0]) : 0;
+ OPL->st[0] = st1;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period);
+ }
+ }
+ break;
+ case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+ break;
+ default:
+ fprint(2, "write to unknown register: %02x\n",r);
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(OPL,slot,v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(OPL,slot,v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(OPL,slot,v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(OPL,slot,v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ OPL->lfo_am_depth = v & 0x80;
+ OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ OPL->rhythm = v&0x3f;
+
+ if(OPL->rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2);
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ /* BD key off */
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ u8int block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = ksl_tab[block_fnum>>6];
+ CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (OPL->mode&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+ CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;
+ break;
+ case 0xe0: /* waveform select */
+ /* simply ignore write to the waveform select register if selecting not enabled in test register */
+ if(OPL->wavesel)
+ {
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ CH = &OPL->P_CH[slot/2];
+
+ CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;
+ }
+ break;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+}
+
+static void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ OPL->eg_timer = 0;
+ OPL->eg_cnt = 0;
+
+ OPL->noise_rng = 1; /* noise shift register */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wavesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9 ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = 0;
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+}
+
+
+static void OPL_postload(FM_OPL *OPL)
+{
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[ch];
+
+ /* Look up key scale level */
+ u32int block_fnum = CH->block_fnum;
+ CH->ksl_base = ksl_tab[block_fnum >> 6];
+ CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));
+
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ /* Calculate key scale rate */
+ SLOT->ksr = CH->kcode >> SLOT->KSR;
+
+ /* Calculate attack, decay and release rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+
+ /* Calculate phase increment */
+ SLOT->Incr = CH->fc * SLOT->mul;
+
+ /* Total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
+
+ /* Connect output */
+ SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;
+ }
+ }
+}
+
+static FM_OPL *
+OPLCreate(u32int clock, u32int rate, int type)
+{
+ FM_OPL *p;
+
+ if(OPL_LockTable() == -1)
+ sysfatal("OPLCreate: locktable");
+
+ /* allocate memory block */
+ if((p = mallocz(sizeof *p, 1)) == nil)
+ sysfatal("mallocz: %r");
+ p->type = type;
+ p->clock = clock; /* Hz */
+ p->rate = rate;
+
+ /* init global tables */
+ OPL_initalize(p);
+
+ return p;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLDestroy(FM_OPL *OPL)
+{
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* Optional handlers */
+
+static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param)
+{
+ OPL->timer_handler = timer_handler;
+ OPL->TimerParam = param;
+}
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+static int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+static unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+
+ /* OPL and OPL2 */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+ return 0xff;
+}
+
+/* CSM Key Controll */
+static void CSMKeyControll(OPL_CH *CH)
+{
+ FM_KEYON (&CH->SLOT[SLOT1], 4);
+ FM_KEYON (&CH->SLOT[SLOT2], 4);
+
+ /* The key off should happen exactly one sample later - not implemented correctly yet */
+
+ FM_KEYOFF(&CH->SLOT[SLOT1], ~4);
+ FM_KEYOFF(&CH->SLOT[SLOT2], ~4);
+}
+
+
+static int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0; ch<9; ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ if (OPL->timer_handler)
+ (OPL->timer_handler)(OPL->TimerParam,c,OPL->TimerBase * OPL->T[c]);
+ return OPL->status>>7;
+}
+
+
+FM_OPL *
+ym3812_init(u32int clock, u32int rate)
+{
+ FM_OPL *p;
+
+ if((p = OPLCreate(clock, rate, OPL_TYPE_YM3812)) == nil)
+ sysfatal("OPLCreate: %r"); /* FIXME */
+ ym3812_reset_chip(p);
+ return p;
+}
+
+/* FIXME: remove abstractions; this is now opl2 only */
+void
+ym3812_shutdown(FM_OPL *p)
+{
+ OPLDestroy(p);
+}
+
+void
+ym3812_reset_chip(FM_OPL *p)
+{
+ OPLResetChip(p);
+}
+
+int
+ym3812_write(FM_OPL *p, int a, int v)
+{
+ return OPLWrite(p, a, v);
+}
+
+uchar
+ym3812_read(FM_OPL *p, int a)
+{
+ /* YM3812 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(p, a) | 0x06 ;
+}
+
+int
+ym3812_timer_over(FM_OPL *p, int c)
+{
+ return OPLTimerOver(p, c);
+}
+
+void
+ym3812_set_timer_handler(FM_OPL *p, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ OPLSetTimerHandler(p, timer_handler, param);
+}
+
+void
+ym3812_set_irq_handler(FM_OPL *p,OPL_IRQHANDLER IRQHandler, void *param)
+{
+ OPLSetIRQHandler(p, IRQHandler, param);
+}
+
+void
+ym3812_set_update_handler(FM_OPL *p, OPL_UPDATEHANDLER UpdateHandler, void *param)
+{
+ OPLSetUpdateHandler(p, UpdateHandler, param);
+}
+
+void
+ym3812_update_one(FM_OPL *p, OPLSAMPLE *buf, int n)
+{
+ int i, lt;
+ u8int rhythm;
+
+ rhythm = p->rhythm & 0x20;
+ for(i=0; i < n; i++){
+ p->output[0] = 0;
+
+ advance_lfo(p);
+
+ /* FM part */
+ OPL_CALC_CH(p, &p->P_CH[0]);
+ OPL_CALC_CH(p, &p->P_CH[1]);
+ OPL_CALC_CH(p, &p->P_CH[2]);
+ OPL_CALC_CH(p, &p->P_CH[3]);
+ OPL_CALC_CH(p, &p->P_CH[4]);
+ OPL_CALC_CH(p, &p->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(p, &p->P_CH[6]);
+ OPL_CALC_CH(p, &p->P_CH[7]);
+ OPL_CALC_CH(p, &p->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(p, &p->P_CH[0], (p->noise_rng>>0)&1 );
+ }
+
+ lt = p->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(p);
+ }
+}
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,10 @@
+FM_OPL* ym3812_init(u32int, u32int);
+void ym3812_shutdown(FM_OPL *);
+void ym3812_reset_chip(FM_OPL *);
+int ym3812_write(FM_OPL *, int, int);
+uchar ym3812_read(FM_OPL *, int);
+int ym3812_timer_over(FM_OPL *, int);
+void ym3812_update_one(FM_OPL *, OPLSAMPLE *, int);
+void ym3812_set_timer_handler(FM_OPL *, OPL_TIMERHANDLER, void *);
+void ym3812_set_irq_handler(FM_OPL *, OPL_IRQHANDLER, void *);
+void ym3812_set_update_handler(FM_OPL *, OPL_UPDATEHANDLER, void *);
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,8 @@
+</$objtype/mkfile
+
+BIN=$home/bin/$objtype
+TARG=omidi
+OFILES= omidi.$O fmopl.$O
+HFILES= dat.h fns.h
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/omidi.c
@@ -1,0 +1,1230 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+enum{
+ NCHIPS = 1,
+ RATE = 44100
+};
+/* FIXME: 8192 bytes or shorts? */
+static short abuf[8192];
+static short *ap;
+static int afd = -1;
+
+/* FIXME: using multiple chips */
+/* FIXME: the only reason this exists and FM_OPL is defined in dat.h instead of being
+ * static in fmopl.c is to have multiple cards... and we don't support that; either
+ * implement it and it's cool and it should be that way, or nuke this */
+static FM_OPL *chip[NCHIPS];
+static int oplretval;
+static int oplregno;
+static double otm;
+
+enum{
+ MINDELAY = 1,
+ OPLBASE = 0x388
+};
+
+static int verbose;
+
+static struct MusData mdat;
+
+int ColorNums = -1;
+
+const long Period[12] =
+{
+ 907,960,1016,1076,
+ 1140,1208,1280,1356,
+ 1440,1524,1616,1712
+};
+
+static const char Portit[9] = {0,1,2, 8,9,10, 16,17,18};
+
+static signed char Pans[18] = {0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0};
+
+signed char Trig[18] = {0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0};
+
+//#define SetBase(a) ((a)==2 ? (a) : 9)
+#define SetBase(a) (a)
+
+static uchar FMVol[65] = {
+ /* Generated similarly as LogTable */
+ 0,32,48,58,64,70,74,77,80,83,86,
+ 88,90,92,93,95,96,98,99,100,102,
+ 103,104,105,106,107,108,108,109,
+ 110,111,112,112,113,114,114,115,
+ 116,116,117,118,118,119,119,120,
+ 120,121,121,122,122,123,123,124,
+ 124,124,125,125,126,126,126,127,
+ 127,127,128,128
+};
+
+static uchar adl[] = {
+ 14,0,0,1,143,242,244,0,8,1,6,242,247,0,14,0,0,1,75,242,244,0,8,1,0,242,247,0,14,0,0,1,73,242,244,0,8,1,0,242,246,0,14,0,0,129,18,242,247,0,6,65,0,242,247,0,14,0,0,1,87,241,247,0,0,1,0,242,247,0,14,0,0,1,147,241,247,0,0,1,0,242,247,0,14,0,0,1,128,161,
+ 242,0,8,22,14,242,245,0,14,0,0,1,146,194,248,0,10,1,0,194,248,0,14,0,0,12,92,246,244,0,0,129,0,243,245,0,14,0,0,7,151,243,242,0,2,17,128,242,241,0,14,0,0,23,33,84,244,0,2,1,0,244,244,0,14,0,0,152,98,243,246,0,0,129,0,242,246,0,14,0,0,24,35,246,246,
+ 0,0,1,0,231,247,0,14,0,0,21,145,246,246,0,4,1,0,246,246,0,14,0,0,69,89,211,243,0,12,129,128,163,243,0,14,0,0,3,73,117,245,1,4,129,128,181,245,0,14,0,0,113,146,246,20,0,2,49,0,241,7,0,14,0,0,114,20,199,88,0,2,48,0,199,8,0,14,0,0,112,68,170,24,0,4,177,
+ 0,138,8,0,14,0,0,35,147,151,35,1,4,177,0,85,20,0,14,0,0,97,19,151,4,1,0,177,128,85,4,0,14,0,0,36,72,152,42,1,12,177,0,70,26,0,14,0,0,97,19,145,6,1,10,33,0,97,7,0,14,0,0,33,19,113,6,0,6,161,137,97,7,0,14,0,0,2,156,243,148,1,12,65,128,243,200,0,14,0,
+ 0,3,84,243,154,1,12,17,0,241,231,0,14,0,0,35,95,241,58,0,0,33,0,242,248,0,14,0,0,3,135,246,34,1,6,33,128,243,248,0,14,0,0,3,71,249,84,0,0,33,0,246,58,0,14,0,0,35,74,145,65,1,8,33,5,132,25,0,14,0,0,35,74,149,25,1,8,33,0,148,25,0,14,0,0,9,161,32,79,
+ 0,8,132,128,209,248,0,14,0,0,33,30,148,6,0,2,162,0,195,166,0,14,0,0,49,18,241,40,0,10,49,0,241,24,0,14,0,0,49,141,241,232,0,10,49,0,241,120,0,14,0,0,49,91,81,40,0,12,50,0,113,72,0,14,0,0,1,139,161,154,0,8,33,64,242,223,0,14,0,0,33,139,162,22,0,8,33,
+ 8,161,223,0,14,0,0,49,139,244,232,0,10,49,0,241,120,0,14,0,0,49,18,241,40,0,10,49,0,241,24,0,14,0,0,49,21,221,19,1,8,33,0,86,38,0,14,0,0,49,22,221,19,1,8,33,0,102,6,0,14,0,0,113,73,209,28,1,8,49,0,97,12,0,14,0,0,33,77,113,18,1,2,35,128,114,6,0,14,
+ 0,0,241,64,241,33,1,2,225,0,111,22,0,14,0,0,2,26,245,117,1,0,1,128,133,53,0,14,0,0,2,29,245,117,1,0,1,128,243,244,0,14,0,0,16,65,245,5,1,2,17,0,242,195,0,14,0,0,33,155,177,37,1,14,162,1,114,8,0,14,0,0,161,152,127,3,1,0,33,0,63,7,1,14,0,0,161,147,193,
+ 18,0,10,97,0,79,5,0,14,0,0,33,24,193,34,0,12,97,0,79,5,0,14,0,0,49,91,244,21,0,0,114,131,138,5,0,14,0,0,161,144,116,57,0,0,97,0,113,103,0,14,0,0,113,87,84,5,0,12,114,0,122,5,0,14,0,0,144,0,84,99,0,8,65,0,165,69,0,14,0,0,33,146,133,23,0,12,33,1,143,
+ 9,0,14,0,0,33,148,117,23,0,12,33,5,143,9,0,14,0,0,33,148,118,21,0,12,97,0,130,55,0,14,0,0,49,67,158,23,1,2,33,0,98,44,1,14,0,0,33,155,97,106,0,2,33,0,127,10,0,14,0,0,97,138,117,31,0,8,34,6,116,15,0,14,0,0,161,134,114,85,1,0,33,131,113,24,0,14,0,0,
+ 33,77,84,60,0,8,33,0,166,28,0,14,0,0,49,143,147,2,1,8,97,0,114,11,0,14,0,0,49,142,147,3,1,8,97,0,114,9,0,14,0,0,49,145,147,3,1,10,97,0,130,9,0,14,0,0,49,142,147,15,1,10,97,0,114,15,0,14,0,0,33,75,170,22,1,8,33,0,143,10,0,14,0,0,49,144,126,23,1,6,33,
+ 0,139,12,1,14,0,0,49,129,117,25,1,0,50,0,97,25,0,14,0,0,50,144,155,33,0,4,33,0,114,23,0,14,0,0,225,31,133,95,0,0,225,0,101,26,0,14,0,0,225,70,136,95,0,0,225,0,101,26,0,14,0,0,161,156,117,31,0,2,33,0,117,10,0,14,0,0,49,139,132,88,0,0,33,0,101,26,0,
+ 14,0,0,225,76,102,86,0,0,161,0,101,38,0,14,0,0,98,203,118,70,0,0,161,0,85,54,0,14,0,0,98,153,87,7,0,11,161,0,86,7,0,14,0,0,98,147,119,7,0,11,161,0,118,7,0,14,0,0,34,89,255,3,2,0,33,0,255,15,0,14,0,0,33,14,255,15,1,0,33,0,255,15,1,14,0,0,34,70,134,
+ 85,0,0,33,128,100,24,0,14,0,0,33,69,102,18,0,0,161,0,150,10,0,14,0,0,33,139,146,42,1,0,34,0,145,42,0,14,0,0,162,158,223,5,0,2,97,64,111,7,0,14,0,0,32,26,239,1,0,0,96,0,143,6,2,14,0,0,33,143,241,41,0,10,33,128,244,9,0,14,0,0,119,165,83,148,0,2,161,
+ 0,160,5,0,14,0,0,97,31,168,17,0,10,177,128,37,3,0,14,0,0,97,23,145,52,0,12,97,0,85,22,0,14,0,0,113,93,84,1,0,0,114,0,106,3,0,14,0,0,33,151,33,67,0,8,162,0,66,53,0,14,0,0,161,28,161,119,1,0,33,0,49,71,1,14,0,0,33,137,17,51,0,10,97,3,66,37,0,14,0,0,
+ 161,21,17,71,1,0,33,0,207,7,0,14,0,0,58,206,248,246,0,2,81,0,134,2,0,14,0,0,33,21,33,35,1,0,33,0,65,19,0,14,0,0,6,91,116,149,0,0,1,0,165,114,0,14,0,0,34,146,177,129,0,12,97,131,242,38,0,14,0,0,65,77,241,81,1,0,66,0,242,245,0,14,0,0,97,148,17,81,1,
+ 6,163,128,17,19,0,14,0,0,97,140,17,49,0,6,161,128,29,3,0,14,0,0,164,76,243,115,1,4,97,0,129,35,0,14,0,0,2,133,210,83,0,0,7,3,242,246,1,14,0,0,17,12,163,17,1,0,19,128,162,229,0,14,0,0,17,6,246,65,1,4,17,0,242,230,2,14,0,0,147,145,212,50,0,8,145,0,235,
+ 17,1,14,0,0,4,79,250,86,0,12,1,0,194,5,0,14,0,0,33,73,124,32,0,6,34,0,111,12,1,14,0,0,49,133,221,51,1,10,33,0,86,22,0,14,0,0,32,4,218,5,2,6,33,129,143,11,0,14,0,0,5,106,241,229,0,6,3,128,195,229,0,14,0,0,7,21,236,38,0,10,2,0,248,22,0,14,0,0,5,157,
+ 103,53,0,8,1,0,223,5,0,14,0,0,24,150,250,40,0,10,18,0,248,229,0,14,0,0,16,134,168,7,0,6,0,3,250,3,0,14,0,0,17,65,248,71,2,4,16,3,243,3,0,14,0,0,1,142,241,6,2,14,16,0,243,2,0,14,0,0,14,0,31,0,0,14,192,0,31,255,3,14,0,0,6,128,248,36,0,14,3,136,86,132,
+ 2,14,0,0,14,0,248,0,0,14,208,5,52,4,3,14,0,0,14,0,246,0,0,14,192,0,31,2,3,14,0,0,213,149,55,163,0,0,218,64,86,55,0,14,0,0,53,92,178,97,2,10,20,8,244,21,0,14,0,0,14,0,246,0,0,14,208,0,79,245,3,14,0,0,38,0,255,1,0,14,228,0,18,22,1,14,0,0,0,0,243,240,
+ 0,14,0,0,246,201,2,14,0,35,16,68,248,119,2,8,17,0,243,6,0,14,0,35,16,68,248,119,2,8,17,0,243,6,0,14,0,52,2,7,249,255,0,8,17,0,248,255,0,14,0,48,0,0,252,5,2,14,0,0,250,23,0,14,0,58,0,2,255,7,0,0,1,0,255,8,0,14,0,60,0,0,252,5,2,14,0,0,250,23,0,14,0,
+ 47,0,0,246,12,0,4,0,0,246,6,0,14,0,43,12,0,246,8,0,10,18,0,251,71,2,14,0,49,0,0,246,12,0,4,0,0,246,6,0,14,0,43,12,0,246,8,0,10,18,5,123,71,2,14,0,51,0,0,246,12,0,4,0,0,246,6,0,14,0,43,12,0,246,2,0,10,18,0,203,67,2,14,0,54,0,0,246,12,0,4,0,0,246,6,
+ 0,14,0,57,0,0,246,12,0,4,0,0,246,6,0,14,0,72,14,0,246,0,0,14,208,0,159,2,3,14,0,60,0,0,246,12,0,4,0,0,246,6,0,14,0,76,14,8,248,66,0,14,7,74,244,228,3,14,0,84,14,0,245,48,0,14,208,10,159,2,0,14,0,36,14,10,228,228,3,6,7,93,245,229,1,14,0,65,2,3,180,
+ 4,0,14,5,10,151,247,0,14,0,84,78,0,246,0,0,14,158,0,159,2,3,14,0,83,17,69,248,55,2,8,16,8,243,5,0,14,0,84,14,0,246,0,0,14,208,0,159,2,3,14,0,24,128,0,255,3,3,12,16,13,255,20,0,14,0,77,14,8,248,66,0,14,7,74,244,228,3,14,0,60,6,11,245,12,0,6,2,0,245,
+ 8,0,14,0,65,1,0,250,191,0,7,2,0,200,151,0,14,0,59,1,81,250,135,0,6,1,0,250,183,0,14,0,51,1,84,250,141,0,6,2,0,248,184,0,14,0,45,1,89,250,136,0,6,2,0,248,182,0,14,0,71,1,0,249,10,3,14,0,0,250,6,0,14,0,60,0,128,249,137,3,14,0,0,246,108,0,14,0,58,3,128,
+ 248,136,3,15,12,8,246,182,0,14,0,53,3,133,248,136,3,15,12,0,246,182,0,14,0,64,14,64,118,79,0,14,0,8,119,24,2,14,0,71,14,64,200,73,0,14,3,0,155,105,2,14,0,61,215,220,173,5,3,14,199,0,141,5,0,14,0,61,215,220,168,4,3,14,199,0,136,4,0,14,0,44,128,0,246,
+ 6,3,14,17,0,103,23,3,14,0,40,128,0,245,5,2,14,17,9,70,22,3,14,0,69,6,63,0,244,0,1,21,0,247,245,0,14,0,68,6,63,0,244,3,0,18,0,247,245,0,14,0,63,6,63,0,244,0,1,18,0,247,245,0,14,0,74,1,88,103,231,0,0,2,0,117,7,0,14,0,60,65,69,248,72,0,0,66,8,117,5,0,
+ 14,0,80,10,64,224,240,3,8,30,78,255,5,0,14,0,64,10,124,224,240,3,8,30,82,255,2,0,14,0,72,14,64,122,74,0,14,0,8,123,27,2,14,0,73,14,10,228,228,3,6,7,64,85,57,1,14,0,70,5,5,249,50,3,14,4,64,214,165,0,14,0,68,2,63,0,243,3,8,21,0,247,245,0,14,0,48,1,79,
+ 250,141,0,7,2,0,248,181,0,14,0,53,0,0,246,12,0,4,0,0,246,6,0
+};
+
+
+static void
+pcmout(int v)
+{
+ if(v < -32768)
+ v = -32768;
+ if(v > 32767)
+ v = 32767;
+ /* FIXME: portability (endianness) */
+ *ap++ = v;
+ /* FIXME: stereo shit */
+ *ap++ = v;
+
+ /* FIXME: write last samples before end */
+ if(ap >= abuf+nelem(abuf)){
+ write(afd, abuf, sizeof abuf);
+ ap = abuf;
+ }
+}
+
+static uint
+tadv(double length, double add)
+{
+ add += otm;
+ otm = fmod(add, length);
+
+ return add / length;
+}
+
+static void
+mix(uint usecs)
+{
+ int i;
+ uint n;
+ s16int *buf;
+
+ n = tadv((double)1.0 / RATE, (double)usecs / 1E6);
+ if((buf = mallocz(n * sizeof *buf, 1)) == nil)
+ sysfatal("mallocz: %r");
+
+ ym3812_update_one(chip[0], buf, n);
+ for(i = 0; i < n; i++)
+ //pcmout((double)buf[i] / 32768.0 * 10000.0); /* FIXME: why? */
+ pcmout(buf[i]);
+
+ free(buf);
+}
+
+static uchar
+inb(uint port)
+{
+ if(port >= 0x388 && port <= 0x38b)
+ return oplretval;
+ return 0;
+}
+
+static void
+outb(uint port, uchar v)
+{
+ uint ind;
+
+ if(port >= 0x388 && port <= 0x38b){
+ ind = port - 0x388;
+ ym3812_write(chip[0], ind, v);
+
+ if(ind & 1){
+ if(oplregno == 4){
+ if(v == 0x80)
+ oplretval = 0x02;
+ else if(v == 0x21)
+ oplretval = 0xc0;
+ }
+ }else
+ oplregno = v;
+ }
+}
+
+static void
+OPL_Byte(uchar Index, uchar Data)
+{
+ int a;
+
+ outb(OPLBASE, Index);
+ for(a=0; a<6; a++)
+ inb(OPLBASE);
+
+ outb(OPLBASE+1, Data);
+ for(a=0; a<35; a++)
+ inb(OPLBASE);
+}
+
+static void
+OPL_NoteOff(int c)
+{
+ Trig[c] = 0;
+
+ c = SetBase(c);
+ if(c<9)
+ {
+ int Ope = Portit[c];
+ /* KEYON_BLOCK+c seems to not work alone?? */
+ OPL_Byte(KEYON_BLOCK+c, 0);
+ OPL_Byte(KSL_LEVEL+ Ope, 0xFF);
+ OPL_Byte(KSL_LEVEL+3+Ope, 0xFF);
+ }
+}
+
+/* OPL_NoteOn changes the frequency on specified
+ channel and guarantees the key is on. (Doesn't
+ retrig, just turns the note on and sets freq.) */
+/* Could be used for pitch bending also. */
+static void OPL_NoteOn(int c, unsigned long Herz)
+{
+ int Oct;
+
+ Trig[c] = 127;
+
+ c = SetBase(c);
+ if(c >= 9)return;
+
+ for(Oct=0; Herz>0x1FF; Oct++)Herz >>= 1;
+
+/*
+ Bytes A0-B8 - Octave / F-Number / Key-On
+
+ 7 6 5 4 3 2 1 0
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | F-Number (least significant byte) | (A0-A8)
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | Unused | Key | Octave | F-Number | (B0-B8)
+ | | On | | most sig. |
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+*/
+
+ OPL_Byte(0xA0+c, Herz&255); //F-Number low 8 bits
+ OPL_Byte(0xB0+c, 0x20 //Key on
+ | ((Herz>>8)&3) //F-number high 2 bits
+ | ((Oct&7)<<2)
+ );
+}
+
+static void
+OPL_Touch(int c, int Instru, ushort Vol)
+{
+ int Ope;
+ //int level;
+
+ c = SetBase(c);
+ if(c >= 9)return;
+
+ Ope = Portit[c];
+
+/*
+ Bytes 40-55 - Level Key Scaling / Total Level
+
+ 7 6 5 4 3 2 1 0
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | Scaling | Total Level |
+ | Level | 24 12 6 3 1.5 .75 | <-- dB
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ bits 7-6 - causes output levels to decrease as the frequency
+ rises:
+ 00 - no change
+ 10 - 1.5 dB/8ve
+ 01 - 3 dB/8ve
+ 11 - 6 dB/8ve
+ bits 5-0 - controls the total output level of the operator.
+ all bits CLEAR is loudest; all bits SET is the
+ softest. Don't ask me why.
+*/
+/* if 1 */
+ OPL_Byte(KSL_LEVEL+ Ope, (mdat.Instr[Instru]->D[2]&KSL_MASK)
+ | (63 + (mdat.Instr[Instru]->D[2]&63) * Vol / 63 - Vol));
+ OPL_Byte(KSL_LEVEL+3+Ope, (mdat.Instr[Instru]->D[3]&KSL_MASK)
+ | (63 + (mdat.Instr[Instru]->D[3]&63) * Vol / 63 - Vol));
+/* else
+ level = (mdat.Instr[Instru]->D[2]&63) - (Vol*72-8);
+ if(level<0)level=0;
+ if(level>63)level=63;
+
+ OPL_Byte(KSL_LEVEL+ Ope, (mdat.Instr[Instru]->D[2]&KSL_MASK) | level);
+
+ level = (mdat.Instr[Instru]->D[3]&63) - (Vol*72-8);
+ if(level<0)level=0;
+ if(level>63)level=63;
+
+ OPL_Byte(KSL_LEVEL+3+Ope, (mdat.Instr[Instru]->D[3]&KSL_MASK) | level);
+ endif */
+}
+
+static void OPL_Pan(int c, uchar val)
+{
+ Pans[c] = val - 128;
+}
+
+static void OPL_Patch(int c, int Instru)
+{
+ int Ope;
+
+ c = SetBase(c);
+ if(c >= 9)return;
+
+ Ope = Portit[c];
+
+ if(Instru > sizeof(mdat.Instr)-1)
+ sysfatal("invalid instrument patch %ud", Instru); /* FIXME: see dat.h comments */
+
+ OPL_Byte(AM_VIB+ Ope, mdat.Instr[Instru]->D[0]);
+ OPL_Byte(ATTACK_DECAY+ Ope, mdat.Instr[Instru]->D[4]);
+ OPL_Byte(SUSTAIN_RELEASE+ Ope, mdat.Instr[Instru]->D[6]);
+ OPL_Byte(WAVE_SELECT+ Ope, mdat.Instr[Instru]->D[8]&3);// 6 high bits used elsewhere
+
+ OPL_Byte(AM_VIB+ 3+Ope, mdat.Instr[Instru]->D[1]);
+ OPL_Byte(ATTACK_DECAY+ 3+Ope, mdat.Instr[Instru]->D[5]);
+ OPL_Byte(SUSTAIN_RELEASE+3+Ope, mdat.Instr[Instru]->D[7]);
+ OPL_Byte(WAVE_SELECT+ 3+Ope, mdat.Instr[Instru]->D[9]&3);// 6 high bits used elsewhere
+
+ /* Panning... */
+ OPL_Byte(FEEDBACK_CONNECTION+c,
+ (mdat.Instr[Instru]->D[10] & ~STEREO_BITS)
+ | (Pans[c]<-32 ? VOICE_TO_LEFT
+ : Pans[c]>32 ? VOICE_TO_RIGHT
+ : (VOICE_TO_LEFT | VOICE_TO_RIGHT)
+ ) );
+}
+
+/* u32int, word and byte have been defined in adlib.h */
+
+static u32int ConvL(u8int *s)
+{
+ return (((u32int)s[0] << 24) | ((u32int)s[1] << 16) | ((u16int)s[2] << 8) | s[3]);
+}
+
+static u16int ConvI(u8int *s)
+{
+ return (s[0] << 8) | s[1];
+}
+
+static struct Midi
+{
+/* Fixed by ReadMIDI() */
+ int Fmt;
+ int TrackCount;
+ int DeltaTicks;
+ ulong *TrackLen;
+ u8int **Tracks;
+/* Used by play() */
+ u32int *Waiting, *sWaiting, *SWaiting;
+ u8int *Running, *sRunning, *SRunning;
+ ulong *Posi, *sPosi, *SPosi;
+ u32int Tempo;
+ u32int oldtempo;
+ u32int initempo;
+/* Per channel */
+ u8int Pan[16];
+ u8int Patch[16];
+ u8int MainVol[16];
+ u8int PitchSense[16];
+ int Bend[16];
+ int OldBend[16];
+ int Used[16][127]; /* contains references to adlib channels per note */
+} MIDI;
+
+#define snNoteOff 0x7fb1
+#define snNoteOn 0x7fb2
+#define snNoteModify 0x7fb3
+#define snNoteUpdate 0x7fb4
+
+enum{
+ MAXS3MCHAN = 9
+};
+
+typedef struct
+{
+/* Important - at offset 0 to be saved to file */
+ long Age;
+ u8int Note;
+ u8int Instru;
+ u8int Volume;
+ u8int cmd;
+ u8int info;
+ u8int KeyFlag; /* Required to diff aftertouch and noteon */
+ /* Byte to save space */
+/* Real volume(0..127) and main volume(0..127) */
+ int RealVol;
+ int MainVol;
+ int exp; /* vol = MainVol * RealVol/127 * exp/127 */
+ /* RealVol must be first non-saved */
+/* Less important */
+ int LastInstru; /* Needed by SuggestNewChan() */
+ int BendFlag;
+ u8int LastPan;
+/* To fasten forcement */
+ int uChn, uNote;
+} S3MChan;
+
+static S3MChan Chan[MAXCHN];
+static S3MChan PrevChan[MAXCHN];
+
+#define chansavesize ((int)((long)&Chan[0].RealVol - (long)&Chan[0].Age))
+
+static int InstruUsed[256];
+static int Forced=0;
+static const int tempochanged = -5;
+
+
+static void AnalyzeRow(void)
+{
+ int a;
+
+ for(a=0; a<MAXS3MCHAN; a++)
+ if(!Chan[a].Age)break;
+
+ if(a==MAXS3MCHAN)
+ return;
+
+ memcpy(PrevChan, Chan, sizeof PrevChan);
+
+ Forced=0;
+
+ for(a=0; a<MAXS3MCHAN; a++)
+ Chan[a].KeyFlag=0;
+}
+
+static void FixSpeed(float *Speed, int *Tempo)
+{
+ int a;
+ float tmp = 1.0;
+ *Speed = MIDI.Tempo * 4E-7 / MIDI.DeltaTicks;
+ *Tempo = 125;
+
+ for(a=0x40; a<=0xFF; a++)
+ {
+ double Tmp;
+ float n = a * *Speed;
+ n = modf(1.0/n, &Tmp);
+ if(n < tmp)
+ {
+ *Tempo = a;
+ tmp = n;
+ }
+ }
+ *Speed *= *Tempo;
+}
+
+static int MakeVolume(int vol)
+{
+ return FMVol[vol*64/127]*63/128;
+}
+
+static void Bendi(int chn, int a)
+{
+ int bc, nt;
+ long HZ1, HZ2, Herz;
+
+ bc = MIDI.Bend[chn];
+ nt = Chan[a].Note;
+
+ if(bc > MIDI.OldBend[chn])
+ Chan[a].BendFlag |= 1;
+ else if(bc < MIDI.OldBend[chn])
+ Chan[a].BendFlag |= 2;
+ else
+ Chan[a].BendFlag = 0;
+
+ MIDI.OldBend[chn] = bc;
+
+ for(; bc < 0; bc += 0x1000)
+ nt--;
+ for(; bc >= 0x1000; bc -= 0x1000)
+ nt++;
+
+ HZ1 = Period[(nt+1)%12] * (8363L << (nt+1)/12) / 44100U;
+ HZ2 = Period[(nt )%12] * (8363L << (nt )/12) / 44100U;
+
+ Herz = HZ2 + bc * (HZ1 - HZ2) / 0x1000;
+
+ OPL_NoteOn(a, Herz);
+}
+
+/* vole = RealVol*MainVol/127 */
+static int SuggestNewChan(int instru, int /*vole*/)
+{
+ int a, c=MAXS3MCHAN, f;
+ long b;
+
+ for(a=f=0; a<MAXS3MCHAN; a++)
+ if(Chan[a].LastInstru==instru)
+ f=1;
+
+ /* Arvostellaan channels */
+ for(b=a=0; a<MAXS3MCHAN; a++)
+ {
+ /* empty if channel is silent */
+ if(!Chan[a].Volume)
+ {
+ long d;
+ /* Pohjapisteet...
+ * Jos instru oli uusi, mieluiten sijoitetaan
+ * sellaiselle kanavalle, joka on pitk��n ollut hiljaa.
+ * Muuten sille, mill� se juuri �skenkin soi.
+ */
+ d = f?1:Chan[a].Age;
+
+ /* Jos kanavan edellinen instru oli joku
+ * soinniltaan hyvin lyhyt, pisteit� annetaan lis�� */
+ if(strchr("\x81\x82\x83\x84\x85\x86\x87\x88\x89"
+ "\x8B\x8D\x8E\x90\x94\x96\x9A\x9B\x9C"
+ "\x9D\xA4\xAA\xAB",
+ Chan[a].LastInstru))d += 2;
+ else
+ {
+ /* Jos oli pitk�sointinen percussion, *
+ * annetaan pisteit� i�n mukaan. */
+ if(Chan[a].LastInstru > 0x80)
+ d += Chan[a].Age*2;
+ }
+
+ /* Jos oli samaa instrua, pisteit� tulee paljon lis�� */
+ if(Chan[a].LastInstru == instru)d += 3;
+
+ //d = (d-1)*Chan[a].Age+1;
+ if(d > b)
+ {
+ b = d;
+ c = a;
+ }
+ }
+ }
+ return c;
+}
+
+/* vole = RealVol*MainVol/127 */
+static int ForceNewChan(int instru, int vole)
+{
+ int a, c;
+ long b=0;
+
+ vole *= 127;
+
+ Forced=1;
+ for(a=c=0; c<MAXS3MCHAN; c++)
+ if(Chan[c].Age
+ > b
+ + (((instru<128 && Chan[c].Instru>128)
+ || (vole > Chan[c].RealVol*Chan[c].MainVol)
+ ) ? 1:0
+ ) )
+ {
+ a=c;
+ b=Chan[c].Age;
+ }
+ return a;
+}
+
+/* Used twice by SetNote. This should be considered as a macro. */
+static void SubNoteOff(int a, int chn, int note)
+{
+ Chan[a].RealVol = 0;
+ Chan[a].MainVol = 0;
+ Chan[a].exp = 0x7f;
+
+ Chan[a].Age = 0;
+ Chan[a].Volume = 0;
+ Chan[a].BendFlag= 0;
+
+ MIDI.Used[chn][note] = 0;
+
+ if(Chan[a].Instru < 0x80)OPL_NoteOff(a);
+}
+
+static void SetNote(int chn, int note, int RealVol,int MainVol, int bend, int e)
+{
+ int a, vole;
+
+ //vole = RealVol*(MainVol*e)/127;
+ vole = RealVol*(MainVol)/127;
+
+ if(!vole && (bend==snNoteOn || bend==snNoteModify))bend=snNoteOff;
+ if(bend==snNoteOn && MIDI.Used[chn][note])bend=snNoteModify;
+
+ switch(bend)
+ {
+ /* snNoteOn:ssa note ei koskaan ole -1 */
+ case snNoteOn:
+ {
+ int p;
+
+ /* FIXME */
+ p = chn==9 ? 128+note-35 : MIDI.Patch[chn];
+
+ a = SuggestNewChan(p, vole);
+
+ if(a==MAXS3MCHAN)
+ {
+ a = ForceNewChan(p, vole);
+ MIDI.Used[Chan[a].uChn][Chan[a].uNote] = 0;
+ }
+
+ if(a < MAXS3MCHAN)
+ {
+ Chan[a].exp = e;
+ Chan[a].RealVol= RealVol;
+ Chan[a].MainVol= MainVol;
+ Chan[a].Note = chn==9 ? 60 : note;
+ Chan[a].Volume = MakeVolume(vole);
+ Chan[a].Age = 0;
+ Chan[a].Instru = p;
+
+ Chan[a].LastInstru = p;
+ Chan[a].KeyFlag= 1;
+ Chan[a].uChn = chn;
+ Chan[a].uNote = note;
+
+/*
+ if(MIDI.Bend[chn] && Chan[a].Volume)
+ {
+ int adlbend = MIDI.Bend[chn] * 127L / 8000;
+
+ //TODO: Fix this. It doesn't work at all.
+
+ Chan[a].cmd = 'N'-64;
+ Chan[a].info= adlbend+0x80; // To make it unsigned
+ }
+ else
+*/
+ if(MIDI.Pan[chn] != Chan[a].LastPan && Chan[a].Volume)
+ {
+ Chan[a].cmd = 'X'-64;
+ Chan[a].info= MIDI.Pan[chn]*2;
+ Chan[a].LastPan = MIDI.Pan[chn];
+ }
+ else
+ {
+ Chan[a].cmd = Chan[a].info = 0;
+ }
+
+ OPL_NoteOff(a);
+
+ OPL_Patch(a, Chan[a].Instru);
+ if(MIDI.Pan[chn] != 64)OPL_Pan(a, Chan[a].info);
+ OPL_Touch(a, Chan[a].Instru, Chan[a].Volume);
+
+ Bendi(chn, a);
+
+ MIDI.Used[chn][note] = a+1;
+ }
+ break;
+ }
+ /* snNoteOff:ssa note voi olla -1 */
+ case snNoteOff:
+ {
+ int b=note, c=note;
+ if(note < 0)b=0, c=127;
+
+ MIDI.Bend[chn] = 0; /* N�in vaikuttaisi olevan hyv� */
+
+ for(note=b; note<=c; note++)
+ {
+ a = MIDI.Used[chn][note];
+ if(a > 0)
+ SubNoteOff(a-1, chn, note);
+ }
+ break;
+ }
+ /* snNoteModify:ssa note ei koskaan ole -1 */
+ case snNoteModify:
+ a = MIDI.Used[chn][note]-1;
+ if(a != -1)
+ {
+ Chan[a].exp = e;
+ Chan[a].RealVol= RealVol;
+ Chan[a].MainVol= MainVol;
+
+ Chan[a].Volume = MakeVolume(vole);
+ Chan[a].Age = 0;
+
+ OPL_Touch(a, Chan[a].Instru, Chan[a].Volume);
+ }
+ break;
+ /* snNoteUpdate:ssa note on aina -1 */
+ case snNoteUpdate:
+ /* snNoteUpdatessa RealVol ei muutu, vain MainVol */
+ for(note=0; note<=127; note++)
+ {
+ a = MIDI.Used[chn][note]-1;
+
+ //vole = MainVol*(Chan[a].RealVol*Chan[a].exp)/127;
+ vole = MainVol*(Chan[a].RealVol)/127;
+
+ if(a >= 0 && Chan[a].Volume != MakeVolume(vole))
+ {
+ Chan[a].MainVol= MainVol;
+
+ if(!vole)
+ SubNoteOff(a, chn, note);
+ else
+ {
+ Chan[a].Volume = MakeVolume(vole);
+ Chan[a].Age = 0;
+ OPL_Touch(a, Chan[a].Instru, Chan[a].Volume);
+ }
+ }
+ }
+ break;
+ /* Bendiss� note on aina -1 */
+ default:
+ MIDI.Bend[chn] = bend;
+
+ for(note=0; note<=127; note++)
+ {
+ a = MIDI.Used[chn][note]-1;
+ if(a >= 0)
+ Bendi(chn, a);
+ }
+ }
+}
+
+static u32int GetVarLen(int Track, ulong *Posi)
+{
+ u32int d = 0;
+ for(;;)
+ {
+ u8int b = MIDI.Tracks[Track][(*Posi)++];
+ d = (d<<7) + (b&127);
+ if(b < 128)break;
+ }
+ return d;
+}
+
+/* NOTICE: This copies len-2 bytes */
+static char *Sana(int len, const unsigned char *buf)
+{
+ static char Buf[128];
+ char *s = Buf;
+ len -= 2;
+#define add(c) if(s<&Buf[sizeof(Buf)-1])*s++=(c)
+ while(len>0)
+ {
+ if(*buf<32||(*buf>=127&&*buf<160)){add('^');add(*buf + 64);}
+ else add(*buf);
+ buf++;
+ len--;
+ }
+#undef add
+ *s=0;
+ return Buf;
+}
+
+static void
+MPU_Byte(uchar)
+{
+
+}
+
+static void
+readmeta(int t, u8int *p, ulong n)
+{
+ u8int b;
+
+ b = *p;
+ p += 2;
+ //fprint(2, "meta %ux\n", b);
+ switch(b){
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ if(verbose)
+ fprint(2, "%s\n", Sana(n, p));
+ break;
+ case 0x2f:
+ MIDI.Posi[t] = MIDI.TrackLen[t];
+ if(verbose)
+ fprint(2, "end\n");
+ break;
+ case 0x51:
+ MIDI.Tempo = p[0]<<16 | p[1]<<8 | p[2];
+ if(verbose)
+ fprint(2, "tempo %ud\n", MIDI.Tempo);
+ break;
+ case 0: case 0x20: case 0x21: case 0x58: case 0x59: case 0x7f:
+ break;
+ default:
+ fprint(2, "unknown metaevent %ux\n", b);
+ }
+}
+
+static void
+Tavuja(int Track, u8int First, ulong posi, ulong Len)
+{
+ ulong a;
+
+ if(verbose){
+ fprint(2, "[%d]: %02ux ", Track, First);
+ for(a=0; a<Len; a++)
+ fprint(2, "%02ux", MIDI.Tracks[Track][a+posi]);
+ fprint(2, "\n");
+ }
+
+ /* FIXME: ignore sysex completely, do it like games/midi */
+ if(First == 0xff)
+ readmeta(Track, MIDI.Tracks[Track]+posi, Len);
+ else if(First < 0xf0){
+ //ulong a; // ← what the fuck
+ a=0;
+ if((First>>4) == 8) /* Note off, modify it a bit */
+ {
+ /* FIXME: */
+ MPU_Byte(First);
+ MPU_Byte(MIDI.Tracks[Track][posi]);
+ MPU_Byte(0);
+ Len -= 2;
+ }
+ else
+ MPU_Byte(First);
+ for(; a<Len; a++)MPU_Byte(MIDI.Tracks[Track][a+posi]);
+ //fprint(2, "ev %ux\n", First >> 4);
+ switch(First >> 4)
+ {
+ case 0x8: /* Note off */
+ SetNote(
+ First&15, /* chn */
+ MIDI.Tracks[Track][posi+0], /* note */
+ MIDI.Tracks[Track][posi+1], /* volume */
+ MIDI.MainVol[First&15], /* mainvol */
+ snNoteOff, 127);
+ break;
+ case 0x9: /* Note on */
+ SetNote(
+ First&15, /* chn */
+ MIDI.Tracks[Track][posi+0], /* note */
+ MIDI.Tracks[Track][posi+1], /* volume */
+ MIDI.MainVol[First&15], /* mainvol */
+ snNoteOn, Chan[First&15].exp);
+ break;
+ case 0xA: /* Key after-touch */
+ SetNote(
+ First&15, /* chn */
+ MIDI.Tracks[Track][posi+0], /* note */
+ MIDI.Tracks[Track][posi+1], /* volume */
+ MIDI.MainVol[First&15], /* mainvol */
+ snNoteModify, Chan[First&15].exp);
+ break;
+ case 0xC: /* Patch change */
+ MIDI.Patch[First&15] = MIDI.Tracks[Track][posi];
+ break;
+ case 0xD: /* Channel after-touch */
+ break;
+ case 0xE: /* Wheel - 0x2000 = no change */
+ a = MIDI.Tracks[Track][posi+0] |
+ MIDI.Tracks[Track][posi+1] << 7;
+ SetNote(First&15,
+ -1, 0,0,
+ /* FIXME: these are fucked up, see below */
+ //(int)((long)a*MIDI.PitchSense[First&15]/2 - 0x2000L), Chan[First&15].exp);
+ //(short)(((long)a - 0x2000L) *(double)MIDI.PitchSense[First&15]/8192.0), Chan[First&15].exp);
+ (short)((long)a - 0x2000L), Chan[First&15].exp);
+ break;
+ case 0xB: /* Controller change */
+ switch(MIDI.Tracks[Track][posi+0])
+ {
+ case 123: /* All notes off on channel */
+ SetNote(First&15, -1,0,0, snNoteOff, 127);
+ break;
+ case 121: /* Reset vibrato and bending */
+ MIDI.PitchSense[First&15] = 2;
+ SetNote(First&15, -1,0,0, 0, Chan[First&15].exp); /* last 0=bend 0 */
+ break;
+ case 7:
+ MIDI.MainVol[First&15] = MIDI.Tracks[Track][posi+1];
+
+ SetNote(First&15, -1, 0, MIDI.MainVol[First&15], snNoteUpdate, Chan[First&15].exp);
+ /* FIXME: why doesn't this work? */
+ //for(a = 0; a < 16; a++)
+ // SetNote(a, -1, 0, MIDI.MainVol[First&15], snNoteUpdate, Chan[First&15].exp);
+ break;
+ case 64:
+ if(verbose)
+ fprint(2, "ctl 64: sustain unsupported\n");
+ break;
+ case 1:
+ if(verbose)
+ fprint(2, "ctl 1: vibrato unsupported\n");
+ break;
+ case 91:
+ if(verbose)
+ fprint(2, "ctl 91 %ud: reverb depth unsupported\n", MIDI.Tracks[Track][posi+1]);
+ break;
+ case 93:
+ if(verbose)
+ fprint(2, "ctl 93 %ud: chorus depth unsupported\n", MIDI.Tracks[Track][posi+1]);
+ break;
+ case 0x06: /* Pitch bender sensitivity */
+ /* FIXME: the fuck is this? whatever it is,
+ * it's broken */
+ MIDI.PitchSense[First&15] = MIDI.Tracks[Track][posi+1];
+ break;
+ case 0x0a: /* Pan */
+ MIDI.Pan[First&15] = MIDI.Tracks[Track][posi+1];
+ break;
+ case 0:
+ /* FIXME: unimplemented: select bank */
+ if(verbose)
+ fprint(2, "ctl 0 %ud: bank select unsupported\n", MIDI.Tracks[Track][posi+1]);
+ break;
+ case 0x0b: /* FIXME: probably bullshizzles */
+ SetNote(First&15, -1,0,MIDI.MainVol[First&15], snNoteUpdate, MIDI.Tracks[Track][posi+1]);
+ break;
+ default:
+ if(verbose)
+ fprint(2, "unknown ctl %ud: %ux\n",
+ MIDI.Tracks[Track][posi+0],
+ MIDI.Tracks[Track][posi+1]);
+ }
+ break;
+ }
+ }
+}
+
+/* Return value: 0=ok, -1=user break */
+static int play(void)
+{
+ int a, NotFirst, Userbreak=0;
+ long Viivetta;
+
+ if((MIDI.Waiting = mallocz(MIDI.TrackCount * sizeof *MIDI.Waiting, 1)) == nil
+ || (MIDI.sWaiting = mallocz(MIDI.TrackCount * sizeof *MIDI.sWaiting, 1)) == nil
+ || (MIDI.SWaiting = mallocz(MIDI.TrackCount * sizeof *MIDI.SWaiting, 1)) == nil
+ || (MIDI.Posi = mallocz(MIDI.TrackCount * sizeof *MIDI.Posi, 1)) == nil
+ || (MIDI.sPosi = mallocz(MIDI.TrackCount * sizeof *MIDI.sPosi, 1)) == nil
+ || (MIDI.SPosi = mallocz(MIDI.TrackCount * sizeof *MIDI.SPosi, 1)) == nil
+ || (MIDI.Running = mallocz(MIDI.TrackCount * sizeof *MIDI.Running, 1)) == nil
+ || (MIDI.sRunning = mallocz(MIDI.TrackCount * sizeof *MIDI.sRunning, 1)) == nil
+ || (MIDI.SRunning = mallocz(MIDI.TrackCount * sizeof *MIDI.SRunning, 1)) == nil)
+ sysfatal("mallocz: %r");
+
+ for(a=0; a<MIDI.TrackCount; a++)
+ {
+ ulong c = 0;
+ MIDI.sWaiting[a]= GetVarLen(a, &c);
+ MIDI.sRunning[a]= 0;
+ MIDI.sPosi[a] = c;
+ }
+
+ for(a=0; a<16; a++)
+ {
+ MIDI.Pan[a] = 64; /* Middle */
+ MIDI.Patch[a] = 1; /* Piano */
+ MIDI.PitchSense[a] = 2; /* � seminotes */
+ MIDI.MainVol[a] = 127;
+ MIDI.Bend[a] = 0;
+ }
+
+ NotFirst = 0;
+//ReLoop:
+ for(a=0; a<MIDI.TrackCount; a++)
+ {
+ MIDI.Posi[a] = MIDI.sPosi[a];
+ MIDI.Waiting[a] = MIDI.sWaiting[a];
+ MIDI.Running[a] = MIDI.sRunning[a];
+ }
+
+ MIDI.Tempo = MIDI.initempo;
+
+ memset(Chan, 0, sizeof Chan);
+ for(a = 0; a < nelem(Chan); a++){
+ Chan[a].exp = 0x7f;
+ }
+
+ memset(MIDI.Used, 0, sizeof MIDI.Used);
+
+ Viivetta = 0;
+
+ for(;;)
+ {
+ int Fin, Act;
+
+ if(NotFirst)
+ {
+ long Lisa = MIDI.Tempo/MIDI.DeltaTicks;
+
+ /* tempo = microseconds per quarter note
+ * deltaticks = number of delta-time ticks per quarter note
+ *
+ * So, when tempo = 200000
+ * and deltaticks = 10,
+ * then 10 ticks have 200000 microseconds.
+ * 20 ticks have 400000 microseconds.
+ * When deltaticks = 5,
+ * then 10 ticks have 40000 microseconds.
+ */
+
+ Viivetta += Lisa;
+ if(Viivetta >= MINDELAY)
+ {
+ mix(Viivetta);
+ Viivetta = 0;
+ }
+ AnalyzeRow();
+ }
+ else
+ {
+ u32int b = 0xFFFFFFFFUL;
+ /* Find the smallest delay */
+ for(a=0; a<MIDI.TrackCount; a++)
+ if(MIDI.Waiting[a] < b)
+ b = MIDI.Waiting[a];
+ /* Elapse that delay from all tracks */
+ for(a=0; a<MIDI.TrackCount; a++)
+ MIDI.Waiting[a] -= b;
+ }
+
+ /* Let the notes on channels become older (Age++) only if *
+ * something happens. This way, we don't overflow the ages *
+ * too easily. */
+ for(Act=a=0; a<MAXS3MCHAN; a++)
+ if(MIDI.Waiting[a]<=1 && MIDI.Posi[a]<MIDI.TrackLen[a])
+ Act++;
+ for(a=0; a<MAXS3MCHAN; a++)
+ if(!Chan[a].Age||Act!=0)
+ Chan[a].Age++;
+
+ for(a=0; a<MIDI.TrackCount; ++a)
+ {
+ MIDI.SPosi[a] = MIDI.Posi[a];
+ MIDI.SWaiting[a] = MIDI.Waiting[a];
+ MIDI.SRunning[a] = MIDI.Running[a];
+ }
+ for(Fin=1, a=0; a<MIDI.TrackCount; a++)
+ {
+ if(MIDI.Waiting[a] > 0)MIDI.Waiting[a]--;
+
+ if(MIDI.Posi[a] < MIDI.TrackLen[a])Fin=0;
+
+ /* While this track has events that we should have handled */
+ while(MIDI.Waiting[a]<=0 && MIDI.Posi[a]<MIDI.TrackLen[a])
+ {
+ ulong pos;
+ u8int b = MIDI.Tracks[a][MIDI.Posi[a]];
+ if(b < 128)
+ b = MIDI.Running[a];
+ else
+ {
+ MIDI.Posi[a]++;
+ if(b < 0xF0)MIDI.Running[a] = b;
+ }
+
+ pos = MIDI.Posi[a];
+
+ //fprint(2, "b %ux\n", b);
+ if(b == 0xFF)
+ {
+ int ls=0;
+ ulong len;
+ u8int typi = MIDI.Tracks[a][MIDI.Posi[a]++];
+ len = (ulong)GetVarLen(a, &MIDI.Posi[a]);
+ if(typi == 6) /* marker */
+ if(!strncmp((char *)(MIDI.Tracks[a]+MIDI.Posi[a]),
+ "loopStart", len))
+ {
+ if(verbose)
+ fprint(2, "Found loopStart\n");
+ ls=1;
+ }
+ MIDI.Posi[a] += len;
+ if(ls)goto SaveLoopStart;
+ }
+ else if(b==0xF7 || b==0xF0)
+ {
+ MIDI.Posi[a] += (ulong)GetVarLen(a, &MIDI.Posi[a]);
+ }
+ else if(b == 0xF3)MIDI.Posi[a]++;
+ else if(b == 0xF2)MIDI.Posi[a]+=2;
+ else if(b>=0xC0 && b<=0xDF)MIDI.Posi[a]++;
+ else if(b>=0x80 && b<=0xEF)
+ {
+ MIDI.Posi[a]+=2;
+ if(b>=0x90 && b<=0x9F && !NotFirst)
+ {
+ int c;
+SaveLoopStart: NotFirst=1;
+ /* Save the starting position for looping */
+ for(c=0; c<MIDI.TrackCount; c++)
+ {
+ MIDI.sPosi[c] = MIDI.SPosi[c];
+ MIDI.sWaiting[c] = MIDI.SWaiting[c];
+ MIDI.sRunning[c] = MIDI.SRunning[c];
+ }
+ MIDI.initempo = MIDI.Tempo;
+ }
+ }
+
+ Tavuja(a, b, pos, MIDI.Posi[a]-pos);
+
+ if(MIDI.Posi[a] < MIDI.TrackLen[a])
+ MIDI.Waiting[a] += GetVarLen(a, &MIDI.Posi[a]);
+ }
+ }
+ if(Fin)
+ break;
+ }
+ write(afd, abuf, ap-abuf);
+ return Userbreak;
+}
+
+static void
+initinst(void)
+{
+ int m, i;
+ InternalSample *t, *p;
+
+ m = sizeof(adl) / 14;
+
+ if((t = calloc(m, sizeof *t)) == nil)
+ sysfatal("initinst: %r");
+ p = t;
+ /* FIXME: .D needs to be uchar(?) */
+ /* FIXME: use char adl[][14] rather than this */
+ /* FIXME: find out where the values are from */
+ for(i=0; i<m; i++){
+ p->D[0] = adl[i * 14 + 3];
+ p->D[1] = adl[i * 14 + 9];
+ p->D[2] = adl[i * 14 + 4];
+ p->D[3] = adl[i * 14 + 10];
+ p->D[4] = adl[i * 14 + 5];
+ p->D[5] = adl[i * 14 + 11];
+ p->D[6] = adl[i * 14 + 6];
+ p->D[7] = adl[i * 14 + 12];
+ p->D[8] = adl[i * 14 + 7] & 3;
+ p->D[9] = adl[i * 14 + 13] & 3;
+ p->D[10]= adl[i * 14 + 8];
+ mdat.Instr[i] = p++;
+ }
+}
+
+/* FIXME: get8/16/32 approach is better */
+/* FIXME: use bio.h */
+static void
+eread(int fd, void *buf, int n)
+{
+ if(readn(fd, buf, n) < 0)
+ sysfatal("read: %r");
+}
+
+static void
+readmid(char *mid)
+{
+ int i, fd;
+ uint n;
+ uchar id[4];
+
+ fd = 0;
+ if(mid != nil && (fd = open(mid, OREAD)) < 0)
+ sysfatal("open: %r");
+
+ /* FIXME: get8/16/32 better; Conv shit is also stupid */
+ /* FIXME: also, don't read file into memory but use bio? */
+ eread(fd, id, 4);
+ if(memcmp(id, "MThd", 4) != 0)
+ sysfatal("invalid header");
+ eread(fd, id, 4);
+ if(ConvL(id) != 6)
+ sysfatal("invalid midi file");
+
+ eread(fd, id, 2);
+ MIDI.Fmt = ConvI(id);
+ eread(fd, id, 2);
+ MIDI.TrackCount = ConvI(id);
+ eread(fd, id, 2);
+ MIDI.DeltaTicks = ConvI(id);
+
+ MIDI.TrackLen = calloc(MIDI.TrackCount, sizeof *MIDI.TrackLen);
+ if(MIDI.TrackLen == nil)
+ sysfatal("calloc len %ux %ux: %r", MIDI.TrackCount, sizeof *MIDI.TrackLen);
+ MIDI.Tracks = calloc(MIDI.TrackCount, sizeof *MIDI.Tracks);
+ if(MIDI.Tracks == nil)
+ sysfatal("calloc trs %ux %ux: %r", MIDI.TrackCount, sizeof *MIDI.Tracks);
+
+ for(i = 0; i < MIDI.TrackCount; i++){
+ eread(fd, id, 4);
+ if(memcmp(id, "MTrk", 4) != 0)
+ sysfatal("invalid track");
+
+ eread(fd, id, 4);
+ n = ConvL(id);
+ MIDI.TrackLen[i] = n;
+ if((MIDI.Tracks[i] = mallocz(n, 1)) == nil)
+ sysfatal("mallocz %ux: %r", n);
+
+ eread(fd, MIDI.Tracks[i], n);
+ }
+ close(fd);
+
+ MIDI.initempo = 150000;
+}
+
+static void
+usage(void)
+{
+ print("%s [-cd] [midfile]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int i;
+
+ ARGBEGIN{
+ case 'c':
+ afd = 1;
+ break;
+ case 'd':
+ verbose++;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ readmid(*argv);
+ initinst();
+ for(i = 0; i < NCHIPS; i++)
+ chip[i] = ym3812_init(1789772*2, RATE);
+
+ if(afd < 0 && (afd = open("/dev/audio", OWRITE)) < 0)
+ sysfatal("open: %r");
+ ap = abuf;
+ play();
+
+ for(i = 0; i < NCHIPS; i++)
+ ym3812_shutdown(chip[i]);
+ exits(nil);
+}