Musings from my desk

Loop Supreme, part 11: Exporting stems and changing inputs

2022-12-08 10:19:45 +0000 UTC

This is part 11 in a series about building a browser-based audio live looper

Goal

Implementation

Lots has happened since the last update! I found myself going down rabbit hole after rabbit hole. As I got the two main “goals” finished, I realized that the most important aspect of the app - recording audio - was still working very poorly. There was a bizarre attenuation effect on the audio recorded from my audio interface that made the app effectively unusable. Trying to fix this led me down several refactors that were ultimately unnecessary. Bizarrely, the fix was extremely simple and unrelated to any code I actually wrote. Rather, it was an implementation detail of the Web Audio API.

Another bug I noticed was that, even with the timing correction I made in #23, loops were experiencing noticeable drift over the course of several loops. I decided that the most pragmatic solution was to stop using the loop parameter on the AudioBufferSourceNode and simply restart the buffer on each loop start event. This seems to introduce a bit more clipping on loop start/end, but for the moment it’s a worthwhile tradeoff for accurate timing.

In addition to fixing those two major bugs, I did add the ability to export stems, and also change inputs on a track. Both of these were important. Changing inputs is important because the “default device” is not always what a user might expect in a browser. And exporting audio is important because it’s fun to have artifacts of what you record! I used another Worker for the exporter, and copied some code from a Google project to write the WAV file blob, so it was pretty easy to implement. The hardest part was learning that an AudioBuffer couldn’t be serialized directly in a Worker message, so I had to extract the Float32Arrays from the channel data directly to export it. I’m not convinced this was worth it, because writing the WAV file is pretty fast and could probably happen on the UI thread, but it doesn’t hurt anything and now it’s done 🤷🏻. Adding the option to change inputs was also pretty straight forward. The Web Audio API provides an option to enumerate devices, so it was just a matter of setting the MediaStream when the device changed.

There were lots of minor bug fixes and improvements, but those are the big updates to cover! I tried to keep notes in the PRs for each specific fix that I made.

Learnings

State of the app

Next steps