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