1 # A Musepack reader/tagger
3 # Copyright 2006 Lukas Lalinsky <lalinsky@gmail.com>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License version 2 as
7 # published by the Free Software Foundation.
9 # $Id: musepack.py 4013 2007-04-23 09:18:22Z luks $
11 """Musepack audio streams with APEv2 tags.
13 Musepack is an audio format originally based on the MPEG-1 Layer-2
14 algorithms. Stream versions 4 through 7 are supported.
16 For more information, see http://www.musepack.net/.
19 __all__
= ["Musepack", "Open", "delete"]
23 from mutagen
.apev2
import APEv2File
, error
, delete
24 from mutagen
.id3
import BitPaddedInt
25 from mutagen
._util
import cdata
27 class MusepackHeaderError(error
): pass
29 RATES
= [44100, 48000, 37800, 32000]
31 class MusepackInfo(object):
32 """Musepack stream information.
35 channels -- number of audio channels
36 length -- file length in seconds, as a float
37 sample_rate -- audio sampling rate in Hz
38 bitrate -- audio bitrate, in bits per second
39 version -- Musepack stream version
42 title_gain, title_peak -- Replay Gain and peak data for this song
43 album_gain, album_peak -- Replay Gain and peak data for this album
45 These attributes are only available in stream version 7. The
46 gains are a float, +/- some dB. The peaks are a percentage [0..1] of
47 the maximum amplitude. This means to get a number comparable to
48 VorbisGain, you must multiply the peak by 2.
51 def __init__(self
, fileobj
):
52 header
= fileobj
.read(32)
54 raise MusepackHeaderError("not a Musepack file")
56 if header
[:3] == "ID3":
57 size
= 10 + BitPaddedInt(header
[6:10])
59 header
= fileobj
.read(32)
61 raise MusepackHeaderError("not a Musepack file")
63 if header
.startswith("MP+"):
64 self
.version
= ord(header
[3]) & 0xF
66 raise MusepackHeaderError("not a Musepack file")
67 frames
= cdata
.uint_le(header
[4:8])
68 flags
= cdata
.uint_le(header
[8:12])
70 self
.title_peak
, self
.title_gain
= struct
.unpack(
72 self
.album_peak
, self
.album_gain
= struct
.unpack(
74 self
.title_gain
/= 100.0
75 self
.album_gain
/= 100.0
76 self
.title_peak
/= 65535.0
77 self
.album_peak
/= 65535.0
79 self
.sample_rate
= RATES
[(flags
>> 16) & 0x0003]
83 header_dword
= cdata
.uint_le(header
[0:4])
84 self
.version
= (header_dword
>> 11) & 0x03FF;
85 if self
.version
< 4 or self
.version
> 6:
86 raise MusepackHeaderError("not a Musepack file")
87 self
.bitrate
= (header_dword
>> 23) & 0x01FF;
88 self
.sample_rate
= 44100
90 frames
= cdata
.uint_le(header
[4:8])
92 frames
= cdata
.ushort_le(header
[6:8])
96 self
.length
= float(frames
* 1152 - 576) / self
.sample_rate
97 if not self
.bitrate
and self
.length
!= 0:
99 self
.bitrate
= int(fileobj
.tell() * 8 / (self
.length
* 1000) + 0.5)
102 if self
.version
>= 7:
103 rg_data
= ", Gain: %+0.2f (title), %+0.2f (album)" %(
104 self
.title_gain
, self
.album_gain
)
107 return "Musepack, %.2f seconds, %d Hz%s" % (
108 self
.length
, self
.sample_rate
, rg_data
)
110 class Musepack(APEv2File
):
112 _mimes
= ["audio/x-musepack", "audio/x-mpc"]
114 def score(filename
, fileobj
, header
):
115 return header
.startswith("MP+") + filename
.endswith(".mpc")
116 score
= staticmethod(score
)