2024-11-05

CLS - Command-Line Soundboard in Rust! And Rust Review

cls soundboard

This right here was what took most of my time in October. Coding up this soundboard.

This is the |CLS| soundboard, which stands for |Command-Line Soundboard|. It has another meaning though. Hongkongers who reading can already tell. "cls" is a common abbreviation for a Cantonese swear "痴撚線", which means "fucking crazy". Honestly, I did feel that while making this program in Rust, as a Javascript developer.

Feature Highlights

Does any of these sound familiar? That's because this is totally inspired by another soundboard I was using called [Soundux](https://soundux.rocks/).

Inspiration

I'm gonna skip the usage part here because I already wrote a guide on the [GitHub page](https://github.com/North-West-Wind/cls). ("cls" is |open source|, btw)

As mentioned in the last part, CLS is inspired by [Soundux](https://soundux.rocks/). Soundux is a pretty solid soundboard made with Electron. If you have seen the UI of Soundux, you would find out how many similarities there are between Soundux and CLS.

soundux ui

The most obvious thing here is the |directory tab|. I just found this to be a really good idea for organizing a lot of audio files (which I do have). Instead of having to import each file as a soundboard entry, you can just add a directory and be done with it. Beyond adding directories, you can also bind a file to a |global key bind|, so you can play it without the UI opened. This is extremely useful for me when I'm streaming.

I even copied |playlist mode| in v1.2.0 :>

Why did I even bother?

So, if Soundux is so good, why do I have to make my own?

Well, one unfortunate thing about |Soundux is that it is going through a major rewrite|. How long is it gonna take? Nobody knows. As of now, it has not received any updates for |2 years|. In these 2 years, quite a lot of stuff happened. For example, |Wayland is becoming more mainstream|. So mainstream in fact, |KDE Plasma 6 is using it as the default display server|.

|Wayland is just so much smoother and I want to switch to that forever|. However, because Soundux is based on Electron (which is itself Chromium), |it doesn't support global hotkeys in Wayland|. This was actually the major motivation for me to create CLS.

Plus, |Electron is so bloated|. Soundux v0.2.7 is 67.6MiB in size, while CLS v1.2.0, being a small CLI program, is 2.9MiB.

Under the Hood

I just call CLS a "glorified file manager and command runner", because that's what it is. When you "play" a file, it's actually just running commands, because I can't be bothered enough to learn how to use an actual library. At the back, it's just running `ffmpeg` (for transcoding) piped to `pacat` (for playing in a PulseAudio sink). You also need to load some modules to loopback the "cls" sink in order to have the soundboard play to actual inputs/outputs.

As you can see, it's heavily reliant on having PulseAudio client. Personally, I use `pipewire-pulse` for a PulseAudio client with Pipewire backend, so that works perfectly. But this means the program is not cross-platform at all, as Windows definitely doesn't have PulseAudio. |I don't plan to make this cross-platform, so don't ask me about it|.

Honest Rust Review

We haven't talked about the other significant topic here. |Why the hell did a JS dev decided to use Rust?| Well, that's simply because I kept seeing people talking about how good Rust is, so I gave it a try.

And I'm annoyed.

Before I start ranting though, let's talk about some good things about it first. |It has a package manager.| That's a big win over other compiled languages like `C/C++` already where dependency management is a mess. I just need one command to install things for this specific program only. It's like `npm`.

And obviously, with Rust being a compiled language, it's definitely faster than interpreted languages like JS, which I write a lot. Although speed isn't really a concern here, I have been taking courses in university and basically became a |computer speedrunner|, so I do care.

Onto the rants.

This so-called memory-safe feature is so annoying. My code is just littered with a bunch of `.unwrap()`, because everything is behind `Option`, because there's nothing as `undefined` or `null`! For some reason, I can't use `default()` on my static variables (because it's "unsafe" I guess), so I have to wrap everything up with `Option` and then initialize them afterwards. |Source code-wise there's just so much bloat.|

Rust also doesn't have |Object-Oriented Programming| (OOP) features. They say they have something similar. It's like:
'Me: Mom, can we have OOP?'
"Mom: We have OOP at home."
And it sucks. Yeah sure the `traits` allow you to implement templates for your `structs`, but |can I PLEASE use them as types|?

In my program, there are different "blocks", and they basically all have the same functions for rendering and key handling. However, they can't be the same `struct`! So what ended up happening was `enums` and a bunch of `match case` (which is just Rust version of `switch case`).

In conclusion, |I don't like Rust.| I dislike it even more after I learnt "Go", yet another compiled language.