def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new MP2Audio object""" from audiotools import (PCMConverter, BufferedPCMReader, ChannelMask, __default_quality__, EncodingError) from audiotools.encoders import encode_mp2 import bisect if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) if pcmreader.sample_rate in (32000, 48000, 44100): sample_rate = pcmreader.sample_rate else: sample_rate = [32000, 32000, 44100, 48000][bisect.bisect([32000, 44100, 48000], pcmreader.sample_rate)] if total_pcm_frames is not None: from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) try: encode_mp2(filename, PCMConverter(pcmreader, sample_rate=sample_rate, channels=min(pcmreader.channels, 2), channel_mask=ChannelMask.from_channels( min(pcmreader.channels, 2)), bits_per_sample=16), int(compression)) if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return MP2Audio(filename) except (ValueError, IOError) as err: cls.__unlink__(filename) raise EncodingError(str(err)) finally: pcmreader.close()
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new VorbisAudio object""" from audiotools import (BufferedPCMReader, __default_quality__, EncodingError) from audiotools.encoders import encode_vorbis if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) if pcmreader.bits_per_sample not in {8, 16, 24}: from audiotools import UnsupportedBitsPerSample pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) if (pcmreader.channels > 2) and (pcmreader.channels <= 8): channel_mask = int(pcmreader.channel_mask) if ((channel_mask != 0) and (channel_mask not in (0x7, # FR, FC, FL 0x33, # FR, FL, BR, BL 0x37, # FR, FC, FL, BL, BR 0x3f, # FR, FC, FL, BL, BR, LFE 0x70f, # FL, FC, FR, SL, SR, BC, LFE 0x63f))): # FL, FC, FR, SL, SR, BL, BR, LFE from audiotools import UnsupportedChannelMask pcmreader.close() raise UnsupportedChannelMask(filename, channel_mask) if total_pcm_frames is not None: from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) try: encode_vorbis(filename, pcmreader, float(compression) / 10) if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return VorbisAudio(filename) except (ValueError, IOError) as err: cls.__unlink__(filename) raise EncodingError(str(err)) finally: pcmreader.close()
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new VorbisAudio object""" from audiotools import (BufferedPCMReader, __default_quality__, EncodingError) from audiotools.encoders import encode_vorbis if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) if pcmreader.bits_per_sample not in {8, 16, 24}: from audiotools import UnsupportedBitsPerSample pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) if (pcmreader.channels > 2) and (pcmreader.channels <= 8): channel_mask = int(pcmreader.channel_mask) if ((channel_mask != 0) and (channel_mask not in ( 0x7, # FR, FC, FL 0x33, # FR, FL, BR, BL 0x37, # FR, FC, FL, BL, BR 0x3f, # FR, FC, FL, BL, BR, LFE 0x70f, # FL, FC, FR, SL, SR, BC, LFE 0x63f))): # FL, FC, FR, SL, SR, BL, BR, LFE from audiotools import UnsupportedChannelMask pcmreader.close() raise UnsupportedChannelMask(filename, channel_mask) if total_pcm_frames is not None: from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) try: encode_vorbis(filename, pcmreader, float(compression) / 10) if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return VorbisAudio(filename) except (ValueError, IOError) as err: cls.__unlink__(filename) raise EncodingError(str(err)) finally: pcmreader.close()
def verify(self, progress=None): """verifies the current file for correctness returns True if the file is okay raises an InvalidFile with an error message if there is some problem with the file""" from audiotools import CounterPCMReader from audiotools import transfer_framelist_data from audiotools import to_pcm_progress try: (header, footer) = self.wave_header_footer() except IOError as err: raise InvalidWave(err) except ValueError as err: raise InvalidWave(err) # ensure header is valid try: (total_size, data_size) = validate_header(header) except ValueError as err: raise InvalidWave(err) # ensure "data" chunk has all its data counter = CounterPCMReader(to_pcm_progress(self, progress)) try: transfer_framelist_data(counter, lambda f: f) except (IOError, ValueError): from audiotools.text import ERR_WAV_TRUNCATED_DATA_CHUNK raise InvalidWave(ERR_WAV_TRUNCATED_DATA_CHUNK) data_bytes_written = counter.bytes_written() # ensure output data size matches the "data" chunk's size if data_size != data_bytes_written: from audiotools.text import ERR_WAV_TRUNCATED_DATA_CHUNK raise InvalidWave(ERR_WAV_TRUNCATED_DATA_CHUNK) # ensure footer validates correctly try: validate_footer(footer, data_bytes_written) except ValueError as err: from audiotools.text import ERR_WAV_INVALID_SIZE raise InvalidWave(ERR_WAV_INVALID_SIZE) # ensure total size is correct if (len(header) + data_size + len(footer)) != total_size: from audiotools.text import ERR_WAV_INVALID_SIZE raise InvalidWave(ERR_WAV_INVALID_SIZE) return True
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None, encoding_function=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new WavPackAudio object""" from audiotools.encoders import encode_wavpack from audiotools import BufferedPCMReader from audiotools import CounterPCMReader from audiotools import EncodingError from audiotools import __default_quality__ if pcmreader.bits_per_sample not in (8, 16, 24): # WavPack technically supports up to 32 bits-per-sample # but nothing else does # so I'll treat it as unsupported for now from audiotools import UnsupportedBitsPerSample pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) counter = CounterPCMReader(pcmreader) try: (encode_wavpack if encoding_function is None else encoding_function)( filename=filename, pcmreader=counter, total_pcm_frames=(total_pcm_frames if total_pcm_frames is not None else 0), compression=compression) counter.close() except (ValueError, IOError) as msg: counter.close() cls.__unlink__(filename) raise EncodingError(str(msg)) except Exception: counter.close() cls.__unlink__(filename) raise # ensure actual total PCM frames matches argument, if any if (((total_pcm_frames is not None) and (counter.frames_written != total_pcm_frames))): cls.__unlink__(filename) from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return cls(filename)
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None, encoding_function=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new WavPackAudio object""" from audiotools.encoders import encode_wavpack from audiotools import BufferedPCMReader from audiotools import CounterPCMReader from audiotools import EncodingError from audiotools import __default_quality__ if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) counter = CounterPCMReader(pcmreader) try: (encode_wavpack if encoding_function is None else encoding_function)( filename, BufferedPCMReader(counter), total_pcm_frames=(total_pcm_frames if total_pcm_frames is not None else 0), **cls.__options__[compression]) counter.close() except (ValueError, IOError) as msg: counter.close() cls.__unlink__(filename) raise EncodingError(str(msg)) except Exception: counter.close() cls.__unlink__(filename) raise # ensure actual total PCM frames matches argument, if any if (((total_pcm_frames is not None) and (counter.frames_written != total_pcm_frames))): cls.__unlink__(filename) from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return cls(filename)
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None, encoding_function=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new WavPackAudio object""" from audiotools.encoders import encode_wavpack from audiotools import BufferedPCMReader from audiotools import CounterPCMReader from audiotools import EncodingError from audiotools import __default_quality__ if pcmreader.bits_per_sample not in (8, 16, 24): # WavPack technically supports up to 32 bits-per-sample # but nothing else does # so I'll treat it as unsupported for now from audiotools import UnsupportedBitsPerSample pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) if (compression is None) or (compression not in cls.COMPRESSION_MODES): compression = __default_quality__(cls.NAME) counter = CounterPCMReader(pcmreader) try: (encode_wavpack if encoding_function is None else encoding_function)( filename=filename, pcmreader=counter, total_pcm_frames=(total_pcm_frames if total_pcm_frames is not None else 0), compression=compression, ) counter.close() except (ValueError, IOError) as msg: counter.close() cls.__unlink__(filename) raise EncodingError(str(msg)) except Exception: counter.close() cls.__unlink__(filename) raise # ensure actual total PCM frames matches argument, if any if (total_pcm_frames is not None) and (counter.frames_written != total_pcm_frames): cls.__unlink__(filename) from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return cls(filename)
def from_wave(cls, filename, header, pcmreader, footer, compression=None, encoding_function=None): """encodes a new file from wave data takes a filename string, header string, PCMReader object, footer string 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 WaveAudio object header + pcm data + footer should always result in the original wave file being restored without need for any padding bytes may raise EncodingError if some problem occurs when encoding the input file""" from audiotools.encoders import encode_wavpack from audiotools import BufferedPCMReader from audiotools import CounterPCMReader from audiotools.wav import (validate_header, validate_footer) from audiotools import EncodingError from audiotools import __default_quality__ if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) # ensure header is valid try: (total_size, data_size) = validate_header(header) except ValueError as err: raise EncodingError(str(err)) counter = CounterPCMReader(pcmreader) try: (encode_wavpack if encoding_function is None else encoding_function)(filename, BufferedPCMReader(counter), wave_header=header, wave_footer=footer, **cls.__options__[compression]) data_bytes_written = counter.bytes_written() # ensure output data size matches the "data" chunk's size if (data_size != data_bytes_written): from audiotools.text import ERR_WAV_TRUNCATED_DATA_CHUNK raise EncodingError(ERR_WAV_TRUNCATED_DATA_CHUNK) # ensure footer validates correctly try: validate_footer(footer, data_bytes_written) except ValueError as err: raise EncodingError(str(err)) # ensure total size is correct if ((len(header) + data_size + len(footer)) != total_size): from audiotools.text import ERR_WAV_INVALID_SIZE raise EncodingError(ERR_WAV_INVALID_SIZE) return cls(filename) except (ValueError, IOError) as msg: cls.__unlink__(filename) raise EncodingError(str(msg)) except Exception as err: cls.__unlink__(filename) raise err
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new WaveAudio object""" from audiotools import EncodingError from audiotools import DecodingError from audiotools import CounterPCMReader from audiotools import transfer_framelist_data if pcmreader.bits_per_sample not in {8, 16, 24}: from audiotools import UnsupportedBitsPerSample pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) try: header = wave_header(pcmreader.sample_rate, pcmreader.channels, pcmreader.channel_mask, pcmreader.bits_per_sample, total_pcm_frames if total_pcm_frames is not None else 0) except ValueError as err: pcmreader.close() raise EncodingError(str(err)) try: f = open(filename, "wb") except IOError as err: pcmreader.close() raise EncodingError(str(err)) counter = CounterPCMReader(pcmreader) f.write(header) try: transfer_framelist_data(counter, f.write, pcmreader.bits_per_sample > 8, False) except (IOError, ValueError) as err: f.close() cls.__unlink__(filename) raise EncodingError(str(err)) except Exception as err: f.close() cls.__unlink__(filename) raise err # handle odd-sized "data" chunks if counter.frames_written % 2: f.write(b"\x00") if total_pcm_frames is not None: f.close() if total_pcm_frames != counter.frames_written: # ensure written number of PCM frames # matches total_pcm_frames argument from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) else: # go back and rewrite populated header # with counted number of PCM frames f.seek(0, 0) f.write(wave_header(pcmreader.sample_rate, pcmreader.channels, pcmreader.channel_mask, pcmreader.bits_per_sample, counter.frames_written)) f.close() return WaveAudio(filename)
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object optional compression level string, and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new AudioFile-compatible object specifying total_pcm_frames, when the number is known in advance, may allow the encoder to work more efficiently but is never required """ import bisect import os import subprocess from audiotools import __default_quality__ from audiotools import transfer_framelist_data from audiotools import EncodingError from audiotools import PCMConverter from audiotools import ChannelMask if ((compression is None) or (compression not in cls.COMPRESSION_MODES)): compression = __default_quality__(cls.NAME) if pcmreader.bits_per_sample not in (8, 16, 24): from audiotools import UnsupportedBitsPerSample raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) if total_pcm_frames is not None: from audiotools import CounterPCMReader counter_reader = CounterPCMReader(pcmreader) else: counter_reader = pcmreader pcmreader = PCMConverter( counter_reader, sample_rate=[8000, 8000, 16000, 32000][bisect.bisect([8000, 16000, 32000], pcmreader.sample_rate)], channels=min(pcmreader.channels, 2), channel_mask=ChannelMask.from_channels(min(pcmreader.channels, 2)), bits_per_sample=min(pcmreader.bits_per_sample, 16)) BITS_PER_SAMPLE = { 8: ['--8bit'], 16: ['--16bit'] }[pcmreader.bits_per_sample] CHANNELS = {1: [], 2: ['--stereo']}[pcmreader.channels] sub = subprocess.Popen( [BIN['speexenc'], '--quality', str(compression), '--rate', str(pcmreader.sample_rate), '--le'] + \ BITS_PER_SAMPLE + \ CHANNELS + \ ['-', filename], stdin=subprocess.PIPE, stderr=subprocess.DEVNULL if hasattr(subprocess, "DEVNULL") else open(os.devnull, "wb")) try: transfer_framelist_data(pcmreader, sub.stdin.write) except (IOError, ValueError) as err: sub.stdin.close() sub.wait() cls.__unlink__(filename) raise EncodingError(str(err)) except Exception as err: sub.stdin.close() sub.wait() cls.__unlink__(filename) raise err sub.stdin.close() if sub.wait() == 0: if ((total_pcm_frames is None) or (total_pcm_frames == counter_reader.frames_written)): return SpeexAudio(filename) else: from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) else: raise EncodingError(u"unable to encode file with speexenc")
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new AudioFile-compatible object may raise EncodingError if some problem occurs when encoding the input file. This includes an error in the input stream, a problem writing the output file, or even an EncodingError subclass such as "UnsupportedBitsPerSample" if the input stream is formatted in a way this class is unable to support """ from audiotools import (BufferedPCMReader, PCMConverter, __default_quality__, EncodingError) from audiotools.encoders import encode_opus if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) if (pcmreader.channels > 2) and (pcmreader.channels <= 8): if ((pcmreader.channel_mask != 0) and (pcmreader.channel_mask not in {0x7, # FR, FC, FL 0x33, # FR, FL, BR, BL 0x37, # FR, FC, FL, BL, BR 0x3f, # FR, FC, FL, BL, BR, LFE 0x70f, # FL, FC, FR, SL, SR, BC, LFE 0x63f})): # FL, FC, FR, SL, SR, BL, BR, LFE from audiotools import UnsupportedChannelMask pcmreader.close() raise UnsupportedChannelMask(filename, channel_mask) try: if total_pcm_frames is not None: from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) encode_opus(filename, PCMConverter(pcmreader, sample_rate=48000, channels=pcmreader.channels, channel_mask=pcmreader.channel_mask, bits_per_sample=16), quality=int(compression), original_sample_rate=pcmreader.sample_rate) pcmreader.close() if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return cls(filename) except (ValueError, IOError) as err: pcmreader.close() cls.__unlink__(filename) raise EncodingError(err)
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new AuAudio object""" from audiotools import EncodingError from audiotools import DecodingError from audiotools import CounterPCMReader from audiotools import transfer_framelist_data if (pcmreader.bits_per_sample not in (8, 16, 24)): from audiotools import Filename from audiotools import UnsupportedBitsPerSample from audiotools.text import ERR_UNSUPPORTED_BITS_PER_SAMPLE raise UnsupportedBitsPerSample( ERR_UNSUPPORTED_BITS_PER_SAMPLE % { "target_filename": Filename(filename), "bps": pcmreader.bits_per_sample }) try: header = au_header( pcmreader.sample_rate, pcmreader.channels, pcmreader.bits_per_sample, total_pcm_frames if total_pcm_frames is not None else 0) except ValueError as err: raise EncodingError(str(err)) try: f = open(filename, "wb") except IOError as err: raise EncodingError(str(err)) counter = CounterPCMReader(pcmreader) f.write(header) try: transfer_framelist_data(counter, f.write, True, True) except (IOError, ValueError) as err: cls.__unlink__(filename) raise EncodingError(str(err)) if (total_pcm_frames is not None): if (total_pcm_frames != counter.frames_written): # ensure written number of PCM frames # matches total_pcm_frames argument from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) else: f.close() else: # go back and rewrite populated header # with counted number of PCM frames f.seek(0, 0) f.write( au_header(pcmreader.sample_rate, pcmreader.channels, pcmreader.bits_per_sample, counter.frames_written)) f.close() return AuAudio(filename)
def from_aiff(cls, filename, header, pcmreader, footer, compression=None, block_size=256, encoding_function=None): """encodes a new file from AIFF data takes a filename string, header string, PCMReader object, footer string 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 AiffAudio object header + pcm data + footer should always result in the original AIFF file being restored without need for any padding bytes may raise EncodingError if some problem occurs when encoding the input file""" from audiotools import (CounterPCMReader, BufferedPCMReader, UnsupportedBitsPerSample, EncodingError) from audiotools.aiff import (validate_header, validate_footer) if (encoding_function is None): from audiotools.encoders import encode_shn else: encode_shn = encoding_function if (pcmreader.bits_per_sample not in (8, 16)): raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) # ensure header is valid try: (total_size, ssnd_size) = validate_header(header) except ValueError as err: raise EncodingError(str(err)) counter = CounterPCMReader(pcmreader) try: if (len(footer) == 0): encode_shn(filename=filename, pcmreader=BufferedPCMReader(counter), is_big_endian=True, signed_samples=True, header_data=header, block_size=block_size) else: encode_shn(filename=filename, pcmreader=BufferedPCMReader(counter), is_big_endian=True, signed_samples=True, header_data=header, footer_data=footer, block_size=block_size) ssnd_bytes_written = counter.bytes_written() # ensure output data size matches the "SSND" chunk's size if (ssnd_size != ssnd_bytes_written): from audiotools.text import ERR_AIFF_TRUNCATED_SSND_CHUNK raise EncodingError(ERR_AIFF_TRUNCATED_SSND_CHUNK) # ensure footer validates correctly try: validate_footer(footer, ssnd_bytes_written) except ValueError as err: raise EncodingError(str(err)) # ensure total size is correct if ((len(header) + ssnd_size + len(footer)) != total_size): from audiotools.text import ERR_AIFF_INVALID_SIZE raise EncodingError(ERR_AIFF_INVALID_SIZE) return cls(filename) except IOError as err: cls.__unlink__(filename) raise EncodingError(str(err)) except Exception as err: cls.__unlink__(filename) raise err
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new AiffAudio object""" from audiotools import EncodingError from audiotools import DecodingError from audiotools import CounterPCMReader from audiotools import transfer_framelist_data try: header = aiff_header( pcmreader.sample_rate, pcmreader.channels, pcmreader.bits_per_sample, total_pcm_frames if total_pcm_frames is not None else 0) except ValueError as err: raise EncodingError(str(err)) try: f = open(filename, "wb") except IOError as msg: raise EncodingError(str(msg)) counter = CounterPCMReader(pcmreader) f.write(header) try: transfer_framelist_data(counter, f.write, True, True) except (IOError, ValueError) as err: cls.__unlink__(filename) raise EncodingError(str(err)) # handle odd-sized SSND chunks if (counter.frames_written % 2): f.write(chr(0)) if (total_pcm_frames is not None): if (total_pcm_frames != counter.frames_written): # ensure written number of PCM frames # matches total_pcm_frames argument from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) else: f.close() else: # go back and rewrite populated header # with counted number of PCM frames f.seek(0, 0) f.write( aiff_header(pcmreader.sample_rate, pcmreader.channels, pcmreader.bits_per_sample, counter.frames_written)) f.close() return AiffAudio(filename)
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new M4AAudio object""" import subprocess import os from audiotools import PCMConverter from audiotools import transfer_data from audiotools import transfer_framelist_data from audiotools import ignore_sigint from audiotools import EncodingError from audiotools import DecodingError from audiotools import ChannelMask from audiotools import __default_quality__ if ((compression is None) or (compression not in cls.COMPRESSION_MODES)): compression = __default_quality__(cls.NAME) if pcmreader.channels > 2: pcmreader = PCMConverter(pcmreader, sample_rate=pcmreader.sample_rate, channels=2, channel_mask=ChannelMask.from_channels(2), bits_per_sample=pcmreader.bits_per_sample) # faac requires files to end with .m4a for some reason if not filename.endswith(".m4a"): import tempfile actual_filename = filename tempfile = tempfile.NamedTemporaryFile(suffix=".m4a") filename = tempfile.name else: actual_filename = tempfile = None sub = subprocess.Popen( [ BIN['faac'], "-q", compression, "-P", "-R", str(pcmreader.sample_rate), "-B", str(pcmreader.bits_per_sample), "-C", str(pcmreader.channels), "-X", "-o", filename, "-" ], stdin=subprocess.PIPE, stderr=subprocess.DEVNULL if hasattr(subprocess, "DEVNULL") else open(os.devnull, "wb"), stdout=subprocess.DEVNULL if hasattr(subprocess, "DEVNULL") else open(os.devnull, "wb"), preexec_fn=ignore_sigint) # Note: faac handles SIGINT on its own, # so trying to ignore it doesn't work like on most other encoders. try: if total_pcm_frames is not None: from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) transfer_framelist_data(pcmreader, sub.stdin.write) if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) except (ValueError, IOError) as err: sub.stdin.close() sub.wait() cls.__unlink__(filename) raise EncodingError(str(err)) except Exception: sub.stdin.close() sub.wait() cls.__unlink__(filename) raise sub.stdin.close() if sub.wait() == 0: if tempfile is not None: filename = actual_filename f = open(filename, 'wb') tempfile.seek(0, 0) transfer_data(tempfile.read, f.write) f.close() tempfile.close() return M4AAudio(filename) else: if tempfile is not None: tempfile.close() raise EncodingError(u"unable to write file with faac")
def from_wave(cls, filename, header, pcmreader, footer, compression=None, block_size=256, encoding_function=None): """encodes a new file from wave data takes a filename string, header string, PCMReader object, footer string 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 WaveAudio object header + pcm data + footer should always result in the original wave file being restored without need for any padding bytes may raise EncodingError if some problem occurs when encoding the input file""" from audiotools import (CounterPCMReader, BufferedPCMReader, UnsupportedBitsPerSample, EncodingError) from audiotools.wav import (validate_header, validate_footer) if encoding_function is None: from audiotools.encoders import encode_shn else: encode_shn = encoding_function if pcmreader.bits_per_sample not in {8, 16}: pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) # ensure header is valid try: (total_size, data_size) = validate_header(header) except ValueError as err: pcmreader.close() raise EncodingError(str(err)) counter = CounterPCMReader(pcmreader) try: if len(footer) == 0: encode_shn(filename=filename, pcmreader=BufferedPCMReader(counter), is_big_endian=False, signed_samples=pcmreader.bits_per_sample == 16, header_data=header, block_size=block_size) else: encode_shn(filename=filename, pcmreader=BufferedPCMReader(counter), is_big_endian=False, signed_samples=pcmreader.bits_per_sample == 16, header_data=header, footer_data=footer, block_size=block_size) counter.close() data_bytes_written = counter.bytes_written() # ensure output data size matches the "data" chunk's size if data_size != data_bytes_written: from audiotools.text import ERR_WAV_TRUNCATED_DATA_CHUNK raise EncodingError(ERR_WAV_TRUNCATED_DATA_CHUNK) # ensure footer validates correctly try: validate_footer(footer, data_bytes_written) except ValueError as err: raise EncodingError(str(err)) # ensure total size is correct if (len(header) + data_size + len(footer)) != total_size: from audiotools.text import ERR_WAV_INVALID_SIZE raise EncodingError(ERR_WAV_INVALID_SIZE) return cls(filename) except (IOError, ValueError) as err: counter.close() cls.__unlink__(filename) raise EncodingError(str(err)) except Exception as err: counter.close() cls.__unlink__(filename) raise err
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None, encoding_function=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new AudioFile-compatible object may raise EncodingError if some problem occurs when encoding the input file. This includes an error in the input stream, a problem writing the output file, or even an EncodingError subclass such as "UnsupportedBitsPerSample" if the input stream is formatted in a way this class is unable to support """ from audiotools import (BufferedPCMReader, CounterPCMReader, transfer_data, EncodingError) # from audiotools.py_encoders import encode_tta from audiotools.encoders import encode_tta from audiotools.bitstream import BitstreamWriter # open output file right away # so we can fail as soon as possible try: file = open(filename, "wb") except IOError as err: pcmreader.close() raise EncodingError(str(err)) writer = BitstreamWriter(file, True) counter = CounterPCMReader(pcmreader) try: if (total_pcm_frames is not None): # write header to disk write_header(writer, pcmreader.channels, pcmreader.bits_per_sample, pcmreader.sample_rate, total_pcm_frames) block_size = (pcmreader.sample_rate * 256) // 245 total_tta_frames = div_ceil(total_pcm_frames, block_size) # write temporary seektable to disk writer.mark() write_seektable(writer, [0] * total_tta_frames) writer.flush() # write frames to disk try: frame_sizes = \ (encode_tta if encoding_function is None else encoding_function)(file, BufferedPCMReader(counter)) except (IOError, ValueError) as err: cls.__unlink__(filename) raise EncodingError(str(err)) # ensure written number of PCM frames # matches total_pcm_frames if (counter.frames_written != total_pcm_frames): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) assert (len(frame_sizes) == total_tta_frames) # go back and rewrite seektable with completed one writer.rewind() write_seektable(writer, frame_sizes) writer.unmark() else: import tempfile frames = tempfile.TemporaryFile() # encode TTA frames to temporary file try: frame_sizes = \ (encode_tta if encoding_function is None else encoding_function)(frames, BufferedPCMReader(counter)) except (IOError, ValueError) as err: frames.close() cls.__unlink__(filename) raise EncodingError(str(err)) # write header to disk write_header(writer, pcmreader.channels, pcmreader.bits_per_sample, pcmreader.sample_rate, counter.frames_written) # write seektable to disk write_seektable(writer, frame_sizes) # transfer TTA frames from temporary space to disk frames.seek(0, 0) transfer_data(frames.read, writer.write_bytes) frames.close() finally: counter.close() if (writer.has_mark()): writer.unmark() writer.close() return cls(filename)
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): from audiotools import __default_quality__ from audiotools import PCMConverter from audiotools import ChannelMask from audiotools.encoders import encode_mpc if (compression is None) or (compression not in cls.COMPRESSION_MODES): compression = __default_quality__(cls.NAME) if pcmreader.bits_per_sample not in {8, 16, 24}: from audiotools import UnsupportedBitsPerSample pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) if pcmreader.sample_rate in (32000, 37800, 44100, 48000): sample_rate = pcmreader.sample_rate if total_pcm_frames is not None: from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) else: from bisect import bisect sample_rate = [32000, 32000, 37800, 44100, 48000][bisect([32000, 37800, 44100, 4800], pcmreader.sample_rate)] total_pcm_frames = None try: encode_mpc( filename, PCMConverter(pcmreader, sample_rate=sample_rate, channels=min(pcmreader.channels, 2), channel_mask=int( ChannelMask.from_channels( min(pcmreader.channels, 2))), bits_per_sample=16), float(compression), total_pcm_frames if (total_pcm_frames is not None) else 0) # ensure PCM frames match, if indicated if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH from audiotools import EncodingError raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return MPCAudio(filename) except (IOError, ValueError) as err: from audiotools import EncodingError cls.__unlink__(filename) raise EncodingError(str(err)) except Exception: cls.__unlink__(filename) raise finally: pcmreader.close()
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): from audiotools import __default_quality__ from audiotools import PCMConverter from audiotools import ChannelMask from audiotools.encoders import encode_mpc if (compression is None) or (compression not in cls.COMPRESSION_MODES): compression = __default_quality__(cls.NAME) if pcmreader.bits_per_sample not in {8, 16, 24}: from audiotools import UnsupportedBitsPerSample pcmreader.close() raise UnsupportedBitsPerSample(filename, pcmreader.bits_per_sample) if pcmreader.sample_rate in (32000, 37800, 44100, 48000): sample_rate = pcmreader.sample_rate if total_pcm_frames is not None: from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) else: from bisect import bisect sample_rate = [32000, 32000, 37800, 44100, 48000][bisect([32000, 37800, 44100, 4800], pcmreader.sample_rate)] total_pcm_frames = None try: encode_mpc( filename, PCMConverter(pcmreader, sample_rate=sample_rate, channels=min(pcmreader.channels, 2), channel_mask=int(ChannelMask.from_channels( min(pcmreader.channels, 2))), bits_per_sample=16), float(compression), total_pcm_frames if (total_pcm_frames is not None) else 0) # ensure PCM frames match, if indicated if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH from audiotools import EncodingError raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return MPCAudio(filename) except (IOError, ValueError) as err: from audiotools import EncodingError cls.__unlink__(filename) raise EncodingError(str(err)) except Exception: cls.__unlink__(filename) raise finally: pcmreader.close()
def from_wave(cls, filename, header, pcmreader, footer, compression=None, encoding_function=None): """encodes a new file from wave data takes a filename string, header string, PCMReader object, footer string 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 WaveAudio object header + pcm data + footer should always result in the original wave file being restored without need for any padding bytes may raise EncodingError if some problem occurs when encoding the input file""" from audiotools.encoders import encode_wavpack from audiotools import BufferedPCMReader from audiotools import CounterPCMReader from audiotools.wav import (validate_header, validate_footer) from audiotools import EncodingError from audiotools import __default_quality__ if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) # ensure header is valid try: (total_size, data_size) = validate_header(header) except ValueError as err: raise EncodingError(str(err)) counter = CounterPCMReader(pcmreader) try: (encode_wavpack if encoding_function is None else encoding_function)(filename, BufferedPCMReader(counter), wave_header=header, wave_footer=footer, **cls.__options__[compression]) counter.close() data_bytes_written = counter.bytes_written() # ensure output data size matches the "data" chunk's size if (data_size != data_bytes_written): from audiotools.text import ERR_WAV_TRUNCATED_DATA_CHUNK raise EncodingError(ERR_WAV_TRUNCATED_DATA_CHUNK) # ensure footer validates correctly try: validate_footer(footer, data_bytes_written) except ValueError as err: raise EncodingError(str(err)) # ensure total size is correct if ((len(header) + data_size + len(footer)) != total_size): from audiotools.text import ERR_WAV_INVALID_SIZE raise EncodingError(ERR_WAV_INVALID_SIZE) return cls(filename) except (ValueError, IOError) as msg: counter.close() cls.__unlink__(filename) raise EncodingError(str(msg)) except Exception as err: counter.close() cls.__unlink__(filename) raise err
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new AudioFile-compatible object may raise EncodingError if some problem occurs when encoding the input file. This includes an error in the input stream, a problem writing the output file, or even an EncodingError subclass such as "UnsupportedBitsPerSample" if the input stream is formatted in a way this class is unable to support """ from audiotools import (BufferedPCMReader, PCMConverter, __default_quality__, EncodingError) from audiotools.encoders import encode_opus if (((compression is None) or (compression not in cls.COMPRESSION_MODES))): compression = __default_quality__(cls.NAME) if ((pcmreader.channels > 2) and (pcmreader.channels <= 8)): channel_mask = int(pcmreader.channel_mask) if ((channel_mask != 0) and (channel_mask not in ( 0x7, # FR, FC, FL 0x33, # FR, FL, BR, BL 0x37, # FR, FC, FL, BL, BR 0x3f, # FR, FC, FL, BL, BR, LFE 0x70f, # FL, FC, FR, SL, SR, BC, LFE 0x63f))): # FL, FC, FR, SL, SR, BL, BR, LFE from audiotools import UnsupportedChannelMask raise UnsupportedChannelMask(filename, channel_mask) try: if (total_pcm_frames is not None): from audiotools import CounterPCMReader pcmreader = CounterPCMReader(pcmreader) encode_opus(filename, BufferedPCMReader( PCMConverter(pcmreader, sample_rate=48000, channels=pcmreader.channels, channel_mask=pcmreader.channel_mask, bits_per_sample=16)), quality=int(compression), original_sample_rate=pcmreader.sample_rate) if ((total_pcm_frames is not None) and (total_pcm_frames != pcmreader.frames_written)): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) return cls(filename) except (ValueError, IOError) as err: cls.__unlink__(filename) raise EncodingError(err)
def from_pcm(cls, filename, pcmreader, compression=None, total_pcm_frames=None, encoding_function=None): """encodes a new file from PCM data takes a filename string, PCMReader object, optional compression level string and optional total_pcm_frames integer encodes a new audio file from pcmreader's data at the given filename with the specified compression level and returns a new AudioFile-compatible object may raise EncodingError if some problem occurs when encoding the input file. This includes an error in the input stream, a problem writing the output file, or even an EncodingError subclass such as "UnsupportedBitsPerSample" if the input stream is formatted in a way this class is unable to support """ from audiotools import (BufferedPCMReader, CounterPCMReader, transfer_data, EncodingError) # from audiotools.py_encoders import encode_tta from audiotools.encoders import encode_tta from audiotools.bitstream import BitstreamWriter # open output file right away # so we can fail as soon as possible try: file = open(filename, "wb") except IOError as err: pcmreader.close() raise EncodingError(str(err)) writer = BitstreamWriter(file, True) counter = CounterPCMReader(pcmreader) try: if (total_pcm_frames is not None): # write header to disk write_header(writer, pcmreader.channels, pcmreader.bits_per_sample, pcmreader.sample_rate, total_pcm_frames) block_size = (pcmreader.sample_rate * 256) // 245 total_tta_frames = div_ceil(total_pcm_frames, block_size) # write temporary seektable to disk writer.mark() write_seektable(writer, [0] * total_tta_frames) writer.flush() # write frames to disk try: frame_sizes = \ (encode_tta if encoding_function is None else encoding_function)(file, BufferedPCMReader(counter)) except (IOError, ValueError) as err: cls.__unlink__(filename) raise EncodingError(str(err)) # ensure written number of PCM frames # matches total_pcm_frames if (counter.frames_written != total_pcm_frames): from audiotools.text import ERR_TOTAL_PCM_FRAMES_MISMATCH cls.__unlink__(filename) raise EncodingError(ERR_TOTAL_PCM_FRAMES_MISMATCH) assert(len(frame_sizes) == total_tta_frames) # go back and rewrite seektable with completed one writer.rewind() write_seektable(writer, frame_sizes) writer.unmark() else: import tempfile frames = tempfile.TemporaryFile() # encode TTA frames to temporary file try: frame_sizes = \ (encode_tta if encoding_function is None else encoding_function)(frames, BufferedPCMReader(counter)) except (IOError, ValueError) as err: frames.close() cls.__unlink__(filename) raise EncodingError(str(err)) # write header to disk write_header(writer, pcmreader.channels, pcmreader.bits_per_sample, pcmreader.sample_rate, counter.frames_written) # write seektable to disk write_seektable(writer, frame_sizes) # transfer TTA frames from temporary space to disk frames.seek(0, 0) transfer_data(frames.read, writer.write_bytes) frames.close() finally: counter.close() if (writer.has_mark()): writer.unmark() writer.close() return cls(filename)