summaryrefslogtreecommitdiff
path: root/sys/src/games
diff options
context:
space:
mode:
authorqwx <devnull@localhost>2021-05-05 16:57:19 +0200
committerqwx <devnull@localhost>2021-05-05 16:57:19 +0200
commit7048f1ca111fb8a2898039f4e9cd255ec3cd42b2 (patch)
tree0455a3ba7bd295fa525cde9e2e41de2f510961a4 /sys/src/games
parentebaddcf030d195e393fdb6308e107fa4e17b8867 (diff)
games/opl3: use correct sampling rate
games/dmid uses the same sample rate as the chip for music, but other applications do not. opl3 and its older version opl2 (not in 9front) read an input stream of commands in basically IMF format, something used in other id Software games and some others, which assumes a given input sampling rate: 700 Hz for Wolfenstein 3D music, 560 Hz for Commander Keen, 60 Hz for Ultima 6, etc. The opl3 emulation on the other hand is not really intended to run at a sampling rate different that the chip's 49.716 kHz sampling rate. Previously, we assumed it runs at 44.1 kHz and just used the input rate as a divisor to get the number of samples per delay tic. From what I understand, the correct way to use it for accurate emulation is to run the opl chip emulator at its intended sampling frequency, then downsample to 44.1 kHz. This means better output but more code. The alternative is to basically do the same as before rev 8433, except with no buffering, but at accuracy/quality loss. This change implements the former and just forks pcmconv to deal with resampling.
Diffstat (limited to 'sys/src/games')
-rw-r--r--sys/src/games/dmid.c4
-rw-r--r--sys/src/games/opl3/opl3m.c48
2 files changed, 43 insertions, 9 deletions
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);
}