DEV Community

Cover image for How I built a simple music player daemon in Rust with a CLI/Web UI
Tsiry Sandratraina
Tsiry Sandratraina

Posted on

How I built a simple music player daemon in Rust with a CLI/Web UI

Intro

I love to listen to music when I work, here are some projects I'm a big fan of : mpd and mopidy.

So I had the idea to create a Music Player daemon in Rust, it could be interesting because it will be easier to install and we will have only one executable without dependencies.

Architecture

architecture

I started by writing a very simple playback engine, which takes an audio file and decodes it.



        let mut hint = Hint::new();

        let path = Path::new(song);

        // Provide the file extension as a hint.
        if let Some(extension) = path.extension() {
            if let Some(extension_str) = extension.to_str() {
                hint.with_extension(extension_str);
            }
        }

        let source = Box::new(File::open(path).unwrap());

        // Create the media source stream using the boxed media source from above.
        let mss = MediaSourceStream::new(source, Default::default());

        let symphonia_decoder = |mss: MediaSourceStream, hint| {
            SymphoniaDecoder::new(mss, hint).map(|mut decoder| {
                Box::new(decoder) as Decoder
            })
        };

        let decoder_type = symphonia_decoder(mss, hint);

        let mut decoder = match decoder_type {
            Ok(decoder) => decoder,
            Err(e) => {
                error!("Failed to create decoder: {}", e);
                return;
            }
        };

        let mut sink = backend(None, audio_format);

        sink.start();

        loop {
            match decoder.next_packet() {
                Ok(result) => {
                    if let Some((ref _packet_position, packet, channels, sample_rate)) = result {
                        match packet.samples() {
                            Ok(_) => {
                                let mut converter =
                                    Converter::new(Some(mk_ditherer::<TriangularDitherer>));
                                sink.write(packet, channels, sample_rate, &mut converter);
                            }
                            Err(e) => {
                                error!("Failed to decode packet: {}", e);
                            }
                        }
                    }
                }
                Err(e) => {
                    error!("Failed to decode packet: {}", e);
                }
            };
        }



Enter fullscreen mode Exit fullscreen mode

Then I added a gRPC API to control the player remotely and an audio file scanner that extracts the metadata and the path and organizes them in a small SQLite database.

How it works?

schema

The final application is a single executable without dependencies but which is both a server (Music Player Daemon + gRPC API + GraphQL API) and a client (CLI).
If you run it for the first time it will start the server API and if you open another terminal and start another one, it will behave like a client.

Project Setup



# Install dependencies
brew install protobuf # macOS
sudo apt-get install -y libasound2-dev protobuf-compiler # Ubuntu/Debian
choco install protoc # Windows using Chocolatey Package Manager
# Compile
git clone https://github.com/tsirysndr/music-player.git
cd music-player/webui/musicplayer
nvm install # install node version specified in .nvmrc (optional on windows)
npm install -g yarn
yarn install && yarn build # build webui
cd ../..
cargo install --path .


Enter fullscreen mode Exit fullscreen mode

Usage



USAGE:
    music-player [SUBCOMMAND]

OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information

SUBCOMMANDS:
    albums      List all albums
    artists     List all artists
    help        Print this message or the help of the given subcommand(s)
    next        Play the next song
    pause       Pause the current song
    open        Open audio file
    playlist    Manage playlists
    prev        Play the previous song
    queue       Manage the queue
    play        Resume the current song
    scan        Scan music library: $HOME/Music
    search      Search for a song, album, artist or playlist
    stop        Stop the current song
    tracks      List all tracks


Enter fullscreen mode Exit fullscreen mode

If you are interested in the project, I invite you to visit the github repo https://github.com/tsirysndr/music-player/ , there are still a lot of features that I haven't done yet

Top comments (2)

Collapse
 
sakshatshinde profile image
Sakshat

Looks great!

Collapse
 
joeljaison394 profile image
Joel Jaison

Nice one ❤️