示例#1
0
    def delete_metadata(self):
        """Deletes the track's MetaData.

        This removes or unsets tags as necessary in order to remove all data.
        Raises IOError if unable to write the file."""

        import tempfile

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        Con.Container(chunk_id=chunk_id,
                                      chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)

        header = Con.Container(aiff_id='FORM',
                               aiff_size=new_aiff.tell() - 8,
                               aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
示例#2
0
    def set_metadata(self, metadata):
        """Takes a MetaData object and sets this track's metadata.

        This metadata includes track name, album name, and so on.
        Raises IOError if unable to write the file."""

        if (metadata is None):
            return

        import tempfile

        id3_chunk = ID3v22Comment.converted(metadata).build()

        new_aiff = tempfile.TemporaryFile()
        new_aiff.seek(12, 0)

        id3_found = False
        for (chunk_id, chunk_length, chunk_file) in self.chunk_files():
            if (chunk_id != 'ID3 '):
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        Con.Container(chunk_id=chunk_id,
                                      chunk_length=chunk_length)))
                transfer_data(chunk_file.read, new_aiff.write)
            else:
                new_aiff.write(
                    self.CHUNK_HEADER.build(
                        Con.Container(chunk_id='ID3 ',
                                      chunk_length=len(id3_chunk))))
                new_aiff.write(id3_chunk)
                id3_found = True

        if (not id3_found):
            new_aiff.write(
                self.CHUNK_HEADER.build(
                    Con.Container(chunk_id='ID3 ',
                                  chunk_length=len(id3_chunk))))
            new_aiff.write(id3_chunk)

        header = Con.Container(aiff_id='FORM',
                               aiff_size=new_aiff.tell() - 8,
                               aiff_type='AIFF')
        new_aiff.seek(0, 0)
        new_aiff.write(self.AIFF_HEADER.build(header))
        new_aiff.seek(0, 0)
        f = open(self.filename, 'wb')
        transfer_data(new_aiff.read, f.write)
        new_aiff.close()
        f.close()
示例#3
0
    def from_pcm(cls, filename, pcmreader, compression=None):
        """Encodes a new file from PCM data.

        Takes a filename string, PCMReader object
        and optional compression level string.
        Encodes a new audio file from pcmreader's data
        at the given filename with the specified compression level
        and returns a new AuAudio object."""

        if (pcmreader.bits_per_sample not in (8, 16, 24)):
            raise InvalidFormat(
                _(u"Unsupported bits per sample %s") %
                (pcmreader.bits_per_sample))

        bytes_per_sample = pcmreader.bits_per_sample / 8

        header = Con.Container(magic_number='.snd',
                               data_offset=0,
                               data_size=0,
                               encoding_format={
                                   8: 2,
                                   16: 3,
                                   24: 4
                               }[pcmreader.bits_per_sample],
                               sample_rate=pcmreader.sample_rate,
                               channels=pcmreader.channels)

        try:
            f = file(filename, 'wb')
        except IOError, err:
            raise EncodingError(str(err))
示例#4
0
    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')
示例#5
0
    def build(self):
        """Returns this VorbisComment as a binary string."""

        comment = Con.Container(vendor_string=self.vendor_string,
                                framing=1,
                                value=[])

        for (key, values) in self.items():
            for value in values:
                if ((value != u"")
                        and not ((key in ("TRACKNUMBER", "TRACKTOTAL",
                                          "DISCNUMBER", "DISCTOTAL")) and
                                 (value == u"0"))):
                    comment.value.append("%s=%s" %
                                         (key, value.encode('utf-8')))
        return self.VORBIS_COMMENT.build(comment)
示例#6
0
    def _encode(self, value, context):
        import math

        if (value < 0):
            signed = True
            value *= -1
        else:
            signed = False

        (fmant, exponent) = math.frexp(value)
        if ((exponent > 16384) or (fmant >= 1)):
            exponent = 0x7FFF
            mantissa = 0
        else:
            exponent += 16382
            mantissa = fmant * (2**64)

        return Con.Container(signed=signed,
                             exponent=exponent,
                             mantissa=mantissa)
示例#7
0
    def build_id3v1(cls, song_title, artist, album, year, comment,
                    track_number):
        """Turns fields into a complete ID3v1 binary tag string.

        All fields are unicode except for track_number, an int."""
        def __s_pad__(s, length):
            if (len(s) < length):
                return s + chr(0) * (length - len(s))
            else:
                s = s[0:length].rstrip()
                return s + chr(0) * (length - len(s))

        c = Con.Container()
        c.identifier = 'TAG'
        c.song_title = __s_pad__(song_title.encode('ascii', 'replace'), 30)
        c.artist = __s_pad__(artist.encode('ascii', 'replace'), 30)
        c.album = __s_pad__(album.encode('ascii', 'replace'), 30)
        c.year = __s_pad__(year.encode('ascii', 'replace'), 4)
        c.comment = __s_pad__(comment.encode('ascii', 'replace'), 28)
        c.track_number = int(track_number)
        c.genre = 0

        return ID3v1Comment.ID3v1.build(c)
示例#8
0
        if (int(pcmreader.channel_mask) in (
                0x4,  # FC
                0x3,  # FL, FR
                0x7,  # FL, FR, FC
                0x33,  # FL, FR, BL, BR
                0x707)):  # FL, SL, FC, FR, SR, BC
            standard_channel_mask = ChannelMask(pcmreader.channel_mask)
            aiff_channel_mask = AIFFChannelMask(standard_channel_mask)
            pcmreader = ReorderedPCMReader(pcmreader, [
                standard_channel_mask.channels().index(channel)
                for channel in aiff_channel_mask.channels()
            ])

        try:
            aiff_header = Con.Container(aiff_id='FORM',
                                        aiff_size=4,
                                        aiff_type='AIFF')

            comm_chunk = Con.Container(channels=pcmreader.channels,
                                       total_sample_frames=0,
                                       sample_size=pcmreader.bits_per_sample,
                                       sample_rate=float(
                                           pcmreader.sample_rate))

            ssnd_header = Con.Container(chunk_id='SSND', chunk_length=0)
            ssnd_alignment = Con.Container(offset=0, blocksize=0)

            #skip ahead to the start of the SSND chunk
            f.seek(
                cls.AIFF_HEADER.sizeof() + cls.CHUNK_HEADER.sizeof() +
                cls.COMM_CHUNK.sizeof() + cls.CHUNK_HEADER.sizeof(), 0)
 def _build(self, obj, stream, context):
     data = self.sub_atom.build(obj)
     stream.write(
         self.header.build(
             Con.Container(type=self.atom_name, size=len(data) + 8)))
     stream.write(data)
示例#10
0
    def set_metadata(self, metadata):
        """Takes a MetaData object and sets this track's metadata.

        This metadata includes track name, album name, and so on.
        Raises IOError if unable to write the file."""

        metadata = VorbisComment.converted(metadata)

        if (metadata is None):
            return

        reader = OggStreamReader(file(self.filename, 'rb'))
        new_file = cStringIO.StringIO()
        writer = OggStreamWriter(new_file)
        current_sequence_number = 0

        pages = reader.pages()

        #transfer our old header
        #this must always be the first packet and the first page
        (header_page, header_data) = pages.next()
        writer.write_page(header_page, header_data)
        current_sequence_number += 1

        #grab the current "comment" and "setup headers" packets
        #these may take one or more pages,
        #but will always end on a page boundary
        del (pages)
        packets = reader.packets(from_beginning=False)

        comment_packet = packets.next()
        headers_packet = packets.next()

        #write the pages for our new "comment" packet
        for (page, data) in OggStreamWriter.build_pages(
                0, header_page.bitstream_serial_number,
                current_sequence_number,
                VorbisAudio.COMMENT_HEADER.build(
                    Con.Container(packet_type=3, vorbis='vorbis')) +
                metadata.build()):
            writer.write_page(page, data)
            current_sequence_number += 1

        #write the pages for the old "setup headers" packet
        for (page, data) in OggStreamWriter.build_pages(
                0, header_page.bitstream_serial_number,
                current_sequence_number, headers_packet):
            writer.write_page(page, data)
            current_sequence_number += 1

        #write the rest of the pages, re-sequenced and re-checksummed
        del (packets)
        pages = reader.pages(from_beginning=False)

        for (i, (page, data)) in enumerate(pages):
            page.page_sequence_number = i + current_sequence_number
            page.checksum = OggStreamReader.calculate_ogg_checksum(page, data)
            writer.write_page(page, data)

        reader.close()

        #re-write the file with our new data in "new_file"
        f = file(self.filename, "wb")
        f.write(new_file.getvalue())
        f.close()
        writer.close()

        self.__read_metadata__()
示例#11
0
    def build_pages(cls,
                    granule_position,
                    serial_number,
                    starting_sequence_number,
                    packet_data,
                    header_type=0):
        """Constructs an Ogg packet for page data.

         takes serial_number, granule_position and starting_sequence_number
         integers and a packet_data string.
         Returns a list of (page_header,page_data) tuples containing
         all of the Ogg pages necessary to contain the packet.
        """

        page = Con.Container(magic_number='OggS',
                             version=0,
                             header_type=header_type,
                             granule_position=granule_position,
                             bitstream_serial_number=serial_number,
                             page_sequence_number=starting_sequence_number,
                             checksum=0)

        if (len(packet_data) == 0):
            #an empty Ogg page, but possibly a continuation

            page.segments = 0
            page.segment_lengths = []
            page.checksum = OggStreamReader.calculate_ogg_checksum(
                page, packet_data)
            return [(page, "")]
        if (len(packet_data) > (255 * 255)):
            #if we need more than one Ogg page to store the packet,
            #handle that case recursively

            page.segments = 255
            page.segment_lengths = [255] * 255
            page.checksum = OggStreamReader.calculate_ogg_checksum(
                page, packet_data[0:255 * 255])

            return [(page, packet_data[0:255 * 255])] + \
                   cls.build_pages(granule_position,
                                   serial_number,
                                   starting_sequence_number + 1,
                                   packet_data[255 * 255:],
                                   header_type)
        elif (len(packet_data) == (255 * 255)):
            #we need two Ogg pages, one of which is empty

            return cls.build_pages(granule_position,
                                   serial_number,
                                   starting_sequence_number,
                                   packet_data,
                                   header_type) + \
                   cls.build_pages(granule_position,
                                   serial_number,
                                   starting_sequence_number + 1,
                                   "",
                                   header_type)
        else:
            #we just need one Ogg page

            page.segments = len(packet_data) / 255
            if ((len(packet_data) % 255) > 0):
                page.segments += 1

            page.segment_lengths = [255] * (len(packet_data) / 255)
            if ((len(packet_data) % 255) > 0):
                page.segment_lengths += [len(packet_data) % 255]

            page.checksum = OggStreamReader.calculate_ogg_checksum(
                page, packet_data)
            return [(page, packet_data)]