12/22/2012

Anki nie odtwarza plików wav

Ostatnio wraz ze zmianą laptopa zmieniłem również system operacyjny z Fedory na Ubuntu. Po zainstalowaniu Anki i zaimportowaniu swojego profilu mogłem zacząć naukę. Przeleciałem kilka decków, wszystko fajnie działa, odtwarza pliki mp3, pokazuje obrazki etc. Lecz na jednym z decków za cholerę nie chciał odtworzyć plików wav.

Info przykładowego pliku:
PerfectP(100).wav
Codec: MPEG 1 Audio, Layer 3 (MP3)
Channels: Mono
Sample rate: 44100 Hz
Bitrate: 63 kbps

No dobra, pewnie nie mam potrzebnych kodeków. Poinstalowałem co trzeba, nadal nic... Co ciekawe w każdym programie do odtwarzania muzyki, pliki te działały. Czyli kodeki mam, to nie ich wina.

To spróbuję dowiedzieć się coś o tym pliku przez soxa:
>> soxi PerfectP\ \(100\).wav
soxi FAIL formats: can't open input file `PerfectP (100).wav': WAV file encoding `MP3' is not supported

Acha. Ciekawie się zaczyna. To może uda mi się go odtworzyć, próbuję:
>> play PerfectP\ \(100\).wav
play FAIL formats: can't open input file `PerfectP (100).wav': WAV file encoding `MP3' is not supported

To samo. W sumie to się tego spodziewałem. Czyli co? MP3 is not supperted. Jak nie, jak specjalnie skompilowałem wcześniej soxa, aby wspierał prawie każdy format audio... Coś tutaj jest nie tak. Zabieram się za grzebanie w kodzie Anki. Od razu otworzyłem plik sound.py w katalogu /usr/share/anki/libanki/anki/.

Okej, czyli Anki korzysta z mplayera. To spróbujemy otworzyć plik za pomocą mplayera:
>> mplayer PerfectP\ \(100\).wav 
MPlayer svn r34540 (Ubuntu), built with gcc-4.6 (C) 2000-2012 MPlayer Team
mplayer: could not connect to socket
mplayer: No such file or directory
Failed to open LIRC support. You will not be able to use your remote control.

Playing PerfectP (100).wav.
libavformat version 53.21.0 (external)
Mismatching header version 53.19.0
Audio only file format detected.
Load subtitles in ./
==========================================================================
Requested audio codec family [mpg123] (afm=mpg123) not available.
Enable it at compilation.
Opening audio decoder: [ffmpeg] FFmpeg/libavcodec audio decoders
libavcodec version 53.35.0 (external)
Mismatching header version 53.32.2
[mp3float @ 0x7f53bd067380]Header missing
[mp3float @ 0x7f53bd067380]Header missing
Unknown/missing audio format -> no sound
ADecoder init failed :(
Opening audio decoder: [ffmpeg] FFmpeg/libavcodec audio decoders
Unknown/missing audio format -> no sound
ADecoder init failed :(
Requested audio codec family [mad] (afm=libmad) not available.
Enable it at compilation.
Requested audio codec family [mp3acm] (afm=acm) not available.
Enable it at compilation.
Opening audio decoder: [hwmpa] MPEG audio pass-through (fake decoder)
Cannot sync MPA frame: 0
ADecoder init failed :(
ADecoder init failed :(
Opening audio decoder: [spdif] libavformat/spdifenc audio pass-through decoder.
AUDIO: 48000 Hz, 2 ch, mpeg2, 768.0 kbit/50.00% (ratio: 96000->192000)
Selected audio codec: [spdifmpa] afm: spdif (libavformat/spdifenc MPEG AUDIO BC pass-through decoder)
==========================================================================
AO: [pulse] 48000Hz 2ch s16le (2 bytes per sample)
[format] Sample format big-endian MPEG-2 not yet supported 
[libaf] Reinitialization did not work, audio filter 'format' returned error code -2
[libaf] Unable to setup filter system can not meet sound-card demands, please send a bug report. 
Couldn't find matching filter/ao format!
Audio: no sound
Video: no video


Exiting... (End of file)

No tak, czyli o co chodzi w końcu? Zaczęło się szperanie po necie i szukanie rozwiązania. Po przestudiowaniu  man-sox znalazłem coś takiego:
-t, --type file-type 
Gives the type of the audio file. This is useful when the file extension is non-standard or when the type can not be determined by looking at the header of the file. The -t option can also be used to override the type implied by an input filename extension, but if overriding with a type that has a header, SoX will exit with an appropriate error message if such a header is not actually present.

O, to spróbuję tego!
>> play -t mp3 PerfectP\ \(100\).wav 
play WARN mp3-util: MAD lost sync

PerfectP (100).wav:

 File Size: 9.26k     Bit Rate: 64.0k
  Encoding: MPEG audio    
  Channels: 1 @ 16-bit   
Samplerate: 44100Hz      
Replaygain: off         
  Duration: 00:00:01.16  

In:94.7% 00:00:01.10 [00:00:00.06] Out:48.4k [      |      ] Hd:4.4 Clip:0    
Done.

Działa! No dobra, to teraz trzeba zmusić mplayera, aby wymuszał najpierw mp3, a potem całą resztę i powinno zadziałać ;)
>> mplayer -ac mp3 PerfectP\ \(100\).wav 
MPlayer svn r34540 (Ubuntu), built with gcc-4.6 (C) 2000-2012 MPlayer Team
mplayer: could not connect to socket
mplayer: No such file or directory
Failed to open LIRC support. You will not be able to use your remote control.

Playing mp3.
File not found: 'mp3'
Failed to open mp3.


Playing PerfectP (100).wav.
libavformat version 53.21.0 (external)
Mismatching header version 53.19.0
Audio only file format detected.
Load subtitles in ./
==========================================================================
Forced audio codec: mp3
Opening audio decoder: [mp3lib] MPEG layer-2, layer-3
AUDIO: 44100 Hz, 2 ch, s16le, 64.0 kbit/4.54% (ratio: 8000->176400)
Selected audio codec: [mp3] afm: mp3lib (mp3lib MPEG layer-2, layer-3)
==========================================================================
AO: [pulse] 44100Hz 2ch s16le (2 bytes per sample)
Video: no video
Starting playback...
A:   0.9 (00.8) of 1.0 (01.0)  0.2% 


Exiting... (End of file)

Yeah! Teraz wystarczy pogrzebać kodzie Anki i dodać komendę -ac mp3. Otwieram plik /usr/share/anki/libanki/anki/sound.py (w najnowszej wersji: /usr/share/anki/anki/sounds.py), przechodzę do linii numer 72 i rozszerzam listę ukrywającą się pod zmienną mplayerCmd o dwa dodatkowe obiekty typu string:
mplayerCmd += ["-really-quiet", "-noautosub", "-ac", "mp3"]

Lub można prościej, edytując pliki konfiguracyjne mplayera. Wystarczy, że do pliku ~/.mplayer/config dodasz linijkę:
ac="mp3"
Zapisuję, odpalam Anki i uczę się dalej ;) Jak ktoś miał podobny problem i rozwiązał go inaczej to zapraszam do dyskusji, co ciekawe bez grzebania w kodzie pod Fedorą wszystko mi działało jak trzeba. Myślałem, że trzeba skompilować mplayera jeszcze raz, ale to też nie pomagało.