So after weeks of doing nothing and looking at lossless audio codecs (in no particular order) I’m going back to developing NihAV
and more particularly an audio player.
The main problem with nihav-player
concept is that 1) it’s primarily video player 2) it’s based on outdated SDL1 instead of SDL2 3) the SDL1 wrapper especially in audio area was incomplete and my shim implementation for audio callback as a trait is not good enough so it deadlocks time from time. So I’ve finally installed SDL2 and started to write a new audio-only player based on it (well, considering that I’m not trying to write a cross-platform player an ALSA wrapper would do but I’d rather avoid it). It turns out that SDL2 has its own audio queue interface which simplifies my life (at least for now)—instead of NIHing it and encountering deadlocks I can now simply send data to the queue, check how many samples are still there to be played and add more when I want to. Maybe later when I need more advanced processing I’ll implement proper callback-based audio processing pipeline but for my current needs this is enough.
And now I’d like to complain about the crates I use and don’t use. This is not a fault of Rust programming language but rather a downside created by modern approach to programming and enabled by all those package managers like Cargo
and most famously npm.js
(which is the synonym with the bloated library dependencies).
First of all I need tcgetattr()
/tcsetattr()
for reading commands from the terminal. If I simply use libc
crate it does what I want it to do, but if I’d use suggested “friendly” wrapper for it called nix
it’ll try to pull additional two or three crates and will fail to build with rustc 1.33
that I still have no reason to migrate from. SDL2 wrappers are even worse. sdl2-sys
that provides just bindings to the library depends on cfg-if
and libc
crates which is reasonable. sdl2
crate though is a bloated beast pulling a dozen of dependencies with some of them being duplicate (before version 0.33 it pulled rand
crate by default which pulled a dozen of other crates, some of them depending on different rand_core
version from the others; and the saddest thing is that it was required just to allow generating random pixel values). And I can’t compile that version because it depends on TryFrom
trait instead of num-traits
crate and you need a compiler at least two months younger to support it. The older version should work just fine though.
The rant is over, back to the NihAV
matters.
Now my player can play various formats supported by NihAV
, pause/resume playback, seek forward and backward. Essentially it’s good enough for everyday music needs. I’m still considering whether I should not support MP3 or definitely not support it.
And for achieving that I had to fix some bugs, add a seeking for FLAC files without seektable (which seems to be virtually all FLAC files out there), make demuxers report duration (either as a container or a stream property) plus some optimisations in Monkey’s Audio decoder.
The optimisations was a funny thing. At first I tried SSE intrinsics provided by the compiler and it turned out that loop unrolling by 16 leads to wrong results in one of the sum registers. But unrolling it by 8 worked fine—except that auto-vectorisation in the compiler did even better job on the same code turned into a standalone function with iterators. Plus IMO x86 intrinsics look ugly with their _mm_operation_epi32
. Maybe if I ever get to writing H.264 decoder for my own viewing needs I’ll try standalone assembly or inline one if asm!()
syntax will be stabilised by then.
So for my player I think I’ll need just to add volume control and print name of the file currently being played and not just current time. But this should not be that hard…
[…] Kostya's Rants around Multimedia « NihAV: towards an audio player […]