1# -*- coding: utf-8 -*- 2# Copyright (C) 2006 Lukas Lalinsky 3# 4# This program is free software; you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation; either version 2 of the License, or 7# (at your option) any later version. 8 9"""OptimFROG audio streams with APEv2 tags. 10 11OptimFROG is a lossless audio compression program. Its main goal is to 12reduce at maximum the size of audio files, while permitting bit 13identical restoration for all input. It is similar with the ZIP 14compression, but it is highly specialized to compress audio data. 15 16Only versions 4.5 and higher are supported. 17 18For more information, see http://www.losslessaudio.org/ 19""" 20 21__all__ = ["OptimFROG", "Open", "delete"] 22 23import struct 24 25from ._compat import endswith 26from ._util import convert_error 27from mutagen import StreamInfo 28from mutagen.apev2 import APEv2File, error, delete 29 30 31class OptimFROGHeaderError(error): 32 pass 33 34 35class OptimFROGInfo(StreamInfo): 36 """OptimFROGInfo() 37 38 OptimFROG stream information. 39 40 Attributes: 41 channels (`int`): number of audio channels 42 length (`float`): file length in seconds, as a float 43 sample_rate (`int`): audio sampling rate in Hz 44 """ 45 46 @convert_error(IOError, OptimFROGHeaderError) 47 def __init__(self, fileobj): 48 """Raises OptimFROGHeaderError""" 49 50 header = fileobj.read(76) 51 if (len(header) != 76 or not header.startswith(b"OFR ") or 52 struct.unpack("<I", header[4:8])[0] not in [12, 15]): 53 raise OptimFROGHeaderError("not an OptimFROG file") 54 (total_samples, total_samples_high, sample_type, self.channels, 55 self.sample_rate) = struct.unpack("<IHBBI", header[8:20]) 56 total_samples += total_samples_high << 32 57 self.channels += 1 58 if self.sample_rate: 59 self.length = float(total_samples) / (self.channels * 60 self.sample_rate) 61 else: 62 self.length = 0.0 63 64 def pprint(self): 65 return u"OptimFROG, %.2f seconds, %d Hz" % (self.length, 66 self.sample_rate) 67 68 69class OptimFROG(APEv2File): 70 """OptimFROG(filething) 71 72 Attributes: 73 info (`OptimFROGInfo`) 74 tags (`mutagen.apev2.APEv2`) 75 """ 76 77 _Info = OptimFROGInfo 78 79 @staticmethod 80 def score(filename, fileobj, header): 81 filename = filename.lower() 82 83 return (header.startswith(b"OFR") + endswith(filename, b".ofr") + 84 endswith(filename, b".ofs")) 85 86Open = OptimFROG 87