add games/dmid and games/opl3
This commit is contained in:
parent
1e3790f7b5
commit
96e511d736
8 changed files with 2053 additions and 0 deletions
10
rc/bin/dmus
Executable file
10
rc/bin/dmus
Executable file
|
@ -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
|
68
sys/man/1/dmid
Normal file
68
sys/man/1/dmid
Normal file
|
@ -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).
|
59
sys/man/1/opl3
Normal file
59
sys/man/1/opl3
Normal file
|
@ -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
|
||||
).
|
554
sys/src/games/dmid.c
Normal file
554
sys/src/games/dmid.c
Normal file
|
@ -0,0 +1,554 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
|
||||
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; o<ople; o++)
|
||||
if(o->c == 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; o<ople; o++){
|
||||
if(o->n < 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; o<ople; o++)
|
||||
if(o->c == 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; i<inst+nelem(inst); i++){
|
||||
n = get8(nil);
|
||||
i->fixed = 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; x<tr+ntrk; x++){
|
||||
if(get32(nil) != 0x4d54726b)
|
||||
sysfatal("invalid track");
|
||||
n = get32(nil);
|
||||
s = emalloc(n);
|
||||
bread(s, n);
|
||||
x->s = 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; n<nelem(freq); n++)
|
||||
freq[n] = 440 * pow(f, n - 69);
|
||||
for(c=chan; c<chan+nelem(chan); c++){
|
||||
c->v = 0x5a;
|
||||
c->bend = 0;
|
||||
c->pan = 3<<4;
|
||||
c->i = inst;
|
||||
}
|
||||
for(o=opl; o<ople; o++)
|
||||
o->n = -1;
|
||||
tempo = 500000;
|
||||
putcmd(Rwse, Mwse, 0);
|
||||
putcmd(Rop3, 1, 0);
|
||||
for(;;){
|
||||
minx = nil;
|
||||
mint = 0;
|
||||
for(x=tr; x<tr+ntrk; x++){
|
||||
if(x->p >= 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);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ TARG=4s\
|
|||
mandel\
|
||||
midi\
|
||||
wadfs\
|
||||
dmid\
|
||||
|
||||
OFILES=
|
||||
HFILES=
|
||||
|
@ -38,6 +39,7 @@ DIRS=\
|
|||
music\
|
||||
md\
|
||||
nes\
|
||||
opl3\
|
||||
snes\
|
||||
sokoban\
|
||||
sudoku\
|
||||
|
|
8
sys/src/games/opl3/mkfile
Normal file
8
sys/src/games/opl3/mkfile
Normal file
|
@ -0,0 +1,8 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
BIN=/$objtype/bin/games
|
||||
TARG=opl3
|
||||
OFILES=opl3.$O opl3m.$O
|
||||
HFILES=
|
||||
|
||||
</sys/src/cmd/mkone
|
1295
sys/src/games/opl3/opl3.c
Normal file
1295
sys/src/games/opl3/opl3.c
Normal file
File diff suppressed because it is too large
Load diff
57
sys/src/games/opl3/opl3m.c
Normal file
57
sys/src/games/opl3/opl3m.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue