Esempio n. 1
0
class AuReader(object):
    def __init__(self, au_filename):
        from audiotools.bitstream import BitstreamReader
        from audiotools.text import (ERR_AU_INVALID_HEADER,
                                     ERR_AU_UNSUPPORTED_FORMAT)

        self.stream = BitstreamReader(open(au_filename, "rb"), False)
        (magic_number,
         self.data_offset,
         data_size,
         encoding_format,
         self.sample_rate,
         self.channels) = self.stream.parse("4b 5* 32u")

        if magic_number != b'.snd':
            self.stream.close()
            raise ValueError(ERR_AU_INVALID_HEADER)
        try:
            self.bits_per_sample = {2: 8, 3: 16, 4: 24}[encoding_format]
        except KeyError:
            self.stream.close()
            raise ValueError(ERR_AU_UNSUPPORTED_FORMAT)

        self.channel_mask = {1: 0x4, 2: 0x3}.get(self.channels, 0)
        self.bytes_per_pcm_frame = ((self.bits_per_sample // 8) *
                                    self.channels)
        self.total_pcm_frames = (data_size // self.bytes_per_pcm_frame)
        self.remaining_pcm_frames = self.total_pcm_frames

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    def read(self, pcm_frames):
        # try to read requested PCM frames or remaining frames
        requested_pcm_frames = min(max(pcm_frames, 1),
                                   self.remaining_pcm_frames)
        requested_bytes = (self.bytes_per_pcm_frame *
                           requested_pcm_frames)
        pcm_data = self.stream.read_bytes(requested_bytes)

        # raise exception if data block exhausted early
        if len(pcm_data) < requested_bytes:
            from audiotools.text import ERR_AU_TRUNCATED_DATA
            raise IOError(ERR_AU_TRUNCATED_DATA)
        else:
            self.remaining_pcm_frames -= requested_pcm_frames

            # return parsed chunk
            return FrameList(pcm_data,
                             self.channels,
                             self.bits_per_sample,
                             True,
                             True)

    def read_closed(self, pcm_frames):
        raise ValueError("cannot read closed stream")

    def seek(self, pcm_frame_offset):
        if pcm_frame_offset < 0:
            from audiotools.text import ERR_NEGATIVE_SEEK
            raise ValueError(ERR_NEGATIVE_SEEK)

        # ensure one doesn't walk off the end of the file
        pcm_frame_offset = min(pcm_frame_offset,
                               self.total_pcm_frames)

        # position file in data block
        self.stream.seek(self.data_offset +
                         (pcm_frame_offset *
                          self.bytes_per_pcm_frame), 0)
        self.remaining_pcm_frames = (self.total_pcm_frames -
                                     pcm_frame_offset)

        return pcm_frame_offset

    def seek_closed(self, pcm_frame_offset):
        raise ValueError("cannot seek closed stream")

    def close(self):
        self.stream.close()
        self.read = self.read_closed
        self.seek = self.seek_closed
Esempio n. 2
0
class WaveReader(object):
    """a PCMReader object for reading wave file contents"""

    def __init__(self, wave_filename):
        """wave_filename is a string"""

        from audiotools.bitstream import BitstreamReader

        self.stream = BitstreamReader(open(wave_filename, "rb"), True)

        # ensure RIFF<size>WAVE header is ok
        try:
            (riff,
             total_size,
             wave) = self.stream.parse("4b 32u 4b")
        except struct.error:
            from audiotools.text import ERR_WAV_INVALID_WAVE
            self.stream.close()
            raise ValueError(ERR_WAV_INVALID_WAVE)

        if riff != b'RIFF':
            from audiotools.text import ERR_WAV_NOT_WAVE
            self.stream.close()
            raise ValueError(ERR_WAV_NOT_WAVE)
        elif wave != b'WAVE':
            from audiotools.text import ERR_WAV_INVALID_WAVE
            self.stream.close()
            raise ValueError(ERR_WAV_INVALID_WAVE)
        else:
            total_size -= 4
            fmt_chunk_read = False

        # walk through chunks until "data" chunk encountered
        while total_size > 0:
            try:
                (chunk_id,
                 chunk_size) = self.stream.parse("4b 32u")
            except struct.error:
                from audiotools.text import ERR_WAV_INVALID_WAVE
                self.stream.close()
                raise ValueError(ERR_WAV_INVALID_WAVE)
            if not frozenset(chunk_id).issubset(WaveAudio.PRINTABLE_ASCII):
                from audiotools.text import ERR_WAV_INVALID_CHUNK
                self.stream.close()
                raise ValueError(ERR_WAV_INVALID_CHUNK)
            else:
                total_size -= 8

            if chunk_id == b"fmt ":
                # when "fmt " chunk encountered,
                # use it to populate PCMReader attributes
                (self.channels,
                 self.sample_rate,
                 self.bits_per_sample,
                 channel_mask) = parse_fmt(self.stream)
                self.channel_mask = int(channel_mask)
                self.bytes_per_pcm_frame = ((self.bits_per_sample // 8) *
                                            self.channels)
                fmt_chunk_read = True
            elif chunk_id == b"data":
                # when "data" chunk encountered,
                # use its size to determine total PCM frames
                # and ready PCMReader for reading
                if not fmt_chunk_read:
                    from audiotools.text import ERR_WAV_PREMATURE_DATA
                    self.stream.close()
                    raise ValueError(ERR_WAV_PREMATURE_DATA)
                else:
                    self.total_pcm_frames = (chunk_size //
                                             self.bytes_per_pcm_frame)
                    self.remaining_pcm_frames = self.total_pcm_frames
                    self.data_start = self.stream.getpos()
                    return
            else:
                # all other chunks are ignored
                self.stream.skip_bytes(chunk_size)

            if chunk_size % 2:
                if len(self.stream.read_bytes(1)) < 1:
                    from audiotools.text import ERR_WAV_INVALID_CHUNK
                    self.stream.close()
                    raise ValueError(ERR_WAV_INVALID_CHUNK)
                total_size -= (chunk_size + 1)
            else:
                total_size -= chunk_size
        else:
            # raise an error if no "data" chunk is encountered
            from audiotools.text import ERR_WAV_NO_DATA_CHUNK
            self.stream.close()
            raise ValueError(ERR_WAV_NO_DATA_CHUNK)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    def read(self, pcm_frames):
        """try to read a pcm.FrameList with the given number of PCM frames"""

        # try to read requested PCM frames or remaining frames
        requested_pcm_frames = min(max(pcm_frames, 1),
                                   self.remaining_pcm_frames)

        requested_bytes = (self.bytes_per_pcm_frame *
                           requested_pcm_frames)
        pcm_data = self.stream.read_bytes(requested_bytes)

        # raise exception if "data" chunk exhausted early
        if len(pcm_data) < requested_bytes:
            from audiotools.text import ERR_WAV_TRUNCATED_DATA_CHUNK
            raise IOError(ERR_WAV_TRUNCATED_DATA_CHUNK)
        else:
            self.remaining_pcm_frames -= requested_pcm_frames

            # return parsed chunk
            return FrameList(pcm_data,
                             self.channels,
                             self.bits_per_sample,
                             False,
                             self.bits_per_sample != 8)

    def read_closed(self, pcm_frames):
        raise ValueError("cannot read closed stream")

    def seek(self, pcm_frame_offset):
        """tries to seek to the given PCM frame offset
        returns the total amount of frames actually seeked over"""

        if pcm_frame_offset < 0:
            from audiotools.text import ERR_NEGATIVE_SEEK
            raise ValueError(ERR_NEGATIVE_SEEK)

        # ensure one doesn't walk off the end of the file
        pcm_frame_offset = min(pcm_frame_offset, self.total_pcm_frames)

        # position file in "data" chunk
        self.stream.setpos(self.data_start)
        self.stream.seek(pcm_frame_offset * self.bytes_per_pcm_frame, 1)
        self.remaining_pcm_frames = (self.total_pcm_frames -
                                     pcm_frame_offset)

        return pcm_frame_offset

    def seek_closed(self, pcm_frame_offset):
        raise ValueError("cannot seek closed stream")

    def close(self):
        """closes the stream for reading"""

        self.stream.close()
        self.read = self.read_closed
        self.seek = self.seek_closed
Esempio n. 3
0
class AiffReader(object):
    """a PCMReader object for reading AIFF file contents"""
    def __init__(self, aiff_filename):
        """aiff_filename is a string"""

        from audiotools.bitstream import BitstreamReader

        self.stream = BitstreamReader(open(aiff_filename, "rb"), False)

        # ensure FORM<size>AIFF header is ok
        try:
            (form, total_size, aiff) = self.stream.parse("4b 32u 4b")
        except struct.error:
            from audiotools.text import ERR_AIFF_INVALID_AIFF
            raise InvalidAIFF(ERR_AIFF_INVALID_AIFF)

        if form != b'FORM':
            from audiotools.text import ERR_AIFF_NOT_AIFF
            raise ValueError(ERR_AIFF_NOT_AIFF)
        elif aiff != b'AIFF':
            from audiotools.text import ERR_AIFF_INVALID_AIFF
            raise ValueError(ERR_AIFF_INVALID_AIFF)
        else:
            total_size -= 4
            comm_chunk_read = False

        # walk through chunks until "SSND" chunk encountered
        while total_size > 0:
            try:
                (chunk_id, chunk_size) = self.stream.parse("4b 32u")
            except struct.error:
                from audiotools.text import ERR_AIFF_INVALID_AIFF
                raise ValueError(ERR_AIFF_INVALID_AIFF)

            if not frozenset(chunk_id).issubset(AiffAudio.PRINTABLE_ASCII):
                from audiotools.text import ERR_AIFF_INVALID_CHUNK_ID
                raise ValueError(ERR_AIFF_INVALID_CHUNK_ID)
            else:
                total_size -= 8

            if chunk_id == b"COMM":
                # when "COMM" chunk encountered,
                # use it to populate PCMReader attributes
                (self.channels, self.total_pcm_frames, self.bits_per_sample,
                 self.sample_rate, channel_mask) = parse_comm(self.stream)
                self.channel_mask = int(channel_mask)
                self.bytes_per_pcm_frame = ((self.bits_per_sample // 8) *
                                            self.channels)
                self.remaining_pcm_frames = self.total_pcm_frames
                comm_chunk_read = True
            elif chunk_id == b"SSND":
                # when "SSND" chunk encountered,
                # strip off the "offset" and "block_size" attributes
                # and ready PCMReader for reading
                if not comm_chunk_read:
                    from audiotools.text import ERR_AIFF_PREMATURE_SSND_CHUNK
                    raise ValueError(ERR_AIFF_PREMATURE_SSND_CHUNK)
                else:
                    self.stream.skip_bytes(8)
                    self.ssnd_start = self.stream.getpos()
                    return
            else:
                # all other chunks are ignored
                self.stream.skip_bytes(chunk_size)

            if chunk_size % 2:
                if len(self.stream.read_bytes(1)) < 1:
                    from audiotools.text import ERR_AIFF_INVALID_CHUNK
                    raise ValueError(ERR_AIFF_INVALID_CHUNK)
                total_size -= (chunk_size + 1)
            else:
                total_size -= chunk_size
        else:
            # raise an error if no "SSND" chunk is encountered
            from audiotools.text import ERR_AIFF_NO_SSND_CHUNK
            raise ValueError(ERR_AIFF_NO_SSND_CHUNK)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    def read(self, pcm_frames):
        """try to read a pcm.FrameList with the given number of PCM frames"""

        # try to read requested PCM frames or remaining frames
        requested_pcm_frames = min(max(pcm_frames, 1),
                                   self.remaining_pcm_frames)
        requested_bytes = (self.bytes_per_pcm_frame * requested_pcm_frames)
        pcm_data = self.stream.read_bytes(requested_bytes)

        # raise exception if "SSND" chunk exhausted early
        if len(pcm_data) < requested_bytes:
            from audiotools.text import ERR_AIFF_TRUNCATED_SSND_CHUNK
            raise IOError(ERR_AIFF_TRUNCATED_SSND_CHUNK)
        else:
            self.remaining_pcm_frames -= requested_pcm_frames

            # return parsed chunk
            return FrameList(pcm_data, self.channels, self.bits_per_sample,
                             True, True)

    def read_closed(self, pcm_frames):
        raise ValueError("cannot read closed stream")

    def seek(self, pcm_frame_offset):
        """tries to seek to the given PCM frame offset
        returns the total amount of frames actually seeked over"""

        if pcm_frame_offset < 0:
            from audiotools.text import ERR_NEGATIVE_SEEK
            raise ValueError(ERR_NEGATIVE_SEEK)

        # ensure one doesn't walk off the end of the file
        pcm_frame_offset = min(pcm_frame_offset, self.total_pcm_frames)

        # position file in "SSND" chunk
        self.stream.setpos(self.ssnd_start)
        self.stream.seek((pcm_frame_offset * self.bytes_per_pcm_frame), 1)
        self.remaining_pcm_frames = (self.total_pcm_frames - pcm_frame_offset)

        return pcm_frame_offset

    def seek_closed(self, pcm_frame_offset):
        raise ValueError("cannot seek closed stream")

    def close(self):
        """closes the stream for reading"""

        self.stream.close()
        self.read = self.read_closed
        self.seek = self.seek_closed
Esempio n. 4
0
class AuReader(object):
    def __init__(self, au_filename):
        from audiotools.bitstream import BitstreamReader
        from audiotools.text import (ERR_AU_INVALID_HEADER,
                                     ERR_AU_UNSUPPORTED_FORMAT)

        self.stream = BitstreamReader(open(au_filename, "rb"), False)
        (magic_number, self.data_offset, data_size, encoding_format,
         self.sample_rate, self.channels) = self.stream.parse("4b 5* 32u")

        if (magic_number != b'.snd'):
            self.stream.close()
            raise ValueError(ERR_AU_INVALID_HEADER)
        try:
            self.bits_per_sample = {2: 8, 3: 16, 4: 24}[encoding_format]
        except KeyError:
            self.stream.close()
            raise ValueError(ERR_AU_UNSUPPORTED_FORMAT)

        self.channel_mask = {1: 0x4, 2: 0x3}.get(self.channels, 0)
        self.bytes_per_pcm_frame = ((self.bits_per_sample // 8) *
                                    self.channels)
        self.total_pcm_frames = (data_size // self.bytes_per_pcm_frame)
        self.remaining_pcm_frames = self.total_pcm_frames

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    def read(self, pcm_frames):
        # try to read requested PCM frames or remaining frames
        requested_pcm_frames = min(max(pcm_frames, 1),
                                   self.remaining_pcm_frames)
        requested_bytes = (self.bytes_per_pcm_frame * requested_pcm_frames)
        pcm_data = self.stream.read_bytes(requested_bytes)

        # raise exception if data block exhausted early
        if (len(pcm_data) < requested_bytes):
            from audiotools.text import ERR_AU_TRUNCATED_DATA
            raise IOError(ERR_AU_TRUNCATED_DATA)
        else:
            self.remaining_pcm_frames -= requested_pcm_frames

            # return parsed chunk
            return FrameList(pcm_data, self.channels, self.bits_per_sample,
                             True, True)

    def read_closed(self, pcm_frames):
        raise ValueError("cannot read closed stream")

    def seek(self, pcm_frame_offset):
        if (pcm_frame_offset < 0):
            from audiotools.text import ERR_NEGATIVE_SEEK
            raise ValueError(ERR_NEGATIVE_SEEK)

        # ensure one doesn't walk off the end of the file
        pcm_frame_offset = min(pcm_frame_offset, self.total_pcm_frames)

        # position file in data block
        self.stream.seek(
            self.data_offset + (pcm_frame_offset * self.bytes_per_pcm_frame),
            0)
        self.remaining_pcm_frames = (self.total_pcm_frames - pcm_frame_offset)

        return pcm_frame_offset

    def seek_closed(self, pcm_frame_offset):
        raise ValueError("cannot seek closed stream")

    def close(self):
        self.stream.close()
        self.read = self.read_closed
        self.seek = self.seek_closed
Esempio n. 5
0
class AiffReader(object):
    """a PCMReader object for reading AIFF file contents"""

    def __init__(self, aiff_filename):
        """aiff_filename is a string"""

        from audiotools.bitstream import BitstreamReader

        self.stream = BitstreamReader(open(aiff_filename, "rb"), False)

        # ensure FORM<size>AIFF header is ok
        try:
            (form,
             total_size,
             aiff) = self.stream.parse("4b 32u 4b")
        except struct.error:
            from audiotools.text import ERR_AIFF_INVALID_AIFF
            raise InvalidAIFF(ERR_AIFF_INVALID_AIFF)

        if form != b'FORM':
            from audiotools.text import ERR_AIFF_NOT_AIFF
            raise ValueError(ERR_AIFF_NOT_AIFF)
        elif aiff != b'AIFF':
            from audiotools.text import ERR_AIFF_INVALID_AIFF
            raise ValueError(ERR_AIFF_INVALID_AIFF)
        else:
            total_size -= 4
            comm_chunk_read = False

        # walk through chunks until "SSND" chunk encountered
        while total_size > 0:
            try:
                (chunk_id,
                 chunk_size) = self.stream.parse("4b 32u")
            except struct.error:
                from audiotools.text import ERR_AIFF_INVALID_AIFF
                raise ValueError(ERR_AIFF_INVALID_AIFF)

            if not frozenset(chunk_id).issubset(AiffAudio.PRINTABLE_ASCII):
                from audiotools.text import ERR_AIFF_INVALID_CHUNK_ID
                raise ValueError(ERR_AIFF_INVALID_CHUNK_ID)
            else:
                total_size -= 8

            if chunk_id == b"COMM":
                # when "COMM" chunk encountered,
                # use it to populate PCMReader attributes
                (self.channels,
                 self.total_pcm_frames,
                 self.bits_per_sample,
                 self.sample_rate,
                 channel_mask) = parse_comm(self.stream)
                self.channel_mask = int(channel_mask)
                self.bytes_per_pcm_frame = ((self.bits_per_sample // 8) *
                                            self.channels)
                self.remaining_pcm_frames = self.total_pcm_frames
                comm_chunk_read = True
            elif chunk_id == b"SSND":
                # when "SSND" chunk encountered,
                # strip off the "offset" and "block_size" attributes
                # and ready PCMReader for reading
                if not comm_chunk_read:
                    from audiotools.text import ERR_AIFF_PREMATURE_SSND_CHUNK
                    raise ValueError(ERR_AIFF_PREMATURE_SSND_CHUNK)
                else:
                    self.stream.skip_bytes(8)
                    self.ssnd_start = self.stream.getpos()
                    return
            else:
                # all other chunks are ignored
                self.stream.skip_bytes(chunk_size)

            if chunk_size % 2:
                if len(self.stream.read_bytes(1)) < 1:
                    from audiotools.text import ERR_AIFF_INVALID_CHUNK
                    raise ValueError(ERR_AIFF_INVALID_CHUNK)
                total_size -= (chunk_size + 1)
            else:
                total_size -= chunk_size
        else:
            # raise an error if no "SSND" chunk is encountered
            from audiotools.text import ERR_AIFF_NO_SSND_CHUNK
            raise ValueError(ERR_AIFF_NO_SSND_CHUNK)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.close()

    def read(self, pcm_frames):
        """try to read a pcm.FrameList with the given number of PCM frames"""

        # try to read requested PCM frames or remaining frames
        requested_pcm_frames = min(max(pcm_frames, 1),
                                   self.remaining_pcm_frames)
        requested_bytes = (self.bytes_per_pcm_frame *
                           requested_pcm_frames)
        pcm_data = self.stream.read_bytes(requested_bytes)

        # raise exception if "SSND" chunk exhausted early
        if len(pcm_data) < requested_bytes:
            from audiotools.text import ERR_AIFF_TRUNCATED_SSND_CHUNK
            raise IOError(ERR_AIFF_TRUNCATED_SSND_CHUNK)
        else:
            self.remaining_pcm_frames -= requested_pcm_frames

            # return parsed chunk
            return FrameList(pcm_data,
                             self.channels,
                             self.bits_per_sample,
                             True,
                             True)

    def read_closed(self, pcm_frames):
        raise ValueError("cannot read closed stream")

    def seek(self, pcm_frame_offset):
        """tries to seek to the given PCM frame offset
        returns the total amount of frames actually seeked over"""

        if pcm_frame_offset < 0:
            from audiotools.text import ERR_NEGATIVE_SEEK
            raise ValueError(ERR_NEGATIVE_SEEK)

        # ensure one doesn't walk off the end of the file
        pcm_frame_offset = min(pcm_frame_offset,
                               self.total_pcm_frames)

        # position file in "SSND" chunk
        self.stream.setpos(self.ssnd_start)
        self.stream.seek((pcm_frame_offset * self.bytes_per_pcm_frame), 1)
        self.remaining_pcm_frames = (self.total_pcm_frames -
                                     pcm_frame_offset)

        return pcm_frame_offset

    def seek_closed(self, pcm_frame_offset):
        raise ValueError("cannot seek closed stream")

    def close(self):
        """closes the stream for reading"""

        self.stream.close()
        self.read = self.read_closed
        self.seek = self.seek_closed