예제 #1
0
파일: toc.py 프로젝트: tlc/whipper
    def parse(self):
        currentFile = None
        currentTrack = None

        state = 'HEADER'
        # counts sources for audio data; SILENCE/ZERO/FILE
        counter = 0
        trackNumber = 0
        indexNumber = 0
        # running absolute offset: where each track starts
        absoluteOffset = 0
        # running relative offset, relative to counter src
        relativeOffset = 0
        # currentLength is accrued during TRACK record parsing length
        # of current track as parsed so far reset on each TRACK statement
        currentLength = 0
        # accrued during TRACK record parsing, total disc
        totalLength = 0
        # length of the pre-gap, current track in for loop
        pregapLength = 0

        # the first track's INDEX 1 can only be gotten from the .toc
        # file once the first pregap is calculated; so we add INDEX 1
        # at the end of each parsed  TRACK record
        handle = codecs.open(self._path, "r", "utf-8")

        for number, line in enumerate(handle.readlines()):
            line = line.rstrip()

            # look for CDTEXT stuff in either header or tracks
            m = _CDTEXT_CANDIDATE_RE.search(line)
            if m:
                key = m.group('key')
                value = m.group('value')
                # usually, value is encoded with octal escapes and in latin-1
                # FIXME: other encodings are possible, does cdrdao handle
                # them ?
                value = value.decode('string-escape').decode('latin-1')
                if key in table.CDTEXT_FIELDS:
                    # FIXME: consider ISRC separate for now, but this
                    # is a limitation of our parser approach
                    if state == 'HEADER':
                        self.table.cdtext[key] = value
                        logger.debug('found disc CD-Text %s: %r', key, value)
                    elif state == 'TRACK':
                        if key != 'ISRC' or not currentTrack \
                                or currentTrack.isrc is not None:
                            logger.debug('found track CD-Text %s: %r', key,
                                         value)
                            currentTrack.cdtext[key] = value

            # look for header elements
            m = _CATALOG_RE.search(line)
            if m:
                self.table.catalog = m.group('catalog')
                logger.debug("found catalog number %s", self.table.catalog)

            # look for TRACK lines
            m = _TRACK_RE.search(line)
            if m:
                state = 'TRACK'

                # set index 1 of previous track if there was one, using
                # pregapLength if applicable
                if currentTrack:
                    self._index(currentTrack, 1, absoluteOffset, pregapLength)

                # create a new track to be filled by later lines
                trackNumber += 1
                trackMode = m.group('mode')
                audio = trackMode == 'AUDIO'
                currentTrack = table.Track(trackNumber, audio=audio)
                self.table.tracks.append(currentTrack)

                # update running totals
                absoluteOffset += currentLength
                relativeOffset += currentLength
                totalLength += currentLength

                # FIXME: track mode
                logger.debug('found track %d, mode %s, at absoluteOffset %d',
                             trackNumber, trackMode, absoluteOffset)

                # reset counters relative to a track
                currentLength = 0
                indexNumber = 1
                pregapLength = 0

                continue

            # look for PRE_EMPHASIS lines
            m = _PRE_EMPHASIS_RE.search(line)
            if m:
                currentTrack.pre_emphasis = True
                logger.debug('track has PRE_EMPHASIS')

            # look for ISRC lines
            m = _ISRC_RE.search(line)
            if m:
                isrc = m.group('isrc')
                currentTrack.isrc = isrc
                logger.debug('found ISRC code %s', isrc)

            # look for SILENCE lines
            m = _SILENCE_RE.search(line)
            if m:
                length = m.group('length')
                logger.debug('silence of %r', length)
                self._sources.append(counter, absoluteOffset, None)
                if currentFile is not None:
                    logger.debug('silence after file, increasing counter')
                    counter += 1
                    relativeOffset = 0
                    currentFile = None
                currentLength += common.msfToFrames(length)

            # look for ZERO lines
            m = _ZERO_RE.search(line)
            if m:
                if currentFile is not None:
                    logger.debug('zero after file, increasing counter')
                    counter += 1
                    relativeOffset = 0
                    currentFile = None
                length = m.group('length')
                currentLength += common.msfToFrames(length)

            # look for FILE lines
            m = _FILE_RE.search(line)
            if m:
                filePath = m.group('name')
                start = m.group('start')
                length = m.group('length')
                logger.debug('file %s, start %r, length %r', filePath,
                             common.msfToFrames(start),
                             common.msfToFrames(length))
                if not currentFile or filePath != currentFile.path:
                    counter += 1
                    relativeOffset = 0
                    logger.debug(
                        'track %d, switched to new file, '
                        'increased counter to %d', trackNumber, counter)
                currentFile = File(filePath, common.msfToFrames(start),
                                   common.msfToFrames(length))
                self._sources.append(counter, absoluteOffset + currentLength,
                                     currentFile)
                currentLength += common.msfToFrames(length)

            # look for DATAFILE lines
            m = _DATAFILE_RE.search(line)
            if m:
                filePath = m.group('name')
                length = m.group('length')
                logger.debug('file %s, length %r', filePath,
                             common.msfToFrames(length))
                if not currentFile or filePath != currentFile.path:
                    counter += 1
                    relativeOffset = 0
                    logger.debug(
                        'track %d, switched to new file, '
                        'increased counter to %d', trackNumber, counter)
                # FIXME: assume that a MODE2_FORM_MIX track always starts at 0
                currentFile = File(filePath, 0, common.msfToFrames(length))
                self._sources.append(counter, absoluteOffset + currentLength,
                                     currentFile)
                currentLength += common.msfToFrames(length)

            # look for START lines
            m = _START_RE.search(line)
            if m:
                if not currentTrack:
                    self.message(number, 'START without preceding TRACK')
                    print('ouch')
                    continue

                length = common.msfToFrames(m.group('length'))
                c, o, s = self._sources.get(absoluteOffset)
                logger.debug('at abs offset %d, we are in source %r',
                             absoluteOffset, s)
                counterStart = self._sources.getCounterStart(c)
                relativeOffset = absoluteOffset - counterStart

                currentTrack.index(0,
                                   path=s and s.path or None,
                                   absolute=absoluteOffset,
                                   relative=relativeOffset,
                                   counter=c)
                logger.debug('[track %02d index 00] added %r',
                             currentTrack.number, currentTrack.getIndex(0))
                # store the pregapLength to add it when we index 1 for this
                # track on the next iteration
                pregapLength = length

            # look for INDEX lines
            m = _INDEX_RE.search(line)
            if m:
                if not currentTrack:
                    self.message(number, 'INDEX without preceding TRACK')
                    print('ouch')
                    continue

                indexNumber += 1
                offset = common.msfToFrames(m.group('offset'))
                self._index(currentTrack, indexNumber, absoluteOffset, offset)

        # handle index 1 of final track, if any
        if currentTrack:
            self._index(currentTrack, 1, absoluteOffset, pregapLength)

        # totalLength was added up to the penultimate track
        self.table.leadout = totalLength + currentLength
        logger.debug('parse: leadout: %r', self.table.leadout)
예제 #2
0
파일: toc.py 프로젝트: JoeLametta/whipper
    def parse(self):
        currentFile = None
        currentTrack = None

        state = 'HEADER'
        # counts sources for audio data; SILENCE/ZERO/FILE
        counter = 0
        trackNumber = 0
        indexNumber = 0
        # running absolute offset: where each track starts
        absoluteOffset = 0
        # running relative offset, relative to counter src
        relativeOffset = 0
        # currentLength is accrued during TRACK record parsing length
        # of current track as parsed so far reset on each TRACK statement
        currentLength = 0
        # accrued during TRACK record parsing, total disc
        totalLength = 0
        # length of the pre-gap, current track in for loop
        pregapLength = 0

        # the first track's INDEX 1 can only be gotten from the .toc
        # file once the first pregap is calculated; so we add INDEX 1
        # at the end of each parsed  TRACK record
        handle = codecs.open(self._path, "r", "utf-8")

        for number, line in enumerate(handle.readlines()):
            line = line.rstrip()

            # look for CDTEXT stuff in either header or tracks
            m = _CDTEXT_CANDIDATE_RE.search(line)
            if m:
                key = m.group('key')
                value = m.group('value')
                # usually, value is encoded with octal escapes and in latin-1
                # FIXME: other encodings are possible, does cdrdao handle
                # them ?
                value = value.decode('string-escape').decode('latin-1')
                if key in table.CDTEXT_FIELDS:
                    # FIXME: consider ISRC separate for now, but this
                    # is a limitation of our parser approach
                    if state == 'HEADER':
                        self.table.cdtext[key] = value
                        logger.debug('found disc CD-Text %s: %r', key, value)
                    elif state == 'TRACK':
                        if key != 'ISRC' or not currentTrack \
                                or currentTrack.isrc is not None:
                            logger.debug('found track CD-Text %s: %r',
                                         key, value)
                            currentTrack.cdtext[key] = value

            # look for header elements
            m = _CATALOG_RE.search(line)
            if m:
                self.table.catalog = m.group('catalog')
                logger.debug("found catalog number %s", self.table.catalog)

            # look for TRACK lines
            m = _TRACK_RE.search(line)
            if m:
                state = 'TRACK'

                # set index 1 of previous track if there was one, using
                # pregapLength if applicable
                if currentTrack:
                    self._index(currentTrack, 1, absoluteOffset, pregapLength)

                # create a new track to be filled by later lines
                trackNumber += 1
                trackMode = m.group('mode')
                audio = trackMode == 'AUDIO'
                currentTrack = table.Track(trackNumber, audio=audio)
                self.table.tracks.append(currentTrack)

                # update running totals
                absoluteOffset += currentLength
                relativeOffset += currentLength
                totalLength += currentLength

                # FIXME: track mode
                logger.debug('found track %d, mode %s, at absoluteOffset %d',
                             trackNumber, trackMode, absoluteOffset)

                # reset counters relative to a track
                currentLength = 0
                indexNumber = 1
                pregapLength = 0

                continue

            # look for PRE_EMPHASIS lines
            m = _PRE_EMPHASIS_RE.search(line)
            if m:
                currentTrack.pre_emphasis = True
                logger.debug('track has PRE_EMPHASIS')

            # look for ISRC lines
            m = _ISRC_RE.search(line)
            if m:
                isrc = m.group('isrc')
                currentTrack.isrc = isrc
                logger.debug('found ISRC code %s', isrc)

            # look for SILENCE lines
            m = _SILENCE_RE.search(line)
            if m:
                length = m.group('length')
                logger.debug('silence of %r', length)
                self._sources.append(counter, absoluteOffset, None)
                if currentFile is not None:
                    logger.debug('silence after file, increasing counter')
                    counter += 1
                    relativeOffset = 0
                    currentFile = None
                currentLength += common.msfToFrames(length)

            # look for ZERO lines
            m = _ZERO_RE.search(line)
            if m:
                if currentFile is not None:
                    logger.debug('zero after file, increasing counter')
                    counter += 1
                    relativeOffset = 0
                    currentFile = None
                length = m.group('length')
                currentLength += common.msfToFrames(length)

            # look for FILE lines
            m = _FILE_RE.search(line)
            if m:
                filePath = m.group('name')
                start = m.group('start')
                length = m.group('length')
                logger.debug('file %s, start %r, length %r',
                             filePath, common.msfToFrames(start),
                             common.msfToFrames(length))
                if not currentFile or filePath != currentFile.path:
                    counter += 1
                    relativeOffset = 0
                    logger.debug('track %d, switched to new file, '
                                 'increased counter to %d',
                                 trackNumber, counter)
                currentFile = File(filePath, common.msfToFrames(start),
                                   common.msfToFrames(length))
                self._sources.append(counter, absoluteOffset + currentLength,
                                     currentFile)
                currentLength += common.msfToFrames(length)

            # look for DATAFILE lines
            m = _DATAFILE_RE.search(line)
            if m:
                filePath = m.group('name')
                length = m.group('length')
                logger.debug('file %s, length %r',
                             filePath, common.msfToFrames(length))
                if not currentFile or filePath != currentFile.path:
                    counter += 1
                    relativeOffset = 0
                    logger.debug('track %d, switched to new file, '
                                 'increased counter to %d',
                                 trackNumber, counter)
                # FIXME: assume that a MODE2_FORM_MIX track always starts at 0
                currentFile = File(filePath, 0, common.msfToFrames(length))
                self._sources.append(counter, absoluteOffset + currentLength,
                                     currentFile)
                currentLength += common.msfToFrames(length)

            # look for START lines
            m = _START_RE.search(line)
            if m:
                if not currentTrack:
                    self.message(number, 'START without preceding TRACK')
                    print('ouch')
                    continue

                length = common.msfToFrames(m.group('length'))
                c, o, s = self._sources.get(absoluteOffset)
                logger.debug('at abs offset %d, we are in source %r',
                             absoluteOffset, s)
                counterStart = self._sources.getCounterStart(c)
                relativeOffset = absoluteOffset - counterStart

                currentTrack.index(0, path=s and s.path or None,
                                   absolute=absoluteOffset,
                                   relative=relativeOffset, counter=c)
                logger.debug('[track %02d index 00] added %r',
                             currentTrack.number, currentTrack.getIndex(0))
                # store the pregapLength to add it when we index 1 for this
                # track on the next iteration
                pregapLength = length

            # look for INDEX lines
            m = _INDEX_RE.search(line)
            if m:
                if not currentTrack:
                    self.message(number, 'INDEX without preceding TRACK')
                    print('ouch')
                    continue

                indexNumber += 1
                offset = common.msfToFrames(m.group('offset'))
                self._index(currentTrack, indexNumber, absoluteOffset, offset)

        # handle index 1 of final track, if any
        if currentTrack:
            self._index(currentTrack, 1, absoluteOffset, pregapLength)

        # totalLength was added up to the penultimate track
        self.table.leadout = totalLength + currentLength
        logger.debug('parse: leadout: %r', self.table.leadout)