blog
Weekly notes 2 โ€” Volume management & typos
in

Firefox

This week I didn’t write too much code, but did some fixes nonetheless.

Management of the volume of an audio stream on Windows

I started the week with an in-depth exploration of volume handling for an audio output stream on Windows. There are four volume stages when you play anything on Windows, that’s maybe a bit much, but there is:

  1. Often software developer implement a “volume” slider in their program. Usually this is simply a multiplication between the audio frames and a number between 0 and 1 (or 1.25 in the case of VLC, and that’s really convenient)
  2. Then there is a server-side input gain in the Windows mixer. This is what an program should use, instead of the step in 1. 1 This is the IAudioStreamVolume interface. This volume is per stream.
  3. Then there is a session volume, which is the volume of all stream of a particular program. For example, Firefox is multiprocess, for security reasons, but there is only one volume slider in the Windows panel (SndVol.exe), that controls the volumes of all streams being played back. This is ISimpleAudioVolume. I’m not entirely sure why it’s simple.
  4. There there is the global system volume (that you adjust in the systray or with your media keys).

All this to change the volume of an audio stream. The more you know ๐Ÿ™ƒ.

What happens when you forget a pair of brackets

Another interesting point this week was that I had written a web-platform-test with this line of code, with b an Uint8Array of a few megabytes, maybe from a fetch() call or something:

var url = URL.createObjectURL(new Blob(b, ["audio/ogg"]));

Fairly straightforward, this creates a Blob from a bit of memory. Except it doesn’t, it crashes Firefox and Chrome with an Out Of Memory error.

What happens is that the Blob constructor is supposed to be an array of parts that will compose the Blob. If you pass directly an iterable, then it will concatenate all the elements of this iterable, and return a Blob.

But because b is iterable (it’s a Uint8Array after all), and because of type coercion of WebIDL, that describes how conversion should happen, this Uint8Array is first converted to an array of Strings of a single character, which is the ASCII value for this byte. Because string objects are more than a byte in size (they include the size, etc.), this is already a waste of memory.

Chrome and Firefox don’t concatenate eagerly (Safari seem to), so you have thousands of those Strings. Later the browser creates objects to read from those Strings, again one per byte of the initial array, and those objects are quite a bit bigger than a single byte again, and this crashes with an Out of Memory error.

In the end, I was trying to make a Blob from a 500kB buffer, and this froze the browser and used about 350MB (megabytes!) of memory. You don’t even get the correct result in the end. All the details are in https://bugzilla.mozilla.org/show_bug.cgi?id=1702447#c6, and the correct code is:

var url = URL.createObjectURL(new Blob([b], ["audio/ogg"]));

the pair of [] being the key here. Quite a big footgun.

Specifications

Lots of work on Web Codecs and the Web Audio API this week. The former is cruising along nicely at a rather high speed. I’ve continued my work around timestamps and memory of the last week, this time diving into system APIs for cross-process memory sharing, possibly backed by GPU buffers, which is a space that is moving rather often apparently.

I’ve merged the very last PR to the Web Audio API, here is a short clip of that:

Apparently we’ve done those steps about 946 times since we started using GitHub, but it’s now functionally done. I assume some editorial pull-requests will follow but the normative text won’t change.

The video is not very interesting, but I felt like recording this moment. I will probably print a hard copy of the final version of this specification, keep it on a shelf, and have a look at it when I retire or something.

Personal

Not much again. This times, France is in lock-down for real, but apparently not quite? Unclear again.

I bought a ton of music because it was Bandcamp Friday and my budget for music, usually dominated by concert tickets, is going back into records and buying releases on Bandcamp. What I’ve bought this time (and all previous times), is available at https://bandcamp.com/padenot, it’s fairly diverse but mostly electronic music, with some black metal and other types of metal. The beauty of it is that you can listen to all the releases and then maybe buy a couple that you like. Most of the money goes to the label and artists, in between 80% and 93%, and quite a few labels are not taking their cut during those Bandcamp Fridays, so even more go to artists, that can’t really do shows to earn a living.

I’ve bought quite a few releases of Egyptian music, especially from Cairo, and quite a few Gqom releases from Japan and South Africa, some footwork stuff, and then the traditional (for me) techno/jungle/uk things/dubstep releases. Have a listen to All Caps by ZULI if you like a good chopped amen break:

and flashes in time by Fauzia if you don’t:

I like both.


  1. 2 is really better than 1, in the case of you pause the audio stream, change the volume or mute, and then unpause. With 1 you have a short period with the old volume and then the new volume is reflected, because there is frequently some data in the sound server with the old volume that has been pushed already. With 2 the volume is applied by the mixer so you potentially save some CPU and touch less memory, and you don’t have this problem. [return]