How I Crafted my audio (looping) library: Planning the architecture

Mikołaj Lemański
3 min readMay 26, 2024

--

The Vision

In my spare time, I play musical instruments and create music. Sometimes, when I want to practice or improvise over a part of a song, I want to loop that part. I was already developing an app to do this on Android, and I thought about creating an actual library that others could use. With that in mind, I started to think about how I can make it useful to as many people as possible.

The goal of providing the library to a broad audience made me think of this cool new tech that is Kotlin Multiplatform. With the rise of Kotlin Multiplatform and its truly native nature on all platforms, many possibilities have emerged for creating fast and efficient libraries that can be utilized by developers from various backgrounds.

In the development world, language often creates barriers. Developers tend to hermetize their environments based on the languages they use. Kotlin provides a solution by creating APIs that can be used by developers from various spaces, from native to web. This is possible with the use of Kotlin Multiplatform, which allows for truly native performance and shared business logic across different platforms. I recommend reading this article on what makes Kotlin Multiplatform genuinely native.

The plan

If I have seen further, it is by standing on the shoulders of giants.” — Sir Isaac Newton

In programming, we generally try not to reinvent the wheel. This principle is a great cue not only in the realm of computer science but also in day-to-day life. There’s no need to make things harder than they need to be.

The first thing I did was find a library that could already handle the basics: I/O, audio recording, and audio playback. I opted for C libraries because they are likely the most mature and also because C is the most interop-friendly language. You can call C from virtually any other language. It took a while, but at last, I found my holy grail: the miniaudio library. It is a single-file audio playback and capture library written in C. It’s designed to be simple and easy to use, making it perfect for my needs.

Next, I needed to learn how to do interop with C from other languages. On iOS and Linux, it’s quite simple. You can call the native methods from your code directly. The actual hard part was C interop with Java. Initially, I planned to use JNI (Java Native Interface), but it was not suitable for the prototyping stage of my trials. Then I found JNA (Java Native Access).

JNA is slower than JNI but much simpler to use and fits perfectly for fast-paced development. It allows Java code to call native C libraries without writing anything but a simple Java interface. I planned to return to JNI once I had a stable API.

Here’s a basic concept of the library architecture:

Context diagram of audio (looping) library

The top layer will hold the module responsible for interacting with the engine and also the Digital Signal Processing module, which will allow for transformations of the audio data. It will also define the interface that should be implemented on each platform in order to support it.

The middle layer will be responsible for communicating with the C library on each platform. For example, on Android, it would be the JNA part.

All this lies on top of the native module that is written in C and exposes the interface that allows for basic operations such as audio recording and playback and file I/O.

The execution

Now with a solid plan in mind the time for executing it has come. I’ve created a kotlin mutliplatform project and added the C engine module. I’ve set up my gradle files and started writing the wrapper code for all the platforms that I support.

In the next parts, I will focus on how I implemented the C interop on each supported platform. I will also touch on the topic of creating DSP utilities in Kotlin.

--

--

Mikołaj Lemański

Programmer and musician blending tech and creativity. Medium is my personal space, where I share insights in hopes that others will find them useful.