def __init__(self, name): Con.Adapter.__init__( self, Con.RepeatUntil(lambda obj, ctx: (obj & 0x80) == 0x00, Con.UBInt8(name)))
class DiscID: """An object representing a 32 bit FreeDB disc ID value.""" DISCID = Con.Struct('discid', Con.UBInt8('digit_sum'), Con.UBInt16('length'), Con.UBInt8('track_count')) def __init__(self, tracks=[], offsets=None, length=None, lead_in=150): """Fields are as follows: tracks - a list of track lengths in CD frames offsets - a list of track offsets in CD frames length - the length of the entire disc in CD frames lead_in - the location of the first track on the CD, in frames These fields are all optional. One will presumably fill them with data later in that event. """ self.tracks = tracks self.__offsets__ = offsets self.__length__ = length self.__lead_in__ = lead_in @classmethod def from_cdda(cls, cdda): """Given a CDDA object, returns a populated DiscID. May raise ValueError if there are no audio tracks on the CD.""" tracks = list(cdda) if (len(tracks) < 1): raise ValueError(_(u"no audio tracks in CDDA object")) return cls(tracks=[t.length() for t in tracks], offsets=[t.offset() for t in tracks], length=cdda.length(), lead_in=tracks[0].offset()) def add(self, track): """Adds a new track length, in CD frames.""" self.tracks.append(track) def offsets(self): """Returns a list of calculated offset integers, from track lengths.""" if (self.__offsets__ is None): offsets = [self.__lead_in__] for track in self.tracks[0:-1]: offsets.append(track + offsets[-1]) return offsets else: return self.__offsets__ def length(self): """Returns the total length of the disc, in seconds.""" if (self.__length__ is None): return sum(self.tracks) else: return self.__length__ def idsuffix(self): """Returns a FreeDB disc ID suffix string. This is for making server queries.""" return str(len(self.tracks)) + " " + \ " ".join([str(offset) for offset in self.offsets()]) + \ " " + str((self.length() + self.__lead_in__) / 75) def __str__(self): def __count_digits__(i): if (i == 0): return 0 else: return (i % 10) + __count_digits__(i / 10) disc_id = Con.Container() disc_id.track_count = len(self.tracks) disc_id.length = self.length() / 75 disc_id.digit_sum = sum( [__count_digits__(o / 75) for o in self.offsets()]) % 0xFF return DiscID.DISCID.build(disc_id).encode('hex') def freedb_id(self): """Returns the entire FreeDB disc ID, including suffix.""" return str(self) + " " + self.idsuffix() def toxmcd(self, output): """Writes a newly created XMCD file to output. Its values are populated from this DiscID's fields.""" output.write( XMCD.from_tracks([ DummyAudioFile(length, None, i + 1) for (i, length) in enumerate(self.tracks) ]).to_string())