From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=0.2 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED autolearn=no autolearn_force=no version=3.4.4 Received: (qmail 16690 invoked from network); 15 Feb 2021 10:37:13 -0000 Received: from 1ess.inri.net (216.126.196.35) by inbox.vuxu.org with ESMTPUTF8; 15 Feb 2021 10:37:13 -0000 Received: from mail-pl1-f170.google.com ([209.85.214.170]) by 1ess; Mon Feb 15 05:25:36 -0500 2021 Received: by mail-pl1-f170.google.com with SMTP id s15so3466126plr.9 for <9front@9front.org>; Mon, 15 Feb 2021 02:25:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mforney-org.20150623.gappssmtp.com; s=20150623; h=message-id:to:subject:from:date:mime-version; bh=8j9CBgoN7vovokbq0lYsOnE+tJvZqFehriaEiC+4RAo=; b=U2a6y0AbkRRVLzjZEzp0LpH2NGp+x6son6hBDV8gq3qmDZp/krry6LHaPK6laMuPS+ 2VFUaD9lq7NF4eHjLg+1P6U2hw+7/iK6qE32McEbcpDC1eJd+n1b8U1v7SJxoBOGoKn3 k8YZzghgOVG8EUqnXrl6bF3M7ZOeHFteX5e1e5KY4YxlwvqIQCLqB5k3aCk5t063uDux 3/ewST8lUM+oVykbF4wblkqcSmp+Abs2QJrAdHNLt1GREE0jpWk3/6HasOvhmDKfopTA PH8+ojzpzrrMhnMzP6TIAakVPB9DYosByNDusbmKXZdDBTGlHQKM/CdW1nkPbDLFSf9D 66Hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:to:subject:from:date:mime-version; bh=8j9CBgoN7vovokbq0lYsOnE+tJvZqFehriaEiC+4RAo=; b=f+0ZnI56Ikt7DDT0jAcqd5BTI3/h0NjlAcmzqASqDNd10iX2DbKoYBq5I7e+r6Qo26 AM6rJ5dOi0COHO16Ctr9peeUkQqU7BR1wBq9S03ddOqvlPSZTdZkVTWHqFOOhtfKMTf8 hbOeFL48YUmGU9KBs0PXTCpGLssYvb+izzxrFAvW3+Tm5nzNVTzdFhJFyt8wUatrnpbY hi/pO3F5L88HxKDubFUyNi9q4aYSNcPrwTbkBA6HYFv55rxepV2aYohHKzlkSbRKec2M esc/YYxm8W7/a0LSZQG/hY6yvS3F1r9Knuux7ZfODeimo7tapMspfOQGx70BJZ79N17S pvKQ== X-Gm-Message-State: AOAM5328Z52BFqOzVRUC7tXoZl9xibJUH/iiwNRsIfwrPOo0Yj0n1KD4 Gd66XcBXlPIXSgxsaEVHXezmIwAyG+CMnanvnCg= X-Google-Smtp-Source: ABdhPJwRe06r15BbpmVTbE4LnIDEEpaQaCdcDpktM4sij4V4JVp7W3lG8EoO+qULRdZVLQ+h7ZcdNA== X-Received: by 2002:a17:90a:fb96:: with SMTP id cp22mr14980512pjb.131.1613384714425; Mon, 15 Feb 2021 02:25:14 -0800 (PST) Return-Path: Received: from arrow.hsd1.ca.comcast.net (c-73-70-188-119.hsd1.ca.comcast.net. [73.70.188.119]) by smtp.gmail.com with ESMTPSA id b14sm17436889pfi.74.2021.02.15.02.25.13 for <9front@9front.org> (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Mon, 15 Feb 2021 02:25:13 -0800 (PST) Message-ID: <71E80890743ACBD0D641DECE5D327028@arrow.hsd1.ca.comcast.net> To: 9front@9front.org From: Michael Forney Date: Mon, 15 Feb 2021 02:25:10 -0800 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="upas-gofyxhrgqexsrphybhkzkmdjsh" List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: lifecycle shader YAML over XML template singleton Subject: [9front] games/snes audio patches Reply-To: 9front@9front.org Precedence: bulk This is a multi-part message in MIME format. --upas-gofyxhrgqexsrphybhkzkmdjsh Content-Disposition: inline Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit I noticed that the audio of games/snes was pretty bad for most games I tried, so I did some bug hunting and found a few. Patches are attached. The main issue (patch 1) was incorrect BRR decoding for filter types 2 and 3, which is the sample compression scheme that the SNES audio chip uses. I also saw that the noise and echo features of the DSP were essentially just stubs. Patches 2 and 3 implement noise and echo respectively. Finally, I saw that it was using nearest-neighbor upsampling from 32 kHz to 44.1 kHz, which results in noticable aliasing. My first thought was to pipe the output through audio/pcmconv which has a high-quality resampler. However, games/snes uses audio write blocking to control the emulation speed, so using a pipe here was problematic. It would fill up the pipe buffer, then hang while pcmconv wrote the upsampled data to /dev/audio, then repeat. Anyone have ideas about a good way to reuse the pcmconv resampler for this (maybe split off into a library)? For now, patch 4 changes it to use linear interpolation, which is a big improvement and may be good enough. Patch 5 is just a style patch to use the enums already defined instead of hex constants in a couple places. Here are some samples demonstrating the specific problems: - BRR decoding https://mforney.org/misc/ct-122a-old.flac https://mforney.org/misc/ct-122a-new.flac - Echo https://mforney.org/misc/ct-206-old.flac https://mforney.org/misc/ct-206-new.flac - Noise (supposed to be wave noises) https://mforney.org/misc/ct-s02-old.flac https://mforney.org/misc/ct-s02-new.flac --upas-gofyxhrgqexsrphybhkzkmdjsh Content-Disposition: attachment; filename=0001-games-snes-fix-BRR-decoding-with-filters-2-and-3.patch Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit From f3f21968489011d0fee39a054c63c70f1a18bb92 From: Michael Forney Date: Mon, 15 Feb 2021 09:28:12 +0000 Subject: games/snes: fix BRR decoding with filters 2 and 3 s1 and s2 should store the last and next to last output, but were set in the wrong order, causing them both to be the last output. This breaks filters 2 and 3, which both utilize s2. diff a78495b8877d2a9e805d098b7963d3a3cf90c7c3 f3f21968489011d0fee39a054c63c70f1a18bb92 --- a/sys/src/games/snes/dsp.c Wed Feb 10 01:00:05 2021 +++ b/sys/src/games/snes/dsp.c Mon Feb 15 01:28:12 2021 @@ -275,8 +275,8 @@ d = (s16int)(clamp16(d) << 1); p->buf[p->bp] = d; p->buf[p->bp++ + 12] = d; - s1 = d; s2 = s1; + s1 = d; brr <<= 4; } if(p->bp == 12) --upas-gofyxhrgqexsrphybhkzkmdjsh Content-Disposition: attachment; filename=0002-games-snes-implement-DSP-noise.patch Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit From c9d42c085558d9ac0272cd3df4e1868a2c410e46 From: Michael Forney Date: Mon, 15 Feb 2021 09:30:29 +0000 Subject: games/snes: implement DSP noise I'm not sure if this LFSR is the same one used by the hardware or is arbitrary, but it matches the noise sequence used by all other snes emulators I looked at. diff f3f21968489011d0fee39a054c63c70f1a18bb92 c9d42c085558d9ac0272cd3df4e1868a2c410e46 --- a/sys/src/games/snes/dsp.c Mon Feb 15 01:28:12 2021 +++ b/sys/src/games/snes/dsp.c Mon Feb 15 01:30:29 2021 @@ -6,7 +6,7 @@ #include "fns.h" u8int dsp[256], dspstate; -u16int dspcounter, noise; +u16int dspcounter, noise = 0x8000; static s16int samp[2], echoin[2]; enum { @@ -517,6 +517,8 @@ } if(dspcounter-- == 0) dspcounter = 0x77ff; + if(envyes(dsp[FLG] & 0x1f)) + noise = (noise << 13 ^ noise << 14) & 0x8000 | noise >> 1 & ~1; break; case 31: voice(0, 4); voice(2, 1); break; } --upas-gofyxhrgqexsrphybhkzkmdjsh Content-Disposition: attachment; filename=0003-games-snes-implement-DSP-echo.patch Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit From b78eaf915465b6d6d13f2dc36964c9aad9a2b76c From: Michael Forney Date: Mon, 15 Feb 2021 09:38:22 +0000 Subject: games/snes: implement DSP echo diff c9d42c085558d9ac0272cd3df4e1868a2c410e46 b78eaf915465b6d6d13f2dc36964c9aad9a2b76c --- a/sys/src/games/snes/dsp.c Mon Feb 15 01:30:29 2021 +++ b/sys/src/games/snes/dsp.c Mon Feb 15 01:38:22 2021 @@ -7,7 +7,9 @@ u8int dsp[256], dspstate; u16int dspcounter, noise = 0x8000; -static s16int samp[2], echoin[2]; +static s16int samp[2], echoin[2], echobuf[2*2*8]; +static u16int echoaddr; +static int echobp, echopos; enum { VOLL = 0, @@ -24,15 +26,17 @@ EFB = 0x0d, MVOLR = 0x1c, EVOLL = 0x2c, - EVOLR = 0x2d, PMON = 0x2d, + EVOLR = 0x3c, NON = 0x3d, EON = 0x4d, KON = 0x4c, KOFF = 0x5c, DIR = 0x5d, FLG = 0x6c, + ESA = 0x6d, ENDX = 0x7c, + EDL = 0x7d, NEWKON = 0x8e, INT = 0x80, }; @@ -96,6 +100,13 @@ return v; } +static void +spcput16(u16int p, u16int v) +{ + spcmem[p++] = v; + spcmem[p] = v >> 8; +} + u8int dspread(u8int p) { @@ -412,33 +423,57 @@ echo(int s) { static s16int echoout[2]; - static u8int fir[8]; + s16int *x; + s8int h; int a, b; + x = echobuf + echobp; switch(s){ case 22: - echoout[0] = 0; - fir[0] = dsp[0x0f]; + echoaddr = (dsp[INT|ESA] << 8) + echopos; + x[0] = x[16] = spc16(echoaddr); + h = dsp[0x0f]; + echoout[0] = x[2] * h >> 7; + echoout[1] = x[3] * h >> 7; break; case 23: - fir[1] = dsp[0x1f]; - fir[2] = dsp[0x2f]; + h = dsp[0x1f]; + echoout[0] += x[4] * h >> 7; + echoout[1] += x[5] * h >> 7; + h = dsp[0x2f]; + echoout[0] += x[6] * h >> 7; + echoout[1] += x[7] * h >> 7; + x[1] = x[17] = spc16(echoaddr + 2); break; case 24: - fir[3] = dsp[0x3f]; - fir[4] = dsp[0x4f]; - fir[5] = dsp[0x5f]; + h = dsp[0x3f]; + echoout[0] += x[8] * h >> 7; + echoout[1] += x[9] * h >> 7; + h = dsp[0x4f]; + echoout[0] += x[10] * h >> 7; + echoout[1] += x[11] * h >> 7; + h = dsp[0x5f]; + echoout[0] += x[12] * h >> 7; + echoout[1] += x[13] * h >> 7; break; case 25: - fir[6] = dsp[0x6f]; - fir[7] = dsp[0x7f]; + h = dsp[0x6f]; + echoout[0] += x[14] * h >> 7; + echoout[1] += x[15] * h >> 7; + h = dsp[0x7f]; + echoout[0] += x[16] * h >> 7; + echoout[1] += x[17] * h >> 7; + echoout[0] &= ~1; + echoout[1] &= ~1; break; case 26: a = (samp[0] * (s8int)dsp[MVOLL]) >> 7; b = (echoout[0] * (s8int)dsp[EVOLL]) >> 7; samp[0] = clamp16(a + b); - echoin[0] = (echoin[0] * (s8int)dsp[EFB]) >> 7; - echoin[1] = (echoin[1] * (s8int)dsp[EFB]) >> 7; + a = echoout[0] * (s8int)dsp[EFB] >> 7; + echoin[0] = clamp16(echoin[0] + a) & ~1; + a = echoout[1] * (s8int)dsp[EFB] >> 7; + echoin[1] = clamp16(echoin[1] + a) & ~1; break; case 27: a = (samp[1] * (s8int)dsp[MVOLR]) >> 7; @@ -449,9 +484,22 @@ dsp[INT|FLG] = dsp[FLG]; break; case 29: + dsp[INT|ESA] = dsp[ESA]; + if(echopos == 0) + dsp[INT|EDL] = dsp[EDL] & 0xf; + echopos += 4; + if(echopos >= dsp[INT|EDL] << 11) + echopos = 0; + if((dsp[INT|FLG] & 0x20) == 0) + spcput16(echoaddr, echoin[0]); + echoin[0] = 0; dsp[INT|FLG] = dsp[FLG]; break; case 30: + if((dsp[INT|FLG] & 0x20) == 0) + spcput16(echoaddr + 2, echoin[1]); + echoin[1] = 0; + echobp = echobp + 2 & 15; break; } } --upas-gofyxhrgqexsrphybhkzkmdjsh Content-Disposition: attachment; filename=0004-games-snes-use-linear-interpolation-to-resample.patch Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit From 909e181a497074c52e0be927c007cb6b6d66fc8c From: Michael Forney Date: Mon, 15 Feb 2021 09:40:11 +0000 Subject: games/snes: use linear interpolation to resample This is still not ideal, but noticably better than nearest-neighbor. diff b78eaf915465b6d6d13f2dc36964c9aad9a2b76c 909e181a497074c52e0be927c007cb6b6d66fc8c --- a/sys/src/games/snes/dsp.c Mon Feb 15 01:38:22 2021 +++ b/sys/src/games/snes/dsp.c Mon Feb 15 01:40:11 2021 @@ -64,13 +64,17 @@ static void audiosample(s16int *s) { + static s16int s1[2]; + stime -= 1<<16; do { - sbufp[0] = s[0]; - sbufp[1] = s[1]; + sbufp[0] = s[0] + ((s[0] - s1[0]) * stime >> 16); + sbufp[1] = s[1] + ((s[1] - s1[1]) * stime >> 16); sbufp += 2; stime += (32000<<16)/Freq; } while(stime < 0); + s1[0] = s[0]; + s1[1] = s[1]; } int --upas-gofyxhrgqexsrphybhkzkmdjsh Content-Disposition: attachment; filename=0005-games-snes-use-enum-constants-KON-and-ENDX-instead-of-their-values.patch Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit From 903b37d82b46bee9050d911e81235c4d4d25e4e2 From: Michael Forney Date: Mon, 15 Feb 2021 09:42:11 +0000 Subject: games/snes: use enum constants KON and ENDX instead of their values diff 909e181a497074c52e0be927c007cb6b6d66fc8c 903b37d82b46bee9050d911e81235c4d4d25e4e2 --- a/sys/src/games/snes/dsp.c Mon Feb 15 01:40:11 2021 +++ b/sys/src/games/snes/dsp.c Mon Feb 15 01:42:11 2021 @@ -124,10 +124,10 @@ if(p >= 0x80) return; switch(p){ - case 0x4c: + case KON: dsp[NEWKON] = v; break; - case 0x7c: + case ENDX: v = 0; break; } --upas-gofyxhrgqexsrphybhkzkmdjsh--