Blame


1 4562b810 2022-02-19 op => gemini://it.omarpolo.com/articoli/amused.gmi This is a partial and unaccurate translation of a post with the same title I published the other day on my Italian capsule.
2 4562b810 2022-02-19 op
3 4562b810 2022-02-19 op Moved by curiosity I wrote a small music player. The ideas was to see if it was possible to decode the audio files in a tightly sandboxed process.
4 4562b810 2022-02-19 op
5 4562b810 2022-02-19 op TL;DR it was an error. I ended up writing a thing I like, now I have to maintain it!
6 4562b810 2022-02-19 op
7 4562b810 2022-02-19 op => //git.omarpolo.com/amused amused
8 4562b810 2022-02-19 op
9 4562b810 2022-02-19 op It’s a small program that plays music on the background and is commanded by a really simple command line interface:
10 4562b810 2022-02-19 op
11 4562b810 2022-02-19 op ``` usage example
12 4562b810 2022-02-19 op $ amused add *.mp3 # enqueue all mp3s
13 4562b810 2022-02-19 op $ amused play
14 4562b810 2022-02-19 op playing /path/to/music.mp3
15 4562b810 2022-02-19 op $ amused pause
16 4562b810 2022-02-19 op $ amused status
17 4562b810 2022-02-19 op paused /path/to/music.mp3
18 4562b810 2022-02-19 op ```
19 4562b810 2022-02-19 op
20 4562b810 2022-02-19 op End. Well, there is a little bit more to be honest. It has a unique (in its category I mean) behaviour with regards to pipes, but I’d like to describe my thought process before showing how it plays with the shell.
21 4562b810 2022-02-19 op
22 4562b810 2022-02-19 op The initial idea was to have a daemon process (“amused”) that plays the music and a client (“playlistctl”?) to control it. Akin to mpd/mpc if you want to. I pretty quickly discarded the idea and went with a single executable – amused – that is both a daemon and a client, and that automatically spawns the daemon if needed. It’s more easier to use I guess.
23 4562b810 2022-02-19 op
24 4562b810 2022-02-19 op One of the first command to be implemented was ‘show’ to print all the enqueued files. Amused is a simple program which has only the notion of the current playing queue, and I wanted to keep it as simple as possible. In particular I didn’t want to add some kind of state persistence. All the state is manipulated via the command line and is ephemeral: once you kill it, it’s gone.
25 4562b810 2022-02-19 op
26 4562b810 2022-02-19 op Then I thought that I could use the ‘show’ command to dump the state to the disk, so I wrote a ‘load’ command to re-import it from a file:
27 4562b810 2022-02-19 op
28 4562b810 2022-02-19 op ```
29 4562b810 2022-02-19 op $ amused show > amused.dump
30 4562b810 2022-02-19 op $ # then, later...
31 4562b810 2022-02-19 op $ amused load < amused.dump
32 4562b810 2022-02-19 op ```
33 4562b810 2022-02-19 op
34 4562b810 2022-02-19 op Pretty cool if I can say so. At this point I had a program lying around that I started to really like, so I was thinking of adding just some more features so that I could actually use it from day to day. One of the first that I thought of was manipulating the playing queue: sorting, shuffling, removing duplicates or specific songs...
35 4562b810 2022-02-19 op
36 4562b810 2022-02-19 op Well, it’s not difficult to do, on the contrary, but do I need to code these features myself? (I think stuff like this more and more recently)
37 4562b810 2022-02-19 op
38 4562b810 2022-02-19 op Then I had a “unix revelation”: I could use the shell!
39 4562b810 2022-02-19 op
40 4562b810 2022-02-19 op ```
41 4562b810 2022-02-19 op $ amused show > list
42 4562b810 2022-02-19 op $ sort -R < list > list.shuffled
43 4562b810 2022-02-19 op $ amused load < list.shuffled
44 4562b810 2022-02-19 op ```
45 4562b810 2022-02-19 op
46 4562b810 2022-02-19 op it works but it’s quite painful to type. I can do better. I can use pipes!
47 4562b810 2022-02-19 op
48 4562b810 2022-02-19 op ```
49 4562b810 2022-02-19 op $ amused show | sort -R | amused load
50 4562b810 2022-02-19 op ```
51 4562b810 2022-02-19 op
52 4562b810 2022-02-19 op many many thanks to Douglas McIlroy for the idea of the pipes! dont-know-how-many-years after they’re still relevant.
53 4562b810 2022-02-19 op
54 4562b810 2022-02-19 op To be honest being able to use the pipes like that required a bit of hacking on the client to avoid races: ‘load’ used to be an alias for ‘flush’ (erase the playlist) and one ‘add’ per file. If the ‘load’ command were executed before ‘show’ due to the random nature of pipelines and timing, well, it wouldn’t end well. However, making it “race condition free” was actually pretty simple to do and made the ‘load’ command more robust.
55 4562b810 2022-02-19 op
56 4562b810 2022-02-19 op Some more examples to give the idea of how it composes well:
57 4562b810 2022-02-19 op
58 4562b810 2022-02-19 op ```
59 4562b810 2022-02-19 op $ # remove all Guccini’ song
60 4562b810 2022-02-19 op $ amused show | grep -vi guccini | amused load
61 4562b810 2022-02-19 op $ # load the dream theater discography
62 4562b810 2022-02-19 op $ find ~/music/dream-theater -iname \*.flac | amused load
63 4562b810 2022-02-19 op $ # select a song with fzf
64 4562b810 2022-02-19 op $ amused jump "$(amused show | fzf)"
65 4562b810 2022-02-19 op $ # ...or with dmenu!
66 4562b810 2022-02-19 op $ amused jump "$(amused show | dmenu)"
67 4562b810 2022-02-19 op ```
68 4562b810 2022-02-19 op
69 4562b810 2022-02-19 op The code is also pretty modest in size:
70 4562b810 2022-02-19 op
71 4562b810 2022-02-19 op ```
72 4562b810 2022-02-19 op % wc -l *.c | tail -1
73 4562b810 2022-02-19 op 2902 total
74 4562b810 2022-02-19 op ```
75 4562b810 2022-02-19 op
76 4562b810 2022-02-19 op of which ~500-1K lines were stolen^W borrowed from other OpenBSD programs and another ~500 are the decoding “backends” for the various audio formats.
77 4562b810 2022-02-19 op
78 4562b810 2022-02-19 op The most difficult part was actually the audio decoding. I never wrote “audio code” before. Well, I have a pending PR for an sndio backend for Godot, but in that case the engine itself does the decoding and the driver only needs to play the given array of samples, so it’s kind of cheating.
79 4562b810 2022-02-19 op
80 4562b810 2022-02-19 op I my naïvety I didn’t think that every format has its own libraries with their own APIs, but it makes sense. What it doesn’t make sense is the complete lack of decent documentation!
81 4562b810 2022-02-19 op
82 4562b810 2022-02-19 op I’m talking about libvorbisfile, libopusfile, libflac and libmad. Out of these four libraries none had a single man page. No, I don’t consider doxygen-generated pages to be “documentation”, nor header files filled with HTML! (Who, who the fuck thought that putting HTML in header files was a good idea?)
83 4562b810 2022-02-19 op
84 4562b810 2022-02-19 op Sure, these libraries have all the functions carefully described in a web page, but what’s lacking is some sort of global picture. (Plus, the example code was awful for the most part.)
85 4562b810 2022-02-19 op
86 4562b810 2022-02-19 op Fortunately they’re not too hard to use and one can actually write a decoder in a couple of hours starting from knowing nothing. However, I’d like to do a special mention for libflac: it beats openssl in my personal list of “worst api naming ever”.
87 4562b810 2022-02-19 op
88 4562b810 2022-02-19 op I forgot one thing: amused has only OpenBSD as a target. It’s the only OS I use and I only “know” how to use sndio, so… but I could try to make a portable release eventually. It’s a pretty stable program which I guess it’s already pretty much done, modulo bugs and eventually adding support for more audio formats.
89 4562b810 2022-02-19 op
90 4562b810 2022-02-19 op Which takes me to the list of missing things:
91 4562b810 2022-02-19 op
92 4562b810 2022-02-19 op * some kind of “monitor” which logs the events: could be useful for stuff that wants to monitor the status of the player (to be used for e.g. with lemonbar)
93 4562b810 2022-02-19 op * seeking forward and backward: i don’t really want to because I don’t want to touch the audio code ever again.
94 4562b810 2022-02-19 op * metatag: I love metatags, but as per the previous point I don’t want to touch the lib{vorbis,opus,flac,mad} code again if possible.
95 4562b810 2022-02-19 op
96 4562b810 2022-02-19 op and fixing bugs, if any :)