Zumi's Scratchpad

I gave myself an existential crisis by making a higher quality MSGS

updated on

So I found this video which claims to use a patched version of the Microsoft GS Wavetable Synth (MSGS), a.k.a the default Windows MIDI synth, a.k.a basically synonymous with MIDI at this point. Here the guy says that they've patched the driver to run at 48000 Hz instead of the 22050 Hz it came with. I looked through this guy's social media links and GitHub to find nothing of the sort, so I did what I always did and went "fine, I'll do it myself."

Enhancing MSGS

Okay, where do I start? For one, I'll have to figure out where the driver even is. As Windows XP is frequently said to be the best incarnation of MSGS (the newer versions apparently is said to have nerfed it for whatever reason), that's what I'll be using as a base.

From Device Manager, I looked for the Microsoft Kernel GS Wavetable Synthesizer. I might have needed to load a MIDI for it to appear, even with "Show hidden devices". Going to Driver Details I'd find that it's in C:\WINDOWS\system32\drivers\swmidi.sys. So I went there.

I extracted the file off the disk and opened it in Ghidra. As it is, it's just a bunch of meaningless data. Fortunately, there's always the public debug symbols for Windows that can help me—someone archived the ones for Windows XP.

Ghidra decompilation window showing the ControlLogic constructor.
yoink

Next, I did a Scalar Search for 22050, and it does seem to be used in a couple of routines. At this point I barely have any clue what I'm doing so I just went in and changed stuff to 44100 for a start.

Ghidra search results for 22050.

This ControlLogic::SetSampleRate function is called by ControlLogic's constructor, so that must be where it was determined. The decompiler says this 22050 (0x5622) value is set in two different places, but the disassembly says this value ultimately comes from the EAX register, which was set earlier. So I changed that to 44100 (0xac44).

Ghidra source view with the 22050 values highlighted

Upon saving and then replacing the driver in Safe Mode, Windows complained about not being able to load the driver, giving me a Code 39. Because this is Windows, no amount of search is going to help me on that one.

Until I realized that the PE checksum was a thing, and it wasn't adjusted yet. With the help of this FixPEChecksum, I got it working!

…Almost. Okay, what do I do now? Through certain hints, apparently I still had to change the audio format it's outputting through. It's a table (a bunch of data) called PinDigitalAudioFormats. There's two values near the end that is the same 0x5622 number I'm looking for. Except it's backwards, because in the actual data, this is little endian.

Ghidra disassembly view with the 22050 values highlighted

Bingo!. Compare that tiny bit to the vanilla output. Doesn't that sound like you unplugged your ears and cleaned out your ear wax? It's no SC-55, but it's close. And you get the bonus of preserving the quirks of this particular synth!

I managed to take it up a notch up and matched the guy's 48000 Hz sample rate, except that I needed to change ControlLogic::SetSampleRate because it clamps the sample rate to 44100, indicating a design assumption. Changing this clamp value "works", but some other assumption I think would be violated somewhere else. But again, it works.

But at what cost?

…At least, that's what I thought at first. It was funny listening to tracks through this spruced-up MSGS as if it had come out of some other synth with a soundfont derived from GM.DLS.

I was then shown a couple of tracks that had pushed MSGS to its limits—and these were tracks that do not work anywhere else than with MSGS.

Strobe's track in particular exploits MSGS's aliasing to produce something that sounds a bit like extratone. Sound warning—and I had to abbreviate it—in order: 22khz, 44khz, and 48khz. The tone is heard most clearly for the 22khz version, still discernable but more painful for the 44khz version, and completely ruined for the 48khz version.

In the case of General Serum, it's not ruined that much (the 44khz version is used here), but there is enough differences in mixing/"EQ" resulting in less punchy bass. The crunchiness of the original synth seems to have helped it.

Those two examples… made me realize something.

This isn't MSGS anymore.

This is a small modification, and yet I have taken away a differentiating characteristic of this synth.

There exists MIDIs specifically making use of this quirk, and here I am "fixing" it.

The point of those MIDIs marked specifically as "MSGS MIDI" is that they only work with MSGS. On any other synth, it won't sound as magical—it won't have the "intended sound". At best, some instruments will be wrong, or the mixing will be different (as shown here). At worst, it will completely break or give your synth a heart attack trying to process it.

Effectively, I had made this version of MSGS cease to be MSGS.

The low sample rate is part of MSGS, and by removing it, it simply isn't MSGS anymore.

I'm reminded of Kyle's custom 2A03 chip. In that video, he explained how he fixed the triangle channel of the NES's 2A03 so that it's an actual triangle wave. It's an interesting and fun experiment, but a quirk of the NES had changed—it's possible that some track depended on the steppy wave of the 2A03, and now that it's smoothed out it doesn't quite "work" anymore, because it isn't a "2A03" anymore. In that case, because the NES and NES music means something specific, the modified chip (even if it's just an emulator patch) is "at fault" here.

Meanwhile, MSGS is one implementation of the overarching MIDI standard. MIDI and MIDI music means something more general. If MSGS specifically is exploited, it's an MSGS MIDI—different in category than a regular MIDI (good in all synths) and a MIDI for some x soundfont or other driver. I think MSGS MIDIs are more impressive because it:

But those tracks break with my modifications to MSGS. It's not MSGS anymore.

My so-called ““““““MSGS”””””” tracks… don't. Little to nothing is sacrificed if I play those tracks in any other MIDI player that supports GS properly. The MIDIs don't exploit anything in particular but the most basic of MSGS stuff, so it isn't as "exclusive". In this case, it's my tracks that are "at fault", not the synth per se—it's just another MIDI synth now.

In contrast to my previous MIDI post, this may be the usual case of "I think I knew something, but I actually didn't" (Dunning-Kruger, valley of despair, etc.), but the conclusion is that most of my ““““““MSGS”””””” MIDIs don't deserve that distinction yet. Because they are "not exclusive" to MSGS, because they don't break outside of MSGS. (So they're not really MSGS MIDIs are they?) As a result, I have stripped the "MSGS" qualifier on most of my MIDI uploads on YouTube. The "Carve Your Own Path" cover, I feel isn't compromised that much, but still will break on other synths, so I think I'm gonna keep it in.

There's also a reason they categorize the Commodore 64 SID as two different variants (generally speaking): the 6581 which has a volume bug exploited for samples (among other things), and the 8580 which fixed the bug and consequently broke samples. A track made for one may have an unintended sound when played with the other.

Maybe I'm wrong about this, and that extremely talented people, along with the specific circumstances of this synth, have massively warped my expectations to the point of questioning myself. I've "known" for a bit that I just make average MIDIs for your average synth/driver, but I think it's only now that I realized what that actually means.

I'm fine.

Okay, but how do I try this?

Oh, uh. Right.

You don't need a debugger for this because all that's been done, but you'll need a hex editor. Got your swmidi.sys (from Windows XP SP3 32-bit, specifically) ready? Good.

The 44.1khz version

  1. Change address 0x138 from 63 79 to CA 7B.
  2. Change addresses 0x2AF2, 0xA75C, and 0xA760 from 22 56 to 44 AC.

The 48khz version

  1. Change address 0x138 from 63 79 to B9 B8.
  2. Change address 0x2874 from 44 AC to 80 BB.
  3. Change address 0x287E from 0A to 09.
  4. Change addresses 0x2AF2, 0xA75C, and 0xA760 from 22 56 to 80 BB.