diff --git a/sys/man/1/opl3 b/sys/man/1/opl3 index 7419ff134..280a3196f 100644 --- a/sys/man/1/opl3 +++ b/sys/man/1/opl3 @@ -17,7 +17,11 @@ is an emulator of a single Yamaha 262 chip, also known as 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, +It then synthesizes stereo 16-bit little-endian PCM samples +at the chip's sampling rate, 49.716 kHz, +resamples them to +.IR audio (3)'s +default 44.1 kHz rate, and writes them to standard out. .PP Commands are 5 bytes wide, in little-endian byte order: @@ -42,14 +46,24 @@ 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 sampling rate, 44100 Hz by default. -This rate can be set using the +The period itself is the inverse of the input stream's sampling rate, +by default the same as the chip's output sampling rate. +The .B -r -parameter. +parameter +sets the input sampling rate. .SH SOURCE .B /sys/src/games/opl3 .SH "SEE ALSO" .IR audio (3) +.PP +Yamaha +``YMF262 Manual'', +1994. +.PP +V. Arnost +``Programmer's Guide to Yamaha YMF 262/OPL3 FM Music Synthesizer'', +version 1.12 dated Nov. 23rd 2000. .SH HISTORY .I Opl3 first appeared in 9front (July, 2018), based on diff --git a/sys/src/games/dmid.c b/sys/src/games/dmid.c index 254d42df9..9361605a4 100644 --- a/sys/src/games/dmid.c +++ b/sys/src/games/dmid.c @@ -8,7 +8,7 @@ typedef struct Opl Opl; typedef struct Chan Chan; typedef struct Trk Trk; enum{ - Rate = 44100, + Rate = 49716, /* opl3 sampling rate */ Ninst = 128 + 81-35+1, Rwse = 0x01, @@ -236,7 +236,7 @@ setoct(Opl *o) e = freq[n] + (d % 0x1000) * (freq[n+1] - freq[n]) / 0x1000; if(o->c->i->fixed) e = (double)(int)e; - f = (e * (1 << 20)) / 49716; + f = (e * (1 << 20)) / Rate; for(b=1; b<8; b++, f>>=1) if(f < 1024) break; diff --git a/sys/src/games/opl3/opl3m.c b/sys/src/games/opl3/opl3m.c index 3d74e9e65..17dbae1e5 100644 --- a/sys/src/games/opl3/opl3m.c +++ b/sys/src/games/opl3/opl3m.c @@ -6,6 +6,10 @@ void opl3out(uchar *, int); void opl3wr(int, int); void opl3init(int); +enum{ + OPLrate = 49716, /* 14318180Hz master clock / 288 */ +}; + void usage(void) { @@ -16,15 +20,18 @@ usage(void) void main(int argc, char **argv) { - int rate, r, v, dt, fd; + int rate, n, r, v, fd, pfd[2]; uchar sb[65536 * 4], u[5]; + double f, dt; Biobuf *bi; fd = 0; - rate = 44100; + rate = OPLrate; ARGBEGIN{ case 'r': rate = atoi(EARGF(usage())); + if(rate <= 0 || rate > OPLrate) + usage(); break; default: usage(); @@ -35,14 +42,41 @@ main(int argc, char **argv) bi = Bfdopen(fd, OREAD); if(bi == nil) sysfatal("Bfdopen: %r"); - opl3init(rate); - while(Bread(bi, u, sizeof u) > 0){ + opl3init(OPLrate); + if(pipe(pfd) < 0) + sysfatal("pipe: %r"); + switch(rfork(RFPROC|RFFDG)){ + case -1: + sysfatal("rfork: %r"); + case 0: + close(0); + close(pfd[1]); + dup(pfd[0], 0); + execl("/bin/audio/pcmconv", "pcmconv", "-i", "s16c2r49716", "-o", "s16c2r44100", nil); + sysfatal("execl: %r"); + default: + close(1); + close(pfd[0]); + } + f = (double)OPLrate / rate; + dt = 0; + while((n = Bread(bi, u, sizeof u)) > 0){ r = u[1] << 8 | u[0]; v = u[2]; opl3wr(r, v); - if(dt = (u[4] << 8 | u[3]) * 4){ /* 16-bit stereo */ - opl3out(sb, dt); - write(1, sb, dt); + dt += (u[4] << 8 | u[3]) * f; + while((n = dt) > 0){ + if(n > sizeof sb / 4) + n = sizeof sb / 4; + dt -= n; + n *= 4; + opl3out(sb, n); + write(pfd[1], sb, n); } } + if(n < 0) + sysfatal("read: %r"); + close(pfd[1]); + waitpid(); + exits(nil); }