The mystery of the a52dec decoded samples

I’ve been working a lot on the audio conversion code for xine-lib, with results that are always more satisfying, although Amarok fails to build on this new version, audio playback improved a lot, and with the code being all self-contained, the option of replacing the functions with more optimised code, using MMX, SSE, AltiVec or whatever other SIMD extension is more feasible.

Unfortunately I’ve hit one issue: beside A52 and DTS having a channel order different than most of other formats, I have an issue with the type of the decoded samples. There’s a function called float_to_int() that I supposed truncated the floating-point values to get 16-bit signed integer samples (that were the only format supported by xine before), but the floats themselves don’t play at all, while floating-point data plays fine for most other formats. I had to look up a lot of documentation to find for sure that floating-point samples, like I supposed before, varies in the interval [–1.0,1.0], so I was doing it alright up to now.

Now, if someone has any idea on what the following code does, I’d be glad to know it. The blah() function (with the same name) is present in xine, MPlayer, FFmpeg, and renamed in Ogle and other software too.


static inline int16_t blah (int32_t i) {
  if (i > 0x43c07fff)
    return 32767;
  else if (i < 0x43bf8000)
    return -32768;
    return i - 0x43c00000;

int main() {
  uint8_t val_1[4] = { 0xFF, 0xFF, 0xBF, 0x43 };

  return printf("%fn%dn", *(float*)(&val_1), blah(*(int32_t*)(&val_1)));

The output (on Little-Endian, as val_1 is the byte representation of a float number in Little-Endian) is: 383.999969 and –1; it makes no sense to me, but maybe someone else has a clue..

And for who’s wondering, the byte-value I chosen there is one of the samples decoded by a52dec, so it’s a proper value I’m trying to understand; and for who’s wondering, the proper correspondence for –1 (int16) in float32 format is (approximated) –3.0517e-05.

Edit: thanks Nathan Smith who made me see the spelling error in the title (I still confuse the two).

A/52 on-the-fly encoding for ALSA: step two: aften

As I wrote, the FFmpeg-based a52 plugin for ALSA is useless with software that uses FFmpeg (libavcodec) as a shared library, like xine and VLC, because libavcodec’s functions are not thread-safe.

As an alternative, Devils-Hawk published on ALSA’s tracker a new a52 plugin that uses aften instead. This version seems to be actually faster, and does not break with xine, not the same way at least. There is still a problem that Julian Scheel reported on xine-devel last week. I’ll see to investigate it tomorrow.

I’ve added the ebuild for this in my overlay with a simple CMakeLists.txt file to use with cmake (as it’s also needed for aften, it’s not an actual extra dependency), it should work, in theory, but it’s still unusable with PulseAudio too: it crashes down miserably when starting, and I can’t really see why, as it is.

Anyway, I’ll see to work on that in the next days, in my spare time.

The annoying thing about alsa’s ac3 plugin: FFmpeg

Not sure if you remember, but I did blog in the past about the ALSA ac3 plugin, that encodes the audio on the fly to play it through an external amplifier and a digital S/PDIF connection.

Well, beside the fact that the plugin was being shipped without proper testing, as it didn’t work at all (missing call to avcodec_init(), nonetheless), I now came to the conclusion that it was poorly designed too.

Think about it: what is that produces 5.1 output most of the time? Multimedia players, like xine, MPlayer and VLC, most of which also use FFmpeg for decoding in someway (at least xine and VLC do, MPlayer uses its own snapshot so it’s a different problem). So what the problem is? Well..

[NULL @ 0x304810cd10]insufficient thread locking around avcodec_open/close()

this is the error you get when you try to play something through xine using the AC3 encoding plugin of ALSA. The reason is simple: FFmpeg API is not thread-safe at all, especially when it comes to open and close, so xine has to take care of handling a mutex to lock access.. but it’s not possible to synchronise with ALSA’s access, so there’s no way to use a FFmpeg-based software together with that plugin while using a shared copy of FFmpeg. Of course upstream will just say that they don’t support shared FFmpeg, that they don’t give a crap and that it’s the design of the rest of the world that has to be changed, but the bottom line remains the same: ALSA’s ac3 plugin is pretty much useless.

An user commented on an ALSA bug with a patch that makes the plugin use another AC3 encoder, aften that is supposedly faster, I’ll have to try it, it might be worth using that just to workaround FFmpeg braindeadness, even if the current CVS version uses CMake (sigh).

Multichannel output

And so back I am discussing my problems with multichannel output with ALSA. Seems like my odissey will last more than I foresee.

So it seems like either I was under drug usage when I was sure that speaker-test is able to send 6 channels output, or via82xx did play trick with me, and encoded it somehow on the fly before sending it through opto out, as it just seems technically impossible to get 6ch PCM output through optical output. Anyway, the point is that it doesn’t work and probably no other card, without using on the fly encoding in AC3, will give me that result.

So, no reason to waste money on a new soundcard, and I’m trying alternatives. The first alternative that came to my mind was to use the a52 plugin for ALSA, present in alsa-plugins, that allows to encode on the fly to AC3 to send to the amplifier, which would decode and play over the 5 channels. Even if it’s a lossy-to-lossy compression most of the time, it shouldn’t be much of a problem on its own. The problem is that, well, it simply wouldn’t work, as it segfaulted inside FFmpeg code. A quick look around told me that ALSA developers need to start reading documentation of the libraries they use, as they weren’t initialising libavcodec at all. Two-liner patch and I’m back on the road. (for who’s wondering, the patch is applied now in alsa-plugins-1.0.14_rc1-r2, while the ffmpeg useflag that was present before has been removed in all previous ebuilds, to avoid users hitting the bug).

So, with on-the-fly ac3 decoding, the situation is a bit better, I can actually play a 6-channel wave file through aplay, and it plays on the rear channels just as well, which is good. But of course, talking about ALSA, it couldn’t be that painless. So let’s see what the problems are.

First of all, when encoding to ac3 you’re using the S/PDIF output in exclusive dedicated way, (if you have a VIA card, this means that the IEC958 Playback AC97-SPSA control in the mixer is set to the higher level, rather than the lower you need to play stereo sound through it), meaning you cannot do neither hardware nor software (dmix) mixing, like if you were using iec958 (because in fact it is using the same output method); for this reason you still need a software mixing daemon like PulseAudio to be able to play more than one multichannel stream at once. This is also true for soundcards that have hardware mixing, like emu10k1- or emu10k2-based cards.

The second problem is tied to PulseAudio, that as I said would be needed to be able to play more than one stream at once. «Would», because it simply won’t work at the current status. As ALSA does not provide a CTL plugin for a52, but only a PCM, the virtual device wouldn’t have a mixer (it’s right), and somehow this leads to the crash of PulseAudio it seems (the crash is, of course, in alsa-lib code). I’ll have a «good» time trying to get a hold of this, I’m afraid.

Now of course, being finally able to have 5.1 output, and having some 5.1 audio samples, is quite interesting for me, as I can test them with xine (and FFplay) so that it can finally play WavPack 5.1 :)

Rip, rip, rip

Well, today I have a pretty long TODO list that I’d like to accomplish, although at the top of my list was also to watch Ice Age 2, while relaxing on the couch, that would probably stop me from finishing any other entry on that list… so I’ll remove that one for now. After all yesterday I as lucky, and tired, enough to watch Pirates of the Carribean (which was, by the way, a quite good movie, especially for someone like me so fascinated with swords of all kinds, thanks Alberto for «forcing» me to watch it ;) ).

I also tried to get pan (not PAM this time) to compile on Gentoo/FreeBSD, but after fixing enchant, I found myself stuck with gmime, as that needs a patch too but I’m not sure how to handle it yet (there’s an extra _POSIX_SOURCE that breaks on FreeBSD). I’ll fix this later on today.

Also Bruce M Simpson from FreeBSD contacted me about the nss-mdns port, so I’ve resumed looking for it, I’ll tonight if I can apply what I found today in the sources to get it working somehow. I don’t think it’s too difficult, it’s just scarcely documented.

But in this whole lot of stuff to do, I was hoping to relax by listening to some music.. something I wanted to try, was to rip the 5.1 tracks out of the Rhapsody’s Live in Canada 2005 to compress to WavPack so that I could listen to it through Amarok (one of the reasons I had was also to test the WavPack 5.1 support in xine/ffmpeg and the ability for PulseAudio to play 5.1 files from xine). Unfortunately, even if there are tricks with mencoder to dump the audio of a track through mplayer to an ac3 file (no way to get it through a GUI it seems), I can’t find a way to pass it to wavpack, as both ffmpeg and a52dec produce a stereo wav file, rather than a multichannel one, and WavPack does not work with AC3. I suppose my best bet would be to code myself a bridge between the two, but I don’t have enough time. Sigh.