This is a little devlog about how I added an audio recorder to spag.cc, which is a website pastagang use to upload samples for our jam sessions. You can see my code in the commit on github.
But why?
I was jamming with the 'gang, and I wanted to upload a little sample of me saying "no more ideas!" to use in the music. I needed to record my voice, and upload it to spag. The issue was my computer didn't have an audio recorder within arms reach. I think I had OBS, but I didn't think to use it, and it's kind of overkill.
Instead I remembered my phone had an audio recorder, because I use it for voice memos. I recorded the sample in that, and stuck it in a discord group chat so I could get it on my computer.
Then I downloaded it and found it was some weird format I'd never heard of (.m4a
, which is not uncommon so that's on me). It probably would have worked in spag but I didn't know if my browser had encoders for it. I hastily googled "m4a to mp3 converter" and several adverts and upsells later I had my mp3, which I finally uploaded to spag.
If you haven't got bored and wondered off, you can see that was quite long, and boring, and unmusical. It was not jammy. In a real jam, I could have just sang, or shouted to my mates in the room!
If only spag.cc had an audio recorder built in, I thought. So I did something about that.
So I did something about that
After a google I found this browser based audio recorder, and had a read of its code. It seemed very complicated, and when I looked up the library it didn't appear to be maintained anymore. Ah well. I decided I couldn't be arsed and just edited the spag.cc page to include a link to it.
But that same night I came across this Mozilla hacks article by Soledad Pendés, which described how to use the MediaRecorder to record almost anything in browser. Damn. That's exactly what I need! The first example is even a minimal voice recorder. I don't even need to read the whole thing!
A few weeks later (today, Saturday the 8th of February) I found myself with a few hours to spare, so I cribbed Mozilla's example code and implemented the audio recorder in spag. It took about 30 minutes to get working, most of which was copying the code out by hand so I learnt it.
The next stage was working out how to get it as a file(?) that could be uploaded to the val.town backend thingy spag uses. I don't know what val is, but it looks like Todepond used it to write an api for uploaded and downloading files, and getting the list of uploaded files (and deleting them).
I first tried converting my recording's blob into a javascript File object, and then stick that in the input element of the form, except you can't do that. Uhh. Then I noticed that spag doesn't even use that (really), it instead detects changes, sticks files in a preview image/audio element and then sends the src
attribute of that off to val. Cool, I can just set the src
to my recording!
Not quite! I was missing the vital step that all the uploads were first being converted to data URIs before becoming the src element. I had no idea what this was, but I googled it and apparently it's just encoding the whole file as a bytestring. Huh. I did that to my recordings and they started to upload too, yay! Everything was working!
Or was it?!
Hahhaha, some of my uploads (quite a lot of them) were failing actually. I thought I had all sorts of problems with whether my file was an audio stream or audio file, whether they were encoded correctly, what format they were in and so on. It turns out that that was all correct. The real issue was...
I had been uploaded them with filenames which contained spaces (or other fun characters like emojis).
Turns out spag can't work with those, and so my val requests kept 404ing. I added some encodeURLComponent
and decodeURLComponent
(one each) and then it all worked perfectly! That took me about an hour of troubleshooting to spot... whoops.
Is that all?
Yup! I also added some text saying what I did, tidied everything up into the main script, refactored out the url data reader thingy because we don't need to make a new one for every upload/recording and made the buttons grey out when they were not clickable.
All in all, this took me about 4 hours of work, including writing this blog post and uploading everything to the github. Quite happy with that! Another project finished.
You can try out the recorder at spag.cc, try singing into it, and then using that as a sample in spagda
in nudel.cc (which is where the pastagang jams happen). We'd love to see you there :)
What have I learned?
- You can capture audio, video or whatever else in browser really easily!
- If you are getting 404 out of your api, always check if the queries are encoded correctly
- DataURLs are just a whole file, encoded as a bytestring with some text at the start saying what type of data it is.
What haven't I learned?
- What javascript promises are and how they work. There is one in this whole project, and Mozilla wrote it for me :))
Surprise additional learning!
Okay so I deployed this and tried it on spag.cc and it didn't work! Oh no! It turns out that spag.cc is a val which contains an iframe pulling in the code from the github. Iframes don't let you do just anything, y'know! They have some standards. You need to tell the iframe that the content inside it is allowed to ask for the microphone. You do this by adding a allows: "microphone"
attribute to the iframe element.