DEV Community

Cover image for I built a free Android soundboard that works with a External keyboard
anoop p
anoop p

Posted on • Originally published at anooptube.in

I built a free Android soundboard that works with a External keyboard

A few months ago I needed a simple soundboard for a live session. I wanted to trigger audio clips from a Bluetooth/External keyboard connected to my Android phone - just press a key, hear the sound, instantly.

Every app I found was either subscription-based, required an account, or didn't support physical keyboards at all. So I built CTunes.


What CTunes does

CTunes maps keyboard keys (A-Z and 0-9) to audio files on your device. Tap the on-screen button or press the physical key — the sound plays immediately.

That's the whole pitch. 36 keys. Any audio file. Zero lag.

Try it

I'd love feedback — especially from anyone who uses soundboards for live performance, streaming, or teaching. What features would make this more useful for your workflow?

Core features

  • Key mapping — pick any audio file from your device and assign it to a key
  • Dual input — works with on-screen taps and physical Bluetooth keyboards
  • 18-color palette — each key gets a unique color so you can read the board at a glance
  • Import / Export — your entire layout serialises to a single JSON file
  • Persistent storage — mappings survive reboots and reinstalls via SQLite + takePersistableUriPermission()
  • Free — ad-supported, no subscription, no sign-in, works fully offline

The tech stack

CTunes is a native Android app written in Java (yes, Java — not Kotlin). Here's how the main pieces fit together:

Architecture

  • Single-module project, Activities only
  • No Fragments, no Navigation component
  • SQLite via a hand-rolled SQLiteOpenHelper
  • SharedPreferences for UI settings (grid size, column count, keyboard visibility)

Audio playback


java
// A new MediaPlayer is created per keypress
// The previous one is released first to avoid leaks
if (currentPlayer != null) {
    currentPlayer.release();
}
MediaPlayer mp = MediaPlayer.create(this, uri);
if (mp != null) {
    mp.setOnCompletionListener(MediaPlayer::release);
    mp.start();
    currentPlayer = mp;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
jakemoreno_dev profile image
Jake Moreno

Nice project! The single-activity architecture keeps things clean. I play guitar and I could see using something like this to trigger backing tracks or metronome clicks during practice without fumbling around on screen.

Curious - does it support looping an audio file? Like if I wanted a drum loop running continuously while I play over it. Also, how does it handle longer files (30s+)? Creating a new MediaPlayer per keypress makes total sense for short samples but wondering about the memory behavior with bigger files.

Collapse
 
anoop_p_22f715057ac8b3901 profile image
anoop p • Edited

Hi @jakemoreno_dev
I’ve actually just pushed an update that adds looping support 🎉
So you can now run a drum loop or backing track continuously while playing over it.
Google Play

Collapse
 
anoop_p_22f715057ac8b3901 profile image
anoop p • Edited

Hi @jakemoreno_dev

Thanks for the kind words and the great use case - backing tracks and metronome clicks during practice is exactly the kind of thing this was built for!

To answer your questions :
Looping - not supported yet, but it's on the roadmap and coming in the next update. The plan is toggle behaviour so pressing the key again stops it instead of restarting.

Longer files - the app only keeps one audio track alive at a time, so pressing any key interrupts whatever is currently playing. For a 30s backing track that means an abrupt cut if you accidentally tap another key. That's a known limitation I plan to address soon.

Multiple sounds - I've been thinking about supporting up to 5 simultaneous sounds, but there are a few edge cases to get right first:
what happens when you hit a 6th sound, whether a looping backing track should stay running while you trigger one-shots on top, what pressing the same key twice should do, and making sure everything cleans up properly when a call comes in or you switch apps.
Once those decisions are made the implementation is straightforward, but I want to get the behaviour right before shipping it.

So looping is coming soon, and proper multi-sound support is being thought through carefully. Your use case - a continuous drum loop with one-shot triggers on top - is actually a perfect test scenario for all

Collapse
 
alyra profile image
Hunter

Hi Anoop, Sweet project!

I actually play in a band and I was looking for a simple interface to use my phone as a sample triggering mechanism to add a little bit of fun on stage.

Currently I'm working on doing some samples for us to be able to cover "Faint" by Linkin Park but there are a few issues with how it works for me.

I have a very short synth stab sound I want to play multiple times really fast (you can hear it in the song at about 1:27) but when I tap multiple times in succession I notice that the sounds do not feel like they play instantly. Between a tap and sound coming out is a delay of around 3-400ms. This happens both when my phone is connected via USB as well as just playing directly through the speaker. It's not a huge deal for longer song intro samples but it causes an issue for tight sounds that I want to play multiple times.

Additionally the stab has a reverb / delay trail that I want to keep but tapping it a second time cancels the first playback. I know you are probably addressing this with what you mentioned about multiple sounds but wanted to bring it up again.

Lastly, a small bug is that the stop music and loop buttons are covered by the android navigation buttons on my phone so they are quite difficult to press.

I'll be keeping an eye on this project, nice work so far