To Top
# Radio Project In August 2019 a friend suggested I keep a playlist of music I've got from various sources, broadcasting as a radio as a project, and initially he helped start the project up. ## ICECAST We went with writing the server in Node.js, and then of course the frontend would be a JS powered web page. He suggested we use the Opus audio codec due to the great audio quality even at low bitrates (we decided to go with 96kbps audio). So initially I wrote some scripts to firstly normalise the audio using [ffmpeg-normalize](https://github.com/slhck/ffmpeg-normalize), and then converted it all to Opus. Initially all the server did was serve some basic static files which just played audio from an [ICECAST](https://icecast.org/) server, and the server took minimal configuration, just a basic [liquidsoap](https://www.liquidsoap.info/doc-1.3.3/) script to get the audio setup, but this wasn't a particularly interesting take on it, and we decided to scrap that and make our own streaming solution. ## Simple Opus Streamer The first major challenge was how exactly were we going to stream it? We wanted it to not use any pre-existing technologies in this case, so I began experimenting with splitting opus files up and sending them over websockets and playing them consecutively. This sort of worked. But the issue was there was always a small clicking sound between the consecutive pieces of audio, but I figured that it'd go away if I instead parsed and played Opus packets one after another. My friend used [uWS.js](https://github.com/uNetworking/uWebSockets.js/) to setup websockets, the file server and streaming logic, and introduced me to [pm2](https://pm2.keymetrics.io/), which remains super helpful even now just to manage various long running processes (like the server here). He also brought up how the file format was documented in various RFC's, specifically [3553](https://datatracker.ietf.org/doc/html/rfc3533), [6716](https://datatracker.ietf.org/doc/html/rfc6716) and [7845](https://www.rfc-editor.org/rfc/rfc7845.html), and wrote a parser which made use of Node.js's Buffer extensively, and sent the data to the client which received on a worker thread, concatenated data as needed, posted to the main thread, decoded using the WebAudio API, and finally played it. This approach however, had the same clicking issue as mentioned before. ## TypeScript Rewrite After taking a break in December, I decided to have a go at parsing the Opus data myself, but this time in TypeScript - I specifically did it more as a challenge to myself. That being the case I decided to try writing it entirely in promise-style code, since the default `read` method in the Node `fs` module returns a promise. This took a while, but I finally managed to get it working, but this time it did the data processing on a worker thread and posted to the main thread, which had a much more simplistic version of the websocket/file server/streaming logic stuff that my friend setup in plain JS earlier, but again in TS. This, quite unsurprisingly, had the same clicking issue as before (since I'd just rewritten it in TypeScript), so I tried various methods to try and rid the project of it, notably having a look at cross fades, initially just trying default ones provided by the Web Audio API, but eventually trying `setValueCurveAtTime` and specifying my own curve (and similar methods suggested to me by my friend), and trying to implement some variaton of an equal power crossfade, before it became apparent that this was an issue with how we were dealing with the files. At this point this had become a project I was somewhat devoted to, and my friend handed it fully over to me. ## WebAssembly The Opus decoder maintains some internal state while going over a file, so if it just started at the beginning of some chunk of audio, it would possible result in slightly different output than if it was continuous playback over one chunk, so I tried to concatenate to audio buffers, and use the crossfade methods from earlier to interpolate into them. This nearly got rid of it, but the click was still just about audible, so I decided to look into a more complete solution - just using WebAssembly. I initially made my Node.js code output a bunch of chunks of audio (which were at this point basically just Ogg pages without headers) to play around with, and used the [libopus](https://opus-codec.org/release/stable/2019/04/12/libopus-1_3_1.html) C library to write a simple decoder, which parsed the Opus packets in the Ogg pages, and split the packets into frames. Since we used the default Opus audio settings in ffmpeg, the audio was CBR and each frame was a predetermined 20ms long, so after some messing around I managed to get the C program I wrote to output a bunch of PCM, and when I stitched those chunks of PCM together - there was no click between them at all. Now I only had to compile this to WebAssembly, invoke it from there, and finally play the PCM (or so I thought). After a couple of weeks of asking around on the [Emscripten Google forums](https://groups.google.com/g/emscripten-discuss), Emscripten being the compiler I was using to compile C to WebAssembly, I finally managed to compile the `libopus` library to WebAssembly, and by using stuff like `Module._malloc` and `Module._free` in my JS, managed to get the audio decoded into raw PCM. The issue was that the output audio didn't sound correct at all, and after searching around some more I realised it was because the PCM was dual chanelled, so it was linear interleaved PCM, so, with help from [this](https://stackoverflow.com/questions/32128206/what-does-interleaved-stereo-pcm-linear-int16-big-endian-audio-look-like) StackOverflow post, I managed to get it to play correctly too. I put up a demo of this [here](https://github.com/aliabbas299792/wasmOpusDecoder). ## Problems By now it was July, and I thought I was done with this, but after having it live for a couple hundred hours, I noticed that there was probably a memory leak in the server somewhere, so I tried to debug this, but despite using some common Node.js debug tools to find memory leaks, I was unable to find it (also at this point I wasn't too keen on spending hours pouring over TS code). I decided to abandon the Node.js server and rewrite the server in C++. ## Intermission After taking a break of a couple of months for exams, I decided to finally start on writing the server again, but I knew virtually nothing about how to write socket servers in C/C++, but from what I did know, I knew to avoid [ `select`](https://man7.org/linux/man-pages/man2/select.2.html) on Linux, so I made a small project to learn how to use [ `epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html). I went and put it in [this](https://github.com/aliabbas299792/cpp_server) repository. ## liburing Just as I was about to write my server using [ `epoll`](https://man7.org/linux/man-pages/man7/epoll.7.html) another friend brought up [`io_uring`](https://kernel.dk/io_uring.pdf), which I became somewhat interested in and decided that I was going to write it using this instead. Except of course I didn't know how to use this either, so I followed some tutorials [here](https://unixism.net/loti/tutorial/cat_liburing.html) to get familar with it and comitted this example stuff to [this](https://github.com/aliabbas299792/practiceCPP) repository, and eventually got familar with `liburing` a wrapper library around `io_uring`. ## Web Server It was now December 2020, over a year since the project had initially started, and I was fairly confident I now had the knowledge required to rewrite the audio streaming server in C++ (finally), so I got started in [this](https://github.com/aliabbas299792/web_server) repository. I made use of a thread safe concurrent queue from [here](https://github.com/cameron314/readerwriterqueue), [liburing](https://github.com/axboe/liburing) and [WolfSSL](https://github.com/wolfSSL/wolfssl) for TLS (I happened to be using it for some embedded work at my internship at the time, and had spotted some functions which seemed really convenient to use in an asynchronous context, so decided to use it over OpenSSL). After working on it until about July (I also had to take a 3 month break), I finished programming the server, which now supported TLS, (part of) HTTP 1.0, (a good chunk of) the WebSockets protocol, multiple threads, a cache for the HTTP requests, as well as a central thread which could communciate with the threads, and allow communication between threads if desired. I'd made it such that it'd be easy to plug in a module which would communicate with the TCP threads, since it could just plug in some messages into the central thread's event loop, so that's exactly the approach I took with finally programming the audio server component. ## Audio Server I added in the WASM Opus decoder code that I made last year to the files being served publically by the server, and, using the TS code from last year as a template, I wrote an Ogg/Opus parser, which would periodically broadcast chunks of audio, which would be seamlessly played by the web based player. Additionally this time I added in the ability to specify the radio 'stations' in a config file which would be read from at startup (and serves from a folder specified in the config file), as well as the ability to select the next track on the radio. Through the entire C/C++ part I had to deal with memory leaks and networking issues a great deal, which are documented as the commit messages in the repositories I linked, and even found and fixed a memory leak in the frontend just a week or so ago (August 2021), but now I've managed to complete the project. 