1 # A Monkey's Audio (APE) 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: monkeysaudio.py 4275 2008-06-01 06:32:37Z piman $
11 """Monkey's Audio streams with APEv2 tags.
13 Monkey's Audio is a very efficient lossless audio compressor developed
16 For more information, see http://www.monkeysaudio.com/.
19 __all__ = ["MonkeysAudio", "Open", "delete"]
23 from mutagen.apev2 import APEv2File, error, delete
24 from mutagen._util import cdata
26 class MonkeysAudioHeaderError(error): pass
28 class MonkeysAudioInfo(object):
29 """Monkey's Audio stream information.
32 channels -- number of audio channels
33 length -- file length in seconds, as a float
34 sample_rate -- audio sampling rate in Hz
35 bits_per_sample -- bits per sample
36 version -- Monkey's Audio stream version, as a float (eg: 3.99)
39 def __init__(self, fileobj):
40 header = fileobj.read(76)
41 if len(header) != 76 or not header.startswith("MAC "):
42 raise MonkeysAudioHeaderError("not a Monkey's Audio file")
43 self.version = cdata.ushort_le(header[4:6])
44 if self.version >= 3980:
45 (blocks_per_frame, final_frame_blocks, total_frames,
46 self.bits_per_sample, self.channels,
47 self.sample_rate) = struct.unpack("<IIIHHI", header[56:76])
49 compression_level = cdata.ushort_le(header[6:8])
50 self.channels, self.sample_rate = struct.unpack(
52 total_frames, final_frame_blocks = struct.unpack(
54 if self.version >= 3950:
55 blocks_per_frame = 73728 * 4
56 elif self.version >= 3900 or (self.version >= 3800 and
57 compression_level == 4):
58 blocks_per_frame = 73728
60 blocks_per_frame = 9216
61 self.version /= 1000.0
63 if self.sample_rate != 0 and total_frames > 0:
64 total_blocks = ((total_frames - 1) * blocks_per_frame +
66 self.length = float(total_blocks) / self.sample_rate
69 return "Monkey's Audio %.2f, %.2f seconds, %d Hz" % (
70 self.version, self.length, self.sample_rate)
72 class MonkeysAudio(APEv2File):
73 _Info = MonkeysAudioInfo
74 _mimes = ["audio/ape", "audio/x-ape"]
76 def score(filename, fileobj, header):
77 return header.startswith("MAC ") + filename.lower().endswith(".ape")
78 score = staticmethod(score)