From 96e511d736ca4f18b70312980591014199afeb1a Mon Sep 17 00:00:00 2001 From: qwx Date: Thu, 12 Jul 2018 09:33:33 +0200 Subject: [PATCH] add games/dmid and games/opl3 --- rc/bin/dmus | 10 + sys/man/1/dmid | 68 ++ sys/man/1/opl3 | 59 ++ sys/src/games/dmid.c | 554 +++++++++++++++ sys/src/games/mkfile | 2 + sys/src/games/opl3/mkfile | 8 + sys/src/games/opl3/opl3.c | 1295 ++++++++++++++++++++++++++++++++++++ sys/src/games/opl3/opl3m.c | 57 ++ 8 files changed, 2053 insertions(+) create mode 100755 rc/bin/dmus create mode 100644 sys/man/1/dmid create mode 100644 sys/man/1/opl3 create mode 100644 sys/src/games/dmid.c create mode 100644 sys/src/games/opl3/mkfile create mode 100644 sys/src/games/opl3/opl3.c create mode 100644 sys/src/games/opl3/opl3m.c diff --git a/rc/bin/dmus b/rc/bin/dmus new file mode 100755 index 000000000..51950d033 --- /dev/null +++ b/rc/bin/dmus @@ -0,0 +1,10 @@ +#!/bin/rc +if(test -f /mnt/wad/genmidi) + c=(games/dmid '|' games/opl3) +if not + c=(games/midi -c) +if(~ `{file -m $1} audio/mus) + c=(games/mus '<' $1 '|' $c) +if not + c=('<' $1 $c) +eval $c diff --git a/sys/man/1/dmid b/sys/man/1/dmid new file mode 100644 index 000000000..df4f72dd5 --- /dev/null +++ b/sys/man/1/dmid @@ -0,0 +1,68 @@ +.TH DMID 1 +.SH NAME +dmid \- MIDI to OPL3 converter using GENMIDI-type instrument banks +.SH SYNOPSIS +.B dmid +[ +.B -2 +] [ +.B -i +.I bank +] [ +.I file +] +.SH DESCRIPTION +.I Dmid +decodes MIDI instructions either from +.I file +or from standard input, +and produces +.SM OPL3 +instructions suitable for playback by +.IR opl3 (1). +To program instruments, an OPL2 instrument bank formatted as +.SM GENMIDI +lumps from +.I doom +must be provided. +Since it is assumed that the bank is contained in a +.I doom WAD +file, its default location is +.BR /mnt/wad/genmidi . +This may be overridden with the +.B -i +command line option. +.PP +In +.SM GENMIDI +lumps, two voices are defined per instrument. +For compatibility, the +.B -2 +flag disables the second voice, reducing the number of +.SM OPL +channels needed. +It also disables +.SM OPL3 +specific features and produces an IMF-format stream, +which can be used in other game engines. +.SH EXAMPLES +Play a MUS file from a +.I doom WAD +file: +.IP +.EX +% games/wadfs /sys/games/lib/doom/doom2.wad +createfile SW18_7: file already exists +% games/mus /mnt/wad/d_doom | games/dmid | games/opl3 >/dev/audio +.EE +.SH SOURCE +.B /sys/src/games/dmid.c +.SH "SEE ALSO" +.IR games (1) , +.IR mus (1) , +.IR opl3 (1) , +.IR audio (3) , +.IR wadfs (4) +.SH HISTORY +.I Dmid +first appeared in 9front (July, 2018). diff --git a/sys/man/1/opl3 b/sys/man/1/opl3 new file mode 100644 index 000000000..efb16d9b0 --- /dev/null +++ b/sys/man/1/opl3 @@ -0,0 +1,59 @@ +.TH OPL3 1 +.SH NAME +opl3 \- OPL3 chip emulator +.SH SYNOPSIS +.B opl3 +[ +.B -n +.I rate +] [ +.I file +] +.SH DESCRIPTION +.I Opl3 +is an emulator of a single Yamaha 262 chip, also known as +.SM OPL3. +.PP +The emulated chip is programmed by a stream of commands either from +.I file +or from standard in. +It then synthesizes a number of stereo 16 bit little-endian samples for a sampling rate of 44.1 kHz, +and writes them to standard out. +.PP +Commands are 5 bytes wide, in little-endian byte order: +.PP +.RS +.IR register [2] +.IR value [1] +.IR delay [2] +.RE +.PP +Each command specifies a +.I value +to be written to an +.SM OPL3 +chip +.IR register , +modifying its internal state. +.PP +The +.I delay +field provides timing. +It is a multiple of a command period, during which the +.SM OPL3 +chip may be sampled before processing the next command. +The period itself is the inverse of the command rate, 44100 Hz by default. +This rate can be set using the +.B -n +parameter. +.SH SOURCE +.B /sys/src/games/opl3 +.SH "SEE ALSO" +.IR audio (3) +.SH HISTORY +.I Opl3 +first appeared in 9front (July, 2018), based on +.I ymf262.c +from the Multiple Arcade Machine Emulator ( +.SM MAME +). diff --git a/sys/src/games/dmid.c b/sys/src/games/dmid.c new file mode 100644 index 000000000..7778e19c0 --- /dev/null +++ b/sys/src/games/dmid.c @@ -0,0 +1,554 @@ +#include +#include +#include + +typedef struct Inst Inst; +typedef struct Opl Opl; +typedef struct Chan Chan; +typedef struct Trk Trk; +enum{ + Rate = 44100, + Ninst = 128 + 81-35+1, + + Rwse = 0x01, + Mwse = 1<<5, /* wave selection enable */ + Rctl = 0x20, + Rsca = 0x40, + Mlvl = 63<<0, /* total level */ + Mscl = 3<<6, /* scaling level */ + Ratk = 0x60, + Rsus = 0x80, + Rnum = 0xa0, /* f number lsb */ + Roct = 0xb0, + Mmsb = 3<<0, /* f number msb */ + Moct = 7<<2, + Mkon = 1<<5, + Rfed = 0xc0, + Rwav = 0xe0, + Rop3 = 0x105, +}; + +struct Inst{ + int fixed; + int dbl; + uchar fine; + uchar n; + uchar i[13]; + uchar i2[13]; + s16int base[2]; +}; +Inst inst[Ninst]; + +struct Opl{ + Chan *c; + int n; + int midn; + int blk; + int v; + vlong t; + uchar *i; +}; +Opl opl[18], *ople = opl + nelem(opl); +int port[] = { + 0x0, 0x1, 0x2, 0x8, 0x9, 0xa, 0x10, 0x11, 0x12, + 0x100, 0x101, 0x102, 0x108, 0x109, 0x10a, 0x110, 0x111, 0x112 +}; +int sport[] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108 +}; +uchar ovol[] = { + 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 +}; + +struct Chan{ + Inst *i; + int v; + int bend; + int pan; +}; +Chan chan[16]; +struct Trk{ + u8int *s; + u8int *p; + u8int *e; + uvlong t; + int ev; +}; +Trk *tr; + +double freq[128]; +int mfmt, ntrk, div, tempo, opl2; +uvlong T; +Biobuf *ib, *ob; + +void * +emalloc(ulong n) +{ + void *p; + + p = mallocz(n, 1); + if(p == nil) + sysfatal("mallocz: %r"); + setmalloctag(p, getcallerpc(&n)); + return p; +} + +Biobuf * +bfdopen(int fd, int mode) +{ + Biobuf *bf; + + bf = Bfdopen(fd, mode); + if(bf == nil) + sysfatal("bfdopen: %r"); + Blethal(bf, nil); + return bf; +} + +Biobuf * +bopen(char *file, int mode) +{ + int fd; + + fd = open(file, mode); + if(fd < 0) + sysfatal("bopen: %r"); + return bfdopen(fd, mode); +} + +void +bread(void *u, int n) +{ + if(Bread(ib, u, n) != n) + sysfatal("bread: short read"); +} + +u8int +get8(Trk *x) +{ + u8int v; + + if(x == nil){ + Bread(ib, &v, 1); + return v; + } + if(x->p >= x->e) + sysfatal("track overflow"); + return *x->p++; +} + +u16int +get16(Trk *x) +{ + u16int v; + + v = get8(x) << 8; + return v | get8(x); +} + +u32int +get32(Trk *x) +{ + u32int v; + + v = get16(x) << 16; + return v | get16(x); +} + +void +putcmd(u16int r, u8int v, u16int dt) +{ + uchar *p, u[5]; + + p = u; + *p++ = r; + if(!opl2) + *p++ = r >> 8; + *p++ = v; + *p++ = dt; + *p++ = dt >> 8; + Bwrite(ob, u, p-u); +} + +void +setinst(Opl *o, uchar *i) +{ + int p; + + p = sport[o - opl]; + putcmd(Roct+p, o->blk, 0); + putcmd(Rfed+p, i[6] & ~0x30 | o->c->pan, 0); + p = port[o - opl]; + putcmd(Rctl+p, i[0], 0); + putcmd(Ratk+p, i[1], 0); + putcmd(Rsus+p, i[2], 0); + putcmd(Rwav+p, i[3] & 3, 0); + putcmd(Rctl+3+p, i[7], 0); + putcmd(Ratk+3+p, i[8], 0); + putcmd(Rsus+3+p, i[9], 0); + putcmd(Rwav+3+p, i[10] & 3, 0); + o->i = i; +} + +void +noteoff(Chan *c, int n, int) +{ + Opl *o; + + for(o=opl; oc == c && o->midn == n){ + putcmd(Roct+sport[o-opl], o->blk, 0); + o->n = -1; + } +} + +Opl * +getch(void) +{ + Opl *o, *p; + + p = opl; + for(o=opl; on < 0) + return o; + if(o->t < p->t) + p = o; + } + return p; +} + +void +setoct(Opl *o) +{ + int n, b, f; + + n = o->n + o->c->bend / 0x1000 & 0x7f; + f = freq[n] + (o->c->bend % 0x1000) * (freq[n+1] - freq[n]) / 0x1000; + f = (f * (1 << 20)) / 49716; + //if(o->i == o->c->i->i2) + // f += o->c->i->fine; /* nope */ + for(b=1; b<8; b++, f>>=1) + if(f < 1024) + break; + o->blk = b << 2 & Moct | f >> 8 & Mmsb; + putcmd(Rnum+sport[o-opl], f & 0xff, 0); + putcmd(Roct+sport[o-opl], Mkon | o->blk, 0); +} + +void +setvol(Opl *o) +{ + int p, w, x; + + p = port[o - opl]; + w = o->v * o->c->v / 127; + w = ovol[w * 64 / 127] * 63 / 128; + x = 63 + (o->i[5] & Mlvl) * w / 63 - w; + putcmd(Rsca+p, o->i[4] & Mscl | x, 0); + x = 63 + (o->i[12] & Mlvl) * w / 63 - w; + putcmd(Rsca+p+3, o->i[11] & Mscl | x, 0); +} + +void +putnote(Chan *c, int midn, int n, int v, vlong t, uchar *i) +{ + Opl *o; + + o = getch(); + o->c = c; + o->n = n; + o->midn = midn; + o->v = v; + o->t = t; + if(o->i != i) + setinst(o, i); + setvol(o); + setoct(o); +} + +void +noteon(Chan *c, int n, int v, vlong t) +{ + int x, m; + + m = n; + if(c - chan == 9){ + /* asspull workaround for percussions above gm set */ + if(m == 85) + m = 37; + if(m == 82) + m = 44; + if(m < 35 || m > 81) + return; + c->i = inst + 128 + m - 35; + } + if(c->i->fixed) + m = c->i->n; + if(v == 0){ + noteoff(c, n, 0); + return; + } + x = m + (c->i->fixed ? 0 : c->i->base[0]); + while(x < 0) + x += 12; + while(x > 8*12-1) + x -= 12; + putnote(c, n, x & 0xff, v, t, c->i->i); + if(c->i->dbl){ + x = m + (c->i->fixed ? 0 : c->i->base[1]); + while(x < 0) + x += 12; + while(x > 95) + x -= 12; + putnote(c, n, x & 0xff, v, t, c->i->i2); + } +} + +void +resetchan(Chan *c) +{ + Opl *o; + + for(o=opl; oc == c && o->n >= 0){ + putcmd(Rfed+sport[o-opl], o->i[6] & ~0x30 | c->pan, 0); + setvol(o); + setoct(o); + } +} + +uvlong +tc(int n) +{ + return ((uvlong)n * tempo * Rate / div) / 1000000; +} + +void +skip(Trk *x, int n) +{ + while(n-- > 0) + get8(x); +} + +int +getvar(Trk *x) +{ + int v, w; + + w = get8(x); + v = w & 0x7f; + while(w & 0x80){ + if(v & 0xff000000) + sysfatal("invalid variable-length number"); + v <<= 7; + w = get8(x); + v |= w & 0x7f; + } + return v; +} + +int +peekvar(Trk *x) +{ + int v; + uchar *p; + + p = x->p; + v = getvar(x); + x->p = p; + return v; +} + +void +samp(uvlong t´) +{ + int dt; + static uvlong t; + + dt = t´ - t; + t += dt; + while(dt > 0){ + putcmd(0, 0, dt > 0xffff ? 0xffff : dt); + dt -= 0xffff; + } +} + +void +ev(Trk *x) +{ + int e, n, m; + Chan *c; + + samp(x->t += tc(getvar(x))); + e = get8(x); + if((e & 0x80) == 0){ + x->p--; + e = x->ev; + if((e & 0x80) == 0) + sysfatal("invalid event"); + }else + x->ev = e; + c = chan + (e & 15); + n = get8(x); + switch(e >> 4){ + case 0x8: noteoff(c, n, get8(x)); break; + case 0x9: noteon(c, n, get8(x), x->t); break; + case 0xb: + m = get8(x); + switch(n){ + case 0x00: case 0x01: case 0x20: break; + case 0x07: c->v = m; resetchan(c); break; + case 0x0a: c->pan = m < 32 ? 1<<4 : m > 96 ? 1<<5 : 3<<4; resetchan(c); break; + default: fprint(2, "unknown controller %d\n", n); + } + break; + case 0xc: c->i = inst + n; break; + case 0xe: + n = get8(x) << 7 | n; + c->bend = n - 0x4000 / 2; + resetchan(c); + break; + case 0xf: + if((e & 0xf) == 0){ + while(get8(x) != 0xf7) + ; + return; + } + m = get8(x); + switch(n){ + case 0x2f: x->p = x->e; return; + case 0x51: tempo = get16(x) << 8; tempo |= get8(x); break; + default: skip(x, m); + } + break; + case 0xa: + case 0xd: get8(x); break; + default: sysfatal("invalid event %#ux\n", e >> 4); + } +} + +void +readinst(char *file) +{ + int n; + uchar u[8]; + Inst *i; + + ib = bopen(file, OREAD); + bread(u, sizeof u); + if(memcmp(u, "#OPL_II#", sizeof u) != 0) + sysfatal("invalid patch file"); + for(i=inst; ifixed = n & 1<<0; + i->dbl = opl2 ? 0 : n & 1<<2; + get8(nil); + i->fine = get8(nil) / 2 - 64; + i->n = get8(nil); + bread(i->i, sizeof i->i); + get8(nil); + n = get8(nil); + n |= get8(nil) << 8; + i->base[0] = (s16int)n; + bread(i->i2, sizeof i->i2); + get8(nil); + n = get8(nil); + n |= get8(nil) << 8; + i->base[1] = (s16int)n; + } + Bterm(ib); +} + +void +readmid(char *file) +{ + u32int n; + uchar *s; + Trk *x; + + ib = file != nil ? bopen(file, OREAD) : bfdopen(0, OREAD); + if(get32(nil) != 0x4d546864 || get32(nil) != 6) + sysfatal("invalid header"); + mfmt = get16(nil); + ntrk = get16(nil); + if(ntrk == 1) + mfmt = 0; + if(mfmt < 0 || mfmt > 1) + sysfatal("unsupported format %d", mfmt); + div = get16(nil); + tr = emalloc(ntrk * sizeof *tr); + for(x=tr; xs = s; + x->p = s; + x->e = s + n; + } + Bterm(ib); +} + +void +usage(void) +{ + fprint(2, "usage: %s [-2] [-i inst] [mid]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int n, t, mint; + char *i; + double f; + Chan *c; + Opl *o; + Trk *x, *minx; + + i = "/mnt/wad/genmidi"; + ARGBEGIN{ + case '2': opl2 = 1; ople = opl + 9; break; + case 'i': i = EARGF(usage()); break; + default: usage(); + }ARGEND + readinst(i); + readmid(*argv); + ob = bfdopen(1, OWRITE); + f = pow(2, 1./12); + for(n=0; nv = 0x5a; + c->bend = 0; + c->pan = 3<<4; + c->i = inst; + } + for(o=opl; on = -1; + tempo = 500000; + putcmd(Rwse, Mwse, 0); + putcmd(Rop3, 1, 0); + for(;;){ + minx = nil; + mint = 0; + for(x=tr; xp >= x->e) + continue; + t = x->t + tc(peekvar(x)); + if(t < mint || minx == nil){ + mint = t; + minx = x; + } + } + if(minx == nil) + exits(nil); + ev(minx); + } +} diff --git a/sys/src/games/mkfile b/sys/src/games/mkfile index 542bb4fde..a42eb89e4 100644 --- a/sys/src/games/mkfile +++ b/sys/src/games/mkfile @@ -16,6 +16,7 @@ TARG=4s\ mandel\ midi\ wadfs\ + dmid\ OFILES= HFILES= @@ -38,6 +39,7 @@ DIRS=\ music\ md\ nes\ + opl3\ snes\ sokoban\ sudoku\ diff --git a/sys/src/games/opl3/mkfile b/sys/src/games/opl3/mkfile new file mode 100644 index 000000000..c151e754a --- /dev/null +++ b/sys/src/games/opl3/mkfile @@ -0,0 +1,8 @@ + +#include + +typedef struct Op Op; +typedef struct Chan Chan; + +#define Clk 14318180.0 +#define FREQ_SH 16 +#define EG_SH 16 +#define LFO_SH 24 +#define TIMER_SH 16 +#define FREQ_MASK ((1 << FREQ_SH) - 1) +#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) +#define MIN_ATT_INDEX 0 +#define SIN_BITS 10 +#define SIN_LEN (1 << SIN_BITS) +#define SIN_MASK (SIN_LEN - 1) +#define TL_RES_LEN 256 + +enum{ + EG_OFF, + EG_REL, + EG_SUS, + EG_DEC, + EG_ATT, +}; + +struct Op +{ + 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 *connect; /* slot 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] */ + + u32int eg_m_ar; /* (attack state) */ + u8int eg_sh_ar; /* (attack state) */ + u8int eg_sel_ar; /* (attack state) */ + u32int eg_m_dr; /* (decay state) */ + u8int eg_sh_dr; /* (decay state) */ + u8int eg_sel_dr; /* (decay state) */ + u32int eg_m_rr; /* (release state) */ + u8int eg_sh_rr; /* (release state) */ + u8int eg_sel_rr; /* (release state) */ + + u32int key; + + u32int AMmask; /* LFO Amplitude Modulation enable mask */ + u8int vib; /* LFO Phase Modulation enable flag (active high)*/ + + u8int waveform_number; + uint wavetable; +}; + +struct Chan +{ + Op SLOT[2]; + u32int block_fnum; /* block+fnum */ + u32int fc; /* Freq. Increment base */ + u32int ksl_base; /* KeyScaleLevel Base step */ + u8int kcode; /* key code (for key scaling) */ + u8int extended; +}; +static Chan chs[18]; +static u32int pan[18*4]; /* channels output masks (0xffffffff = enable); 4 masks per one channel */ +static u32int pan_ctrl_value[18]; /* output control values 1 per one channel (1 value contains 4 masks) */ +static int chanout[18]; +static int phase_modulation, phase_modulation2; /* phase modulation input (SLOT 2 and 3/4) */ +static u32int eg_cnt; /* global envelope generator counter */ +static u32int eg_timer; /* global envelope generator counter works at frequency = chipclock/288 (288=8*36) */ +static u32int eg_timer_add; /* step of eg_timer */ +static u32int eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ +static u32int fn_tab[1024]; +static u32int LFO_AM; +static s32int LFO_PM; +static u8int lfo_am_depth, lfo_pm_depth_range; +static u32int lfo_am_cnt, lfo_am_inc, lfo_pm_cnt, lfo_pm_inc; +static u32int noise_rng, noise_p, noise_f; +static int OPL3_mode, nts; +static u8int rhythm; + +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 for decay */ +/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) */ +/*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 + +#define TL_TAB_LEN (13*2*TL_RES_LEN) +static int tl_tab[TL_TAB_LEN]; + +#define ENV_QUIET (TL_TAB_LEN>>4) + +static uint sin_tab[SIN_LEN * 8]; + +#define LFO_AM_TAB_ELEMENTS 210 + +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*/ +}; + +#define SLOT7_1 (&chs[7].SLOT[0]) +#define SLOT7_2 (&chs[7].SLOT[1]) +#define SLOT8_1 (&chs[8].SLOT[0]) +#define SLOT8_2 (&chs[8].SLOT[1]) + +static void +advance_lfo(void) +{ + u8int tmp; + + lfo_am_cnt += lfo_am_inc; + if(lfo_am_cnt >= ((u32int)LFO_AM_TAB_ELEMENTS<> LFO_SH]; + if(lfo_am_depth) + LFO_AM = tmp; + else + LFO_AM = tmp>>2; + lfo_pm_cnt += lfo_pm_inc; + LFO_PM = (lfo_pm_cnt>>LFO_SH & 7) | lfo_pm_depth_range; +} + +static void +advance(void) +{ + Chan *CH; + Op *op; + int i; + + eg_timer += eg_timer_add; + while (eg_timer >= eg_timer_overflow){ + eg_timer -= eg_timer_overflow; + eg_cnt++; + for (i=0; i<9*2*2; i++){ + CH = &chs[i/2]; + op = &CH->SLOT[i&1]; + switch(op->state){ + case EG_ATT: + if(!(eg_cnt & op->eg_m_ar)){ + op->volume += (s32int)(~op->volume * + (eg_inc[op->eg_sel_ar + ((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: + if(!(eg_cnt & op->eg_m_dr)){ + op->volume += eg_inc[op->eg_sel_dr + ((eg_cnt>>op->eg_sh_dr)&7)]; + if(op->volume >= op->sl) + op->state = EG_SUS; + } + break; + case EG_SUS: + if(op->eg_type) + { + }else{ + if(!(eg_cnt & op->eg_m_rr)){ + op->volume += eg_inc[op->eg_sel_rr + ((eg_cnt>>op->eg_sh_rr)&7)]; + if(op->volume >= MAX_ATT_INDEX) + op->volume = MAX_ATT_INDEX; + } + } + break; + case EG_REL: + if(!(eg_cnt & op->eg_m_rr)){ + op->volume += eg_inc[op->eg_sel_rr + ((eg_cnt>>op->eg_sh_rr)&7)]; + if(op->volume >= MAX_ATT_INDEX){ + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + } + break; + } + } + } + for (i=0; i<9*2*2; i++){ + CH = &chs[i/2]; + op = &CH->SLOT[i&1]; + if(op->vib){ + u8int block; + uint block_fnum = CH->block_fnum; + uint fnum_lfo = (block_fnum&0x0380) >> 7; + int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16*fnum_lfo]; + if(lfo_fn_table_index_offset){ + block_fnum += lfo_fn_table_index_offset; + block = (block_fnum&0x1c00) >> 10; + op->Cnt += (fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul; + }else + op->Cnt += op->Incr; + }else + op->Cnt += op->Incr; + } + noise_p += noise_f; + i = noise_p >> FREQ_SH; + noise_p &= FREQ_MASK; + while (i){ + if(noise_rng & 1) noise_rng ^= 0x800302; + noise_rng >>= 1; + i--; + } +} + +static int +op_calc(u32int phase, uint env, int pm, uint wave_tab) +{ + u32int p; + + p = (env<<4) + sin_tab[wave_tab + ((((int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH) & SIN_MASK)]; + if(p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +static int +op_calc1(u32int phase, uint env, int pm, uint wave_tab) +{ + u32int p; + + p = (env<<4) + sin_tab[wave_tab + ((((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) + (LFO_AM & (OP)->AMmask)) + +static void +chan_calc(Chan *CH) +{ + Op *SLOT; + uint env; + int out; + + phase_modulation = 0; + phase_modulation2= 0; + SLOT = &CH->SLOT[0]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[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<FB), SLOT->wavetable); + } + *SLOT->connect += SLOT->op1_out[1]; + SLOT++; + env = volume_calc(SLOT); + if(env < ENV_QUIET) + *SLOT->connect += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable); +} + +static void +chan_calc_ext(Chan *CH) +{ + Op *SLOT; + uint env; + + phase_modulation = 0; + SLOT = &CH->SLOT[0]; + env = volume_calc(SLOT); + if(env < ENV_QUIET) + *SLOT->connect += op_calc(SLOT->Cnt, env, phase_modulation2, SLOT->wavetable); + SLOT++; + env = volume_calc(SLOT); + if(env < ENV_QUIET) + *SLOT->connect += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable); +} + +static void +chan_calc_rhythm(Chan *CH, uint noise) +{ + Op *SLOT; + int out; + uint env; + + phase_modulation = 0; + SLOT = &CH[6].SLOT[0]; + env = volume_calc(SLOT); + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + if(!SLOT->CON) + phase_modulation = 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<FB), SLOT->wavetable); + } + SLOT++; + env = volume_calc(SLOT); + if(env < ENV_QUIET) + chanout[6] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2; + env = volume_calc(SLOT7_1); + if(env < ENV_QUIET){ + uchar bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + uchar bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + uchar bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + uchar res1 = (bit2 ^ bit7) | bit3; + u32int phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; + uchar bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + uchar bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + uchar res2 = (bit3e ^ bit5e); + if(res2) + phase = (0x200|(0xd0>>2)); + if(phase&0x200){ + if(noise) + phase = 0x200|0xd0; + }else + { + if(noise) + phase = 0xd0>>2; + } + chanout[7] += op_calc(phase<wavetable) * 2; + } + env = volume_calc(SLOT7_2); + if(env < ENV_QUIET){ + uchar bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1; + u32int phase = bit8 ? 0x200 : 0x100; + if(noise) + phase ^= 0x100; + chanout[7] += op_calc(phase<wavetable) * 2; + } + env = volume_calc(SLOT8_1); + if(env < ENV_QUIET) + chanout[8] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2; + env = volume_calc(SLOT8_2); + if(env < ENV_QUIET){ + uchar bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1; + uchar bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1; + uchar bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1; + uchar res1 = (bit2 ^ bit7) | bit3; + u32int phase = res1 ? 0x300 : 0x100; + uchar bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1; + uchar bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1; + uchar res2 = (bit3e ^ bit5e); + if(res2) + phase = 0x300; + chanout[8] += op_calc(phase<wavetable) * 2; + } +} + +static void +FM_KEYON(Op *SLOT, u32int key_set) +{ + if(!SLOT->key){ + SLOT->Cnt = 0; + SLOT->state = EG_ATT; + } + SLOT->key |= key_set; +} + +static void +FM_KEYOFF(Op *SLOT, u32int key_clr) +{ + if(SLOT->key){ + SLOT->key &= key_clr; + if(!SLOT->key){ + 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(Chan *CH, Op *SLOT) +{ + int ksr; + + SLOT->Incr = CH->fc * SLOT->mul; + ksr = CH->kcode >> SLOT->KSR; + if(SLOT->ksr != ksr){ + SLOT->ksr = ksr; + if((SLOT->ar + SLOT->ksr) < 16+60){ + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr]; + }else{ + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr]; + SLOT->eg_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; + } +} + +static void +set_mul(int slot, int v) +{ + Chan *CH = &chs[slot/2]; + Op *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; + if(OPL3_mode & 1){ + int chan_no = slot/2; + switch(chan_no){ + case 0: case 1: case 2: case 9: case 10: case 11: + CALC_FCSLOT(CH,SLOT); + break; + case 3: case 4: case 5: case 12: case 13: case 14: + if((CH-3)->extended) + CALC_FCSLOT(CH-3,SLOT); + else + CALC_FCSLOT(CH,SLOT); + break; + default: + CALC_FCSLOT(CH,SLOT); + break; + } + }else{ + CALC_FCSLOT(CH,SLOT); + } +} + +static void +set_ksl_tl(int slot, int v) +{ + Chan *CH = &chs[slot/2]; + Op *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) */ + if(OPL3_mode & 1){ + int chan_no = slot/2; + switch(chan_no){ + case 0: case 1: case 2: case 9: case 10: case 11: + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + break; + case 3: case 4: case 5: case 12: case 13: case 14: + if((CH-3)->extended) + SLOT->TLL = SLOT->TL + ((CH-3)->ksl_base>>SLOT->ksl); + else + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + break; + default: + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); + break; + } + }else + SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); +} + +static void +set_ar_dr(int slot, int v) +{ + Chan *CH = &chs[slot/2]; + Op *SLOT = &CH->SLOT[slot&1]; + + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; + if((SLOT->ar + SLOT->ksr) < 16+60){ + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr]; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr]; + }else{ + SLOT->eg_sh_ar = 0; + SLOT->eg_m_ar = (1<eg_sh_ar)-1; + 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_m_dr = (1<eg_sh_dr)-1; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr]; +} + +static void +set_sl_rr(int slot, int v) +{ + Chan *CH = &chs[slot/2]; + Op *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_m_rr = (1<eg_sh_rr)-1; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr]; +} + +void +opl3wr(int r, int v) +{ + Chan *CH; + uint ch_offset = 0; + int slot; + int block_fnum; + + v &= 0xff; + if(r&0x100){ + switch(r){ + case 0x101: + return; + case 0x104: + CH = &chs[0]; + CH->extended = (v>>0) & 1; + CH++; + CH->extended = (v>>1) & 1; + CH++; + CH->extended = (v>>2) & 1; + CH = &chs[9]; + CH->extended = (v>>3) & 1; + CH++; + CH->extended = (v>>4) & 1; + CH++; + CH->extended = (v>>5) & 1; + return; + case 0x105: + OPL3_mode = v & 1; + return; + } + ch_offset = 9; + } + r &= 0xff; + v &= 0xff; + switch(r&0xe0){ + case 0x00: + switch(r&0x1f){ + case 0x08: + nts = v; + break; + } + break; + case 0x20: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_mul(slot + ch_offset*2, v); + break; + case 0x40: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ksl_tl(slot + ch_offset*2, v); + break; + case 0x60: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_ar_dr(slot + ch_offset*2, v); + break; + case 0x80: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + set_sl_rr(slot + ch_offset*2, v); + break; + case 0xa0: + if(r == 0xbd){ + if(ch_offset != 0) + return; + lfo_am_depth = v & 0x80; + lfo_pm_depth_range = (v&0x40) ? 8 : 0; + rhythm = v & 0x3f; + if(rhythm & 0x20){ + if(v&0x10){ + FM_KEYON (&chs[6].SLOT[0], 2); + FM_KEYON (&chs[6].SLOT[1], 2); + }else{ + FM_KEYOFF(&chs[6].SLOT[0],~2); + FM_KEYOFF(&chs[6].SLOT[1],~2); + } + if(v&0x01) FM_KEYON (&chs[7].SLOT[0], 2); + else FM_KEYOFF(&chs[7].SLOT[0],~2); + if(v&0x08) FM_KEYON (&chs[7].SLOT[1], 2); + else FM_KEYOFF(&chs[7].SLOT[1],~2); + if(v&0x04) FM_KEYON (&chs[8].SLOT[0], 2); + else FM_KEYOFF(&chs[8].SLOT[0],~2); + if(v&0x02) FM_KEYON (&chs[8].SLOT[1], 2); + else FM_KEYOFF(&chs[8].SLOT[1],~2); + }else{ + FM_KEYOFF(&chs[6].SLOT[0],~2); + FM_KEYOFF(&chs[6].SLOT[1],~2); + FM_KEYOFF(&chs[7].SLOT[0],~2); + FM_KEYOFF(&chs[7].SLOT[1],~2); + FM_KEYOFF(&chs[8].SLOT[0],~2); + FM_KEYOFF(&chs[8].SLOT[1],~2); + } + return; + } + if((r&0x0f) > 8) return; + CH = &chs[(r&0x0f) + ch_offset]; + if(!(r&0x10)){ + block_fnum = (CH->block_fnum&0x1f00) | v; + }else{ + block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff); + if(OPL3_mode & 1){ + int chan_no = (r&0x0f) + ch_offset; + switch(chan_no){ + case 0: case 1: case 2: case 9: case 10: case 11: + if(CH->extended){ + if(v&0x20){ + FM_KEYON (&CH->SLOT[0], 1); + FM_KEYON (&CH->SLOT[1], 1); + FM_KEYON (&(CH+3)->SLOT[0], 1); + FM_KEYON (&(CH+3)->SLOT[1], 1); + }else{ + FM_KEYOFF(&CH->SLOT[0],~1); + FM_KEYOFF(&CH->SLOT[1],~1); + FM_KEYOFF(&(CH+3)->SLOT[0],~1); + FM_KEYOFF(&(CH+3)->SLOT[1],~1); + } + }else{ + if(v&0x20){ + FM_KEYON (&CH->SLOT[0], 1); + FM_KEYON (&CH->SLOT[1], 1); + }else{ + FM_KEYOFF(&CH->SLOT[0],~1); + FM_KEYOFF(&CH->SLOT[1],~1); + } + } + break; + case 3: case 4: case 5: case 12: case 13: case 14: + if((CH-3)->extended){ + }else{ + if(v&0x20){ + FM_KEYON (&CH->SLOT[0], 1); + FM_KEYON (&CH->SLOT[1], 1); + }else{ + FM_KEYOFF(&CH->SLOT[0],~1); + FM_KEYOFF(&CH->SLOT[1],~1); + } + } + break; + default: + if(v&0x20){ + FM_KEYON (&CH->SLOT[0], 1); + FM_KEYON (&CH->SLOT[1], 1); + }else{ + FM_KEYOFF(&CH->SLOT[0],~1); + FM_KEYOFF(&CH->SLOT[1],~1); + } + break; + } + }else{ + if(v&0x20){ + FM_KEYON (&CH->SLOT[0], 1); + FM_KEYON (&CH->SLOT[1], 1); + }else{ + FM_KEYOFF(&CH->SLOT[0],~1); + FM_KEYOFF(&CH->SLOT[1],~1); + } + } + } + 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 = fn_tab[block_fnum&0x03ff] >> (7-block); + CH->kcode = (CH->block_fnum&0x1c00)>>9; + if(nts&0x40) + CH->kcode |= (CH->block_fnum&0x100)>>8; + else + CH->kcode |= (CH->block_fnum&0x200)>>9; + if(OPL3_mode & 1){ + int chan_no = (r&0x0f) + ch_offset; + switch(chan_no){ + case 0: case 1: case 2: case 9: case 10: case 11: + if(CH->extended){ + CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl); + CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl); + (CH+3)->SLOT[0].TLL = (CH+3)->SLOT[0].TL + (CH->ksl_base>>(CH+3)->SLOT[0].ksl); + (CH+3)->SLOT[1].TLL = (CH+3)->SLOT[1].TL + (CH->ksl_base>>(CH+3)->SLOT[1].ksl); + CALC_FCSLOT(CH,&CH->SLOT[0]); + CALC_FCSLOT(CH,&CH->SLOT[1]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[0]); + CALC_FCSLOT(CH,&(CH+3)->SLOT[1]); + }else{ + CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl); + CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl); + CALC_FCSLOT(CH,&CH->SLOT[0]); + CALC_FCSLOT(CH,&CH->SLOT[1]); + } + break; + case 3: case 4: case 5: case 12: case 13: case 14: + if((CH-3)->extended){ + }else{ + CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl); + CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl); + CALC_FCSLOT(CH,&CH->SLOT[0]); + CALC_FCSLOT(CH,&CH->SLOT[1]); + } + break; + default: + CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl); + CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl); + CALC_FCSLOT(CH,&CH->SLOT[0]); + CALC_FCSLOT(CH,&CH->SLOT[1]); + break; + } + }else{ + CH->SLOT[0].TLL = CH->SLOT[0].TL + (CH->ksl_base>>CH->SLOT[0].ksl); + CH->SLOT[1].TLL = CH->SLOT[1].TL + (CH->ksl_base>>CH->SLOT[1].ksl); + CALC_FCSLOT(CH,&CH->SLOT[0]); + CALC_FCSLOT(CH,&CH->SLOT[1]); + } + } + break; + case 0xc0: + if((r&0xf) > 8) return; + CH = &chs[(r&0xf) + ch_offset]; + if(OPL3_mode & 1){ + int base = ((r&0xf) + ch_offset) * 4; + pan[base] = (v & 0x10) ? ~0 : 0; + pan[base +1] = (v & 0x20) ? ~0 : 0; + pan[base +2] = (v & 0x40) ? ~0 : 0; + pan[base +3] = (v & 0x80) ? ~0 : 0; + }else{ + int base = ((r&0xf) + ch_offset) * 4; + pan[base] = ~0; + pan[base +1] = ~0; + pan[base +2] = ~0; + pan[base +3] = ~0; + } + pan_ctrl_value[(r&0xf) + ch_offset] = v; + CH->SLOT[0].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[0].CON = v&1; + if(OPL3_mode & 1){ + int chan_no = (r&0x0f) + ch_offset; + switch(chan_no){ + case 0: case 1: case 2: case 9: case 10: case 11: + if(CH->extended){ + u8int conn = (CH->SLOT[0].CON<<1) | ((CH+3)->SLOT[0].CON<<0); + switch(conn){ + case 0: + CH->SLOT[0].connect = &phase_modulation; + CH->SLOT[1].connect = &phase_modulation2; + (CH+3)->SLOT[0].connect = &phase_modulation; + (CH+3)->SLOT[1].connect = &chanout[chan_no + 3]; + break; + case 1: + CH->SLOT[0].connect = &phase_modulation; + CH->SLOT[1].connect = &chanout[chan_no]; + (CH+3)->SLOT[0].connect = &phase_modulation; + (CH+3)->SLOT[1].connect = &chanout[chan_no + 3]; + break; + case 2: + CH->SLOT[0].connect = &chanout[chan_no]; + CH->SLOT[1].connect = &phase_modulation2; + (CH+3)->SLOT[0].connect = &phase_modulation; + (CH+3)->SLOT[1].connect = &chanout[chan_no + 3]; + break; + case 3: + CH->SLOT[0].connect = &chanout[chan_no]; + CH->SLOT[1].connect = &phase_modulation2; + (CH+3)->SLOT[0].connect = &chanout[chan_no + 3]; + (CH+3)->SLOT[1].connect = &chanout[chan_no + 3]; + break; + } + }else{ + CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation; + CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset]; + } + break; + case 3: case 4: case 5: case 12: case 13: case 14: + if((CH-3)->extended){ + u8int conn = ((CH-3)->SLOT[0].CON<<1) | (CH->SLOT[0].CON<<0); + switch(conn){ + case 0: + (CH-3)->SLOT[0].connect = &phase_modulation; + (CH-3)->SLOT[1].connect = &phase_modulation2; + CH->SLOT[0].connect = &phase_modulation; + CH->SLOT[1].connect = &chanout[chan_no]; + break; + case 1: + (CH-3)->SLOT[0].connect = &phase_modulation; + (CH-3)->SLOT[1].connect = &chanout[chan_no - 3]; + CH->SLOT[0].connect = &phase_modulation; + CH->SLOT[1].connect = &chanout[chan_no]; + break; + case 2: + (CH-3)->SLOT[0].connect = &chanout[chan_no - 3]; + (CH-3)->SLOT[1].connect = &phase_modulation2; + CH->SLOT[0].connect = &phase_modulation; + CH->SLOT[1].connect = &chanout[chan_no]; + break; + case 3: + (CH-3)->SLOT[0].connect = &chanout[chan_no - 3]; + (CH-3)->SLOT[1].connect = &phase_modulation2; + CH->SLOT[0].connect = &chanout[chan_no]; + CH->SLOT[1].connect = &chanout[chan_no]; + break; + } + }else{ + CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation; + CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset]; + } + break; + default: + CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation; + CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset]; + break; + } + }else{ + CH->SLOT[0].connect = CH->SLOT[0].CON ? &chanout[(r&0xf)+ch_offset] : &phase_modulation; + CH->SLOT[1].connect = &chanout[(r&0xf)+ch_offset]; + } + break; + case 0xe0: + slot = slot_array[r&0x1f]; + if(slot < 0) return; + slot += ch_offset*2; + CH = &chs[slot/2]; + v &= 7; + CH->SLOT[slot&1].waveform_number = v; + if(!(OPL3_mode & 1)) + v &= 3; + CH->SLOT[slot&1].wavetable = v * SIN_LEN; + break; + } +} + +void +opl3out(uchar *p, int n) +{ + uchar *e; + + for(e=p+n; p>0)&1); + chan_calc(&chs[9]); + if(chs[9].extended) + chan_calc_ext(&chs[12]); + else + chan_calc(&chs[12]); + chan_calc(&chs[10]); + if(chs[10].extended) + chan_calc_ext(&chs[13]); + else + chan_calc(&chs[13]); + chan_calc(&chs[11]); + if(chs[11].extended) + chan_calc_ext(&chs[14]); + else + chan_calc(&chs[14]); + chan_calc(&chs[15]); + chan_calc(&chs[16]); + chan_calc(&chs[17]); + a = chanout[0] & pan[0]; + b = chanout[0] & pan[1]; + a += chanout[1] & pan[4]; + b += chanout[1] & pan[5]; + a += chanout[2] & pan[8]; + b += chanout[2] & pan[9]; + a += chanout[3] & pan[12]; + b += chanout[3] & pan[13]; + a += chanout[4] & pan[16]; + b += chanout[4] & pan[17]; + a += chanout[5] & pan[20]; + b += chanout[5] & pan[21]; + a += chanout[6] & pan[24]; + b += chanout[6] & pan[25]; + a += chanout[7] & pan[28]; + b += chanout[7] & pan[29]; + a += chanout[8] & pan[32]; + b += chanout[8] & pan[33]; + a += chanout[9] & pan[36]; + b += chanout[9] & pan[37]; + a += chanout[10] & pan[40]; + b += chanout[10] & pan[41]; + a += chanout[11] & pan[44]; + b += chanout[11] & pan[45]; + a += chanout[12] & pan[48]; + b += chanout[12] & pan[49]; + a += chanout[13] & pan[52]; + b += chanout[13] & pan[53]; + a += chanout[14] & pan[56]; + b += chanout[14] & pan[57]; + a += chanout[15] & pan[60]; + b += chanout[15] & pan[61]; + a += chanout[16] & pan[64]; + b += chanout[16] & pan[65]; + a += chanout[17] & pan[68]; + b += chanout[17] & pan[69]; + if(a > 32767) + a = 32767; + else if(a < -32768) + a = -32768; + if(b > 32767) + b = 32767; + else if(b < -32768) + b = -32768; + p[0] = a; + p[1] = a >> 8; + p[2] = b; + p[3] = b >> 8; + advance(); + } +} + +static int +init_tables(void) +{ + int i, x, n; + double o, m; + + for (x=0; x>= 4; + if(n&1) + n = (n>>1)+1; + else + n = n>>1; + n <<= 1; + tl_tab[x*2 + 0] = n; + tl_tab[x*2 + 1] = ~tl_tab[x*2 + 0]; + for (i=1; i<13; 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; i0.0) + o = 8*log(1.0/m)/log(2.0); + else + o = 8*log(-1.0/m)/log(2.0); + o = o / (ENV_STEP/4); + n = (int)(2.0*o); + if(n&1) + n = (n>>1)+1; + else + n = n>>1; + sin_tab[i] = n*2 + (m>=0.0? 0: 1); + } + for (i=0; i>1)]; + 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)]; + if(i & (1<<(SIN_BITS-1))) + sin_tab[4*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[4*SIN_LEN+i] = sin_tab[i*2]; + if(i & (1<<(SIN_BITS-1))) + sin_tab[5*SIN_LEN+i] = TL_TAB_LEN; + else + sin_tab[5*SIN_LEN+i] = sin_tab[(i*2) & (SIN_MASK>>1)]; + if(i & (1<<(SIN_BITS-1))) + sin_tab[6*SIN_LEN+i] = 1; + else + sin_tab[6*SIN_LEN+i] = 0; + if(i & (1<<(SIN_BITS-1))) + x = ((SIN_LEN-1)-i)*16 + 1; + else + x = i*16; + if(x > TL_TAB_LEN) + x = TL_TAB_LEN; + sin_tab[7*SIN_LEN+i] = x; + } + return 1; +} + +void +opl3init(int rate) +{ + int i, o; + double f0; + + init_tables(); + f0 = (Clk / (8.0*36)) / rate; + for(i=0 ; i < 1024 ; i++) + fn_tab[i] = (u32int)((double)i * 64 * f0 * (1<<(FREQ_SH-10))); + lfo_am_inc = (1.0 / 64.0) * (1<=0x20; i--) + opl3wr(i, 0); + for(i=0x1ff; i>=0x120; i--) + opl3wr(i, 0); + for(i=0; i<9*2; i++){ + Chan *CH = &chs[i]; + for(o=0; o<2; o++){ + CH->SLOT[o].state = EG_OFF; + CH->SLOT[o].volume = MAX_ATT_INDEX; + } + } +} diff --git a/sys/src/games/opl3/opl3m.c b/sys/src/games/opl3/opl3m.c new file mode 100644 index 000000000..ef3dd6672 --- /dev/null +++ b/sys/src/games/opl3/opl3m.c @@ -0,0 +1,57 @@ +#include +#include +#include + +void opl3out(uchar *, int); +void opl3wr(int, int); +void opl3init(int); + +enum{ + Rate = 44100, +}; + +void +usage(void) +{ + fprint(2, "usage: %s [-n nsamp] [file]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int r, v, dt, nsamp, fd; + uchar *sb, u[5]; + Biobuf *bi, *bo; + + fd = 0; + nsamp = 1; + ARGBEGIN{ + case 'n': + nsamp = Rate / atoi(EARGF(usage())); + break; + default: + usage(); + }ARGEND; + if(*argv != nil) + if((fd = open(*argv, OREAD)) < 0) + sysfatal("open: %r"); + bi = Bfdopen(fd, OREAD); + bo = Bfdopen(1, OWRITE); + if(bi == nil || bo == nil) + sysfatal("Bfdopen: %r"); + nsamp *= 4; + if((sb = malloc(nsamp)) == nil) + sysfatal("malloc: %r"); + opl3init(Rate); + while(Bread(bi, u, sizeof u) > 0){ + r = u[1] << 8 | u[0]; + v = u[2]; + dt = u[4] << 8 | u[3]; + opl3wr(r, v); + while(dt-- > 0){ + opl3out(sb, nsamp); + Bwrite(bo, sb, nsamp); + } + } +}