diff --git a/sbapp/pydub/__init__.py b/sbapp/pydub/__init__.py deleted file mode 100644 index 65e30b4..0000000 --- a/sbapp/pydub/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .audio_segment import AudioSegment \ No newline at end of file diff --git a/sbapp/pydub/audio_segment.py b/sbapp/pydub/audio_segment.py deleted file mode 100644 index 14ea46e..0000000 --- a/sbapp/pydub/audio_segment.py +++ /dev/null @@ -1,1399 +0,0 @@ -from __future__ import division - -import array -import os -import subprocess -from tempfile import TemporaryFile, NamedTemporaryFile -import wave -import sys -import struct -from .logging_utils import log_conversion, log_subprocess_output -from .utils import mediainfo_json, fsdecode -import base64 -from collections import namedtuple - -try: - from StringIO import StringIO -except: - from io import StringIO - -from io import BytesIO - -try: - from itertools import izip -except: - izip = zip - -from .utils import ( - _fd_or_path_or_tempfile, - db_to_float, - ratio_to_db, - get_encoder_name, - get_array_type, - audioop, -) -from .exceptions import ( - TooManyMissingFrames, - InvalidDuration, - InvalidID3TagVersion, - InvalidTag, - CouldntDecodeError, - CouldntEncodeError, - MissingAudioParameter, -) - -if sys.version_info >= (3, 0): - basestring = str - xrange = range - StringIO = BytesIO - - -class ClassPropertyDescriptor(object): - - def __init__(self, fget, fset=None): - self.fget = fget - self.fset = fset - - def __get__(self, obj, klass=None): - if klass is None: - klass = type(obj) - return self.fget.__get__(obj, klass)() - - def __set__(self, obj, value): - if not self.fset: - raise AttributeError("can't set attribute") - type_ = type(obj) - return self.fset.__get__(obj, type_)(value) - - def setter(self, func): - if not isinstance(func, (classmethod, staticmethod)): - func = classmethod(func) - self.fset = func - return self - - -def classproperty(func): - if not isinstance(func, (classmethod, staticmethod)): - func = classmethod(func) - - return ClassPropertyDescriptor(func) - - -AUDIO_FILE_EXT_ALIASES = { - "m4a": "mp4", - "wave": "wav", -} - -WavSubChunk = namedtuple('WavSubChunk', ['id', 'position', 'size']) -WavData = namedtuple('WavData', ['audio_format', 'channels', 'sample_rate', - 'bits_per_sample', 'raw_data']) - - -def extract_wav_headers(data): - # def search_subchunk(data, subchunk_id): - pos = 12 # The size of the RIFF chunk descriptor - subchunks = [] - while pos + 8 <= len(data) and len(subchunks) < 10: - subchunk_id = data[pos:pos + 4] - subchunk_size = struct.unpack_from(' 2**32: - raise CouldntDecodeError("Unable to process >4GB files") - - # Set the file size in the RIFF chunk descriptor - data[4:8] = struct.pack(' b'\x7f'[0]]) - old_bytes = struct.pack(pack_fmt, b0, b1, b2) - byte_buffer.write(old_bytes) - - self._data = byte_buffer.getvalue() - self.sample_width = 4 - self.frame_width = self.channels * self.sample_width - - super(AudioSegment, self).__init__(*args, **kwargs) - - @property - def raw_data(self): - """ - public access to the raw audio data as a bytestring - """ - return self._data - - def get_array_of_samples(self, array_type_override=None): - """ - returns the raw_data as an array of samples - """ - if array_type_override is None: - array_type_override = self.array_type - return array.array(array_type_override, self._data) - - @property - def array_type(self): - return get_array_type(self.sample_width * 8) - - def __len__(self): - """ - returns the length of this audio segment in milliseconds - """ - return round(1000 * (self.frame_count() / self.frame_rate)) - - def __eq__(self, other): - try: - return self._data == other._data - except: - return False - - def __hash__(self): - return hash(AudioSegment) ^ hash((self.channels, self.frame_rate, self.sample_width, self._data)) - - def __ne__(self, other): - return not (self == other) - - def __iter__(self): - return (self[i] for i in xrange(len(self))) - - def __getitem__(self, millisecond): - if isinstance(millisecond, slice): - if millisecond.step: - return ( - self[i:i + millisecond.step] - for i in xrange(*millisecond.indices(len(self))) - ) - - start = millisecond.start if millisecond.start is not None else 0 - end = millisecond.stop if millisecond.stop is not None \ - else len(self) - - start = min(start, len(self)) - end = min(end, len(self)) - else: - start = millisecond - end = millisecond + 1 - - start = self._parse_position(start) * self.frame_width - end = self._parse_position(end) * self.frame_width - data = self._data[start:end] - - # ensure the output is as long as the requester is expecting - expected_length = end - start - missing_frames = (expected_length - len(data)) // self.frame_width - if missing_frames: - if missing_frames > self.frame_count(ms=2): - raise TooManyMissingFrames( - "You should never be filling in " - " more than 2 ms with silence here, " - "missing frames: %s" % missing_frames) - silence = audioop.mul(data[:self.frame_width], - self.sample_width, 0) - data += (silence * missing_frames) - - return self._spawn(data) - - def get_sample_slice(self, start_sample=None, end_sample=None): - """ - Get a section of the audio segment by sample index. - - NOTE: Negative indices do *not* address samples backword - from the end of the audio segment like a python list. - This is intentional. - """ - max_val = int(self.frame_count()) - - def bounded(val, default): - if val is None: - return default - if val < 0: - return 0 - if val > max_val: - return max_val - return val - - start_i = bounded(start_sample, 0) * self.frame_width - end_i = bounded(end_sample, max_val) * self.frame_width - - data = self._data[start_i:end_i] - return self._spawn(data) - - def __add__(self, arg): - if isinstance(arg, AudioSegment): - return self.append(arg, crossfade=0) - else: - return self.apply_gain(arg) - - def __radd__(self, rarg): - """ - Permit use of sum() builtin with an iterable of AudioSegments - """ - if rarg == 0: - return self - raise TypeError("Gains must be the second addend after the " - "AudioSegment") - - def __sub__(self, arg): - if isinstance(arg, AudioSegment): - raise TypeError("AudioSegment objects can't be subtracted from " - "each other") - else: - return self.apply_gain(-arg) - - def __mul__(self, arg): - """ - If the argument is an AudioSegment, overlay the multiplied audio - segment. - - If it's a number, just use the string multiply operation to repeat the - audio. - - The following would return an AudioSegment that contains the - audio of audio_seg eight times - - `audio_seg * 8` - """ - if isinstance(arg, AudioSegment): - return self.overlay(arg, position=0, loop=True) - else: - return self._spawn(data=self._data * arg) - - def _spawn(self, data, overrides={}): - """ - Creates a new audio segment using the metadata from the current one - and the data passed in. Should be used whenever an AudioSegment is - being returned by an operation that would alters the current one, - since AudioSegment objects are immutable. - """ - # accept lists of data chunks - if isinstance(data, list): - data = b''.join(data) - - if isinstance(data, array.array): - try: - data = data.tobytes() - except: - data = data.tostring() - - # accept file-like objects - if hasattr(data, 'read'): - if hasattr(data, 'seek'): - data.seek(0) - data = data.read() - - metadata = { - 'sample_width': self.sample_width, - 'frame_rate': self.frame_rate, - 'frame_width': self.frame_width, - 'channels': self.channels - } - metadata.update(overrides) - return self.__class__(data=data, metadata=metadata) - - @classmethod - def _sync(cls, *segs): - channels = max(seg.channels for seg in segs) - frame_rate = max(seg.frame_rate for seg in segs) - sample_width = max(seg.sample_width for seg in segs) - - return tuple( - seg.set_channels(channels).set_frame_rate(frame_rate).set_sample_width(sample_width) - for seg in segs - ) - - def _parse_position(self, val): - if val < 0: - val = len(self) - abs(val) - val = self.frame_count(ms=len(self)) if val == float("inf") else \ - self.frame_count(ms=val) - return int(val) - - @classmethod - def empty(cls): - return cls(b'', metadata={ - "channels": 1, - "sample_width": 1, - "frame_rate": 1, - "frame_width": 1 - }) - - @classmethod - def silent(cls, duration=1000, frame_rate=11025): - """ - Generate a silent audio segment. - duration specified in milliseconds (default duration: 1000ms, default frame_rate: 11025). - """ - frames = int(frame_rate * (duration / 1000.0)) - data = b"\0\0" * frames - return cls(data, metadata={"channels": 1, - "sample_width": 2, - "frame_rate": frame_rate, - "frame_width": 2}) - - @classmethod - def from_mono_audiosegments(cls, *mono_segments): - if not len(mono_segments): - raise ValueError("At least one AudioSegment instance is required") - - segs = cls._sync(*mono_segments) - - if segs[0].channels != 1: - raise ValueError( - "AudioSegment.from_mono_audiosegments requires all arguments are mono AudioSegment instances") - - channels = len(segs) - sample_width = segs[0].sample_width - frame_rate = segs[0].frame_rate - - frame_count = max(int(seg.frame_count()) for seg in segs) - data = array.array( - segs[0].array_type, - b'\0' * (frame_count * sample_width * channels) - ) - - for i, seg in enumerate(segs): - data[i::channels] = seg.get_array_of_samples() - - return cls( - data, - channels=channels, - sample_width=sample_width, - frame_rate=frame_rate, - ) - - @classmethod - def from_file_using_temporary_files(cls, file, format=None, codec=None, parameters=None, start_second=None, duration=None, **kwargs): - orig_file = file - file, close_file = _fd_or_path_or_tempfile(file, 'rb', tempfile=False) - - if format: - format = format.lower() - format = AUDIO_FILE_EXT_ALIASES.get(format, format) - - def is_format(f): - f = f.lower() - if format == f: - return True - if isinstance(orig_file, basestring): - return orig_file.lower().endswith(".{0}".format(f)) - if isinstance(orig_file, bytes): - return orig_file.lower().endswith((".{0}".format(f)).encode('utf8')) - return False - - if is_format("wav"): - try: - obj = cls._from_safe_wav(file) - if close_file: - file.close() - if start_second is None and duration is None: - return obj - elif start_second is not None and duration is None: - return obj[start_second*1000:] - elif start_second is None and duration is not None: - return obj[:duration*1000] - else: - return obj[start_second*1000:(start_second+duration)*1000] - except: - file.seek(0) - elif is_format("raw") or is_format("pcm"): - sample_width = kwargs['sample_width'] - frame_rate = kwargs['frame_rate'] - channels = kwargs['channels'] - metadata = { - 'sample_width': sample_width, - 'frame_rate': frame_rate, - 'channels': channels, - 'frame_width': channels * sample_width - } - obj = cls(data=file.read(), metadata=metadata) - if close_file: - file.close() - if start_second is None and duration is None: - return obj - elif start_second is not None and duration is None: - return obj[start_second * 1000:] - elif start_second is None and duration is not None: - return obj[:duration * 1000] - else: - return obj[start_second * 1000:(start_second + duration) * 1000] - - input_file = NamedTemporaryFile(mode='wb', delete=False) - try: - input_file.write(file.read()) - except(OSError): - input_file.flush() - input_file.close() - input_file = NamedTemporaryFile(mode='wb', delete=False, buffering=2 ** 31 - 1) - if close_file: - file.close() - close_file = True - file = open(orig_file, buffering=2 ** 13 - 1, mode='rb') - reader = file.read(2 ** 31 - 1) - while reader: - input_file.write(reader) - reader = file.read(2 ** 31 - 1) - input_file.flush() - if close_file: - file.close() - - output = NamedTemporaryFile(mode="rb", delete=False) - - conversion_command = [cls.converter, - '-y', # always overwrite existing files - ] - - # If format is not defined - # ffmpeg/avconv will detect it automatically - if format: - conversion_command += ["-f", format] - - if codec: - # force audio decoder - conversion_command += ["-acodec", codec] - - conversion_command += [ - "-i", input_file.name, # input_file options (filename last) - "-vn", # Drop any video streams if there are any - "-f", "wav" # output options (filename last) - ] - - if start_second is not None: - conversion_command += ["-ss", str(start_second)] - - if duration is not None: - conversion_command += ["-t", str(duration)] - - conversion_command += [output.name] - - if parameters is not None: - # extend arguments with arbitrary set - conversion_command.extend(parameters) - - log_conversion(conversion_command) - - with open(os.devnull, 'rb') as devnull: - p = subprocess.Popen(conversion_command, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - p_out, p_err = p.communicate() - - log_subprocess_output(p_out) - log_subprocess_output(p_err) - - try: - if p.returncode != 0: - raise CouldntDecodeError( - "Decoding failed. ffmpeg returned error code: {0}\n\nOutput from ffmpeg/avlib:\n\n{1}".format( - p.returncode, p_err.decode(errors='ignore') )) - obj = cls._from_safe_wav(output) - finally: - input_file.close() - output.close() - os.unlink(input_file.name) - os.unlink(output.name) - - if start_second is None and duration is None: - return obj - elif start_second is not None and duration is None: - return obj[0:] - elif start_second is None and duration is not None: - return obj[:duration * 1000] - else: - return obj[0:duration * 1000] - - - @classmethod - def from_file(cls, file, format=None, codec=None, parameters=None, start_second=None, duration=None, **kwargs): - orig_file = file - try: - filename = fsdecode(file) - except TypeError: - filename = None - file, close_file = _fd_or_path_or_tempfile(file, 'rb', tempfile=False) - - if format: - format = format.lower() - format = AUDIO_FILE_EXT_ALIASES.get(format, format) - - def is_format(f): - f = f.lower() - if format == f: - return True - - if filename: - return filename.lower().endswith(".{0}".format(f)) - - return False - - if is_format("wav"): - try: - if start_second is None and duration is None: - return cls._from_safe_wav(file) - elif start_second is not None and duration is None: - return cls._from_safe_wav(file)[start_second*1000:] - elif start_second is None and duration is not None: - return cls._from_safe_wav(file)[:duration*1000] - else: - return cls._from_safe_wav(file)[start_second*1000:(start_second+duration)*1000] - except: - file.seek(0) - elif is_format("raw") or is_format("pcm"): - sample_width = kwargs['sample_width'] - frame_rate = kwargs['frame_rate'] - channels = kwargs['channels'] - metadata = { - 'sample_width': sample_width, - 'frame_rate': frame_rate, - 'channels': channels, - 'frame_width': channels * sample_width - } - if start_second is None and duration is None: - return cls(data=file.read(), metadata=metadata) - elif start_second is not None and duration is None: - return cls(data=file.read(), metadata=metadata)[start_second*1000:] - elif start_second is None and duration is not None: - return cls(data=file.read(), metadata=metadata)[:duration*1000] - else: - return cls(data=file.read(), metadata=metadata)[start_second*1000:(start_second+duration)*1000] - - conversion_command = [cls.converter, - '-y', # always overwrite existing files - ] - - # If format is not defined - # ffmpeg/avconv will detect it automatically - if format: - conversion_command += ["-f", format] - - if codec: - # force audio decoder - conversion_command += ["-acodec", codec] - - read_ahead_limit = kwargs.get('read_ahead_limit', -1) - if filename: - conversion_command += ["-i", filename] - stdin_parameter = None - stdin_data = None - else: - if cls.converter == 'ffmpeg': - conversion_command += ["-read_ahead_limit", str(read_ahead_limit), - "-i", "cache:pipe:0"] - else: - conversion_command += ["-i", "-"] - stdin_parameter = subprocess.PIPE - stdin_data = file.read() - - if codec: - info = None - else: - info = mediainfo_json(orig_file, read_ahead_limit=read_ahead_limit) - if info: - audio_streams = [x for x in info['streams'] - if x['codec_type'] == 'audio'] - # This is a workaround for some ffprobe versions that always say - # that mp3/mp4/aac/webm/ogg files contain fltp samples - audio_codec = audio_streams[0].get('codec_name') - if (audio_streams[0].get('sample_fmt') == 'fltp' and - audio_codec in ['mp3', 'mp4', 'aac', 'webm', 'ogg']): - bits_per_sample = 16 - else: - bits_per_sample = audio_streams[0]['bits_per_sample'] - if bits_per_sample == 8: - acodec = 'pcm_u8' - else: - acodec = 'pcm_s%dle' % bits_per_sample - - conversion_command += ["-acodec", acodec] - - conversion_command += [ - "-vn", # Drop any video streams if there are any - "-f", "wav" # output options (filename last) - ] - - if start_second is not None: - conversion_command += ["-ss", str(start_second)] - - if duration is not None: - conversion_command += ["-t", str(duration)] - - conversion_command += ["-"] - - if parameters is not None: - # extend arguments with arbitrary set - conversion_command.extend(parameters) - - log_conversion(conversion_command) - - p = subprocess.Popen(conversion_command, stdin=stdin_parameter, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - p_out, p_err = p.communicate(input=stdin_data) - - if p.returncode != 0 or len(p_out) == 0: - if close_file: - file.close() - raise CouldntDecodeError( - "Decoding failed. ffmpeg returned error code: {0}\n\nOutput from ffmpeg/avlib:\n\n{1}".format( - p.returncode, p_err.decode(errors='ignore') )) - - p_out = bytearray(p_out) - fix_wav_headers(p_out) - p_out = bytes(p_out) - obj = cls(p_out) - - if close_file: - file.close() - - if start_second is None and duration is None: - return obj - elif start_second is not None and duration is None: - return obj[0:] - elif start_second is None and duration is not None: - return obj[:duration * 1000] - else: - return obj[0:duration * 1000] - - @classmethod - def from_mp3(cls, file, parameters=None): - return cls.from_file(file, 'mp3', parameters=parameters) - - @classmethod - def from_flv(cls, file, parameters=None): - return cls.from_file(file, 'flv', parameters=parameters) - - @classmethod - def from_ogg(cls, file, parameters=None): - return cls.from_file(file, 'ogg', parameters=parameters) - - @classmethod - def from_wav(cls, file, parameters=None): - return cls.from_file(file, 'wav', parameters=parameters) - - @classmethod - def from_raw(cls, file, **kwargs): - return cls.from_file(file, 'raw', sample_width=kwargs['sample_width'], frame_rate=kwargs['frame_rate'], - channels=kwargs['channels']) - - @classmethod - def _from_safe_wav(cls, file): - file, close_file = _fd_or_path_or_tempfile(file, 'rb', tempfile=False) - file.seek(0) - obj = cls(data=file) - if close_file: - file.close() - return obj - - def export(self, out_f=None, format='mp3', codec=None, bitrate=None, parameters=None, tags=None, id3v2_version='4', - cover=None): - """ - Export an AudioSegment to a file with given options - - out_f (string): - Path to destination audio file. Also accepts os.PathLike objects on - python >= 3.6 - - format (string) - Format for destination audio file. - ('mp3', 'wav', 'raw', 'ogg' or other ffmpeg/avconv supported files) - - codec (string) - Codec used to encode the destination file. - - bitrate (string) - Bitrate used when encoding destination file. (64, 92, 128, 256, 312k...) - Each codec accepts different bitrate arguments so take a look at the - ffmpeg documentation for details (bitrate usually shown as -b, -ba or - -a:b). - - parameters (list of strings) - Aditional ffmpeg/avconv parameters - - tags (dict) - Set metadata information to destination files - usually used as tags. ({title='Song Title', artist='Song Artist'}) - - id3v2_version (string) - Set ID3v2 version for tags. (default: '4') - - cover (file) - Set cover for audio file from image file. (png or jpg) - """ - id3v2_allowed_versions = ['3', '4'] - - if format == "raw" and (codec is not None or parameters is not None): - raise AttributeError( - 'Can not invoke ffmpeg when export format is "raw"; ' - 'specify an ffmpeg raw format like format="s16le" instead ' - 'or call export(format="raw") with no codec or parameters') - - out_f, _ = _fd_or_path_or_tempfile(out_f, 'wb+') - out_f.seek(0) - - if format == "raw": - out_f.write(self._data) - out_f.seek(0) - return out_f - - # wav with no ffmpeg parameters can just be written directly to out_f - easy_wav = format == "wav" and codec is None and parameters is None - - if easy_wav: - data = out_f - else: - data = NamedTemporaryFile(mode="wb", delete=False) - - pcm_for_wav = self._data - if self.sample_width == 1: - # convert to unsigned integers for wav - pcm_for_wav = audioop.bias(self._data, 1, 128) - - wave_data = wave.open(data, 'wb') - wave_data.setnchannels(self.channels) - wave_data.setsampwidth(self.sample_width) - wave_data.setframerate(self.frame_rate) - # For some reason packing the wave header struct with - # a float in python 2 doesn't throw an exception - wave_data.setnframes(int(self.frame_count())) - wave_data.writeframesraw(pcm_for_wav) - wave_data.close() - - # for easy wav files, we're done (wav data is written directly to out_f) - if easy_wav: - out_f.seek(0) - return out_f - - output = NamedTemporaryFile(mode="w+b", delete=False) - - # build converter command to export - conversion_command = [ - self.converter, - '-y', # always overwrite existing files - "-f", "wav", "-i", data.name, # input options (filename last) - ] - - if codec is None: - codec = self.DEFAULT_CODECS.get(format, None) - - if cover is not None: - if cover.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tif', '.tiff')) and format == "mp3": - conversion_command.extend(["-i", cover, "-map", "0", "-map", "1", "-c:v", "mjpeg"]) - else: - raise AttributeError( - "Currently cover images are only supported by MP3 files. The allowed image formats are: .tif, .jpg, .bmp, .jpeg and .png.") - - if codec is not None: - # force audio encoder - conversion_command.extend(["-acodec", codec]) - - if bitrate is not None: - conversion_command.extend(["-b:a", bitrate]) - - if parameters is not None: - # extend arguments with arbitrary set - conversion_command.extend(parameters) - - if tags is not None: - if not isinstance(tags, dict): - raise InvalidTag("Tags must be a dictionary.") - else: - # Extend converter command with tags - # print(tags) - for key, value in tags.items(): - conversion_command.extend( - ['-metadata', '{0}={1}'.format(key, value)]) - - if format == 'mp3': - # set id3v2 tag version - if id3v2_version not in id3v2_allowed_versions: - raise InvalidID3TagVersion( - "id3v2_version not allowed, allowed versions: %s" % id3v2_allowed_versions) - conversion_command.extend([ - "-id3v2_version", id3v2_version - ]) - - if sys.platform == 'darwin' and codec == 'mp3': - conversion_command.extend(["-write_xing", "0"]) - - conversion_command.extend([ - "-f", format, output.name, # output options (filename last) - ]) - - log_conversion(conversion_command) - - # read stdin / write stdout - with open(os.devnull, 'rb') as devnull: - p = subprocess.Popen(conversion_command, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - p_out, p_err = p.communicate() - - log_subprocess_output(p_out) - log_subprocess_output(p_err) - - if p.returncode != 0: - raise CouldntEncodeError( - "Encoding failed. ffmpeg/avlib returned error code: {0}\n\nCommand:{1}\n\nOutput from ffmpeg/avlib:\n\n{2}".format( - p.returncode, conversion_command, p_err.decode(errors='ignore') )) - - output.seek(0) - out_f.write(output.read()) - - data.close() - output.close() - - os.unlink(data.name) - os.unlink(output.name) - - out_f.seek(0) - return out_f - - def get_frame(self, index): - frame_start = index * self.frame_width - frame_end = frame_start + self.frame_width - return self._data[frame_start:frame_end] - - def frame_count(self, ms=None): - """ - returns the number of frames for the given number of milliseconds, or - if not specified, the number of frames in the whole AudioSegment - """ - if ms is not None: - return ms * (self.frame_rate / 1000.0) - else: - return float(len(self._data) // self.frame_width) - - def set_sample_width(self, sample_width): - if sample_width == self.sample_width: - return self - - frame_width = self.channels * sample_width - - return self._spawn( - audioop.lin2lin(self._data, self.sample_width, sample_width), - overrides={'sample_width': sample_width, 'frame_width': frame_width} - ) - - def set_frame_rate(self, frame_rate): - if frame_rate == self.frame_rate: - return self - - if self._data: - converted, _ = audioop.ratecv(self._data, self.sample_width, - self.channels, self.frame_rate, - frame_rate, None) - else: - converted = self._data - - return self._spawn(data=converted, - overrides={'frame_rate': frame_rate}) - - def set_channels(self, channels): - if channels == self.channels: - return self - - if channels == 2 and self.channels == 1: - fn = audioop.tostereo - frame_width = self.frame_width * 2 - fac = 1 - converted = fn(self._data, self.sample_width, fac, fac) - elif channels == 1 and self.channels == 2: - fn = audioop.tomono - frame_width = self.frame_width // 2 - fac = 0.5 - converted = fn(self._data, self.sample_width, fac, fac) - elif channels == 1: - channels_data = [seg.get_array_of_samples() for seg in self.split_to_mono()] - frame_count = int(self.frame_count()) - converted = array.array( - channels_data[0].typecode, - b'\0' * (frame_count * self.sample_width) - ) - for raw_channel_data in channels_data: - for i in range(frame_count): - converted[i] += raw_channel_data[i] // self.channels - frame_width = self.frame_width // self.channels - elif self.channels == 1: - dup_channels = [self for iChannel in range(channels)] - return AudioSegment.from_mono_audiosegments(*dup_channels) - else: - raise ValueError( - "AudioSegment.set_channels only supports mono-to-multi channel and multi-to-mono channel conversion") - - return self._spawn(data=converted, - overrides={ - 'channels': channels, - 'frame_width': frame_width}) - - def split_to_mono(self): - if self.channels == 1: - return [self] - - samples = self.get_array_of_samples() - - mono_channels = [] - for i in range(self.channels): - samples_for_current_channel = samples[i::self.channels] - - try: - mono_data = samples_for_current_channel.tobytes() - except AttributeError: - mono_data = samples_for_current_channel.tostring() - - mono_channels.append( - self._spawn(mono_data, overrides={"channels": 1, "frame_width": self.sample_width}) - ) - - return mono_channels - - @property - def rms(self): - return audioop.rms(self._data, self.sample_width) - - @property - def dBFS(self): - rms = self.rms - if not rms: - return -float("infinity") - return ratio_to_db(self.rms / self.max_possible_amplitude) - - @property - def max(self): - return audioop.max(self._data, self.sample_width) - - @property - def max_possible_amplitude(self): - bits = self.sample_width * 8 - max_possible_val = (2 ** bits) - - # since half is above 0 and half is below the max amplitude is divided - return max_possible_val / 2 - - @property - def max_dBFS(self): - return ratio_to_db(self.max, self.max_possible_amplitude) - - @property - def duration_seconds(self): - return self.frame_rate and self.frame_count() / self.frame_rate or 0.0 - - def get_dc_offset(self, channel=1): - """ - Returns a value between -1.0 and 1.0 representing the DC offset of a - channel (1 for left, 2 for right). - """ - if not 1 <= channel <= 2: - raise ValueError("channel value must be 1 (left) or 2 (right)") - - if self.channels == 1: - data = self._data - elif channel == 1: - data = audioop.tomono(self._data, self.sample_width, 1, 0) - else: - data = audioop.tomono(self._data, self.sample_width, 0, 1) - - return float(audioop.avg(data, self.sample_width)) / self.max_possible_amplitude - - def remove_dc_offset(self, channel=None, offset=None): - """ - Removes DC offset of given channel. Calculates offset if it's not given. - Offset values must be in range -1.0 to 1.0. If channel is None, removes - DC offset from all available channels. - """ - if channel and not 1 <= channel <= 2: - raise ValueError("channel value must be None, 1 (left) or 2 (right)") - - if offset and not -1.0 <= offset <= 1.0: - raise ValueError("offset value must be in range -1.0 to 1.0") - - if offset: - offset = int(round(offset * self.max_possible_amplitude)) - - def remove_data_dc(data, off): - if not off: - off = audioop.avg(data, self.sample_width) - return audioop.bias(data, self.sample_width, -off) - - if self.channels == 1: - return self._spawn(data=remove_data_dc(self._data, offset)) - - left_channel = audioop.tomono(self._data, self.sample_width, 1, 0) - right_channel = audioop.tomono(self._data, self.sample_width, 0, 1) - - if not channel or channel == 1: - left_channel = remove_data_dc(left_channel, offset) - - if not channel or channel == 2: - right_channel = remove_data_dc(right_channel, offset) - - left_channel = audioop.tostereo(left_channel, self.sample_width, 1, 0) - right_channel = audioop.tostereo(right_channel, self.sample_width, 0, 1) - - return self._spawn(data=audioop.add(left_channel, right_channel, - self.sample_width)) - - def apply_gain(self, volume_change): - return self._spawn(data=audioop.mul(self._data, self.sample_width, - db_to_float(float(volume_change)))) - - def overlay(self, seg, position=0, loop=False, times=None, gain_during_overlay=None): - """ - Overlay the provided segment on to this segment starting at the - specificed position and using the specfied looping beahvior. - - seg (AudioSegment): - The audio segment to overlay on to this one. - - position (optional int): - The position to start overlaying the provided segment in to this - one. - - loop (optional bool): - Loop seg as many times as necessary to match this segment's length. - Overrides loops param. - - times (optional int): - Loop seg the specified number of times or until it matches this - segment's length. 1 means once, 2 means twice, ... 0 would make the - call a no-op - gain_during_overlay (optional int): - Changes this segment's volume by the specified amount during the - duration of time that seg is overlaid on top of it. When negative, - this has the effect of 'ducking' the audio under the overlay. - """ - - if loop: - # match loop=True's behavior with new times (count) mechinism. - times = -1 - elif times is None: - # no times specified, just once through - times = 1 - elif times == 0: - # it's a no-op, make a copy since we never mutate - return self._spawn(self._data) - - output = StringIO() - - seg1, seg2 = AudioSegment._sync(self, seg) - sample_width = seg1.sample_width - spawn = seg1._spawn - - output.write(seg1[:position]._data) - - # drop down to the raw data - seg1 = seg1[position:]._data - seg2 = seg2._data - pos = 0 - seg1_len = len(seg1) - seg2_len = len(seg2) - while times: - remaining = max(0, seg1_len - pos) - if seg2_len >= remaining: - seg2 = seg2[:remaining] - seg2_len = remaining - # we've hit the end, we're done looping (if we were) and this - # is our last go-around - times = 1 - - if gain_during_overlay: - seg1_overlaid = seg1[pos:pos + seg2_len] - seg1_adjusted_gain = audioop.mul(seg1_overlaid, self.sample_width, - db_to_float(float(gain_during_overlay))) - output.write(audioop.add(seg1_adjusted_gain, seg2, sample_width)) - else: - output.write(audioop.add(seg1[pos:pos + seg2_len], seg2, - sample_width)) - pos += seg2_len - - # dec times to break our while loop (eventually) - times -= 1 - - output.write(seg1[pos:]) - - return spawn(data=output) - - def append(self, seg, crossfade=100): - seg1, seg2 = AudioSegment._sync(self, seg) - - if not crossfade: - return seg1._spawn(seg1._data + seg2._data) - elif crossfade > len(self): - raise ValueError("Crossfade is longer than the original AudioSegment ({}ms > {}ms)".format( - crossfade, len(self) - )) - elif crossfade > len(seg): - raise ValueError("Crossfade is longer than the appended AudioSegment ({}ms > {}ms)".format( - crossfade, len(seg) - )) - - xf = seg1[-crossfade:].fade(to_gain=-120, start=0, end=float('inf')) - xf *= seg2[:crossfade].fade(from_gain=-120, start=0, end=float('inf')) - - output = TemporaryFile() - - output.write(seg1[:-crossfade]._data) - output.write(xf._data) - output.write(seg2[crossfade:]._data) - - output.seek(0) - obj = seg1._spawn(data=output) - output.close() - return obj - - def fade(self, to_gain=0, from_gain=0, start=None, end=None, - duration=None): - """ - Fade the volume of this audio segment. - - to_gain (float): - resulting volume_change in db - - start (int): - default = beginning of the segment - when in this segment to start fading in milliseconds - - end (int): - default = end of the segment - when in this segment to start fading in milliseconds - - duration (int): - default = until the end of the audio segment - the duration of the fade - """ - if None not in [duration, end, start]: - raise TypeError('Only two of the three arguments, "start", ' - '"end", and "duration" may be specified') - - # no fade == the same audio - if to_gain == 0 and from_gain == 0: - return self - - start = min(len(self), start) if start is not None else None - end = min(len(self), end) if end is not None else None - - if start is not None and start < 0: - start += len(self) - if end is not None and end < 0: - end += len(self) - - if duration is not None and duration < 0: - raise InvalidDuration("duration must be a positive integer") - - if duration: - if start is not None: - end = start + duration - elif end is not None: - start = end - duration - else: - duration = end - start - - from_power = db_to_float(from_gain) - - output = [] - - # original data - up until the crossfade portion, as is - before_fade = self[:start]._data - if from_gain != 0: - before_fade = audioop.mul(before_fade, - self.sample_width, - from_power) - output.append(before_fade) - - gain_delta = db_to_float(to_gain) - from_power - - # fades longer than 100ms can use coarse fading (one gain step per ms), - # shorter fades will have audible clicks so they use precise fading - # (one gain step per sample) - if duration > 100: - scale_step = gain_delta / duration - - for i in range(duration): - volume_change = from_power + (scale_step * i) - chunk = self[start + i] - chunk = audioop.mul(chunk._data, - self.sample_width, - volume_change) - - output.append(chunk) - else: - start_frame = self.frame_count(ms=start) - end_frame = self.frame_count(ms=end) - fade_frames = end_frame - start_frame - scale_step = gain_delta / fade_frames - - for i in range(int(fade_frames)): - volume_change = from_power + (scale_step * i) - sample = self.get_frame(int(start_frame + i)) - sample = audioop.mul(sample, self.sample_width, volume_change) - - output.append(sample) - - # original data after the crossfade portion, at the new volume - after_fade = self[end:]._data - if to_gain != 0: - after_fade = audioop.mul(after_fade, - self.sample_width, - db_to_float(to_gain)) - output.append(after_fade) - - return self._spawn(data=output) - - def fade_out(self, duration): - return self.fade(to_gain=-120, duration=duration, end=float('inf')) - - def fade_in(self, duration): - return self.fade(from_gain=-120, duration=duration, start=0) - - def reverse(self): - return self._spawn( - data=audioop.reverse(self._data, self.sample_width) - ) - - def _repr_html_(self): - src = """ - - """ - fh = self.export() - data = base64.b64encode(fh.read()).decode('ascii') - return src.format(base64=data) - - -from . import effects diff --git a/sbapp/pydub/effects.py b/sbapp/pydub/effects.py deleted file mode 100644 index 0210521..0000000 --- a/sbapp/pydub/effects.py +++ /dev/null @@ -1,341 +0,0 @@ -import sys -import math -import array -from .utils import ( - db_to_float, - ratio_to_db, - register_pydub_effect, - make_chunks, - audioop, - get_min_max_value -) -from .silence import split_on_silence -from .exceptions import TooManyMissingFrames, InvalidDuration - -if sys.version_info >= (3, 0): - xrange = range - - -@register_pydub_effect -def apply_mono_filter_to_each_channel(seg, filter_fn): - n_channels = seg.channels - - channel_segs = seg.split_to_mono() - channel_segs = [filter_fn(channel_seg) for channel_seg in channel_segs] - - out_data = seg.get_array_of_samples() - for channel_i, channel_seg in enumerate(channel_segs): - for sample_i, sample in enumerate(channel_seg.get_array_of_samples()): - index = (sample_i * n_channels) + channel_i - out_data[index] = sample - - return seg._spawn(out_data) - - -@register_pydub_effect -def normalize(seg, headroom=0.1): - """ - headroom is how close to the maximum volume to boost the signal up to (specified in dB) - """ - peak_sample_val = seg.max - - # if the max is 0, this audio segment is silent, and can't be normalized - if peak_sample_val == 0: - return seg - - target_peak = seg.max_possible_amplitude * db_to_float(-headroom) - - needed_boost = ratio_to_db(target_peak / peak_sample_val) - return seg.apply_gain(needed_boost) - - -@register_pydub_effect -def speedup(seg, playback_speed=1.5, chunk_size=150, crossfade=25): - # we will keep audio in 150ms chunks since one waveform at 20Hz is 50ms long - # (20 Hz is the lowest frequency audible to humans) - - # portion of AUDIO TO KEEP. if playback speed is 1.25 we keep 80% (0.8) and - # discard 20% (0.2) - atk = 1.0 / playback_speed - - if playback_speed < 2.0: - # throwing out more than half the audio - keep 50ms chunks - ms_to_remove_per_chunk = int(chunk_size * (1 - atk) / atk) - else: - # throwing out less than half the audio - throw out 50ms chunks - ms_to_remove_per_chunk = int(chunk_size) - chunk_size = int(atk * chunk_size / (1 - atk)) - - # the crossfade cannot be longer than the amount of audio we're removing - crossfade = min(crossfade, ms_to_remove_per_chunk - 1) - - # DEBUG - #print("chunk: {0}, rm: {1}".format(chunk_size, ms_to_remove_per_chunk)) - - chunks = make_chunks(seg, chunk_size + ms_to_remove_per_chunk) - if len(chunks) < 2: - raise Exception("Could not speed up AudioSegment, it was too short {2:0.2f}s for the current settings:\n{0}ms chunks at {1:0.1f}x speedup".format( - chunk_size, playback_speed, seg.duration_seconds)) - - # we'll actually truncate a bit less than we calculated to make up for the - # crossfade between chunks - ms_to_remove_per_chunk -= crossfade - - # we don't want to truncate the last chunk since it is not guaranteed to be - # the full chunk length - last_chunk = chunks[-1] - chunks = [chunk[:-ms_to_remove_per_chunk] for chunk in chunks[:-1]] - - out = chunks[0] - for chunk in chunks[1:]: - out = out.append(chunk, crossfade=crossfade) - - out += last_chunk - return out - - -@register_pydub_effect -def strip_silence(seg, silence_len=1000, silence_thresh=-16, padding=100): - if padding > silence_len: - raise InvalidDuration("padding cannot be longer than silence_len") - - chunks = split_on_silence(seg, silence_len, silence_thresh, padding) - crossfade = padding / 2 - - if not len(chunks): - return seg[0:0] - - seg = chunks[0] - for chunk in chunks[1:]: - seg = seg.append(chunk, crossfade=crossfade) - - return seg - - -@register_pydub_effect -def compress_dynamic_range(seg, threshold=-20.0, ratio=4.0, attack=5.0, release=50.0): - """ - Keyword Arguments: - - threshold - default: -20.0 - Threshold in dBFS. default of -20.0 means -20dB relative to the - maximum possible volume. 0dBFS is the maximum possible value so - all values for this argument sould be negative. - - ratio - default: 4.0 - Compression ratio. Audio louder than the threshold will be - reduced to 1/ratio the volume. A ratio of 4.0 is equivalent to - a setting of 4:1 in a pro-audio compressor like the Waves C1. - - attack - default: 5.0 - Attack in milliseconds. How long it should take for the compressor - to kick in once the audio has exceeded the threshold. - - release - default: 50.0 - Release in milliseconds. How long it should take for the compressor - to stop compressing after the audio has falled below the threshold. - - - For an overview of Dynamic Range Compression, and more detailed explanation - of the related terminology, see: - - http://en.wikipedia.org/wiki/Dynamic_range_compression - """ - - thresh_rms = seg.max_possible_amplitude * db_to_float(threshold) - - look_frames = int(seg.frame_count(ms=attack)) - def rms_at(frame_i): - return seg.get_sample_slice(frame_i - look_frames, frame_i).rms - def db_over_threshold(rms): - if rms == 0: return 0.0 - db = ratio_to_db(rms / thresh_rms) - return max(db, 0) - - output = [] - - # amount to reduce the volume of the audio by (in dB) - attenuation = 0.0 - - attack_frames = seg.frame_count(ms=attack) - release_frames = seg.frame_count(ms=release) - for i in xrange(int(seg.frame_count())): - rms_now = rms_at(i) - - # with a ratio of 4.0 this means the volume will exceed the threshold by - # 1/4 the amount (of dB) that it would otherwise - max_attenuation = (1 - (1.0 / ratio)) * db_over_threshold(rms_now) - - attenuation_inc = max_attenuation / attack_frames - attenuation_dec = max_attenuation / release_frames - - if rms_now > thresh_rms and attenuation <= max_attenuation: - attenuation += attenuation_inc - attenuation = min(attenuation, max_attenuation) - else: - attenuation -= attenuation_dec - attenuation = max(attenuation, 0) - - frame = seg.get_frame(i) - if attenuation != 0.0: - frame = audioop.mul(frame, - seg.sample_width, - db_to_float(-attenuation)) - - output.append(frame) - - return seg._spawn(data=b''.join(output)) - - -# Invert the phase of the signal. - -@register_pydub_effect - -def invert_phase(seg, channels=(1, 1)): - """ - channels- specifies which channel (left or right) to reverse the phase of. - Note that mono AudioSegments will become stereo. - """ - if channels == (1, 1): - inverted = audioop.mul(seg._data, seg.sample_width, -1.0) - return seg._spawn(data=inverted) - - else: - if seg.channels == 2: - left, right = seg.split_to_mono() - else: - raise Exception("Can't implicitly convert an AudioSegment with " + str(seg.channels) + " channels to stereo.") - - if channels == (1, 0): - left = left.invert_phase() - else: - right = right.invert_phase() - - return seg.from_mono_audiosegments(left, right) - - - -# High and low pass filters based on implementation found on Stack Overflow: -# http://stackoverflow.com/questions/13882038/implementing-simple-high-and-low-pass-filters-in-c - -@register_pydub_effect -def low_pass_filter(seg, cutoff): - """ - cutoff - Frequency (in Hz) where higher frequency signal will begin to - be reduced by 6dB per octave (doubling in frequency) above this point - """ - RC = 1.0 / (cutoff * 2 * math.pi) - dt = 1.0 / seg.frame_rate - - alpha = dt / (RC + dt) - - original = seg.get_array_of_samples() - filteredArray = array.array(seg.array_type, original) - - frame_count = int(seg.frame_count()) - - last_val = [0] * seg.channels - for i in range(seg.channels): - last_val[i] = filteredArray[i] = original[i] - - for i in range(1, frame_count): - for j in range(seg.channels): - offset = (i * seg.channels) + j - last_val[j] = last_val[j] + (alpha * (original[offset] - last_val[j])) - filteredArray[offset] = int(last_val[j]) - - return seg._spawn(data=filteredArray) - - -@register_pydub_effect -def high_pass_filter(seg, cutoff): - """ - cutoff - Frequency (in Hz) where lower frequency signal will begin to - be reduced by 6dB per octave (doubling in frequency) below this point - """ - RC = 1.0 / (cutoff * 2 * math.pi) - dt = 1.0 / seg.frame_rate - - alpha = RC / (RC + dt) - - minval, maxval = get_min_max_value(seg.sample_width * 8) - - original = seg.get_array_of_samples() - filteredArray = array.array(seg.array_type, original) - - frame_count = int(seg.frame_count()) - - last_val = [0] * seg.channels - for i in range(seg.channels): - last_val[i] = filteredArray[i] = original[i] - - for i in range(1, frame_count): - for j in range(seg.channels): - offset = (i * seg.channels) + j - offset_minus_1 = ((i-1) * seg.channels) + j - - last_val[j] = alpha * (last_val[j] + original[offset] - original[offset_minus_1]) - filteredArray[offset] = int(min(max(last_val[j], minval), maxval)) - - return seg._spawn(data=filteredArray) - - -@register_pydub_effect -def pan(seg, pan_amount): - """ - pan_amount should be between -1.0 (100% left) and +1.0 (100% right) - - When pan_amount == 0.0 the left/right balance is not changed. - - Panning does not alter the *perceived* loundness, but since loudness - is decreasing on one side, the other side needs to get louder to - compensate. When panned hard left, the left channel will be 3dB louder. - """ - if not -1.0 <= pan_amount <= 1.0: - raise ValueError("pan_amount should be between -1.0 (100% left) and +1.0 (100% right)") - - max_boost_db = ratio_to_db(2.0) - boost_db = abs(pan_amount) * max_boost_db - - boost_factor = db_to_float(boost_db) - reduce_factor = db_to_float(max_boost_db) - boost_factor - - reduce_db = ratio_to_db(reduce_factor) - - # Cut boost in half (max boost== 3dB) - in reality 2 speakers - # do not sum to a full 6 dB. - boost_db = boost_db / 2.0 - - if pan_amount < 0: - return seg.apply_gain_stereo(boost_db, reduce_db) - else: - return seg.apply_gain_stereo(reduce_db, boost_db) - - -@register_pydub_effect -def apply_gain_stereo(seg, left_gain=0.0, right_gain=0.0): - """ - left_gain - amount of gain to apply to the left channel (in dB) - right_gain - amount of gain to apply to the right channel (in dB) - - note: mono audio segments will be converted to stereo - """ - if seg.channels == 1: - left = right = seg - elif seg.channels == 2: - left, right = seg.split_to_mono() - - l_mult_factor = db_to_float(left_gain) - r_mult_factor = db_to_float(right_gain) - - left_data = audioop.mul(left._data, left.sample_width, l_mult_factor) - left_data = audioop.tostereo(left_data, left.sample_width, 1, 0) - - right_data = audioop.mul(right._data, right.sample_width, r_mult_factor) - right_data = audioop.tostereo(right_data, right.sample_width, 0, 1) - - output = audioop.add(left_data, right_data, seg.sample_width) - - return seg._spawn(data=output, - overrides={'channels': 2, - 'frame_width': 2 * seg.sample_width}) diff --git a/sbapp/pydub/exceptions.py b/sbapp/pydub/exceptions.py deleted file mode 100644 index 79d0743..0000000 --- a/sbapp/pydub/exceptions.py +++ /dev/null @@ -1,32 +0,0 @@ -class PydubException(Exception): - """ - Base class for any Pydub exception - """ - - -class TooManyMissingFrames(PydubException): - pass - - -class InvalidDuration(PydubException): - pass - - -class InvalidTag(PydubException): - pass - - -class InvalidID3TagVersion(PydubException): - pass - - -class CouldntDecodeError(PydubException): - pass - - -class CouldntEncodeError(PydubException): - pass - - -class MissingAudioParameter(PydubException): - pass diff --git a/sbapp/pydub/generators.py b/sbapp/pydub/generators.py deleted file mode 100644 index b04cb4c..0000000 --- a/sbapp/pydub/generators.py +++ /dev/null @@ -1,142 +0,0 @@ -""" -Each generator will return float samples from -1.0 to 1.0, which can be -converted to actual audio with 8, 16, 24, or 32 bit depth using the -SiganlGenerator.to_audio_segment() method (on any of it's subclasses). - -See Wikipedia's "waveform" page for info on some of the generators included -here: http://en.wikipedia.org/wiki/Waveform -""" - -import math -import array -import itertools -import random -from .audio_segment import AudioSegment -from .utils import ( - db_to_float, - get_frame_width, - get_array_type, - get_min_max_value -) - - - -class SignalGenerator(object): - def __init__(self, sample_rate=44100, bit_depth=16): - self.sample_rate = sample_rate - self.bit_depth = bit_depth - - def to_audio_segment(self, duration=1000.0, volume=0.0): - """ - Duration in milliseconds - (default: 1 second) - Volume in DB relative to maximum amplitude - (default 0.0 dBFS, which is the maximum value) - """ - minval, maxval = get_min_max_value(self.bit_depth) - sample_width = get_frame_width(self.bit_depth) - array_type = get_array_type(self.bit_depth) - - gain = db_to_float(volume) - sample_count = int(self.sample_rate * (duration / 1000.0)) - - sample_data = (int(val * maxval * gain) for val in self.generate()) - sample_data = itertools.islice(sample_data, 0, sample_count) - - data = array.array(array_type, sample_data) - - try: - data = data.tobytes() - except: - data = data.tostring() - - return AudioSegment(data=data, metadata={ - "channels": 1, - "sample_width": sample_width, - "frame_rate": self.sample_rate, - "frame_width": sample_width, - }) - - def generate(self): - raise NotImplementedError("SignalGenerator subclasses must implement the generate() method, and *should not* call the superclass implementation.") - - - -class Sine(SignalGenerator): - def __init__(self, freq, **kwargs): - super(Sine, self).__init__(**kwargs) - self.freq = freq - - def generate(self): - sine_of = (self.freq * 2 * math.pi) / self.sample_rate - sample_n = 0 - while True: - yield math.sin(sine_of * sample_n) - sample_n += 1 - - - -class Pulse(SignalGenerator): - def __init__(self, freq, duty_cycle=0.5, **kwargs): - super(Pulse, self).__init__(**kwargs) - self.freq = freq - self.duty_cycle = duty_cycle - - def generate(self): - sample_n = 0 - - # in samples - cycle_length = self.sample_rate / float(self.freq) - pulse_length = cycle_length * self.duty_cycle - - while True: - if (sample_n % cycle_length) < pulse_length: - yield 1.0 - else: - yield -1.0 - sample_n += 1 - - - -class Square(Pulse): - def __init__(self, freq, **kwargs): - kwargs['duty_cycle'] = 0.5 - super(Square, self).__init__(freq, **kwargs) - - - -class Sawtooth(SignalGenerator): - def __init__(self, freq, duty_cycle=1.0, **kwargs): - super(Sawtooth, self).__init__(**kwargs) - self.freq = freq - self.duty_cycle = duty_cycle - - def generate(self): - sample_n = 0 - - # in samples - cycle_length = self.sample_rate / float(self.freq) - midpoint = cycle_length * self.duty_cycle - ascend_length = midpoint - descend_length = cycle_length - ascend_length - - while True: - cycle_position = sample_n % cycle_length - if cycle_position < midpoint: - yield (2 * cycle_position / ascend_length) - 1.0 - else: - yield 1.0 - (2 * (cycle_position - midpoint) / descend_length) - sample_n += 1 - - - -class Triangle(Sawtooth): - def __init__(self, freq, **kwargs): - kwargs['duty_cycle'] = 0.5 - super(Triangle, self).__init__(freq, **kwargs) - - -class WhiteNoise(SignalGenerator): - def generate(self): - while True: - yield (random.random() * 2) - 1.0 diff --git a/sbapp/pydub/logging_utils.py b/sbapp/pydub/logging_utils.py deleted file mode 100644 index a312bd2..0000000 --- a/sbapp/pydub/logging_utils.py +++ /dev/null @@ -1,14 +0,0 @@ -""" - -""" -import logging - -converter_logger = logging.getLogger("pydub.converter") - -def log_conversion(conversion_command): - converter_logger.debug("subprocess.call(%s)", repr(conversion_command)) - -def log_subprocess_output(output): - if output: - for line in output.rstrip().splitlines(): - converter_logger.debug('subprocess output: %s', line.rstrip()) diff --git a/sbapp/pydub/playback.py b/sbapp/pydub/playback.py deleted file mode 100644 index 72ce4a5..0000000 --- a/sbapp/pydub/playback.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Support for playing AudioSegments. Pyaudio will be used if it's installed, -otherwise will fallback to ffplay. Pyaudio is a *much* nicer solution, but -is tricky to install. See my notes on installing pyaudio in a virtualenv (on -OSX 10.10): https://gist.github.com/jiaaro/9767512210a1d80a8a0d -""" - -import subprocess -from tempfile import NamedTemporaryFile -from .utils import get_player_name, make_chunks - -def _play_with_ffplay(seg): - PLAYER = get_player_name() - with NamedTemporaryFile("w+b", suffix=".wav") as f: - seg.export(f.name, "wav") - subprocess.call([PLAYER, "-nodisp", "-autoexit", "-hide_banner", f.name]) - - -def _play_with_pyaudio(seg): - import pyaudio - - p = pyaudio.PyAudio() - stream = p.open(format=p.get_format_from_width(seg.sample_width), - channels=seg.channels, - rate=seg.frame_rate, - output=True) - - # Just in case there were any exceptions/interrupts, we release the resource - # So as not to raise OSError: Device Unavailable should play() be used again - try: - # break audio into half-second chunks (to allows keyboard interrupts) - for chunk in make_chunks(seg, 500): - stream.write(chunk._data) - finally: - stream.stop_stream() - stream.close() - - p.terminate() - - -def _play_with_simpleaudio(seg): - import simpleaudio - return simpleaudio.play_buffer( - seg.raw_data, - num_channels=seg.channels, - bytes_per_sample=seg.sample_width, - sample_rate=seg.frame_rate - ) - - -def play(audio_segment): - try: - playback = _play_with_simpleaudio(audio_segment) - try: - playback.wait_done() - except KeyboardInterrupt: - playback.stop() - except ImportError: - pass - else: - return - - try: - _play_with_pyaudio(audio_segment) - return - except ImportError: - pass - else: - return - - _play_with_ffplay(audio_segment) diff --git a/sbapp/pydub/pyaudioop.py b/sbapp/pydub/pyaudioop.py deleted file mode 100644 index 9b1e2fb..0000000 --- a/sbapp/pydub/pyaudioop.py +++ /dev/null @@ -1,553 +0,0 @@ -try: - from __builtin__ import max as builtin_max - from __builtin__ import min as builtin_min -except ImportError: - from builtins import max as builtin_max - from builtins import min as builtin_min -import math -import struct -try: - from fractions import gcd -except ImportError: # Python 3.9+ - from math import gcd -from ctypes import create_string_buffer - - -class error(Exception): - pass - - -def _check_size(size): - if size != 1 and size != 2 and size != 4: - raise error("Size should be 1, 2 or 4") - - -def _check_params(length, size): - _check_size(size) - if length % size != 0: - raise error("not a whole number of frames") - - -def _sample_count(cp, size): - return len(cp) / size - - -def _get_samples(cp, size, signed=True): - for i in range(_sample_count(cp, size)): - yield _get_sample(cp, size, i, signed) - - -def _struct_format(size, signed): - if size == 1: - return "b" if signed else "B" - elif size == 2: - return "h" if signed else "H" - elif size == 4: - return "i" if signed else "I" - - -def _get_sample(cp, size, i, signed=True): - fmt = _struct_format(size, signed) - start = i * size - end = start + size - return struct.unpack_from(fmt, buffer(cp)[start:end])[0] - - -def _put_sample(cp, size, i, val, signed=True): - fmt = _struct_format(size, signed) - struct.pack_into(fmt, cp, i * size, val) - - -def _get_maxval(size, signed=True): - if signed and size == 1: - return 0x7f - elif size == 1: - return 0xff - elif signed and size == 2: - return 0x7fff - elif size == 2: - return 0xffff - elif signed and size == 4: - return 0x7fffffff - elif size == 4: - return 0xffffffff - - -def _get_minval(size, signed=True): - if not signed: - return 0 - elif size == 1: - return -0x80 - elif size == 2: - return -0x8000 - elif size == 4: - return -0x80000000 - - -def _get_clipfn(size, signed=True): - maxval = _get_maxval(size, signed) - minval = _get_minval(size, signed) - return lambda val: builtin_max(min(val, maxval), minval) - - -def _overflow(val, size, signed=True): - minval = _get_minval(size, signed) - maxval = _get_maxval(size, signed) - if minval <= val <= maxval: - return val - - bits = size * 8 - if signed: - offset = 2**(bits-1) - return ((val + offset) % (2**bits)) - offset - else: - return val % (2**bits) - - -def getsample(cp, size, i): - _check_params(len(cp), size) - if not (0 <= i < len(cp) / size): - raise error("Index out of range") - return _get_sample(cp, size, i) - - -def max(cp, size): - _check_params(len(cp), size) - - if len(cp) == 0: - return 0 - - return builtin_max(abs(sample) for sample in _get_samples(cp, size)) - - -def minmax(cp, size): - _check_params(len(cp), size) - - max_sample, min_sample = 0, 0 - for sample in _get_samples(cp, size): - max_sample = builtin_max(sample, max_sample) - min_sample = builtin_min(sample, min_sample) - - return min_sample, max_sample - - -def avg(cp, size): - _check_params(len(cp), size) - sample_count = _sample_count(cp, size) - if sample_count == 0: - return 0 - return sum(_get_samples(cp, size)) / sample_count - - -def rms(cp, size): - _check_params(len(cp), size) - - sample_count = _sample_count(cp, size) - if sample_count == 0: - return 0 - - sum_squares = sum(sample**2 for sample in _get_samples(cp, size)) - return int(math.sqrt(sum_squares / sample_count)) - - -def _sum2(cp1, cp2, length): - size = 2 - total = 0 - for i in range(length): - total += getsample(cp1, size, i) * getsample(cp2, size, i) - return total - - -def findfit(cp1, cp2): - size = 2 - - if len(cp1) % 2 != 0 or len(cp2) % 2 != 0: - raise error("Strings should be even-sized") - - if len(cp1) < len(cp2): - raise error("First sample should be longer") - - len1 = _sample_count(cp1, size) - len2 = _sample_count(cp2, size) - - sum_ri_2 = _sum2(cp2, cp2, len2) - sum_aij_2 = _sum2(cp1, cp1, len2) - sum_aij_ri = _sum2(cp1, cp2, len2) - - result = (sum_ri_2 * sum_aij_2 - sum_aij_ri * sum_aij_ri) / sum_aij_2 - - best_result = result - best_i = 0 - - for i in range(1, len1 - len2 + 1): - aj_m1 = _get_sample(cp1, size, i - 1) - aj_lm1 = _get_sample(cp1, size, i + len2 - 1) - - sum_aij_2 += aj_lm1**2 - aj_m1**2 - sum_aij_ri = _sum2(buffer(cp1)[i*size:], cp2, len2) - - result = (sum_ri_2 * sum_aij_2 - sum_aij_ri * sum_aij_ri) / sum_aij_2 - - if result < best_result: - best_result = result - best_i = i - - factor = _sum2(buffer(cp1)[best_i*size:], cp2, len2) / sum_ri_2 - - return best_i, factor - - -def findfactor(cp1, cp2): - size = 2 - - if len(cp1) % 2 != 0: - raise error("Strings should be even-sized") - - if len(cp1) != len(cp2): - raise error("Samples should be same size") - - sample_count = _sample_count(cp1, size) - - sum_ri_2 = _sum2(cp2, cp2, sample_count) - sum_aij_ri = _sum2(cp1, cp2, sample_count) - - return sum_aij_ri / sum_ri_2 - - -def findmax(cp, len2): - size = 2 - sample_count = _sample_count(cp, size) - - if len(cp) % 2 != 0: - raise error("Strings should be even-sized") - - if len2 < 0 or sample_count < len2: - raise error("Input sample should be longer") - - if sample_count == 0: - return 0 - - result = _sum2(cp, cp, len2) - best_result = result - best_i = 0 - - for i in range(1, sample_count - len2 + 1): - sample_leaving_window = getsample(cp, size, i - 1) - sample_entering_window = getsample(cp, size, i + len2 - 1) - - result -= sample_leaving_window**2 - result += sample_entering_window**2 - - if result > best_result: - best_result = result - best_i = i - - return best_i - - -def avgpp(cp, size): - _check_params(len(cp), size) - sample_count = _sample_count(cp, size) - - prevextremevalid = False - prevextreme = None - avg = 0 - nextreme = 0 - - prevval = getsample(cp, size, 0) - val = getsample(cp, size, 1) - - prevdiff = val - prevval - - for i in range(1, sample_count): - val = getsample(cp, size, i) - diff = val - prevval - - if diff * prevdiff < 0: - if prevextremevalid: - avg += abs(prevval - prevextreme) - nextreme += 1 - - prevextremevalid = True - prevextreme = prevval - - prevval = val - if diff != 0: - prevdiff = diff - - if nextreme == 0: - return 0 - - return avg / nextreme - - -def maxpp(cp, size): - _check_params(len(cp), size) - sample_count = _sample_count(cp, size) - - prevextremevalid = False - prevextreme = None - max = 0 - - prevval = getsample(cp, size, 0) - val = getsample(cp, size, 1) - - prevdiff = val - prevval - - for i in range(1, sample_count): - val = getsample(cp, size, i) - diff = val - prevval - - if diff * prevdiff < 0: - if prevextremevalid: - extremediff = abs(prevval - prevextreme) - if extremediff > max: - max = extremediff - prevextremevalid = True - prevextreme = prevval - - prevval = val - if diff != 0: - prevdiff = diff - - return max - - -def cross(cp, size): - _check_params(len(cp), size) - - crossings = 0 - last_sample = 0 - for sample in _get_samples(cp, size): - if sample <= 0 < last_sample or sample >= 0 > last_sample: - crossings += 1 - last_sample = sample - - return crossings - - -def mul(cp, size, factor): - _check_params(len(cp), size) - clip = _get_clipfn(size) - - result = create_string_buffer(len(cp)) - - for i, sample in enumerate(_get_samples(cp, size)): - sample = clip(int(sample * factor)) - _put_sample(result, size, i, sample) - - return result.raw - - -def tomono(cp, size, fac1, fac2): - _check_params(len(cp), size) - clip = _get_clipfn(size) - - sample_count = _sample_count(cp, size) - - result = create_string_buffer(len(cp) / 2) - - for i in range(0, sample_count, 2): - l_sample = getsample(cp, size, i) - r_sample = getsample(cp, size, i + 1) - - sample = (l_sample * fac1) + (r_sample * fac2) - sample = clip(sample) - - _put_sample(result, size, i / 2, sample) - - return result.raw - - -def tostereo(cp, size, fac1, fac2): - _check_params(len(cp), size) - - sample_count = _sample_count(cp, size) - - result = create_string_buffer(len(cp) * 2) - clip = _get_clipfn(size) - - for i in range(sample_count): - sample = _get_sample(cp, size, i) - - l_sample = clip(sample * fac1) - r_sample = clip(sample * fac2) - - _put_sample(result, size, i * 2, l_sample) - _put_sample(result, size, i * 2 + 1, r_sample) - - return result.raw - - -def add(cp1, cp2, size): - _check_params(len(cp1), size) - - if len(cp1) != len(cp2): - raise error("Lengths should be the same") - - clip = _get_clipfn(size) - sample_count = _sample_count(cp1, size) - result = create_string_buffer(len(cp1)) - - for i in range(sample_count): - sample1 = getsample(cp1, size, i) - sample2 = getsample(cp2, size, i) - - sample = clip(sample1 + sample2) - - _put_sample(result, size, i, sample) - - return result.raw - - -def bias(cp, size, bias): - _check_params(len(cp), size) - - result = create_string_buffer(len(cp)) - - for i, sample in enumerate(_get_samples(cp, size)): - sample = _overflow(sample + bias, size) - _put_sample(result, size, i, sample) - - return result.raw - - -def reverse(cp, size): - _check_params(len(cp), size) - sample_count = _sample_count(cp, size) - - result = create_string_buffer(len(cp)) - for i, sample in enumerate(_get_samples(cp, size)): - _put_sample(result, size, sample_count - i - 1, sample) - - return result.raw - - -def lin2lin(cp, size, size2): - _check_params(len(cp), size) - _check_size(size2) - - if size == size2: - return cp - - new_len = (len(cp) / size) * size2 - - result = create_string_buffer(new_len) - - for i in range(_sample_count(cp, size)): - sample = _get_sample(cp, size, i) - if size < size2: - sample = sample << (4 * size2 / size) - elif size > size2: - sample = sample >> (4 * size / size2) - - sample = _overflow(sample, size2) - - _put_sample(result, size2, i, sample) - - return result.raw - - -def ratecv(cp, size, nchannels, inrate, outrate, state, weightA=1, weightB=0): - _check_params(len(cp), size) - if nchannels < 1: - raise error("# of channels should be >= 1") - - bytes_per_frame = size * nchannels - frame_count = len(cp) / bytes_per_frame - - if bytes_per_frame / nchannels != size: - raise OverflowError("width * nchannels too big for a C int") - - if weightA < 1 or weightB < 0: - raise error("weightA should be >= 1, weightB should be >= 0") - - if len(cp) % bytes_per_frame != 0: - raise error("not a whole number of frames") - - if inrate <= 0 or outrate <= 0: - raise error("sampling rate not > 0") - - d = gcd(inrate, outrate) - inrate /= d - outrate /= d - - prev_i = [0] * nchannels - cur_i = [0] * nchannels - - if state is None: - d = -outrate - else: - d, samps = state - - if len(samps) != nchannels: - raise error("illegal state argument") - - prev_i, cur_i = zip(*samps) - prev_i, cur_i = list(prev_i), list(cur_i) - - q = frame_count / inrate - ceiling = (q + 1) * outrate - nbytes = ceiling * bytes_per_frame - - result = create_string_buffer(nbytes) - - samples = _get_samples(cp, size) - out_i = 0 - while True: - while d < 0: - if frame_count == 0: - samps = zip(prev_i, cur_i) - retval = result.raw - - # slice off extra bytes - trim_index = (out_i * bytes_per_frame) - len(retval) - retval = buffer(retval)[:trim_index] - - return (retval, (d, tuple(samps))) - - for chan in range(nchannels): - prev_i[chan] = cur_i[chan] - cur_i[chan] = samples.next() - - cur_i[chan] = ( - (weightA * cur_i[chan] + weightB * prev_i[chan]) - / (weightA + weightB) - ) - - frame_count -= 1 - d += outrate - - while d >= 0: - for chan in range(nchannels): - cur_o = ( - (prev_i[chan] * d + cur_i[chan] * (outrate - d)) - / outrate - ) - _put_sample(result, size, out_i, _overflow(cur_o, size)) - out_i += 1 - d -= inrate - - -def lin2ulaw(cp, size): - raise NotImplementedError() - - -def ulaw2lin(cp, size): - raise NotImplementedError() - - -def lin2alaw(cp, size): - raise NotImplementedError() - - -def alaw2lin(cp, size): - raise NotImplementedError() - - -def lin2adpcm(cp, size, state): - raise NotImplementedError() - - -def adpcm2lin(cp, size, state): - raise NotImplementedError() diff --git a/sbapp/pydub/scipy_effects.py b/sbapp/pydub/scipy_effects.py deleted file mode 100644 index abab2b4..0000000 --- a/sbapp/pydub/scipy_effects.py +++ /dev/null @@ -1,175 +0,0 @@ -""" -This module provides scipy versions of high_pass_filter, and low_pass_filter -as well as an additional band_pass_filter. - -Of course, you will need to install scipy for these to work. - -When this module is imported the high and low pass filters from this module -will be used when calling audio_segment.high_pass_filter() and -audio_segment.high_pass_filter() instead of the slower, less powerful versions -provided by pydub.effects. -""" -from scipy.signal import butter, sosfilt -from .utils import (register_pydub_effect,stereo_to_ms,ms_to_stereo) - - -def _mk_butter_filter(freq, type, order): - """ - Args: - freq: The cutoff frequency for highpass and lowpass filters. For - band filters, a list of [low_cutoff, high_cutoff] - type: "lowpass", "highpass", or "band" - order: nth order butterworth filter (default: 5th order). The - attenuation is -6dB/octave beyond the cutoff frequency (for 1st - order). A Higher order filter will have more attenuation, each level - adding an additional -6dB (so a 3rd order butterworth filter would - be -18dB/octave). - - Returns: - function which can filter a mono audio segment - - """ - def filter_fn(seg): - assert seg.channels == 1 - - nyq = 0.5 * seg.frame_rate - try: - freqs = [f / nyq for f in freq] - except TypeError: - freqs = freq / nyq - - sos = butter(order, freqs, btype=type, output='sos') - y = sosfilt(sos, seg.get_array_of_samples()) - - return seg._spawn(y.astype(seg.array_type)) - - return filter_fn - - -@register_pydub_effect -def band_pass_filter(seg, low_cutoff_freq, high_cutoff_freq, order=5): - filter_fn = _mk_butter_filter([low_cutoff_freq, high_cutoff_freq], 'band', order=order) - return seg.apply_mono_filter_to_each_channel(filter_fn) - - -@register_pydub_effect -def high_pass_filter(seg, cutoff_freq, order=5): - filter_fn = _mk_butter_filter(cutoff_freq, 'highpass', order=order) - return seg.apply_mono_filter_to_each_channel(filter_fn) - - -@register_pydub_effect -def low_pass_filter(seg, cutoff_freq, order=5): - filter_fn = _mk_butter_filter(cutoff_freq, 'lowpass', order=order) - return seg.apply_mono_filter_to_each_channel(filter_fn) - - -@register_pydub_effect -def _eq(seg, focus_freq, bandwidth=100, mode="peak", gain_dB=0, order=2): - """ - Args: - focus_freq - middle frequency or known frequency of band (in Hz) - bandwidth - range of the equalizer band - mode - Mode of Equalization(Peak/Notch(Bell Curve),High Shelf, Low Shelf) - order - Rolloff factor(1 - 6dB/Octave 2 - 12dB/Octave) - - Returns: - Equalized/Filtered AudioSegment - """ - filt_mode = ["peak", "low_shelf", "high_shelf"] - if mode not in filt_mode: - raise ValueError("Incorrect Mode Selection") - - if gain_dB >= 0: - if mode == "peak": - sec = band_pass_filter(seg, focus_freq - bandwidth/2, focus_freq + bandwidth/2, order = order) - seg = seg.overlay(sec - (3 - gain_dB)) - return seg - - if mode == "low_shelf": - sec = low_pass_filter(seg, focus_freq, order=order) - seg = seg.overlay(sec - (3 - gain_dB)) - return seg - - if mode == "high_shelf": - sec = high_pass_filter(seg, focus_freq, order=order) - seg = seg.overlay(sec - (3 - gain_dB)) - return seg - - if gain_dB < 0: - if mode == "peak": - sec = high_pass_filter(seg, focus_freq - bandwidth/2, order=order) - seg = seg.overlay(sec - (3 + gain_dB)) + gain_dB - sec = low_pass_filter(seg, focus_freq + bandwidth/2, order=order) - seg = seg.overlay(sec - (3 + gain_dB)) + gain_dB - return seg - - if mode == "low_shelf": - sec = high_pass_filter(seg, focus_freq, order=order) - seg = seg.overlay(sec - (3 + gain_dB)) + gain_dB - return seg - - if mode=="high_shelf": - sec=low_pass_filter(seg, focus_freq, order=order) - seg=seg.overlay(sec - (3 + gain_dB)) +gain_dB - return seg - - -@register_pydub_effect -def eq(seg, focus_freq, bandwidth=100, channel_mode="L+R", filter_mode="peak", gain_dB=0, order=2): - """ - Args: - focus_freq - middle frequency or known frequency of band (in Hz) - bandwidth - range of the equalizer band - channel_mode - Select Channels to be affected by the filter. - L+R - Standard Stereo Filter - L - Only Left Channel is Filtered - R - Only Right Channel is Filtered - M+S - Blumlien Stereo Filter(Mid-Side) - M - Only Mid Channel is Filtered - S - Only Side Channel is Filtered - Mono Audio Segments are completely filtered. - filter_mode - Mode of Equalization(Peak/Notch(Bell Curve),High Shelf, Low Shelf) - order - Rolloff factor(1 - 6dB/Octave 2 - 12dB/Octave) - - Returns: - Equalized/Filtered AudioSegment - """ - channel_modes = ["L+R", "M+S", "L", "R", "M", "S"] - if channel_mode not in channel_modes: - raise ValueError("Incorrect Channel Mode Selection") - - if seg.channels == 1: - return _eq(seg, focus_freq, bandwidth, filter_mode, gain_dB, order) - - if channel_mode == "L+R": - return _eq(seg, focus_freq, bandwidth, filter_mode, gain_dB, order) - - if channel_mode == "L": - seg = seg.split_to_mono() - seg = [_eq(seg[0], focus_freq, bandwidth, filter_mode, gain_dB, order), seg[1]] - return AudioSegment.from_mono_audio_segements(seg[0], seg[1]) - - if channel_mode == "R": - seg = seg.split_to_mono() - seg = [seg[0], _eq(seg[1], focus_freq, bandwidth, filter_mode, gain_dB, order)] - return AudioSegment.from_mono_audio_segements(seg[0], seg[1]) - - if channel_mode == "M+S": - seg = stereo_to_ms(seg) - seg = _eq(seg, focus_freq, bandwidth, filter_mode, gain_dB, order) - return ms_to_stereo(seg) - - if channel_mode == "M": - seg = stereo_to_ms(seg).split_to_mono() - seg = [_eq(seg[0], focus_freq, bandwidth, filter_mode, gain_dB, order), seg[1]] - seg = AudioSegment.from_mono_audio_segements(seg[0], seg[1]) - return ms_to_stereo(seg) - - if channel_mode == "S": - seg = stereo_to_ms(seg).split_to_mono() - seg = [seg[0], _eq(seg[1], focus_freq, bandwidth, filter_mode, gain_dB, order)] - seg = AudioSegment.from_mono_audio_segements(seg[0], seg[1]) - return ms_to_stereo(seg) - - diff --git a/sbapp/pydub/silence.py b/sbapp/pydub/silence.py deleted file mode 100644 index 0ad1499..0000000 --- a/sbapp/pydub/silence.py +++ /dev/null @@ -1,182 +0,0 @@ -""" -Various functions for finding/manipulating silence in AudioSegments -""" -import itertools - -from .utils import db_to_float - - -def detect_silence(audio_segment, min_silence_len=1000, silence_thresh=-16, seek_step=1): - """ - Returns a list of all silent sections [start, end] in milliseconds of audio_segment. - Inverse of detect_nonsilent() - - audio_segment - the segment to find silence in - min_silence_len - the minimum length for any silent section - silence_thresh - the upper bound for how quiet is silent in dFBS - seek_step - step size for interating over the segment in ms - """ - seg_len = len(audio_segment) - - # you can't have a silent portion of a sound that is longer than the sound - if seg_len < min_silence_len: - return [] - - # convert silence threshold to a float value (so we can compare it to rms) - silence_thresh = db_to_float(silence_thresh) * audio_segment.max_possible_amplitude - - # find silence and add start and end indicies to the to_cut list - silence_starts = [] - - # check successive (1 sec by default) chunk of sound for silence - # try a chunk at every "seek step" (or every chunk for a seek step == 1) - last_slice_start = seg_len - min_silence_len - slice_starts = range(0, last_slice_start + 1, seek_step) - - # guarantee last_slice_start is included in the range - # to make sure the last portion of the audio is searched - if last_slice_start % seek_step: - slice_starts = itertools.chain(slice_starts, [last_slice_start]) - - for i in slice_starts: - audio_slice = audio_segment[i:i + min_silence_len] - if audio_slice.rms <= silence_thresh: - silence_starts.append(i) - - # short circuit when there is no silence - if not silence_starts: - return [] - - # combine the silence we detected into ranges (start ms - end ms) - silent_ranges = [] - - prev_i = silence_starts.pop(0) - current_range_start = prev_i - - for silence_start_i in silence_starts: - continuous = (silence_start_i == prev_i + seek_step) - - # sometimes two small blips are enough for one particular slice to be - # non-silent, despite the silence all running together. Just combine - # the two overlapping silent ranges. - silence_has_gap = silence_start_i > (prev_i + min_silence_len) - - if not continuous and silence_has_gap: - silent_ranges.append([current_range_start, - prev_i + min_silence_len]) - current_range_start = silence_start_i - prev_i = silence_start_i - - silent_ranges.append([current_range_start, - prev_i + min_silence_len]) - - return silent_ranges - - -def detect_nonsilent(audio_segment, min_silence_len=1000, silence_thresh=-16, seek_step=1): - """ - Returns a list of all nonsilent sections [start, end] in milliseconds of audio_segment. - Inverse of detect_silent() - - audio_segment - the segment to find silence in - min_silence_len - the minimum length for any silent section - silence_thresh - the upper bound for how quiet is silent in dFBS - seek_step - step size for interating over the segment in ms - """ - silent_ranges = detect_silence(audio_segment, min_silence_len, silence_thresh, seek_step) - len_seg = len(audio_segment) - - # if there is no silence, the whole thing is nonsilent - if not silent_ranges: - return [[0, len_seg]] - - # short circuit when the whole audio segment is silent - if silent_ranges[0][0] == 0 and silent_ranges[0][1] == len_seg: - return [] - - prev_end_i = 0 - nonsilent_ranges = [] - for start_i, end_i in silent_ranges: - nonsilent_ranges.append([prev_end_i, start_i]) - prev_end_i = end_i - - if end_i != len_seg: - nonsilent_ranges.append([prev_end_i, len_seg]) - - if nonsilent_ranges[0] == [0, 0]: - nonsilent_ranges.pop(0) - - return nonsilent_ranges - - -def split_on_silence(audio_segment, min_silence_len=1000, silence_thresh=-16, keep_silence=100, - seek_step=1): - """ - Returns list of audio segments from splitting audio_segment on silent sections - - audio_segment - original pydub.AudioSegment() object - - min_silence_len - (in ms) minimum length of a silence to be used for - a split. default: 1000ms - - silence_thresh - (in dBFS) anything quieter than this will be - considered silence. default: -16dBFS - - keep_silence - (in ms or True/False) leave some silence at the beginning - and end of the chunks. Keeps the sound from sounding like it - is abruptly cut off. - When the length of the silence is less than the keep_silence duration - it is split evenly between the preceding and following non-silent - segments. - If True is specified, all the silence is kept, if False none is kept. - default: 100ms - - seek_step - step size for interating over the segment in ms - """ - - # from the itertools documentation - def pairwise(iterable): - "s -> (s0,s1), (s1,s2), (s2, s3), ..." - a, b = itertools.tee(iterable) - next(b, None) - return zip(a, b) - - if isinstance(keep_silence, bool): - keep_silence = len(audio_segment) if keep_silence else 0 - - output_ranges = [ - [ start - keep_silence, end + keep_silence ] - for (start,end) - in detect_nonsilent(audio_segment, min_silence_len, silence_thresh, seek_step) - ] - - for range_i, range_ii in pairwise(output_ranges): - last_end = range_i[1] - next_start = range_ii[0] - if next_start < last_end: - range_i[1] = (last_end+next_start)//2 - range_ii[0] = range_i[1] - - return [ - audio_segment[ max(start,0) : min(end,len(audio_segment)) ] - for start,end in output_ranges - ] - - -def detect_leading_silence(sound, silence_threshold=-50.0, chunk_size=10): - """ - Returns the millisecond/index that the leading silence ends. - - audio_segment - the segment to find silence in - silence_threshold - the upper bound for how quiet is silent in dFBS - chunk_size - chunk size for interating over the segment in ms - """ - trim_ms = 0 # ms - assert chunk_size > 0 # to avoid infinite loop - while sound[trim_ms:trim_ms+chunk_size].dBFS < silence_threshold and trim_ms < len(sound): - trim_ms += chunk_size - - # if there is no end it should return the length of the segment - return min(trim_ms, len(sound)) - - diff --git a/sbapp/pydub/utils.py b/sbapp/pydub/utils.py deleted file mode 100644 index 740c500..0000000 --- a/sbapp/pydub/utils.py +++ /dev/null @@ -1,434 +0,0 @@ -from __future__ import division - -import json -import os -import re -import sys -from subprocess import Popen, PIPE -from math import log, ceil -from tempfile import TemporaryFile -from warnings import warn -from functools import wraps - -try: - import audioop -except ImportError: - import pyaudioop as audioop - -if sys.version_info >= (3, 0): - basestring = str - -FRAME_WIDTHS = { - 8: 1, - 16: 2, - 32: 4, -} -ARRAY_TYPES = { - 8: "b", - 16: "h", - 32: "i", -} -ARRAY_RANGES = { - 8: (-0x80, 0x7f), - 16: (-0x8000, 0x7fff), - 32: (-0x80000000, 0x7fffffff), -} - - -def get_frame_width(bit_depth): - return FRAME_WIDTHS[bit_depth] - - -def get_array_type(bit_depth, signed=True): - t = ARRAY_TYPES[bit_depth] - if not signed: - t = t.upper() - return t - - -def get_min_max_value(bit_depth): - return ARRAY_RANGES[bit_depth] - - -def _fd_or_path_or_tempfile(fd, mode='w+b', tempfile=True): - close_fd = False - if fd is None and tempfile: - fd = TemporaryFile(mode=mode) - close_fd = True - - if isinstance(fd, basestring): - fd = open(fd, mode=mode) - close_fd = True - - try: - if isinstance(fd, os.PathLike): - fd = open(fd, mode=mode) - close_fd = True - except AttributeError: - # module os has no attribute PathLike, so we're on python < 3.6. - # The protocol we're trying to support doesn't exist, so just pass. - pass - - return fd, close_fd - - -def db_to_float(db, using_amplitude=True): - """ - Converts the input db to a float, which represents the equivalent - ratio in power. - """ - db = float(db) - if using_amplitude: - return 10 ** (db / 20) - else: # using power - return 10 ** (db / 10) - - -def ratio_to_db(ratio, val2=None, using_amplitude=True): - """ - Converts the input float to db, which represents the equivalent - to the ratio in power represented by the multiplier passed in. - """ - ratio = float(ratio) - - # accept 2 values and use the ratio of val1 to val2 - if val2 is not None: - ratio = ratio / val2 - - # special case for multiply-by-zero (convert to silence) - if ratio == 0: - return -float('inf') - - if using_amplitude: - return 20 * log(ratio, 10) - else: # using power - return 10 * log(ratio, 10) - - -def register_pydub_effect(fn, name=None): - """ - decorator for adding pydub effects to the AudioSegment objects. - example use: - @register_pydub_effect - def normalize(audio_segment): - ... - or you can specify a name: - @register_pydub_effect("normalize") - def normalize_audio_segment(audio_segment): - ... - """ - if isinstance(fn, basestring): - name = fn - return lambda fn: register_pydub_effect(fn, name) - - if name is None: - name = fn.__name__ - - from .audio_segment import AudioSegment - setattr(AudioSegment, name, fn) - return fn - - -def make_chunks(audio_segment, chunk_length): - """ - Breaks an AudioSegment into chunks that are milliseconds - long. - if chunk_length is 50 then you'll get a list of 50 millisecond long audio - segments back (except the last one, which can be shorter) - """ - number_of_chunks = ceil(len(audio_segment) / float(chunk_length)) - return [audio_segment[i * chunk_length:(i + 1) * chunk_length] - for i in range(int(number_of_chunks))] - - -def which(program): - """ - Mimics behavior of UNIX which command. - """ - # Add .exe program extension for windows support - if os.name == "nt" and not program.endswith(".exe"): - program += ".exe" - - envdir_list = [os.curdir] + os.environ["PATH"].split(os.pathsep) - - for envdir in envdir_list: - program_path = os.path.join(envdir, program) - if os.path.isfile(program_path) and os.access(program_path, os.X_OK): - return program_path - - -def get_encoder_name(): - """ - Return enconder default application for system, either avconv or ffmpeg - """ - if which("avconv"): - return "avconv" - elif which("ffmpeg"): - return "ffmpeg" - else: - # should raise exception - warn("Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not work", RuntimeWarning) - return "ffmpeg" - - -def get_player_name(): - """ - Return enconder default application for system, either avconv or ffmpeg - """ - if which("avplay"): - return "avplay" - elif which("ffplay"): - return "ffplay" - else: - # should raise exception - warn("Couldn't find ffplay or avplay - defaulting to ffplay, but may not work", RuntimeWarning) - return "ffplay" - - -def get_prober_name(): - """ - Return probe application, either avconv or ffmpeg - """ - if which("avprobe"): - return "avprobe" - elif which("ffprobe"): - return "ffprobe" - else: - # should raise exception - warn("Couldn't find ffprobe or avprobe - defaulting to ffprobe, but may not work", RuntimeWarning) - return "ffprobe" - - -def fsdecode(filename): - """Wrapper for os.fsdecode which was introduced in python 3.2 .""" - - if sys.version_info >= (3, 2): - PathLikeTypes = (basestring, bytes) - if sys.version_info >= (3, 6): - PathLikeTypes += (os.PathLike,) - if isinstance(filename, PathLikeTypes): - return os.fsdecode(filename) - else: - if isinstance(filename, bytes): - return filename.decode(sys.getfilesystemencoding()) - if isinstance(filename, basestring): - return filename - - raise TypeError("type {0} not accepted by fsdecode".format(type(filename))) - - -def get_extra_info(stderr): - """ - avprobe sometimes gives more information on stderr than - on the json output. The information has to be extracted - from stderr of the format of: - ' Stream #0:0: Audio: flac, 88200 Hz, stereo, s32 (24 bit)' - or (macOS version): - ' Stream #0:0: Audio: vorbis' - ' 44100 Hz, stereo, fltp, 320 kb/s' - - :type stderr: str - :rtype: list of dict - """ - extra_info = {} - - re_stream = r'(?P +)Stream #0[:\.](?P([0-9]+))(?P.+)\n?(?! *Stream)((?P +)(?P.+))?' - for i in re.finditer(re_stream, stderr): - if i.group('space_end') is not None and len(i.group('space_start')) <= len( - i.group('space_end')): - content_line = ','.join([i.group('content_0'), i.group('content_1')]) - else: - content_line = i.group('content_0') - tokens = [x.strip() for x in re.split('[:,]', content_line) if x] - extra_info[int(i.group('stream_id'))] = tokens - return extra_info - - -def mediainfo_json(filepath, read_ahead_limit=-1): - """Return json dictionary with media info(codec, duration, size, bitrate...) from filepath - """ - prober = get_prober_name() - command_args = [ - "-v", "info", - "-show_format", - "-show_streams", - ] - try: - command_args += [fsdecode(filepath)] - stdin_parameter = None - stdin_data = None - except TypeError: - if prober == 'ffprobe': - command_args += ["-read_ahead_limit", str(read_ahead_limit), - "cache:pipe:0"] - else: - command_args += ["-"] - stdin_parameter = PIPE - file, close_file = _fd_or_path_or_tempfile(filepath, 'rb', tempfile=False) - file.seek(0) - stdin_data = file.read() - if close_file: - file.close() - - command = [prober, '-of', 'json'] + command_args - res = Popen(command, stdin=stdin_parameter, stdout=PIPE, stderr=PIPE) - output, stderr = res.communicate(input=stdin_data) - output = output.decode("utf-8", 'ignore') - stderr = stderr.decode("utf-8", 'ignore') - - info = json.loads(output) - - if not info: - # If ffprobe didn't give any information, just return it - # (for example, because the file doesn't exist) - return info - - extra_info = get_extra_info(stderr) - - audio_streams = [x for x in info['streams'] if x['codec_type'] == 'audio'] - if len(audio_streams) == 0: - return info - - # We just operate on the first audio stream in case there are more - stream = audio_streams[0] - - def set_property(stream, prop, value): - if prop not in stream or stream[prop] == 0: - stream[prop] = value - - for token in extra_info[stream['index']]: - m = re.match('([su]([0-9]{1,2})p?) \(([0-9]{1,2}) bit\)$', token) - m2 = re.match('([su]([0-9]{1,2})p?)( \(default\))?$', token) - if m: - set_property(stream, 'sample_fmt', m.group(1)) - set_property(stream, 'bits_per_sample', int(m.group(2))) - set_property(stream, 'bits_per_raw_sample', int(m.group(3))) - elif m2: - set_property(stream, 'sample_fmt', m2.group(1)) - set_property(stream, 'bits_per_sample', int(m2.group(2))) - set_property(stream, 'bits_per_raw_sample', int(m2.group(2))) - elif re.match('(flt)p?( \(default\))?$', token): - set_property(stream, 'sample_fmt', token) - set_property(stream, 'bits_per_sample', 32) - set_property(stream, 'bits_per_raw_sample', 32) - elif re.match('(dbl)p?( \(default\))?$', token): - set_property(stream, 'sample_fmt', token) - set_property(stream, 'bits_per_sample', 64) - set_property(stream, 'bits_per_raw_sample', 64) - return info - - -def mediainfo(filepath): - """Return dictionary with media info(codec, duration, size, bitrate...) from filepath - """ - - prober = get_prober_name() - command_args = [ - "-v", "quiet", - "-show_format", - "-show_streams", - filepath - ] - - command = [prober, '-of', 'old'] + command_args - res = Popen(command, stdout=PIPE) - output = res.communicate()[0].decode("utf-8") - - if res.returncode != 0: - command = [prober] + command_args - output = Popen(command, stdout=PIPE).communicate()[0].decode("utf-8") - - rgx = re.compile(r"(?:(?P.*?):)?(?P.*?)\=(?P.*?)$") - info = {} - - if sys.platform == 'win32': - output = output.replace("\r", "") - - for line in output.split("\n"): - # print(line) - mobj = rgx.match(line) - - if mobj: - # print(mobj.groups()) - inner_dict, key, value = mobj.groups() - - if inner_dict: - try: - info[inner_dict] - except KeyError: - info[inner_dict] = {} - info[inner_dict][key] = value - else: - info[key] = value - - return info - - -def cache_codecs(function): - cache = {} - - @wraps(function) - def wrapper(): - try: - return cache[0] - except: - cache[0] = function() - return cache[0] - - return wrapper - - -@cache_codecs -def get_supported_codecs(): - encoder = get_encoder_name() - command = [encoder, "-codecs"] - res = Popen(command, stdout=PIPE, stderr=PIPE) - output = res.communicate()[0].decode("utf-8") - if res.returncode != 0: - return [] - - if sys.platform == 'win32': - output = output.replace("\r", "") - - - rgx = re.compile(r"^([D.][E.][AVS.][I.][L.][S.]) (\w*) +(.*)") - decoders = set() - encoders = set() - for line in output.split('\n'): - match = rgx.match(line.strip()) - if not match: - continue - flags, codec, name = match.groups() - - if flags[0] == 'D': - decoders.add(codec) - - if flags[1] == 'E': - encoders.add(codec) - - return (decoders, encoders) - - -def get_supported_decoders(): - return get_supported_codecs()[0] - - -def get_supported_encoders(): - return get_supported_codecs()[1] - -def stereo_to_ms(audio_segment): - ''' - Left-Right -> Mid-Side - ''' - channel = audio_segment.split_to_mono() - channel = [channel[0].overlay(channel[1]), channel[0].overlay(channel[1].invert_phase())] - return AudioSegment.from_mono_audiosegments(channel[0], channel[1]) - -def ms_to_stereo(audio_segment): - ''' - Mid-Side -> Left-Right - ''' - channel = audio_segment.split_to_mono() - channel = [channel[0].overlay(channel[1]) - 3, channel[0].overlay(channel[1].invert_phase()) - 3] - return AudioSegment.from_mono_audiosegments(channel[0], channel[1]) - diff --git a/sbapp/pyogg/__init__.py b/sbapp/pyogg/__init__.py deleted file mode 100644 index a97b0d2..0000000 --- a/sbapp/pyogg/__init__.py +++ /dev/null @@ -1,108 +0,0 @@ -import ctypes - -from .pyogg_error import PyOggError -from .ogg import PYOGG_OGG_AVAIL -from .vorbis import PYOGG_VORBIS_AVAIL, PYOGG_VORBIS_FILE_AVAIL, PYOGG_VORBIS_ENC_AVAIL -from .opus import PYOGG_OPUS_AVAIL, PYOGG_OPUS_FILE_AVAIL, PYOGG_OPUS_ENC_AVAIL -from .flac import PYOGG_FLAC_AVAIL - - -#: PyOgg version number. Versions should comply with PEP440. -__version__ = '0.7' - - -if (PYOGG_OGG_AVAIL and PYOGG_VORBIS_AVAIL and PYOGG_VORBIS_FILE_AVAIL): - # VorbisFile - from .vorbis_file import VorbisFile - # VorbisFileStream - from .vorbis_file_stream import VorbisFileStream - -else: - class VorbisFile: # type: ignore - def __init__(*args, **kw): - if not PYOGG_OGG_AVAIL: - raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - raise PyOggError("The Vorbis libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - - class VorbisFileStream: # type: ignore - def __init__(*args, **kw): - if not PYOGG_OGG_AVAIL: - raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - raise PyOggError("The Vorbis libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - - - -if (PYOGG_OGG_AVAIL and PYOGG_OPUS_AVAIL and PYOGG_OPUS_FILE_AVAIL): - # OpusFile - from .opus_file import OpusFile - # OpusFileStream - from .opus_file_stream import OpusFileStream - -else: - class OpusFile: # type: ignore - def __init__(*args, **kw): - if not PYOGG_OGG_AVAIL: - raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - if not PYOGG_OPUS_AVAIL: - raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - if not PYOGG_OPUS_FILE_AVAIL: - raise PyOggError("The OpusFile library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - raise PyOggError("Unknown initialisation error") - - class OpusFileStream: # type: ignore - def __init__(*args, **kw): - if not PYOGG_OGG_AVAIL: - raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - if not PYOGG_OPUS_AVAIL: - raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - if not PYOGG_OPUS_FILE_AVAIL: - raise PyOggError("The OpusFile library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - raise PyOggError("Unknown initialisation error") - - -if PYOGG_OPUS_AVAIL: - # OpusEncoder - from .opus_encoder import OpusEncoder - # OpusBufferedEncoder - from .opus_buffered_encoder import OpusBufferedEncoder - # OpusDecoder - from .opus_decoder import OpusDecoder - -else: - class OpusEncoder: # type: ignore - def __init__(*args, **kw): - raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - - class OpusBufferedEncoder: # type: ignore - def __init__(*args, **kw): - raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - - class OpusDecoder: # type: ignore - def __init__(*args, **kw): - raise PyOggError("The Opus library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - -if (PYOGG_OGG_AVAIL and PYOGG_OPUS_AVAIL): - # OggOpusWriter - from .ogg_opus_writer import OggOpusWriter - -else: - class OggOpusWriter: # type: ignore - def __init__(*args, **kw): - if not PYOGG_OGG_AVAIL: - raise PyOggError("The Ogg library wasn't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - raise PyOggError("The Opus library was't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - - -if PYOGG_FLAC_AVAIL: - # FlacFile - from .flac_file import FlacFile - # FlacFileStream - from .flac_file_stream import FlacFileStream -else: - class FlacFile: # type: ignore - def __init__(*args, **kw): - raise PyOggError("The FLAC libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") - - class FlacFileStream: # type: ignore - def __init__(*args, **kw): - raise PyOggError("The FLAC libraries weren't found or couldn't be loaded (maybe you're trying to use 64bit libraries with 32bit Python?)") diff --git a/sbapp/pyogg/audio_file.py b/sbapp/pyogg/audio_file.py deleted file mode 100644 index 4fb77a2..0000000 --- a/sbapp/pyogg/audio_file.py +++ /dev/null @@ -1,59 +0,0 @@ -from .pyogg_error import PyOggError - -class AudioFile: - """Abstract base class for audio files. - - This class is a base class for audio files (such as Vorbis, Opus, - and FLAC). It should not be instatiated directly. - """ - - def __init__(self): - raise PyOggError("AudioFile is an Abstract Base Class "+ - "and should not be instantiated") - - def as_array(self): - """Returns the buffer as a NumPy array. - - The shape of the returned array is in units of (number of - samples per channel, number of channels). - - The data type is either 8-bit or 16-bit signed integers, - depending on bytes_per_sample. - - The buffer is not copied, but rather the NumPy array - shares the memory with the buffer. - - """ - # Assumes that self.buffer is a one-dimensional array of - # bytes and that channels are interleaved. - - import numpy # type: ignore - - assert self.buffer is not None - assert self.channels is not None - - # The following code assumes that the bytes in the buffer - # represent 8-bit or 16-bit signed ints. Ensure the number of - # bytes per sample matches that assumption. - assert self.bytes_per_sample == 1 or self.bytes_per_sample == 2 - - # Create a dictionary mapping bytes per sample to numpy data - # types - dtype = { - 1: numpy.int8, - 2: numpy.int16 - } - - # Convert the ctypes buffer to a NumPy array - array = numpy.frombuffer( - self.buffer, - dtype=dtype[self.bytes_per_sample] - ) - - # Reshape the array - return array.reshape( - (len(self.buffer) - // self.bytes_per_sample - // self.channels, - self.channels) - ) diff --git a/sbapp/pyogg/flac.py b/sbapp/pyogg/flac.py deleted file mode 100644 index d44509e..0000000 --- a/sbapp/pyogg/flac.py +++ /dev/null @@ -1,2061 +0,0 @@ -############################################################ -# Flac license: # -############################################################ -""" -Copyright (C) 2000-2009 Josh Coalson -Copyright (C) 2011-2016 Xiph.Org Foundation - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiph.org Foundation nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import ctypes -from ctypes import c_int, c_int8, c_int16, c_int32, c_int64, c_uint, c_uint8, c_uint16, c_uint32, c_uint64, c_float, c_long, c_ulong, c_char, c_bool, c_char_p, c_ubyte, c_longlong, c_ulonglong, c_size_t, c_void_p, c_double, POINTER, pointer, cast, CFUNCTYPE, Structure, Union -import ctypes.util -import sys -from traceback import print_exc as _print_exc -import os - -from .ogg import * - -from .library_loader import ExternalLibrary, ExternalLibraryError - -__here = os.getcwd() - -libflac = None - -try: - names = { - "Windows": "libFLAC.dll", - "Darwin": "libFLAC.8.dylib", - "external": "FLAC" - } - libflac = Library.load(names, tests = [lambda lib: hasattr(lib, "FLAC__EntropyCodingMethodTypeString")]) -except ExternalLibraryError: - pass -except: - _print_exc() - -if libflac: - PYOGG_FLAC_AVAIL = True -else: - PYOGG_FLAC_AVAIL = False - -# ctypes -c_ubyte_p = POINTER(c_ubyte) -c_uchar_p = c_ubyte_p -c_uint_p = POINTER(c_uint) -c_size_t_p = POINTER(c_size_t) -c_off_t = c_int32 -# /ctypes - -if PYOGG_FLAC_AVAIL: - # Sanity check also satisfies mypy type checking - assert libflac is not None - - # ordinals - - FLAC__int8 = c_int8 - FLAC__uint8 = c_uint8 - - FLAC__int16 = c_int16 - - FLAC__int32 = c_int32 - FLAC__int32_p = POINTER(FLAC__int32) - - FLAC__int64 = c_int64 - FLAC__uint16 = c_uint16 - FLAC__uint32 = c_uint32 - FLAC__uint64 = c_uint64 - - FLAC__uint64_p = POINTER(FLAC__uint64) - - FLAC__bool = c_bool - - FLAC__byte = c_uint8 - - FLAC__byte_p = POINTER(FLAC__byte) - - c_char_p_p = POINTER(c_char_p) - - # /ordinals - - # callback - - FLAC__IOHandle = CFUNCTYPE(c_void_p) - - FLAC__IOCallback_Read = CFUNCTYPE(c_size_t, - c_void_p, - c_size_t, - c_size_t, - FLAC__IOHandle) - - FLAC__IOCallback_Write = CFUNCTYPE(c_size_t, c_void_p, c_size_t, c_size_t, FLAC__IOHandle) - - FLAC__IOCallback_Seek = CFUNCTYPE(c_int, FLAC__IOHandle, FLAC__int64, c_int) - - FLAC__IOCallback_Tell = CFUNCTYPE(FLAC__int64, FLAC__IOHandle) - - FLAC__IOCallback_Eof = CFUNCTYPE(c_int, FLAC__IOHandle) - - FLAC__IOCallback_Close = CFUNCTYPE(c_int, FLAC__IOHandle) - - class FLAC__IOCallbacks(Structure): - _fields_ = [("read", FLAC__IOCallback_Read), - ("write", FLAC__IOCallback_Write), - ("seek", FLAC__IOCallback_Seek), - ("tell", FLAC__IOCallback_Tell), - ("eof", FLAC__IOCallback_Eof), - ("close", FLAC__IOCallback_Close)] - - # /callback - - # format - - FLAC__MAX_METADATA_TYPE_CODE =(126) - FLAC__MIN_BLOCK_SIZE =(16) - FLAC__MAX_BLOCK_SIZE =(65535) - FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ =(4608) - FLAC__MAX_CHANNELS =(8) - FLAC__MIN_BITS_PER_SAMPLE =(4) - FLAC__MAX_BITS_PER_SAMPLE =(32) - FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE =(24) - FLAC__MAX_SAMPLE_RATE =(655350) - FLAC__MAX_LPC_ORDER =(32) - FLAC__SUBSET_MAX_LPC_ORDER_48000HZ =(12) - FLAC__MIN_QLP_COEFF_PRECISION =(5) - FLAC__MAX_QLP_COEFF_PRECISION =(15) - FLAC__MAX_FIXED_ORDER =(4) - FLAC__MAX_RICE_PARTITION_ORDER =(15) - FLAC__SUBSET_MAX_RICE_PARTITION_ORDER =(8) - - FLAC__VERSION_STRING = c_char_p.in_dll(libflac, "FLAC__VERSION_STRING") - - FLAC__VENDOR_STRING = c_char_p.in_dll(libflac, "FLAC__VENDOR_STRING") - - FLAC__STREAM_SYNC_STRING = (FLAC__byte * 4).in_dll(libflac, "FLAC__STREAM_SYNC_STRING") - - FLAC__STREAM_SYNC = c_uint.in_dll(libflac, "FLAC__STREAM_SYNC") - - FLAC__STREAM_SYNC_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_SYNC_LEN") - - FLAC__STREAM_SYNC_LENGTH =(4) - - - - FLAC__EntropyCodingMethodType = c_int - - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0 - - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1 - - - - libflac.FLAC__EntropyCodingMethodTypeString.restype = c_char_p - libflac.FLAC__EntropyCodingMethodTypeString.argtypes = [] - - def FLAC__EntropyCodingMethodTypeString(): - return libflac.FLAC__EntropyCodingMethodTypeString() - - - - class FLAC__EntropyCodingMethod_PartitionedRiceContents(Structure): - _fields_ = [("parameters", c_uint_p), - ("raw_bits", c_uint_p), - ("capacity_by_order", c_uint)] - - class FLAC__EntropyCodingMethod_PartitionedRice(Structure): - _fields_ = [("order", c_uint), - ("contents", POINTER(FLAC__EntropyCodingMethod_PartitionedRiceContents))] - - - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN") - - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN") - - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN") - - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN") - - FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER") - - - class FLAC__EntropyCodingMethod_data(Union): - _fields_ = [("partitioned_rice", FLAC__EntropyCodingMethod_PartitionedRice)] - - class FLAC__EntropyCodingMethod(Structure): - _fields_ = [("type", POINTER(FLAC__EntropyCodingMethodType)), - ("data", FLAC__EntropyCodingMethod_data)] - - FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__ENTROPY_CODING_METHOD_TYPE_LEN") - - - - FLAC__SubframeType = c_int - FLAC__SUBFRAME_TYPE_CONSTANT = 0 - FLAC__SUBFRAME_TYPE_VERBATIM = 1 - FLAC__SUBFRAME_TYPE_FIXED = 2 - FLAC__SUBFRAME_TYPE_LPC = 3 - - - - libflac.FLAC__SubframeTypeString.restype = c_char_p - libflac.FLAC__SubframeTypeString.argtypes = [] - - def FLAC__SubframeTypeString(): - return libflac.FLAC__SubframeTypeString() - - - - class FLAC__Subframe_Constant(Structure): - _fields_ = [("value", FLAC__int32)] - - - class FLAC__Subframe_Verbatim(Structure): - _fields_ = [("data", FLAC__int32_p)] - - - class FLAC__Subframe_Fixed(Structure): - _fields_ = [("entropy_coding_method", FLAC__EntropyCodingMethod), - ("order", c_uint), - ("warmup", FLAC__int32 * FLAC__MAX_FIXED_ORDER), - ("residual", FLAC__int32_p)] - - - class FLAC__Subframe_LPC(Structure): - _fields_ = [("entropy_coding_method", FLAC__EntropyCodingMethod), - ("order", c_uint), - ("qlp_coeff_precision", c_uint), - ("quantization_level", c_int), - ("qlp_coeff", FLAC__int32 * FLAC__MAX_LPC_ORDER), - ("warmup", FLAC__int32 * FLAC__MAX_LPC_ORDER), - ("residual", FLAC__int32_p)] - - - FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN") - - FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN") - - - - class FLAC__Subframe_data(Union): - _fields_ = [("constant", FLAC__Subframe_Constant), - ("fixed", FLAC__Subframe_Fixed), - ("lpc", FLAC__Subframe_LPC), - ("verbatim", FLAC__Subframe_Verbatim)] - - class FLAC__Subframe(Structure): - _fields_ = [("type", FLAC__SubframeType), - ("data", FLAC__Subframe_data), - ("wasted_bits", c_uint)] - - - FLAC__SUBFRAME_ZERO_PAD_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_ZERO_PAD_LEN") - - FLAC__SUBFRAME_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_LEN") - - FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN = c_uint.in_dll(libflac, "FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN") - - FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK") - - FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK") - - FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK") - - FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK = c_uint.in_dll(libflac, "FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK") - - - FLAC__ChannelAssignment = c_int - - FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0 - FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1 - FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2 - FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 - - - - libflac.FLAC__ChannelAssignmentString.restype = c_char_p - libflac.FLAC__ChannelAssignmentString.argtypes = [] - - def FLAC__ChannelAssignmentString(): - return libflac.FLAC__ChannelAssignmentString() - - FLAC__FrameNumberType = c_int - - - libflac.FLAC__FrameNumberTypeString.restype = c_char_p - libflac.FLAC__FrameNumberTypeString.argtypes = [] - - def FLAC__FrameNumberTypeString(): - return libflac.FLAC__FrameNumberTypeString() - - - class FLAC__FrameHeader_number(Union): - _fields_ =[("frame_number", FLAC__uint32), - ("sample_number", FLAC__uint64)] - - class FLAC__FrameHeader(Structure): - _fields_ = [("blocksize", c_uint), - ("sample_rate", c_uint), - ("channels", c_uint), - ("channel_assignment", FLAC__ChannelAssignment), - ("bits_per_sample", c_uint), - ("number_type", FLAC__FrameNumberType), - ("number", FLAC__FrameHeader_number), - ("crc", FLAC__uint8)] - - - FLAC__FRAME_HEADER_SYNC = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_SYNC") - - FLAC__FRAME_HEADER_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_RESERVED_LEN") - - FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN") - - FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_BLOCK_SIZE_LEN") - - FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_SAMPLE_RATE_LEN") - - FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN") - - FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN") - - FLAC__FRAME_HEADER_ZERO_PAD_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_ZERO_PAD_LEN") - - FLAC__FRAME_HEADER_CRC_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_HEADER_CRC_LEN") - - - - class FLAC__FrameFooter(Structure): - _fields_ = [("crc", FLAC__uint16)] - - FLAC__FRAME_FOOTER_CRC_LEN = c_uint.in_dll(libflac, "FLAC__FRAME_FOOTER_CRC_LEN") - - - - class FLAC__Frame(Structure): - _fields_ = [("header", FLAC__FrameHeader), - ("subframes", FLAC__Subframe * FLAC__MAX_CHANNELS), - ("footer", FLAC__FrameFooter)] - - - FLAC__MetadataType = c_int - - FLAC__METADATA_TYPE_STREAMINFO = 0 - - FLAC__METADATA_TYPE_PADDING = 1 - - FLAC__METADATA_TYPE_APPLICATION = 2 - - FLAC__METADATA_TYPE_SEEKTABLE = 3 - - FLAC__METADATA_TYPE_VORBIS_COMMENT = 4 - - FLAC__METADATA_TYPE_CUESHEET = 5 - - FLAC__METADATA_TYPE_PICTURE = 6 - - FLAC__METADATA_TYPE_UNDEFINED = 7 - - FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE - - - - libflac.FLAC__MetadataTypeString.restype = c_char_p - libflac.FLAC__MetadataTypeString.argtypes = [] - - def FLAC__MetadataTypeString(): - return libflac.FLAC__MetadataTypeString() - - - - class FLAC__StreamMetadata_StreamInfo(Structure): - _fields_ = [("min_blocksize", c_uint), - ("max_framesize", c_uint), - ("min_framesize", c_uint), - ("max_framesize", c_uint), - ("sample_rate", c_uint), - ("channels", c_uint), - ("bits_per_sample", c_uint), - ("total_samples", FLAC__uint64), - ("md5sum", FLAC__byte*16)] - - FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN") - - - FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN") - - FLAC__STREAM_METADATA_STREAMINFO_LENGTH =(34) - - - class FLAC__StreamMetadata_Padding(Structure): - _fields_ = [("dummy", c_int)] - - - - class FLAC__StreamMetadata_Application(Structure): - _fields_ = [("id", FLAC__byte*4), - ("data", FLAC__byte_p)] - - FLAC__STREAM_METADATA_APPLICATION_ID_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_APPLICATION_ID_LEN") - - - class FLAC__StreamMetadata_SeekPoint(Structure): - _fields_ = [("sample_number", FLAC__uint64), - ("stream_offset", FLAC__uint64), - ("frame_samples", c_uint)] - - FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN") - - FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN") - - FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN") - - FLAC__STREAM_METADATA_SEEKPOINT_LENGTH =(18) - - - FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__uint64.in_dll(libflac, "FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER") - - class FLAC__StreamMetadata_SeekTable(Structure): - _fields_ = [("num_points", c_uint), - ("points", POINTER(FLAC__StreamMetadata_SeekPoint))] - - class FLAC__StreamMetadata_VorbisComment_Entry(Structure): - _fields_ = [("length", FLAC__uint32), - ("entry", FLAC__byte_p)] - - FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN") - - - class FLAC__StreamMetadata_VorbisComment(Structure): - _fields_ = [("vendor_string", FLAC__StreamMetadata_VorbisComment_Entry), - ("num_comments", FLAC__uint32), - ("comments", POINTER(FLAC__StreamMetadata_VorbisComment_Entry))] - - FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN") - - - class FLAC__StreamMetadata_CueSheet_Index(Structure): - _fields_ = [("offset", FLAC__uint64), - ("number", FLAC__byte)] - - - FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN") - - FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN") - - FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN") - - - class FLAC__StreamMetadata_CueSheet_Track(Structure): - _fields_ = [("offset", FLAC__uint64), - ("number", FLAC__byte), - ("isrc", c_char*13), - ("type", c_uint), - ("pre_emphasis", c_uint), - ("num_indices", FLAC__byte), - ("indices", POINTER(FLAC__StreamMetadata_CueSheet_Index))] - - FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN") - - FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN") - - FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN") - - FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN") - - FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN") - - FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN") - - FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN") - - - class FLAC__StreamMetadata_CueSheet(Structure): - _fields_ = [("media_catalog_number", c_char*129), - ("lead_in", FLAC__uint64), - ("is_cd", FLAC__bool), - ("num_tracks", c_uint), - ("tracks", POINTER(FLAC__StreamMetadata_CueSheet_Track))] - - FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN") - - - FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN") - - FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN") - - FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN") - - FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN") - - - FLAC__StreamMetadata_Picture_Type = c_int - FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0 - FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1 - FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2 - FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3 - FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4 - FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5 - FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6 - FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7 - FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8 - FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9 - FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10 - FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11 - FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12 - FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13 - FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14 - FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15 - FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16 - FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17 - FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18 - FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19 - FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20 - - - libflac.FLAC__StreamMetadata_Picture_TypeString.restype = c_char_p - libflac.FLAC__StreamMetadata_Picture_TypeString.argtypes = [] - - def FLAC__StreamMetadata_Picture_TypeString(): - return libflac.FLAC__StreamMetadata_Picture_TypeString() - - - class FLAC__StreamMetadata_Picture(Structure): - _fields_ = [("type", FLAC__StreamMetadata_Picture_Type), - ("mime_type", c_char_p), - ("description", FLAC__byte_p), - ("width", FLAC__uint32), - ("height", FLAC__uint32), - ("depth", FLAC__uint32), - ("colors", FLAC__uint32), - ("data_length", FLAC__uint32), - ("data", FLAC__byte)] - - FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_TYPE_LEN") - - FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN") - - FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN") - - FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN") - - FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN") - - - FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN") - - FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_COLORS_LEN") - - FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN") - - - class FLAC__StreamMetadata_Unknown(Structure): - _fields_ = [("data", FLAC__byte_p)] - - - class FLAC__StreamMetadata_data(Union): - _fields_ = [("stream_info", FLAC__StreamMetadata_StreamInfo), - ("padding", FLAC__StreamMetadata_Padding), - ("application", FLAC__StreamMetadata_Application), - ("seek_table", FLAC__StreamMetadata_SeekTable), - ("vorbis_comment", FLAC__StreamMetadata_VorbisComment), - ("cue_sheet", FLAC__StreamMetadata_CueSheet), - ("picture", FLAC__StreamMetadata_Picture), - ("unknown", FLAC__StreamMetadata_Unknown)] - - class FLAC__StreamMetadata(Structure): - _fields_ = [("type", FLAC__MetadataType), - ("is_last", FLAC__bool), - ("length", c_uint), - ("data", FLAC__StreamMetadata_data)] - - FLAC__STREAM_METADATA_IS_LAST_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_IS_LAST_LEN") - - FLAC__STREAM_METADATA_TYPE_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_TYPE_LEN") - - FLAC__STREAM_METADATA_LENGTH_LEN = c_uint.in_dll(libflac, "FLAC__STREAM_METADATA_LENGTH_LEN") - - FLAC__STREAM_METADATA_HEADER_LENGTH =(4) - - - - libflac.FLAC__format_sample_rate_is_valid.restype = FLAC__bool - libflac.FLAC__format_sample_rate_is_valid.argtypes = [c_uint] - - def FLAC__format_sample_rate_is_valid(sample_rate): - return libflac.FLAC__format_sample_rate_is_valid(sample_rate) - - - libflac.FLAC__format_blocksize_is_subset.restype = FLAC__bool - libflac.FLAC__format_blocksize_is_subset.argtypes = [c_uint, c_uint] - - def FLAC__format_blocksize_is_subset(blocksize, sample_rate): - return libflac.FLAC__format_blocksize_is_subset(blocksize, sample_rate) - - - libflac.FLAC__format_sample_rate_is_subset.restype = FLAC__bool - libflac.FLAC__format_sample_rate_is_subset.argtypes = [c_uint] - - def FLAC__format_sample_rate_is_subset(sample_rate): - return libflac.FLAC__format_sample_rate_is_subset(sample_rate) - - - libflac.FLAC__format_vorbiscomment_entry_name_is_legal.restype = FLAC__bool - libflac.FLAC__format_vorbiscomment_entry_name_is_legal.argtypes = [c_char_p] - - def FLAC__format_vorbiscomment_entry_name_is_legal(name): - return libflac.FLAC__format_vorbiscomment_entry_name_is_legal(name) - - libflac.FLAC__format_vorbiscomment_entry_value_is_legal.restype = FLAC__bool - libflac.FLAC__format_vorbiscomment_entry_value_is_legal.argtypes = [FLAC__byte_p, c_uint] - - def FLAC__format_vorbiscomment_entry_value_is_legal(value, length): - return libflac.FLAC__format_vorbiscomment_entry_value_is_legal(value, length) - - libflac.FLAC__format_vorbiscomment_entry_is_legal.restype = FLAC__bool - libflac.FLAC__format_vorbiscomment_entry_is_legal.argtypes = [FLAC__byte_p, c_uint] - - def FLAC__format_vorbiscomment_entry_is_legal(entry, length): - return libflac.FLAC__format_vorbiscomment_entry_is_legal(entry, length) - - libflac.FLAC__format_seektable_is_legal.restype = FLAC__bool - libflac.FLAC__format_seektable_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_SeekTable)] - - def FLAC__format_seektable_is_legal(seek_table): - return libflac.FLAC__format_seektable_is_legal(seek_table) - - - libflac.FLAC__format_seektable_sort.restype = FLAC__bool - libflac.FLAC__format_seektable_sort.argtypes = [POINTER(FLAC__StreamMetadata_SeekTable)] - - def FLAC__format_seektable_sort(seek_table): - return libflac.FLAC__format_seektable_sort(seek_table) - - libflac.FLAC__format_cuesheet_is_legal.restype = FLAC__bool - libflac.FLAC__format_cuesheet_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet), FLAC__bool, c_char_p_p] - - def FLAC__format_cuesheet_is_legal(cue_sheet, check_cd_da_subset, violation): - return libflac.FLAC__format_cuesheet_is_legal(cue_sheet, check_cd_da_subset, violation) - - # /format - - # metadata - - libflac.FLAC__metadata_get_streaminfo.restype = FLAC__bool - libflac.FLAC__metadata_get_streaminfo.argtypes = [c_char_p, POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_get_streaminfo(filename, streaminfo): - return libflac.FLAC__metadata_get_streaminfo(filename, streaminfo) - - libflac.FLAC__metadata_get_tags.restype = FLAC__bool - libflac.FLAC__metadata_get_tags.argtypes = [c_char_p, POINTER(POINTER(FLAC__StreamMetadata))] - - def FLAC__metadata_get_tags(filename, tags): - return libflac.FLAC__metadata_get_tags(filename, tags) - - libflac.FLAC__metadata_get_cuesheet.restype = FLAC__bool - libflac.FLAC__metadata_get_cuesheet.argtypes = [c_char_p, POINTER(POINTER(FLAC__StreamMetadata))] - - def FLAC__metadata_get_cuesheet(filename, cuesheet): - return libflac.FLAC__metadata_get_cuesheet(filename, cuesheet) - - libflac.FLAC__metadata_get_picture.restype = FLAC__bool - libflac.FLAC__metadata_get_picture.argtypes = [c_char_p, POINTER(POINTER(FLAC__StreamMetadata)), FLAC__StreamMetadata_Picture_Type, c_char_p, FLAC__byte_p, c_uint, c_uint, c_uint, c_uint] - - def FLAC__metadata_get_picture(filename, picture, type, mime_type, description, max_width, max_height, max_depth, max_colors): - return libflac.FLAC__metadata_get_picture(filename, picture, type, mime_type, description, max_width, max_height, max_depth, max_colors) - - - class FLAC__Metadata_SimpleIterator(Structure): - _fields_ = [("dummy", c_int)] - - FLAC__Metadata_SimpleIteratorStatus = c_int - - FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0 - - - libflac.FLAC__Metadata_SimpleIteratorStatusString.restype = c_char_p - libflac.FLAC__Metadata_SimpleIteratorStatusString.argtypes = [] - - def FLAC__Metadata_SimpleIteratorStatusString(): - return libflac.FLAC__Metadata_SimpleIteratorStatusString() - - - libflac.FLAC__metadata_simple_iterator_new.restype = POINTER(FLAC__Metadata_SimpleIterator) - libflac.FLAC__metadata_simple_iterator_new.argtypes = [] - - def FLAC__metadata_simple_iterator_new(): - return libflac.FLAC__metadata_simple_iterator_new() - - - libflac.FLAC__metadata_simple_iterator_delete.restype = None - libflac.FLAC__metadata_simple_iterator_delete.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_delete(iterator): - return libflac.FLAC__metadata_simple_iterator_delete(iterator) - - - libflac.FLAC__metadata_simple_iterator_status.restype = FLAC__Metadata_SimpleIteratorStatus - libflac.FLAC__metadata_simple_iterator_status.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_status(iterator): - return libflac.FLAC__metadata_simple_iterator_status(iterator) - - libflac.FLAC__metadata_simple_iterator_init.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_init.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), c_char_p, FLAC__bool, FLAC__bool] - - def FLAC__metadata_simple_iterator_init(iterator, filename, read_only, preserve_file_stats): - return libflac.FLAC__metadata_simple_iterator_init(iterator, filename, read_only, preserve_file_stats) - - libflac.FLAC__metadata_simple_iterator_is_writable.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_is_writable.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_is_writable(iterator): - return libflac.FLAC__metadata_simple_iterator_is_writable(iterator) - - libflac.FLAC__metadata_simple_iterator_next.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_next.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_next(iterator): - return libflac.FLAC__metadata_simple_iterator_next(iterator) - - libflac.FLAC__metadata_simple_iterator_prev.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_prev.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_prev(iterator): - return libflac.FLAC__metadata_simple_iterator_prev(iterator) - - libflac.FLAC__metadata_simple_iterator_is_last.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_is_last.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_is_last(iterator): - return libflac.FLAC__metadata_simple_iterator_is_last(iterator) - - libflac.FLAC__metadata_simple_iterator_get_block_offset.restype = c_off_t - libflac.FLAC__metadata_simple_iterator_get_block_offset.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_get_block_offset(iterator): - return libflac.FLAC__metadata_simple_iterator_get_block_offset(iterator) - - libflac.FLAC__metadata_simple_iterator_get_block_type.restype = FLAC__MetadataType - libflac.FLAC__metadata_simple_iterator_get_block_type.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_get_block_type(iterator): - return libflac.FLAC__metadata_simple_iterator_get_block_type(iterator) - - libflac.FLAC__metadata_simple_iterator_get_block_length.restype = c_uint - libflac.FLAC__metadata_simple_iterator_get_block_length.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_get_block_length(iterator): - return libflac.FLAC__metadata_simple_iterator_get_block_length(iterator) - - libflac.FLAC__metadata_simple_iterator_get_application_id.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_get_application_id.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), FLAC__byte_p] - - def FLAC__metadata_simple_iterator_get_application_id(iterator, id): - return libflac.FLAC__metadata_simple_iterator_get_application_id(iterator, id) - - libflac.FLAC__metadata_simple_iterator_get_block.restype = POINTER(FLAC__StreamMetadata) - libflac.FLAC__metadata_simple_iterator_get_block.argtypes = [POINTER(FLAC__Metadata_SimpleIterator)] - - def FLAC__metadata_simple_iterator_get_block(iterator): - return libflac.FLAC__metadata_simple_iterator_get_block(iterator) - - libflac.FLAC__metadata_simple_iterator_set_block.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_set_block.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), POINTER(FLAC__StreamMetadata), FLAC__bool] - - def FLAC__metadata_simple_iterator_set_block(iterator, block, use_padding): - return libflac.FLAC__metadata_simple_iterator_set_block(iterator, block, use_padding) - - libflac.FLAC__metadata_simple_iterator_insert_block_after.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_insert_block_after.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), POINTER(FLAC__StreamMetadata), FLAC__bool] - - def FLAC__metadata_simple_iterator_insert_block_after(iterator, block, use_padding): - return libflac.FLAC__metadata_simple_iterator_insert_block_after(iterator, block, use_padding) - - libflac.FLAC__metadata_simple_iterator_delete_block.restype = FLAC__bool - libflac.FLAC__metadata_simple_iterator_delete_block.argtypes = [POINTER(FLAC__Metadata_SimpleIterator), FLAC__bool] - - def FLAC__metadata_simple_iterator_delete_block(iterator, use_padding): - return libflac.FLAC__metadata_simple_iterator_delete_block(iterator, use_padding) - - class FLAC__Metadata_Chain(Structure): - _fields_ = [("dummy", c_int)] - - class FLAC__Metadata_Iterator(Structure): - _fields_ = [("dummy", c_int)] - - FLAC__Metadata_ChainStatus = c_int - - FLAC__METADATA_CHAIN_STATUS_OK = 0 - - libflac.FLAC__Metadata_ChainStatusString.restype = c_char_p - libflac.FLAC__Metadata_ChainStatusString.argtypes = [] - - def FLAC__Metadata_ChainStatusString(): - return libflac.FLAC__Metadata_ChainStatusString() - - libflac.FLAC__metadata_chain_new.restype = POINTER(FLAC__Metadata_Chain) - libflac.FLAC__metadata_chain_new.argtypes = [] - - def FLAC__metadata_chain_new(): - return libflac.FLAC__metadata_chain_new() - - libflac.FLAC__metadata_chain_delete.restype = None - libflac.FLAC__metadata_chain_delete.argtypes = [POINTER(FLAC__Metadata_Chain)] - - def FLAC__metadata_chain_delete(chain): - return libflac.FLAC__metadata_chain_delete(chain) - - libflac.FLAC__metadata_chain_status.restype = FLAC__Metadata_ChainStatus - libflac.FLAC__metadata_chain_status.argtypes = [POINTER(FLAC__Metadata_Chain)] - - def FLAC__metadata_chain_status(chain): - return libflac.FLAC__metadata_chain_status(chain) - - libflac.FLAC__metadata_chain_read.restype = FLAC__bool - libflac.FLAC__metadata_chain_read.argtypes = [POINTER(FLAC__Metadata_Chain), c_char_p] - - def FLAC__metadata_chain_read(chain, filename): - return libflac.FLAC__metadata_chain_read(chain, filename) - - libflac.FLAC__metadata_chain_read_ogg.restype = FLAC__bool - libflac.FLAC__metadata_chain_read_ogg.argtypes = [POINTER(FLAC__Metadata_Chain), c_char_p] - - def FLAC__metadata_chain_read_ogg(chain, filename): - return libflac.FLAC__metadata_chain_read_ogg(chain, filename) - - libflac.FLAC__metadata_chain_read_with_callbacks.restype = FLAC__bool - libflac.FLAC__metadata_chain_read_with_callbacks.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__IOHandle, FLAC__IOCallbacks] - - def FLAC__metadata_chain_read_with_callbacks(chain, handle, callbacks): - return libflac.FLAC__metadata_chain_read_with_callbacks(chain, handle, callbacks) - - libflac.FLAC__metadata_chain_read_ogg_with_callbacks.restype = FLAC__bool - libflac.FLAC__metadata_chain_read_ogg_with_callbacks.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__IOHandle, FLAC__IOCallbacks] - - def FLAC__metadata_chain_read_ogg_with_callbacks(chain, handle, callbacks): - return libflac.FLAC__metadata_chain_read_ogg_with_callbacks(chain, handle, callbacks) - - libflac.FLAC__metadata_chain_check_if_tempfile_needed.restype = FLAC__bool - libflac.FLAC__metadata_chain_check_if_tempfile_needed.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool] - - def FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding): - return libflac.FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding) - - libflac.FLAC__metadata_chain_write.restype = FLAC__bool - libflac.FLAC__metadata_chain_write.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool, FLAC__bool] - - def FLAC__metadata_chain_write(chain, use_padding, preserve_file_stats): - return libflac.FLAC__metadata_chain_write(chain, use_padding, preserve_file_stats) - - libflac.FLAC__metadata_chain_write_with_callbacks.restype = FLAC__bool - libflac.FLAC__metadata_chain_write_with_callbacks.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool, FLAC__IOHandle, FLAC__IOCallbacks] - - def FLAC__metadata_chain_write_with_callbacks(chain, use_padding, handle, callbacks): - return libflac.FLAC__metadata_chain_write_with_callbacks(chain, use_padding, handle, callbacks) - - libflac.FLAC__metadata_chain_write_with_callbacks_and_tempfile.restype = FLAC__bool - libflac.FLAC__metadata_chain_write_with_callbacks_and_tempfile.argtypes = [POINTER(FLAC__Metadata_Chain), FLAC__bool, FLAC__IOHandle, FLAC__IOCallbacks, FLAC__IOHandle, FLAC__IOCallbacks] - - def FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, use_padding, handle, callbacks, temp_handle, temp_callbacks): - return libflac.FLAC__metadata_chain_write_with_callbacks_and_tempfile(chain, use_padding, handle, callbacks, temp_handle, temp_callbacks) - - libflac.FLAC__metadata_chain_merge_padding.restype = None - libflac.FLAC__metadata_chain_merge_padding.argtypes = [POINTER(FLAC__Metadata_Chain)] - - def FLAC__metadata_chain_merge_padding(chain): - return libflac.FLAC__metadata_chain_merge_padding(chain) - - libflac.FLAC__metadata_chain_sort_padding.restype = None - libflac.FLAC__metadata_chain_sort_padding.argtypes = [POINTER(FLAC__Metadata_Chain)] - - def FLAC__metadata_chain_sort_padding(chain): - return libflac.FLAC__metadata_chain_sort_padding(chain) - - libflac.FLAC__metadata_iterator_new.restype = POINTER(FLAC__Metadata_Iterator) - libflac.FLAC__metadata_iterator_new.argtypes = [] - - def FLAC__metadata_iterator_new(): - return libflac.FLAC__metadata_iterator_new() - - libflac.FLAC__metadata_iterator_delete.restype = None - libflac.FLAC__metadata_iterator_delete.argtypes = [POINTER(FLAC__Metadata_Iterator)] - - def FLAC__metadata_iterator_delete(iterator): - return libflac.FLAC__metadata_iterator_delete(iterator) - - libflac.FLAC__metadata_iterator_init.restype = None - libflac.FLAC__metadata_iterator_init.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__Metadata_Chain)] - - def FLAC__metadata_iterator_init(iterator, chain): - return libflac.FLAC__metadata_iterator_init(iterator, chain) - - libflac.FLAC__metadata_iterator_next.restype = FLAC__bool - libflac.FLAC__metadata_iterator_next.argtypes = [POINTER(FLAC__Metadata_Iterator)] - - def FLAC__metadata_iterator_next(iterator): - return libflac.FLAC__metadata_iterator_next(iterator) - - libflac.FLAC__metadata_iterator_prev.restype = FLAC__bool - libflac.FLAC__metadata_iterator_prev.argtypes = [POINTER(FLAC__Metadata_Iterator)] - - def FLAC__metadata_iterator_prev(iterator): - return libflac.FLAC__metadata_iterator_prev(iterator) - - libflac.FLAC__metadata_iterator_get_block_type.restype = FLAC__MetadataType - libflac.FLAC__metadata_iterator_get_block_type.argtypes = [POINTER(FLAC__Metadata_Iterator)] - - def FLAC__metadata_iterator_get_block_type(iterator): - return libflac.FLAC__metadata_iterator_get_block_type(iterator) - - libflac.FLAC__metadata_iterator_get_block_type.restype = POINTER(FLAC__StreamMetadata) - libflac.FLAC__metadata_iterator_get_block_type.argtypes = [POINTER(FLAC__Metadata_Iterator)] - - def FLAC__metadata_iterator_get_block_type(iterator): - return libflac.FLAC__metadata_iterator_get_block_type(iterator) - - libflac.FLAC__metadata_iterator_set_block.restype = FLAC__bool - libflac.FLAC__metadata_iterator_set_block.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_iterator_set_block(iterator, block): - return libflac.FLAC__metadata_iterator_set_block(iterator, block) - - libflac.FLAC__metadata_iterator_delete_block.restype = FLAC__bool - libflac.FLAC__metadata_iterator_delete_block.argtypes = [POINTER(FLAC__Metadata_Iterator), FLAC__bool] - - def FLAC__metadata_iterator_delete_block(iterator, replace_with_padding): - return libflac.FLAC__metadata_iterator_delete_block(iterator, replace_with_padding) - - libflac.FLAC__metadata_iterator_insert_block_before.restype = FLAC__bool - libflac.FLAC__metadata_iterator_insert_block_before.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_iterator_insert_block_before(iterator, block): - return libflac.FLAC__metadata_iterator_insert_block_before(iterator, block) - - libflac.FLAC__metadata_iterator_insert_block_after.restype = FLAC__bool - libflac.FLAC__metadata_iterator_insert_block_after.argtypes = [POINTER(FLAC__Metadata_Iterator), POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_iterator_insert_block_after(iterator, block): - return libflac.FLAC__metadata_iterator_insert_block_after(iterator, block) - - libflac.FLAC__metadata_object_new.restype = POINTER(FLAC__StreamMetadata) - libflac.FLAC__metadata_object_new.argtypes = [POINTER(FLAC__MetadataType)] - - def FLAC__metadata_object_new(type): - return libflac.FLAC__metadata_object_new(type) - - libflac.FLAC__metadata_object_clone.restype = POINTER(FLAC__StreamMetadata) - libflac.FLAC__metadata_object_clone.argtypes = [POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_object_clone(object): - return libflac.FLAC__metadata_object_clone(object) - - libflac.FLAC__metadata_object_delete.restype = None - libflac.FLAC__metadata_object_delete.argtypes = [POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_object_delete(object): - return libflac.FLAC__metadata_object_delete(object) - - libflac.FLAC__metadata_object_is_equal.restype = FLAC__bool - libflac.FLAC__metadata_object_is_equal.argtypes = [POINTER(FLAC__StreamMetadata), POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_object_is_equal(block1, block2): - return libflac.FLAC__metadata_object_is_equal(block1, block2) - - libflac.FLAC__metadata_object_application_set_data.restype = FLAC__bool - libflac.FLAC__metadata_object_application_set_data.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__byte_p, c_uint, FLAC__bool] - - def FLAC__metadata_object_application_set_data(object, data, length, copy): - return libflac.FLAC__metadata_object_application_set_data(object, data, length, copy) - - libflac.FLAC__metadata_object_seektable_resize_points.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_resize_points.argtypes = [POINTER(FLAC__StreamMetadata),c_uint] - - def FLAC__metadata_object_seektable_resize_points(object, new_num_points): - return libflac.FLAC__metadata_object_seektable_resize_points(object, new_num_points) - - libflac.FLAC__metadata_object_seektable_set_point.restype = None - libflac.FLAC__metadata_object_seektable_set_point.argtypes = [POINTER(FLAC__StreamMetadata),c_uint, FLAC__StreamMetadata_SeekPoint] - - def FLAC__metadata_object_seektable_set_point(object, point_num, point): - return libflac.FLAC__metadata_object_seektable_set_point(object, point_num, point) - - libflac.FLAC__metadata_object_seektable_insert_point.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_insert_point.argtypes = [POINTER(FLAC__StreamMetadata),c_uint, FLAC__StreamMetadata_SeekPoint] - - def FLAC__metadata_object_seektable_insert_point(object, point_num, point): - return libflac.FLAC__metadata_object_seektable_insert_point(object, point_num, point) - - libflac.FLAC__metadata_object_seektable_delete_point.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_delete_point.argtypes = [POINTER(FLAC__StreamMetadata),c_uint] - - def FLAC__metadata_object_seektable_delete_point(object, point_num): - return libflac.FLAC__metadata_object_seektable_delete_point(object, point_num) - - libflac.FLAC__metadata_object_seektable_is_legal.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_is_legal.argtypes = [POINTER(FLAC__StreamMetadata)] - - def FLAC__metadata_object_seektable_is_legal(object): - return libflac.FLAC__metadata_object_seektable_is_legal(object) - - libflac.FLAC__metadata_object_seektable_template_append_placeholders.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_template_append_placeholders.argtypes = [POINTER(FLAC__StreamMetadata), c_uint] - - def FLAC__metadata_object_seektable_template_append_placeholders(object, num): - return libflac.FLAC__metadata_object_seektable_template_append_placeholders(object, num) - - libflac.FLAC__metadata_object_seektable_template_append_point.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_template_append_point.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__uint64] - - def FLAC__metadata_object_seektable_template_append_point(object, sample_number): - return libflac.FLAC__metadata_object_seektable_template_append_point(object, sample_number) - - libflac.FLAC__metadata_object_seektable_template_append_points.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_template_append_points.argtypes = [POINTER(FLAC__StreamMetadata), POINTER(FLAC__uint64*0), c_uint] - - def FLAC__metadata_object_seektable_template_append_points(object, sample_numbers, num): - return libflac.FLAC__metadata_object_seektable_template_append_points(object, sample_numbers, num) - - libflac.FLAC__metadata_object_seektable_template_append_spaced_points.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_template_append_spaced_points.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__uint64] - - def FLAC__metadata_object_seektable_template_append_spaced_points(object, num, total_samples): - return libflac.FLAC__metadata_object_seektable_template_append_spaced_points(object, num, total_samples) - - libflac.FLAC__metadata_object_seektable_template_append_spaced_points_by_samples.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_template_append_spaced_points_by_samples.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__uint64] - - def FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(object, samples, total_samples): - return libflac.FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(object, samples, total_samples) - - libflac.FLAC__metadata_object_seektable_template_sort.restype = FLAC__bool - libflac.FLAC__metadata_object_seektable_template_sort.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__bool] - - def FLAC__metadata_object_seektable_template_sort(object, compact): - return libflac.FLAC__metadata_object_seektable_template_sort(object, compact) - - libflac.FLAC__metadata_object_vorbiscomment_set_vendor_string.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_set_vendor_string.argtypes = [POINTER(FLAC__StreamMetadata), FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool] - - def FLAC__metadata_object_vorbiscomment_set_vendor_string(object, entry, copy): - return libflac.FLAC__metadata_object_vorbiscomment_set_vendor_string(object, entry, copy) - - libflac.FLAC__metadata_object_vorbiscomment_resize_comments.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_resize_comments.argtypes = [POINTER(FLAC__StreamMetadata), c_uint] - - def FLAC__metadata_object_vorbiscomment_resize_comments(object, new_num_comments): - return libflac.FLAC__metadata_object_vorbiscomment_resize_comments(object, new_num_comments) - - libflac.FLAC__metadata_object_vorbiscomment_set_comment.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_set_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool] - - def FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy): - return libflac.FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy) - - libflac.FLAC__metadata_object_vorbiscomment_insert_comment.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_insert_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool] - - def FLAC__metadata_object_vorbiscomment_insert_comment(object, comment_num, entry, copy): - return libflac.FLAC__metadata_object_vorbiscomment_insert_comment(object, comment_num, entry, copy) - - libflac.FLAC__metadata_object_vorbiscomment_append_comment.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_append_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool] - - def FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy): - return libflac.FLAC__metadata_object_vorbiscomment_append_comment(object,entry, copy) - - libflac.FLAC__metadata_object_vorbiscomment_replace_comment.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_replace_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, FLAC__StreamMetadata_VorbisComment_Entry, FLAC__bool, FLAC__bool] - - def FLAC__metadata_object_vorbiscomment_replace_comment(object, entry, all, copy): - return libflac.FLAC__metadata_object_vorbiscomment_replace_comment(object,entry, all, copy) - - libflac.FLAC__metadata_object_vorbiscomment_delete_comment.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_delete_comment.argtypes = [POINTER(FLAC__StreamMetadata), c_uint] - - def FLAC__metadata_object_vorbiscomment_delete_comment(object, comment_num): - return libflac.FLAC__metadata_object_vorbiscomment_delete_comment(object,comment_num) - - libflac.FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair.argtypes = [POINTER(FLAC__StreamMetadata_VorbisComment_Entry), c_char_p, c_char_p] - - def FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(entry, field_name, field_value): - return libflac.FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(entry, field_name, field_value) - - libflac.FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair.argtypes = [POINTER(FLAC__StreamMetadata_VorbisComment_Entry), c_char_p_p, c_char_p_p] - - def FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, field_name, field_value): - return libflac.FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, field_name, field_value) - - libflac.FLAC__metadata_object_vorbiscomment_entry_matches.restype = FLAC__bool - libflac.FLAC__metadata_object_vorbiscomment_entry_matches.argtypes = [POINTER(FLAC__StreamMetadata_VorbisComment_Entry), c_char_p, c_uint] - - def FLAC__metadata_object_vorbiscomment_entry_matches(entry, field_name, field_value): - return libflac.FLAC__metadata_object_vorbiscomment_entry_matches(entry, field_name, field_value) - - libflac.FLAC__metadata_object_vorbiscomment_find_entry_from.restype = c_int - libflac.FLAC__metadata_object_vorbiscomment_find_entry_from.argtypes = [POINTER(FLAC__StreamMetadata), c_uint, c_char_p] - - def FLAC__metadata_object_vorbiscomment_find_entry_from(object, offset, field_name): - return libflac.FLAC__metadata_object_vorbiscomment_find_entry_from(object, offset, field_name) - - libflac.FLAC__metadata_object_vorbiscomment_remove_entry_matching.restype = c_int - libflac.FLAC__metadata_object_vorbiscomment_remove_entry_matching.argtypes = [POINTER(FLAC__StreamMetadata), c_char_p] - - def FLAC__metadata_object_vorbiscomment_remove_entry_matching(object, field_name): - return libflac.FLAC__metadata_object_vorbiscomment_remove_entry_matching(object, field_name) - - libflac.FLAC__metadata_object_vorbiscomment_remove_entries_matching.restype = c_int - libflac.FLAC__metadata_object_vorbiscomment_remove_entries_matching.argtypes = [POINTER(FLAC__StreamMetadata), c_char_p] - - def FLAC__metadata_object_vorbiscomment_remove_entries_matching(object, field_name): - return libflac.FLAC__metadata_object_vorbiscomment_remove_entries_matching(object, field_name) - - libflac.FLAC__metadata_object_cuesheet_track_new.restype = POINTER(FLAC__StreamMetadata_CueSheet_Track) - libflac.FLAC__metadata_object_cuesheet_track_new.argtypes = [] - - def FLAC__metadata_object_cuesheet_track_new(): - return libflac.FLAC__metadata_object_cuesheet_track_new() - - libflac.FLAC__metadata_object_cuesheet_track_delete.restype = None - libflac.FLAC__metadata_object_cuesheet_track_delete.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track)] - - def FLAC__metadata_object_cuesheet_track_delete(object): - return libflac.FLAC__metadata_object_cuesheet_track_delete(object) - - libflac.FLAC__metadata_object_cuesheet_track_resize_indices.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_track_resize_indices.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint] - - def FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, new_num_indices): - return libflac.FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, new_num_indices) - - libflac.FLAC__metadata_object_cuesheet_track_insert_index.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_track_insert_index.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint, FLAC__StreamMetadata_CueSheet_Index] - - def FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index): - return libflac.FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index) - - libflac.FLAC__metadata_object_cuesheet_track_insert_blank_index.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_track_insert_blank_index.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint] - - def FLAC__metadata_object_cuesheet_track_insert_blank_index(object, track_num, index_num): - return libflac.FLAC__metadata_object_cuesheet_track_insert_blank_index(object, track_num, index_num) - - libflac.FLAC__metadata_object_cuesheet_track_delete_index.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_track_delete_index.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, c_uint] - - def FLAC__metadata_object_cuesheet_track_delete_index(object, track_num, index_num): - return libflac.FLAC__metadata_object_cuesheet_track_delete_index(object, track_num, index_num) - - libflac.FLAC__metadata_object_cuesheet_resize_tracks.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_resize_tracks.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint] - - def FLAC__metadata_object_cuesheet_resize_tracks(object, new_num_tracks): - return libflac.FLAC__metadata_object_cuesheet_resize_tracks(object, new_num_tracks) - - libflac.FLAC__metadata_object_cuesheet_set_track.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_set_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__bool] - - def FLAC__metadata_object_cuesheet_set_track(object, new_num_tracks, track, copy): - return libflac.FLAC__metadata_object_cuesheet_set_track(object, new_num_tracks, track, copy) - - libflac.FLAC__metadata_object_cuesheet_insert_track.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_insert_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint, POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__bool] - - def FLAC__metadata_object_cuesheet_insert_track(object, track_num, track, copy): - return libflac.FLAC__metadata_object_cuesheet_insert_track(object, track_num, track, copy) - - libflac.FLAC__metadata_object_cuesheet_insert_blank_track.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_insert_blank_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint] - - def FLAC__metadata_object_cuesheet_insert_blank_track(object, track_num): - return libflac.FLAC__metadata_object_cuesheet_insert_blank_track(object, track_num) - - libflac.FLAC__metadata_object_cuesheet_delete_track.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_delete_track.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_uint] - - def FLAC__metadata_object_cuesheet_delete_track(object, track_num): - return libflac.FLAC__metadata_object_cuesheet_delete_track(object, track_num) - - libflac.FLAC__metadata_object_cuesheet_is_legal.restype = FLAC__bool - libflac.FLAC__metadata_object_cuesheet_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__bool, c_char_p_p] - - def FLAC__metadata_object_cuesheet_is_legal(object, check_cd_da_subset, violation): - return libflac.FLAC__metadata_object_cuesheet_is_legal(object, check_cd_da_subset, violation) - - libflac.FLAC__metadata_object_cuesheet_calculate_cddb_id.restype = FLAC__uint32 - libflac.FLAC__metadata_object_cuesheet_calculate_cddb_id.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track)] - - def FLAC__metadata_object_cuesheet_calculate_cddb_id(object): - return libflac.FLAC__metadata_object_cuesheet_calculate_cddb_id(object) - - libflac.FLAC__metadata_object_picture_set_mime_type.restype = FLAC__bool - libflac.FLAC__metadata_object_picture_set_mime_type.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_char_p, FLAC__bool] - - def FLAC__metadata_object_picture_set_mime_type(object, mime_type, copy): - return libflac.FLAC__metadata_object_picture_set_mime_type(object, mime_type, copy) - - libflac.FLAC__metadata_object_picture_set_description.restype = FLAC__bool - libflac.FLAC__metadata_object_picture_set_description.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__byte_p, FLAC__bool] - - def FLAC__metadata_object_picture_set_description(object, description, copy): - return libflac.FLAC__metadata_object_picture_set_description(object, mime_type, copy) - - libflac.FLAC__metadata_object_picture_set_data.restype = FLAC__bool - libflac.FLAC__metadata_object_picture_set_data.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), FLAC__byte_p,FLAC__uint32, FLAC__bool] - - def FLAC__metadata_object_picture_set_data(object, data, length, copy): - return libflac.FLAC__metadata_object_picture_set_data(object, mime_type, copy) - - libflac.FLAC__metadata_object_picture_is_legal.restype = FLAC__bool - libflac.FLAC__metadata_object_picture_is_legal.argtypes = [POINTER(FLAC__StreamMetadata_CueSheet_Track), c_char_p] - - def FLAC__metadata_object_picture_is_legal(object, violation): - return libflac.FLAC__metadata_object_picture_is_legal(object, violation) - - # /metadata - - # stream_decoder - - FLAC__StreamDecoderState = c_int - FLAC__StreamDecoderStateEnum = ["FLAC__STREAM_DECODER_SEARCH_FOR_METADATA", - "FLAC__STREAM_DECODER_READ_METADATA", - "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC", - "FLAC__STREAM_DECODER_READ_FRAME", - "FLAC__STREAM_DECODER_END_OF_STREAM", - "FLAC__STREAM_DECODER_OGG_ERROR", - "FLAC__STREAM_DECODER_SEEK_ERROR", - "FLAC__STREAM_DECODER_ABORTED", - "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR", - "FLAC__STREAM_DECODER_UNINITIALIZED"] - - libflac.FLAC__StreamDecoderStateString.restype = c_char_p - libflac.FLAC__StreamDecoderStateString.argtypes = [] - - def FLAC__StreamDecoderStateString(): - return libflac.FLAC__StreamDecoderStateString() - - - FLAC__StreamDecoderInitStatus = c_int - FLAC__StreamDecoderInitStatusEnum = ["FLAC__STREAM_DECODER_INIT_STATUS_OK", - "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER", - "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS", - "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR", - "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE", - "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED"] - - libflac.FLAC__StreamDecoderInitStatusString.restype = c_char_p - libflac.FLAC__StreamDecoderInitStatusString.argtypes = [] - - def FLAC__StreamDecoderInitStatusString(): - return libflac.FLAC__StreamDecoderInitStatusString() - - - FLAC__StreamDecoderReadStatus = c_int - FLAC__StreamDecoderReadStatusEnum = ["FLAC__STREAM_DECODER_READ_STATUS_CONTINUE", - "FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM", - "FLAC__STREAM_DECODER_READ_STATUS_ABORT"] - - libflac.FLAC__StreamDecoderReadStatusString.restype = c_char_p - libflac.FLAC__StreamDecoderReadStatusString.argtypes = [] - - def FLAC__StreamDecoderReadStatusString(): - return libflac.FLAC__StreamDecoderReadStatusString() - - - FLAC__StreamDecoderSeekStatus = c_int - FLAC__StreamDecoderSeekStatusEnum = ["FLAC__STREAM_DECODER_SEEK_STATUS_OK", - "FLAC__STREAM_DECODER_SEEK_STATUS_ERROR", - "FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED"] - - libflac.FLAC__StreamDecoderSeekStatusString.restype = c_char_p - libflac.FLAC__StreamDecoderSeekStatusString.argtypes = [] - - def FLAC__StreamDecoderSeekStatusString(): - return libflac.FLAC__StreamDecoderSeekStatusString() - - - FLAC__StreamDecoderTellStatus = c_int - FLAC__StreamDecoderTellStatusEnum = ["FLAC__STREAM_DECODER_TELL_STATUS_OK", - "FLAC__STREAM_DECODER_TELL_STATUS_ERROR", - "FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED"] - - libflac.FLAC__StreamDecoderTellStatusString.restype = c_char_p - libflac.FLAC__StreamDecoderTellStatusString.argtypes = [] - - def FLAC__StreamDecoderTellStatusString(): - return libflac.FLAC__StreamDecoderTellStatusString() - - - FLAC__StreamDecoderLengthStatus = c_int - FLAC__StreamDecoderLengthStatusEnum = ["FLAC__STREAM_DECODER_LENGTH_STATUS_OK", - "FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR", - "FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED"] - - libflac.FLAC__StreamDecoderLengthStatusString.restype = c_char_p - libflac.FLAC__StreamDecoderLengthStatusString.argtypes = [] - - def FLAC__StreamDecoderLengthStatusString(): - return libflac.FLAC__StreamDecoderLengthStatusString() - - - FLAC__StreamDecoderWriteStatus = c_int - FLAC__StreamDecoderWriteStatusEnum = ["FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE", "FLAC__STREAM_DECODER_WRITE_STATUS_ABORT"] - - libflac.FLAC__StreamDecoderWriteStatusString.restype = c_char_p - libflac.FLAC__StreamDecoderWriteStatusString.argtypes = [] - - def FLAC__StreamDecoderWriteStatusString(): - return libflac.FLAC__StreamDecoderWriteStatusString() - - FLAC__StreamDecoderErrorStatus = c_int - FLAC__StreamDecoderErrorStatusEnum = ["FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC", - "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER", - "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH", - "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM"] - - libflac.FLAC__StreamDecoderErrorStatusString.restype = c_char_p - libflac.FLAC__StreamDecoderErrorStatusString.argtypes = [] - - def FLAC__StreamDecoderErrorStatusString(): - return libflac.FLAC__StreamDecoderErrorStatusString() - - - - class FLAC__StreamDecoderProtected(Structure): - _fields_ = [("dummy", c_int)] - - class FLAC__StreamDecoderPrivate(Structure): - _fields_ = [("dummy", c_int)] - - class FLAC__StreamDecoder(Structure): - _fields_ = [("protected_", POINTER(FLAC__StreamDecoderProtected)), - ("private_", POINTER(FLAC__StreamDecoderPrivate))] - - FLAC__StreamDecoderReadCallback = CFUNCTYPE( - FLAC__StreamDecoderReadStatus, - POINTER(FLAC__StreamDecoder), - POINTER(FLAC__byte*0), - c_size_t_p, - c_void_p - ) - - FLAC__StreamDecoderSeekCallback = CFUNCTYPE( - FLAC__StreamDecoderSeekStatus, - POINTER(FLAC__StreamDecoder), - FLAC__uint64, - c_void_p - ) - - FLAC__StreamDecoderTellCallback = CFUNCTYPE( - FLAC__StreamDecoderTellStatus, - POINTER(FLAC__StreamDecoder), - FLAC__uint64_p, - c_void_p - ) - - FLAC__StreamDecoderLengthCallback = CFUNCTYPE( - FLAC__StreamDecoderLengthStatus, - POINTER(FLAC__StreamDecoder), - FLAC__uint64_p, - c_void_p - ) - - FLAC__StreamDecoderEofCallback = CFUNCTYPE( - FLAC__bool, - POINTER(FLAC__StreamDecoder), - c_void_p - ) - - FLAC__StreamDecoderWriteCallback = CFUNCTYPE( - FLAC__StreamDecoderWriteStatus, - POINTER(FLAC__StreamDecoder), - POINTER(FLAC__Frame), - POINTER(FLAC__int32_p*0), - c_void_p - ) - - FLAC__StreamDecoderMetadataCallback = CFUNCTYPE( - None, - POINTER(FLAC__StreamDecoder), - POINTER(FLAC__StreamMetadata), - c_void_p - ) - - FLAC__StreamDecoderErrorCallback = CFUNCTYPE( - None, - POINTER(FLAC__StreamDecoder), - FLAC__StreamDecoderErrorStatus, - c_void_p - ) - - - libflac.FLAC__stream_decoder_new.restype = POINTER(FLAC__StreamDecoder) - libflac.FLAC__stream_decoder_new.argtypes = [] - - def FLAC__stream_decoder_new(): - return libflac.FLAC__stream_decoder_new() - - libflac.FLAC__stream_decoder_delete.restype = None - libflac.FLAC__stream_decoder_delete.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_delete(decoder): - return libflac.FLAC__stream_decoder_delete(decoder) - - - libflac.FLAC__stream_decoder_set_ogg_serial_number.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_ogg_serial_number.argtypes = [POINTER(FLAC__StreamDecoder), c_long] - - def FLAC__stream_decoder_set_ogg_serial_number(decoder, serial_number): - return libflac.FLAC__stream_decoder_set_ogg_serial_number(decoder, serial_number) - - libflac.FLAC__stream_decoder_set_md5_checking.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_md5_checking.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__bool] - - def FLAC__stream_decoder_set_md5_checking(decoder, value): - return libflac.FLAC__stream_decoder_set_md5_checking(decoder, value) - - libflac.FLAC__stream_decoder_set_metadata_respond.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_metadata_respond.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__MetadataType] - - def FLAC__stream_decoder_set_metadata_respond(decoder, type): - return libflac.FLAC__stream_decoder_set_metadata_respond(decoder, type) - - libflac.FLAC__stream_decoder_set_metadata_respond_application.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_metadata_respond_application.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__byte*4] - - def FLAC__stream_decoder_set_metadata_respond_application(decoder, id): - return libflac.FLAC__stream_decoder_set_metadata_respond_application(decoder, id) - - libflac.FLAC__stream_decoder_set_metadata_respond_all.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_metadata_respond_all.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_set_metadata_respond_all(decoder): - return libflac.FLAC__stream_decoder_set_metadata_respond_all(decoder) - - libflac.FLAC__stream_decoder_set_metadata_ignore.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_metadata_ignore.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__MetadataType] - - def FLAC__stream_decoder_set_metadata_ignore(decoder, type): - return libflac.FLAC__stream_decoder_set_metadata_ignore(decoder, type) - - libflac.FLAC__stream_decoder_set_metadata_ignore_application.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_metadata_ignore_application.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__byte*4] - - def FLAC__stream_decoder_set_metadata_ignore_application(decoder, id): - return libflac.FLAC__stream_decoder_set_metadata_ignore_application(decoder, id) - - libflac.FLAC__stream_decoder_set_metadata_ignore_all.restype = FLAC__bool - libflac.FLAC__stream_decoder_set_metadata_ignore_all.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_set_metadata_ignore_all(decoder): - return libflac.FLAC__stream_decoder_set_metadata_ignore_all(decoder) - - libflac.FLAC__stream_decoder_get_state.restype = FLAC__StreamDecoderState - libflac.FLAC__stream_decoder_get_state.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_state(decoder): - return libflac.FLAC__stream_decoder_get_state(decoder) - - libflac.FLAC__stream_decoder_get_resolved_state_string.restype = c_char_p - libflac.FLAC__stream_decoder_get_resolved_state_string.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_resolved_state_string(decoder): - return libflac.FLAC__stream_decoder_get_resolved_state_string(decoder) - - libflac.FLAC__stream_decoder_get_md5_checking.restype = FLAC__bool - libflac.FLAC__stream_decoder_get_md5_checking.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_md5_checking(decoder): - return libflac.FLAC__stream_decoder_get_md5_checking(decoder) - - libflac.FLAC__stream_decoder_get_total_samples.restype = FLAC__uint64 - libflac.FLAC__stream_decoder_get_total_samples.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_total_samples(decoder): - return libflac.FLAC__stream_decoder_get_total_samples(decoder) - - libflac.FLAC__stream_decoder_get_channels.restype = c_uint - libflac.FLAC__stream_decoder_get_channels.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_channels(decoder): - return libflac.FLAC__stream_decoder_get_channels(decoder) - - libflac.FLAC__stream_decoder_get_channel_assignment.restype = FLAC__ChannelAssignment - libflac.FLAC__stream_decoder_get_channel_assignment.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_channel_assignment(decoder): - return libflac.FLAC__stream_decoder_get_channel_assignment(decoder) - - libflac.FLAC__stream_decoder_get_bits_per_sample.restype = c_uint - libflac.FLAC__stream_decoder_get_bits_per_sample.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_bits_per_sample(decoder): - return libflac.FLAC__stream_decoder_get_bits_per_sample(decoder) - - libflac.FLAC__stream_decoder_get_sample_rate.restype = c_uint - libflac.FLAC__stream_decoder_get_sample_rate.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_sample_rate(decoder): - return libflac.FLAC__stream_decoder_get_sample_rate(decoder) - - libflac.FLAC__stream_decoder_get_blocksize.restype = c_uint - libflac.FLAC__stream_decoder_get_blocksize.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_get_blocksize(decoder): - return libflac.FLAC__stream_decoder_get_blocksize(decoder) - - libflac.FLAC__stream_decoder_get_decode_position.restype = FLAC__bool - libflac.FLAC__stream_decoder_get_decode_position.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__uint64_p] - - def FLAC__stream_decoder_get_decode_position(decoder, position): - return libflac.FLAC__stream_decoder_get_decode_position(decoder, position) - - libflac.FLAC__stream_decoder_init_stream.restype = FLAC__StreamDecoderInitStatus - libflac.FLAC__stream_decoder_init_stream.argtypes = [POINTER(FLAC__StreamDecoder), - FLAC__StreamDecoderReadCallback, - FLAC__StreamDecoderSeekCallback, - FLAC__StreamDecoderTellCallback, - FLAC__StreamDecoderLengthCallback, - FLAC__StreamDecoderEofCallback, - FLAC__StreamDecoderWriteCallback, - FLAC__StreamDecoderMetadataCallback, - FLAC__StreamDecoderErrorCallback, - c_void_p] - - def FLAC__stream_decoder_init_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data): - return libflac.FLAC__stream_decoder_init_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data) - - - libflac.FLAC__stream_decoder_init_ogg_stream.restype = FLAC__StreamDecoderInitStatus - libflac.FLAC__stream_decoder_init_ogg_stream.argtypes = [POINTER(FLAC__StreamDecoder), - FLAC__StreamDecoderReadCallback, - FLAC__StreamDecoderSeekCallback, - FLAC__StreamDecoderTellCallback, - FLAC__StreamDecoderLengthCallback, - FLAC__StreamDecoderEofCallback, - FLAC__StreamDecoderWriteCallback, - FLAC__StreamDecoderMetadataCallback, - FLAC__StreamDecoderErrorCallback, - c_void_p] - - def FLAC__stream_decoder_init_ogg_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data): - return libflac.FLAC__stream_decoder_init_ogg_stream(decoder, read_callback, seek_callback, tell_callback, length_callback, eof_callback, write_callback, metadata_callback, error_callback, client_data) - - libflac.FLAC__stream_decoder_init_file.restype = FLAC__StreamDecoderInitStatus - libflac.FLAC__stream_decoder_init_file.argtypes = [POINTER(FLAC__StreamDecoder), - c_char_p, - FLAC__StreamDecoderWriteCallback, - FLAC__StreamDecoderMetadataCallback, - FLAC__StreamDecoderErrorCallback, - c_void_p] - - def FLAC__stream_decoder_init_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data): - return libflac.FLAC__stream_decoder_init_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data) - - libflac.FLAC__stream_decoder_init_ogg_file.restype = FLAC__StreamDecoderInitStatus - libflac.FLAC__stream_decoder_init_ogg_file.argtypes = [POINTER(FLAC__StreamDecoder), - c_char_p, - FLAC__StreamDecoderWriteCallback, - FLAC__StreamDecoderMetadataCallback, - FLAC__StreamDecoderErrorCallback, - c_void_p] - - def FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data): - return libflac.FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback, metadata_callback, error_callback, client_data) - - libflac.FLAC__stream_decoder_finish.restype = FLAC__bool - libflac.FLAC__stream_decoder_finish.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_finish(decoder): - return libflac.FLAC__stream_decoder_finish(decoder) - - libflac.FLAC__stream_decoder_flush.restype = FLAC__bool - libflac.FLAC__stream_decoder_flush.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_flush(decoder): - return libflac.FLAC__stream_decoder_flush(decoder) - - libflac.FLAC__stream_decoder_reset.restype = FLAC__bool - libflac.FLAC__stream_decoder_reset.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_reset(decoder): - return libflac.FLAC__stream_decoder_reset(decoder) - - libflac.FLAC__stream_decoder_process_single.restype = FLAC__bool - libflac.FLAC__stream_decoder_process_single.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_process_single(decoder): - return libflac.FLAC__stream_decoder_process_single(decoder) - - libflac.FLAC__stream_decoder_process_until_end_of_metadata.restype = FLAC__bool - libflac.FLAC__stream_decoder_process_until_end_of_metadata.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_process_until_end_of_metadata(decoder): - return libflac.FLAC__stream_decoder_process_until_end_of_metadata(decoder) - - libflac.FLAC__stream_decoder_process_until_end_of_stream.restype = FLAC__bool - libflac.FLAC__stream_decoder_process_until_end_of_stream.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_process_until_end_of_stream(decoder): - return libflac.FLAC__stream_decoder_process_until_end_of_stream(decoder) - - libflac.FLAC__stream_decoder_skip_single_frame.restype = FLAC__bool - libflac.FLAC__stream_decoder_skip_single_frame.argtypes = [POINTER(FLAC__StreamDecoder)] - - def FLAC__stream_decoder_skip_single_frame(decoder): - return libflac.FLAC__stream_decoder_skip_single_frame(decoder) - - libflac.FLAC__stream_decoder_seek_absolute.restype = FLAC__bool - libflac.FLAC__stream_decoder_seek_absolute.argtypes = [POINTER(FLAC__StreamDecoder), FLAC__uint64] - - def FLAC__stream_decoder_seek_absolute(decoder, sample): - return libflac.FLAC__stream_decoder_seek_absolute(decoder, sample) - - # /stream_decoder - - # stream_encoder - - FLAC__StreamEncoderState = c_int - - libflac.FLAC__StreamEncoderStateString.restype = c_char_p - libflac.FLAC__StreamEncoderStateString.argtypes = [] - - def FLAC__StreamEncoderStateString(): - return libflac.FLAC__StreamEncoderStateString() - - - FLAC__StreamEncoderInitStatus = c_int - - libflac.FLAC__StreamEncoderInitStatusString.restype = c_char_p - libflac.FLAC__StreamEncoderInitStatusString.argtypes = [] - - def FLAC__StreamEncoderInitStatusString(): - return libflac.FLAC__StreamEncoderInitStatusString() - - - FLAC__StreamEncoderReadStatus = c_int - - libflac.FLAC__StreamEncoderReadStatusString.restype = c_char_p - libflac.FLAC__StreamEncoderReadStatusString.argtypes = [] - - def FLAC__StreamEncoderReadStatusString(): - return libflac.FLAC__StreamEncoderReadStatusString() - - - FLAC__StreamEncoderWriteStatus = c_int - - libflac.FLAC__StreamEncoderWriteStatusString.restype = c_char_p - libflac.FLAC__StreamEncoderWriteStatusString.argtypes = [] - - def FLAC__StreamEncoderWriteStatusString(): - return libflac.FLAC__StreamEncoderWriteStatusString() - - - FLAC__StreamEncoderSeekStatus = c_int - - libflac.FLAC__StreamEncoderSeekStatusString.restype = c_char_p - libflac.FLAC__StreamEncoderSeekStatusString.argtypes = [] - - def FLAC__StreamEncoderSeekStatusString(): - return libflac.FLAC__StreamEncoderSeekStatusString() - - - FLAC__StreamEncoderTellStatus = c_int - - libflac.FLAC__StreamEncoderTellStatusString.restype = c_char_p - libflac.FLAC__StreamEncoderTellStatusString.argtypes = [] - - def FLAC__StreamEncoderTellStatusString(): - return libflac.FLAC__StreamEncoderTellStatusString() - - - class FLAC__StreamEncoderProtected(Structure): - _fields_ = [("dummy", c_int)] - - class FLAC__StreamEncoderPrivate(Structure): - _fields_ = [("dummy", c_int)] - - class FLAC__StreamEncoder(Structure): - _fields_ = [("protected_", POINTER(FLAC__StreamEncoderProtected)), - ("private_", POINTER(FLAC__StreamEncoderPrivate))] - - FLAC__StreamEncoderReadCallback = CFUNCTYPE(FLAC__StreamEncoderReadStatus, POINTER(FLAC__StreamEncoder), POINTER(FLAC__byte*0), c_size_t_p, c_void_p) - - FLAC__StreamEncoderWriteCallback = CFUNCTYPE(FLAC__StreamEncoderWriteStatus, POINTER(FLAC__StreamEncoder), POINTER(FLAC__byte*0), c_size_t, c_uint, c_uint, c_void_p) - - FLAC__StreamEncoderSeekCallback = CFUNCTYPE(FLAC__StreamEncoderSeekStatus, POINTER(FLAC__StreamEncoder), FLAC__uint64, c_void_p) - - FLAC__StreamEncoderTellCallback = CFUNCTYPE(FLAC__StreamEncoderTellStatus, POINTER(FLAC__StreamEncoder), FLAC__uint64_p, c_void_p) - - FLAC__StreamEncoderMetadataCallback = CFUNCTYPE(None, POINTER(FLAC__StreamEncoder), POINTER(FLAC__StreamMetadata), c_void_p) - - FLAC__StreamEncoderProgressCallback = CFUNCTYPE(None, POINTER(FLAC__StreamEncoder), FLAC__uint64,FLAC__uint64, c_uint, c_uint, c_void_p) - - - libflac.FLAC__stream_encoder_new.restype = POINTER(FLAC__StreamEncoder) - libflac.FLAC__stream_encoder_new.argtypes = [] - - def FLAC__stream_encoder_new(): - return libflac.FLAC__stream_encoder_new() - - libflac.FLAC__stream_encoder_delete.restype = None - libflac.FLAC__stream_encoder_delete.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_delete(encoder): - return libflac.FLAC__stream_encoder_delete(encoder) - - - libflac.FLAC__stream_encoder_set_ogg_serial_number.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_ogg_serial_number.argtypes = [POINTER(FLAC__StreamEncoder), c_long] - - def FLAC__stream_encoder_set_ogg_serial_number(encoder, serial_number): - return libflac.FLAC__stream_encoder_set_ogg_serial_number(encoder, serial_number) - - libflac.FLAC__stream_encoder_set_verify.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_verify.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_verify(encoder, value): - return libflac.FLAC__stream_encoder_set_verify(encoder, value) - - libflac.FLAC__stream_encoder_set_streamable_subset.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_streamable_subset.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_streamable_subset(encoder, value): - return libflac.FLAC__stream_encoder_set_streamable_subset(encoder, value) - - libflac.FLAC__stream_encoder_set_channels.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_channels.argtypes = [POINTER(FLAC__StreamEncoder), c_uint] - - def FLAC__stream_encoder_set_channels(encoder, value): - return libflac.FLAC__stream_encoder_set_channels(encoder, value) - - libflac.FLAC__stream_encoder_set_bits_per_sample.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_bits_per_sample.argtypes = [POINTER(FLAC__StreamEncoder), c_uint] - - def FLAC__stream_encoder_set_bits_per_sample(encoder, value): - return libflac.FLAC__stream_encoder_set_bits_per_sample(encoder, value) - - libflac.FLAC__stream_encoder_set_sample_rate.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_sample_rate.argtypes = [POINTER(FLAC__StreamEncoder), c_uint] - - def FLAC__stream_encoder_set_sample_rate(encoder, value): - return libflac.FLAC__stream_encoder_set_sample_rate(encoder, value) - - libflac.FLAC__stream_encoder_set_compression_level.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_compression_level.argtypes = [POINTER(FLAC__StreamEncoder), c_uint] - - def FLAC__stream_encoder_set_compression_level(encoder, value): - return libflac.FLAC__stream_encoder_set_compression_level(encoder, value) - - libflac.FLAC__stream_encoder_set_blocksize.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_blocksize.argtypes = [POINTER(FLAC__StreamEncoder), c_uint] - - def FLAC__stream_encoder_set_blocksize(encoder, value): - return libflac.FLAC__stream_encoder_set_blocksize(encoder, value) - - libflac.FLAC__stream_encoder_set_do_mid_side_stereo.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_do_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_do_mid_side_stereo(encoder, value): - return libflac.FLAC__stream_encoder_set_do_mid_side_stereo(encoder, value) - - libflac.FLAC__stream_encoder_set_loose_mid_side_stereo.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_loose_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, value): - return libflac.FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, value) - - libflac.FLAC__stream_encoder_set_apodization.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_apodization.argtypes = [POINTER(FLAC__StreamEncoder), c_char_p] - - def FLAC__stream_encoder_set_apodization(encoder, specification): - return libflac.FLAC__stream_encoder_set_apodization(encoder, specification) - - libflac.FLAC__stream_encoder_set_max_lpc_order.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_max_lpc_order.argtypes = [POINTER(FLAC__StreamEncoder), c_uint] - - def FLAC__stream_encoder_set_max_lpc_order(encoder, value): - return libflac.FLAC__stream_encoder_set_max_lpc_order(encoder, value) - - libflac.FLAC__stream_encoder_set_qlp_coeff_precision.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_qlp_coeff_precision.argtypes = [POINTER(FLAC__StreamEncoder), c_uint] - - def FLAC__stream_encoder_set_qlp_coeff_precision(encoder, value): - return libflac.FLAC__stream_encoder_set_qlp_coeff_precision(encoder, value) - - libflac.FLAC__stream_encoder_set_do_qlp_coeff_prec_search.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_do_qlp_coeff_prec_search.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, value): - return libflac.FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, value) - - libflac.FLAC__stream_encoder_set_do_escape_coding.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_do_escape_coding.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_do_escape_coding(encoder, value): - return libflac.FLAC__stream_encoder_set_do_escape_coding(encoder, value) - - libflac.FLAC__stream_encoder_set_do_exhaustive_model_search.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_do_exhaustive_model_search.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, value): - return libflac.FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, value) - - libflac.FLAC__stream_encoder_set_min_residual_partition_order.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_min_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_min_residual_partition_order(encoder, value): - return libflac.FLAC__stream_encoder_set_min_residual_partition_order(encoder, value) - - libflac.FLAC__stream_encoder_set_max_residual_partition_order.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_max_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_max_residual_partition_order(encoder, value): - return libflac.FLAC__stream_encoder_set_max_residual_partition_order(encoder, value) - - libflac.FLAC__stream_encoder_set_rice_parameter_search_dist.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_rice_parameter_search_dist.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__bool] - - def FLAC__stream_encoder_set_rice_parameter_search_dist(encoder, value): - return libflac.FLAC__stream_encoder_set_rice_parameter_search_dist(encoder, value) - - libflac.FLAC__stream_encoder_set_total_samples_estimate.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_total_samples_estimate.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__uint64] - - def FLAC__stream_encoder_set_total_samples_estimate(encoder, value): - return libflac.FLAC__stream_encoder_set_total_samples_estimate(encoder, value) - - libflac.FLAC__stream_encoder_set_metadata.restype = FLAC__bool - libflac.FLAC__stream_encoder_set_metadata.argtypes = [POINTER(FLAC__StreamEncoder), POINTER(POINTER(FLAC__StreamMetadata)), c_uint] - - def FLAC__stream_encoder_set_metadata(encoder, metadata, num_blocks): - return libflac.FLAC__stream_encoder_set_metadata(encoder, metadata, num_blocks) - - libflac.FLAC__stream_encoder_get_state.restype = FLAC__StreamEncoderState - libflac.FLAC__stream_encoder_get_state.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_state(encoder): - return libflac.FLAC__stream_encoder_get_state(encoder) - - libflac.FLAC__stream_encoder_get_verify_decoder_state.restype = FLAC__StreamEncoderState - libflac.FLAC__stream_encoder_get_verify_decoder_state.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_verify_decoder_state(encoder): - return libflac.FLAC__stream_encoder_get_verify_decoder_state(encoder) - - libflac.FLAC__stream_encoder_get_resolved_state_string.restype = c_char_p - libflac.FLAC__stream_encoder_get_resolved_state_string.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_resolved_state_string(encoder): - return libflac.FLAC__stream_encoder_get_resolved_state_string(encoder) - - libflac.FLAC__stream_encoder_get_verify_decoder_error_stats.restype = None - libflac.FLAC__stream_encoder_get_verify_decoder_error_stats.argtypes = [POINTER(FLAC__StreamEncoder), FLAC__uint64_p, c_uint_p, c_uint_p, c_uint_p, FLAC__int32_p, FLAC__int32_p] - - def FLAC__stream_encoder_get_verify_decoder_error_stats(encoder, absolute_sample, frame_number, channel, sample, expected, got): - return libflac.FLAC__stream_encoder_get_verify_decoder_error_stats(encoder, absolute_sample, frame_number, channel, sample, expected, got) - - libflac.FLAC__stream_encoder_get_verify.restype = FLAC__bool - libflac.FLAC__stream_encoder_get_verify.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_verify(encoder): - return libflac.FLAC__stream_encoder_get_verify(encoder) - - libflac.FLAC__stream_encoder_get_streamable_subset.restype = FLAC__bool - libflac.FLAC__stream_encoder_get_streamable_subset.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_streamable_subset(encoder): - return libflac.FLAC__stream_encoder_get_streamable_subset(encoder) - - libflac.FLAC__stream_encoder_get_channels.restype = c_uint - libflac.FLAC__stream_encoder_get_channels.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_channels(encoder): - return libflac.FLAC__stream_encoder_get_channels(encoder) - - libflac.FLAC__stream_encoder_get_bits_per_sample.restype = c_uint - libflac.FLAC__stream_encoder_get_bits_per_sample.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_bits_per_sample(encoder): - return libflac.FLAC__stream_encoder_get_bits_per_sample(encoder) - - libflac.FLAC__stream_encoder_get_sample_rate.restype = c_uint - libflac.FLAC__stream_encoder_get_sample_rate.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_sample_rate(encoder): - return libflac.FLAC__stream_encoder_get_sample_rate(encoder) - - libflac.FLAC__stream_encoder_get_blocksize.restype = c_uint - libflac.FLAC__stream_encoder_get_blocksize.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_blocksize(encoder): - return libflac.FLAC__stream_encoder_get_blocksize(encoder) - - libflac.FLAC__stream_encoder_get_do_mid_side_stereo.restype = FLAC__bool - libflac.FLAC__stream_encoder_get_do_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_do_mid_side_stereo(encoder): - return libflac.FLAC__stream_encoder_get_do_mid_side_stereo(encoder) - - libflac.FLAC__stream_encoder_get_loose_mid_side_stereo.restype = FLAC__bool - libflac.FLAC__stream_encoder_get_loose_mid_side_stereo.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_loose_mid_side_stereo(encoder): - return libflac.FLAC__stream_encoder_get_loose_mid_side_stereo(encoder) - - libflac.FLAC__stream_encoder_get_max_lpc_order.restype = c_uint - libflac.FLAC__stream_encoder_get_max_lpc_order.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_max_lpc_order(encoder): - return libflac.FLAC__stream_encoder_get_max_lpc_order(encoder) - - libflac.FLAC__stream_encoder_get_qlp_coeff_precision.restype = c_uint - libflac.FLAC__stream_encoder_get_qlp_coeff_precision.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_qlp_coeff_precision(encoder): - return libflac.FLAC__stream_encoder_get_qlp_coeff_precision(encoder) - - libflac.FLAC__stream_encoder_get_do_qlp_coeff_prec_search.restype = FLAC__bool - libflac.FLAC__stream_encoder_get_do_qlp_coeff_prec_search.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_do_qlp_coeff_prec_search(encoder): - return libflac.FLAC__stream_encoder_get_do_qlp_coeff_prec_search(encoder) - - libflac.FLAC__stream_encoder_get_do_escape_coding.restype = FLAC__bool - libflac.FLAC__stream_encoder_get_do_escape_coding.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_do_escape_coding(encoder): - return libflac.FLAC__stream_encoder_get_do_escape_coding(encoder) - - libflac.FLAC__stream_encoder_get_do_exhaustive_model_search.restype = FLAC__bool - libflac.FLAC__stream_encoder_get_do_exhaustive_model_search.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_do_exhaustive_model_search(encoder): - return libflac.FLAC__stream_encoder_get_do_exhaustive_model_search(encoder) - - libflac.FLAC__stream_encoder_get_min_residual_partition_order.restype = c_uint - libflac.FLAC__stream_encoder_get_min_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_min_residual_partition_order(encoder): - return libflac.FLAC__stream_encoder_get_min_residual_partition_order(encoder) - - libflac.FLAC__stream_encoder_get_max_residual_partition_order.restype = c_uint - libflac.FLAC__stream_encoder_get_max_residual_partition_order.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_max_residual_partition_order(encoder): - return libflac.FLAC__stream_encoder_get_max_residual_partition_order(encoder) - - libflac.FLAC__stream_encoder_get_rice_parameter_search_dist.restype = c_uint - libflac.FLAC__stream_encoder_get_rice_parameter_search_dist.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_rice_parameter_search_dist(encoder): - return libflac.FLAC__stream_encoder_get_rice_parameter_search_dist(encoder) - - libflac.FLAC__stream_encoder_get_total_samples_estimate.restype = FLAC__uint64 - libflac.FLAC__stream_encoder_get_total_samples_estimate.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_get_total_samples_estimate(encoder): - return libflac.FLAC__stream_encoder_get_total_samples_estimate(encoder) - - libflac.FLAC__stream_encoder_init_stream.restype = FLAC__StreamEncoderInitStatus - libflac.FLAC__stream_encoder_init_stream.argtypes = [POINTER(FLAC__StreamEncoder), - FLAC__StreamEncoderWriteCallback, - FLAC__StreamEncoderSeekCallback, - FLAC__StreamEncoderTellCallback, - FLAC__StreamEncoderMetadataCallback, - c_void_p] - - def FLAC__stream_encoder_init_stream(encoder, write_callback, seek_callback, tell_callback, metadata_callback,client_data): - return libflac.FLAC__stream_encoder_init_stream(encoder, write_callback, seek_callback, tell_callback, metadata_callback,client_data) - - libflac.FLAC__stream_encoder_init_ogg_stream.restype = FLAC__StreamEncoderInitStatus - libflac.FLAC__stream_encoder_init_ogg_stream.argtypes = [POINTER(FLAC__StreamEncoder), - FLAC__StreamEncoderReadCallback, - FLAC__StreamEncoderWriteCallback, - FLAC__StreamEncoderSeekCallback, - FLAC__StreamEncoderTellCallback, - FLAC__StreamEncoderMetadataCallback, - c_void_p] - - def FLAC__stream_encoder_init_ogg_stream(encoder, read_callback, write_callback, seek_callback, tell_callback, metadata_callback,client_data): - return libflac.FLAC__stream_encoder_init_ogg_stream(encoder, read_callback, write_callback, seek_callback, tell_callback, metadata_callback,client_data) - - libflac.FLAC__stream_encoder_init_file.restype = FLAC__StreamEncoderInitStatus - libflac.FLAC__stream_encoder_init_file.argtypes = [POINTER(FLAC__StreamEncoder), - c_char_p, - FLAC__StreamEncoderProgressCallback, - c_void_p] - - def FLAC__stream_encoder_init_file(encoder, filename, progress_callback,client_data): - return libflac.FLAC__stream_encoder_init_file(encoder, filename, progress_callback,client_data) - - - libflac.FLAC__stream_encoder_init_ogg_file.restype = FLAC__StreamEncoderInitStatus - libflac.FLAC__stream_encoder_init_ogg_file.argtypes = [POINTER(FLAC__StreamEncoder), - c_char_p, - FLAC__StreamEncoderProgressCallback, - c_void_p] - - def FLAC__stream_encoder_init_ogg_file(encoder, filename, progress_callback,client_data): - return libflac.FLAC__stream_encoder_init_ogg_file(encoder, filename, progress_callback,client_data) - - libflac.FLAC__stream_encoder_finish.restype = FLAC__bool - libflac.FLAC__stream_encoder_finish.argtypes = [POINTER(FLAC__StreamEncoder)] - - def FLAC__stream_encoder_finish(encoder): - return libflac.FLAC__stream_encoder_finish(encoder) - - libflac.FLAC__stream_encoder_process.restype = FLAC__bool - libflac.FLAC__stream_encoder_process.argtypes = [POINTER(FLAC__StreamEncoder), POINTER(FLAC__int32_p*0), c_uint] - - def FLAC__stream_encoder_process(encoder, buffer, samples): - return libflac.FLAC__stream_encoder_process(encoder, buffer, samples) - - libflac.FLAC__stream_encoder_process_interleaved.restype = FLAC__bool - libflac.FLAC__stream_encoder_process_interleaved.argtypes = [POINTER(FLAC__StreamEncoder), POINTER(FLAC__int32*0), c_uint] - - def FLAC__stream_encoder_process_interleaved(encoder, buffer, samples): - return libflac.FLAC__stream_encoder_process_interleaved(encoder, buffer, samples) - - # /stream_encoder diff --git a/sbapp/pyogg/flac_file.py b/sbapp/pyogg/flac_file.py deleted file mode 100644 index 7e97ca7..0000000 --- a/sbapp/pyogg/flac_file.py +++ /dev/null @@ -1,114 +0,0 @@ -import ctypes -from itertools import chain - -from . import flac -from .audio_file import AudioFile -from .pyogg_error import PyOggError - -def _to_char_p(string): - try: - return ctypes.c_char_p(string.encode("utf-8")) - except: - return ctypes.c_char_p(string) - -def _resize_array(array, new_size): - return (array._type_*new_size).from_address(ctypes.addressof(array)) - - -class FlacFile(AudioFile): - def write_callback(self, decoder, frame, buffer, client_data): - multi_channel_buf = _resize_array(buffer.contents, self.channels) - arr_size = frame.contents.header.blocksize - if frame.contents.header.channels >= 2: - arrays = [] - for i in range(frame.contents.header.channels): - arr = ctypes.cast(multi_channel_buf[i], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents - arrays.append(arr[:]) - - arr = list(chain.from_iterable(zip(*arrays))) - - self.buffer[self.buffer_pos : self.buffer_pos + len(arr)] = arr[:] - self.buffer_pos += len(arr) - - else: - arr = ctypes.cast(multi_channel_buf[0], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents - self.buffer[self.buffer_pos : self.buffer_pos + arr_size] = arr[:] - self.buffer_pos += arr_size - return 0 - - def metadata_callback(self,decoder, metadata, client_data): - if not self.buffer: - self.total_samples = metadata.contents.data.stream_info.total_samples - self.channels = metadata.contents.data.stream_info.channels - Buffer = flac.FLAC__int16*(self.total_samples * self.channels) - self.buffer = Buffer() - self.frequency = metadata.contents.data.stream_info.sample_rate - - def error_callback(self,decoder, status, client_data): - raise PyOggError("An error occured during the process of decoding. Status enum: {}".format(flac.FLAC__StreamDecoderErrorStatusEnum[status])) - - def __init__(self, path): - self.decoder = flac.FLAC__stream_decoder_new() - - self.client_data = ctypes.c_void_p() - - #: Number of channels in audio file. - self.channels = None - - #: Number of samples per second (per channel). For - # example, 44100. - self.frequency = None - - self.total_samples = None - - #: Raw PCM data from audio file. - self.buffer = None - - self.buffer_pos = 0 - - write_callback_ = flac.FLAC__StreamDecoderWriteCallback(self.write_callback) - - metadata_callback_ = flac.FLAC__StreamDecoderMetadataCallback(self.metadata_callback) - - error_callback_ = flac.FLAC__StreamDecoderErrorCallback(self.error_callback) - - init_status = flac.FLAC__stream_decoder_init_file( - self.decoder, - _to_char_p(path), # This will have an issue with Unicode filenames - write_callback_, - metadata_callback_, - error_callback_, - self.client_data - ) - - if init_status: # error - error = flac.FLAC__StreamDecoderInitStatusEnum[init_status] - raise PyOggError( - "An error occured when trying to open '{}': {}".format(path, error) - ) - - metadata_status = (flac.FLAC__stream_decoder_process_until_end_of_metadata(self.decoder)) - if not metadata_status: # error - raise PyOggError("An error occured when trying to decode the metadata of {}".format(path)) - - stream_status = (flac.FLAC__stream_decoder_process_until_end_of_stream(self.decoder)) - if not stream_status: # error - raise PyOggError("An error occured when trying to decode the audio stream of {}".format(path)) - - flac.FLAC__stream_decoder_finish(self.decoder) - - #: Length of buffer - self.buffer_length = len(self.buffer) - - self.bytes_per_sample = ctypes.sizeof(flac.FLAC__int16) # See definition of Buffer in metadata_callback() - - # Cast buffer to one-dimensional array of chars - CharBuffer = ( - ctypes.c_byte * - (self.bytes_per_sample * len(self.buffer)) - ) - self.buffer = CharBuffer.from_buffer(self.buffer) - - # FLAC audio is always signed. See - # https://xiph.org/flac/api/group__flac__stream__decoder.html#gaf98a4f9e2cac5747da6018c3dfc8dde1 - self.signed = True diff --git a/sbapp/pyogg/flac_file_stream.py b/sbapp/pyogg/flac_file_stream.py deleted file mode 100644 index f832c31..0000000 --- a/sbapp/pyogg/flac_file_stream.py +++ /dev/null @@ -1,141 +0,0 @@ -import ctypes -from itertools import chain - -from . import flac -from .pyogg_error import PyOggError - -def _to_char_p(string): - try: - return ctypes.c_char_p(string.encode("utf-8")) - except: - return ctypes.c_char_p(string) - -def _resize_array(array, new_size): - return (array._type_*new_size).from_address(ctypes.addressof(array)) - - -class FlacFileStream: - def write_callback(self,decoder, frame, buffer, client_data): - multi_channel_buf = _resize_array(buffer.contents, self.channels) - arr_size = frame.contents.header.blocksize - if frame.contents.header.channels >= 2: - arrays = [] - for i in range(frame.contents.header.channels): - arr = ctypes.cast(multi_channel_buf[i], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents - arrays.append(arr[:]) - - arr = list(chain.from_iterable(zip(*arrays))) - - self.buffer = (flac.FLAC__int16*len(arr))(*arr) - self.bytes_written = len(arr) * 2 - - else: - arr = ctypes.cast(multi_channel_buf[0], ctypes.POINTER(flac.FLAC__int32*arr_size)).contents - self.buffer = (flac.FLAC__int16*len(arr))(*arr[:]) - self.bytes_written = arr_size * 2 - return 0 - - def metadata_callback(self,decoder, metadata, client_data): - self.total_samples = metadata.contents.data.stream_info.total_samples - self.channels = metadata.contents.data.stream_info.channels - self.frequency = metadata.contents.data.stream_info.sample_rate - - def error_callback(self,decoder, status, client_data): - raise PyOggError("An error occured during the process of decoding. Status enum: {}".format(flac.FLAC__StreamDecoderErrorStatusEnum[status])) - - def __init__(self, path): - self.decoder = flac.FLAC__stream_decoder_new() - - self.client_data = ctypes.c_void_p() - - #: Number of channels in audio file. - self.channels = None - - #: Number of samples per second (per channel). For - # example, 44100. - self.frequency = None - - self.total_samples = None - - self.buffer = None - - self.bytes_written = None - - self.write_callback_ = flac.FLAC__StreamDecoderWriteCallback(self.write_callback) - - self.metadata_callback_ = flac.FLAC__StreamDecoderMetadataCallback(self.metadata_callback) - - self.error_callback_ = flac.FLAC__StreamDecoderErrorCallback(self.error_callback) - - init_status = flac.FLAC__stream_decoder_init_file(self.decoder, - _to_char_p(path), - self.write_callback_, - self.metadata_callback_, - self.error_callback_, - self.client_data) - - if init_status: # error - raise PyOggError("An error occured when trying to open '{}': {}".format(path, flac.FLAC__StreamDecoderInitStatusEnum[init_status])) - - metadata_status = (flac.FLAC__stream_decoder_process_until_end_of_metadata(self.decoder)) - if not metadata_status: # error - raise PyOggError("An error occured when trying to decode the metadata of {}".format(path)) - - #: Bytes per sample - self.bytes_per_sample = 2 - - def get_buffer(self): - """Returns the buffer. - - Returns buffer (a bytes object) or None if all data has - been read from the file. - - """ - # Attempt to read a single frame of audio - stream_status = (flac.FLAC__stream_decoder_process_single(self.decoder)) - if not stream_status: # error - raise PyOggError("An error occured when trying to decode the audio stream of {}".format(path)) - - # Check if we encountered the end of the stream - if (flac.FLAC__stream_decoder_get_state(self.decoder) == 4): # end of stream - return None - - buffer_as_bytes = bytes(self.buffer) - return buffer_as_bytes - - def clean_up(self): - flac.FLAC__stream_decoder_finish(self.decoder) - - def get_buffer_as_array(self): - """Provides the buffer as a NumPy array. - - Note that the underlying data type is 16-bit signed - integers. - - Does not copy the underlying data, so the returned array - should either be processed or copied before the next call - to get_buffer() or get_buffer_as_array(). - - """ - import numpy # type: ignore - - # Read the next samples from the stream - buf = self.get_buffer() - - # Check if we've come to the end of the stream - if buf is None: - return None - - # Convert the bytes buffer to a NumPy array - array = numpy.frombuffer( - buf, - dtype=numpy.int16 - ) - - # Reshape the array - return array.reshape( - (len(buf) - // self.bytes_per_sample - // self.channels, - self.channels) - ) diff --git a/sbapp/pyogg/library_loader.py b/sbapp/pyogg/library_loader.py deleted file mode 100644 index 711b1ba..0000000 --- a/sbapp/pyogg/library_loader.py +++ /dev/null @@ -1,147 +0,0 @@ -import ctypes -import ctypes.util -import os -import sys -import platform -from typing import ( - Optional, - Dict, - List -) - -_here = os.path.dirname(__file__) - -class ExternalLibraryError(Exception): - pass - -architecture = platform.architecture()[0] - -_windows_styles = ["{}", "lib{}", "lib{}_dynamic", "{}_dynamic"] - -_other_styles = ["{}", "lib{}"] - -if architecture == "32bit": - for arch_style in ["32bit", "32" "86", "win32", "x86", "_x86", "_32", "_win32", "_32bit"]: - for style in ["{}", "lib{}"]: - _windows_styles.append(style.format("{}"+arch_style)) - -elif architecture == "64bit": - for arch_style in ["64bit", "64" "86_64", "amd64", "win_amd64", "x86_64", "_x86_64", "_64", "_amd64", "_64bit"]: - for style in ["{}", "lib{}"]: - _windows_styles.append(style.format("{}"+arch_style)) - - -run_tests = lambda lib, tests: [f(lib) for f in tests] - -# Get the appropriate directory for the shared libraries depending -# on the current platform and architecture -platform_ = platform.system() -lib_dir = None -if platform_ == "Darwin": - lib_dir = "libs/macos" -elif platform_ == "Windows": - if architecture == "32bit": - lib_dir = "libs/win32" - elif architecture == "64bit": - lib_dir = "libs/win_amd64" - - -class Library: - @staticmethod - def load(names: Dict[str, str], paths: Optional[List[str]] = None, tests = []) -> Optional[ctypes.CDLL]: - lib = InternalLibrary.load(names, tests) - if lib is None: - lib = ExternalLibrary.load(names["external"], paths, tests) - return lib - - -class InternalLibrary: - @staticmethod - def load(names: Dict[str, str], tests) -> Optional[ctypes.CDLL]: - # If we do not have a library directory, give up immediately - if lib_dir is None: - return None - - # Get the appropriate library filename given the platform - try: - name = names[platform_] - except KeyError: - return None - - # Attempt to load the library from here - path = _here + "/" + lib_dir + "/" + name - try: - lib = ctypes.CDLL(path) - except OSError as e: - return None - - # Check that the library passes the tests - if tests and all(run_tests(lib, tests)): - return lib - - # Library failed tests - return None - -# Cache of libraries that have already been loaded -_loaded_libraries: Dict[str, ctypes.CDLL] = {} - -class ExternalLibrary: - @staticmethod - def load(name, paths = None, tests = []): - if name in _loaded_libraries: - return _loaded_libraries[name] - if sys.platform == "win32": - lib = ExternalLibrary.load_windows(name, paths, tests) - _loaded_libraries[name] = lib - return lib - else: - lib = ExternalLibrary.load_other(name, paths, tests) - _loaded_libraries[name] = lib - return lib - - @staticmethod - def load_other(name, paths = None, tests = []): - os.environ["PATH"] += ";" + ";".join((os.getcwd(), _here)) - if paths: os.environ["PATH"] += ";" + ";".join(paths) - - for style in _other_styles: - candidate = style.format(name) - library = ctypes.util.find_library(candidate) - if library: - try: - lib = ctypes.CDLL(library) - if tests and all(run_tests(lib, tests)): - return lib - except: - pass - - @staticmethod - def load_windows(name, paths = None, tests = []): - os.environ["PATH"] += ";" + ";".join((os.getcwd(), _here)) - if paths: os.environ["PATH"] += ";" + ";".join(paths) - - not_supported = [] # libraries that were found, but are not supported - for style in _windows_styles: - candidate = style.format(name) - library = ctypes.util.find_library(candidate) - if library: - try: - lib = ctypes.CDLL(library) - if tests and all(run_tests(lib, tests)): - return lib - not_supported.append(library) - except WindowsError: - pass - except OSError: - not_supported.append(library) - - - if not_supported: - raise ExternalLibraryError("library '{}' couldn't be loaded, because the following candidates were not supported:".format(name) - + ("\n{}" * len(not_supported)).format(*not_supported)) - - raise ExternalLibraryError("library '{}' couldn't be loaded".format(name)) - - - - diff --git a/sbapp/pyogg/ogg.py b/sbapp/pyogg/ogg.py deleted file mode 100644 index 08a944b..0000000 --- a/sbapp/pyogg/ogg.py +++ /dev/null @@ -1,672 +0,0 @@ -############################################################ -# Ogg license: # -############################################################ -""" -Copyright (c) 2002, Xiph.org Foundation - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiph.org Foundation nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import ctypes -from ctypes import c_int, c_int8, c_int16, c_int32, c_int64, c_uint, c_uint8, c_uint16, c_uint32, c_uint64, c_float, c_long, c_ulong, c_char, c_char_p, c_ubyte, c_longlong, c_ulonglong, c_size_t, c_void_p, c_double, POINTER, pointer, cast -import ctypes.util -import sys -from traceback import print_exc as _print_exc -import os - -from .library_loader import Library, ExternalLibrary, ExternalLibraryError - - -def get_raw_libname(name): - name = os.path.splitext(name)[0].lower() - for x in "0123456789._- ":name=name.replace(x,"") - return name - -# Define a function to convert strings to char-pointers. In Python 3 -# all strings are Unicode, while in Python 2 they were ASCII-encoded. -# FIXME: Does PyOgg even support Python 2? -if sys.version_info.major > 2: - to_char_p = lambda s: s.encode('utf-8') -else: - to_char_p = lambda s: s - -__here = os.getcwd() - -libogg = None - -try: - names = { - "Windows": "ogg.dll", - "Darwin": "libogg.0.dylib", - "external": "ogg" - } - libogg = Library.load(names, tests = [lambda lib: hasattr(lib, "oggpack_writeinit")]) -except ExternalLibraryError: - pass -except: - _print_exc() - -if libogg is not None: - PYOGG_OGG_AVAIL = True -else: - PYOGG_OGG_AVAIL = False - -if PYOGG_OGG_AVAIL: - # Sanity check also satisfies mypy type checking - assert libogg is not None - - # ctypes - c_ubyte_p = POINTER(c_ubyte) - c_uchar = c_ubyte - c_uchar_p = c_ubyte_p - c_float_p = POINTER(c_float) - c_float_p_p = POINTER(c_float_p) - c_float_p_p_p = POINTER(c_float_p_p) - c_char_p_p = POINTER(c_char_p) - c_int_p = POINTER(c_int) - c_long_p = POINTER(c_long) - - # os_types - ogg_int16_t = c_int16 - ogg_uint16_t = c_uint16 - ogg_int32_t = c_int32 - ogg_uint32_t = c_uint32 - ogg_int64_t = c_int64 - ogg_uint64_t = c_uint64 - ogg_int64_t_p = POINTER(ogg_int64_t) - - # ogg - class ogg_iovec_t(ctypes.Structure): - """ - Wrapper for: - typedef struct ogg_iovec_t; - """ - _fields_ = [("iov_base", c_void_p), - ("iov_len", c_size_t)] - - class oggpack_buffer(ctypes.Structure): - """ - Wrapper for: - typedef struct oggpack_buffer; - """ - _fields_ = [("endbyte", c_long), - ("endbit", c_int), - ("buffer", c_uchar_p), - ("ptr", c_uchar_p), - ("storage", c_long)] - - class ogg_page(ctypes.Structure): - """ - Wrapper for: - typedef struct ogg_page; - """ - _fields_ = [("header", c_uchar_p), - ("header_len", c_long), - ("body", c_uchar_p), - ("body_len", c_long)] - - class ogg_stream_state(ctypes.Structure): - """ - Wrapper for: - typedef struct ogg_stream_state; - """ - _fields_ = [("body_data", c_uchar_p), - ("body_storage", c_long), - ("body_fill", c_long), - ("body_returned", c_long), - - ("lacing_vals", c_int), - ("granule_vals", ogg_int64_t), - - ("lacing_storage", c_long), - ("lacing_fill", c_long), - ("lacing_packet", c_long), - ("lacing_returned", c_long), - - ("header", c_uchar*282), - ("header_fill", c_int), - - ("e_o_s", c_int), - ("b_o_s", c_int), - - ("serialno", c_long), - ("pageno", c_long), - ("packetno", ogg_int64_t), - ("granulepos", ogg_int64_t)] - - class ogg_packet(ctypes.Structure): - """ - Wrapper for: - typedef struct ogg_packet; - """ - _fields_ = [("packet", c_uchar_p), - ("bytes", c_long), - ("b_o_s", c_long), - ("e_o_s", c_long), - - ("granulepos", ogg_int64_t), - - ("packetno", ogg_int64_t)] - - def __str__(self): - bos = "" - if self.b_o_s: - bos = "beginning of stream, " - eos = "" - if self.e_o_s: - eos = "end of stream, " - - # Converting the data will cause a seg-fault if the memory isn't valid - data = bytes(self.packet[0:self.bytes]) - value = ( - f"Ogg Packet <{hex(id(self))}>: " + - f"number {self.packetno}, " + - f"granule position {self.granulepos}, " + - bos + eos + - f"{self.bytes} bytes" - ) - return value - - class ogg_sync_state(ctypes.Structure): - """ - Wrapper for: - typedef struct ogg_sync_state; - """ - _fields_ = [("data", c_uchar_p), - ("storage", c_int), - ("fill", c_int), - ("returned", c_int), - - ("unsynched", c_int), - ("headerbytes", c_int), - ("bodybytes", c_int)] - - b_p = POINTER(oggpack_buffer) - oy_p = POINTER(ogg_sync_state) - op_p = POINTER(ogg_packet) - og_p = POINTER(ogg_page) - os_p = POINTER(ogg_stream_state) - iov_p = POINTER(ogg_iovec_t) - - libogg.oggpack_writeinit.restype = None - libogg.oggpack_writeinit.argtypes = [b_p] - - def oggpack_writeinit(b): - libogg.oggpack_writeinit(b) - - try: - libogg.oggpack_writecheck.restype = c_int - libogg.oggpack_writecheck.argtypes = [b_p] - def oggpack_writecheck(b): - libogg.oggpack_writecheck(b) - except: - pass - - libogg.oggpack_writetrunc.restype = None - libogg.oggpack_writetrunc.argtypes = [b_p, c_long] - - def oggpack_writetrunc(b, bits): - libogg.oggpack_writetrunc(b, bits) - - libogg.oggpack_writealign.restype = None - libogg.oggpack_writealign.argtypes = [b_p] - - def oggpack_writealign(b): - libogg.oggpack_writealign(b) - - libogg.oggpack_writecopy.restype = None - libogg.oggpack_writecopy.argtypes = [b_p, c_void_p, c_long] - - def oggpack_writecopy(b, source, bits): - libogg.oggpack_writecopy(b, source, bits) - - libogg.oggpack_reset.restype = None - libogg.oggpack_reset.argtypes = [b_p] - - def oggpack_reset(b): - libogg.oggpack_reset(b) - - libogg.oggpack_writeclear.restype = None - libogg.oggpack_writeclear.argtypes = [b_p] - - def oggpack_writeclear(b): - libogg.oggpack_writeclear(b) - - libogg.oggpack_readinit.restype = None - libogg.oggpack_readinit.argtypes = [b_p, c_uchar_p, c_int] - - def oggpack_readinit(b, buf, bytes): - libogg.oggpack_readinit(b, buf, bytes) - - libogg.oggpack_write.restype = None - libogg.oggpack_write.argtypes = [b_p, c_ulong, c_int] - - def oggpack_write(b, value, bits): - libogg.oggpack_write(b, value, bits) - - libogg.oggpack_look.restype = c_long - libogg.oggpack_look.argtypes = [b_p, c_int] - - def oggpack_look(b, bits): - return libogg.oggpack_look(b, bits) - - libogg.oggpack_look1.restype = c_long - libogg.oggpack_look1.argtypes = [b_p] - - def oggpack_look1(b): - return libogg.oggpack_look1(b) - - libogg.oggpack_adv.restype = None - libogg.oggpack_adv.argtypes = [b_p, c_int] - - def oggpack_adv(b, bits): - libogg.oggpack_adv(b, bits) - - libogg.oggpack_adv1.restype = None - libogg.oggpack_adv1.argtypes = [b_p] - - def oggpack_adv1(b): - libogg.oggpack_adv1(b) - - libogg.oggpack_read.restype = c_long - libogg.oggpack_read.argtypes = [b_p, c_int] - - def oggpack_read(b, bits): - return libogg.oggpack_read(b, bits) - - libogg.oggpack_read1.restype = c_long - libogg.oggpack_read1.argtypes = [b_p] - - def oggpack_read1(b): - return libogg.oggpack_read1(b) - - libogg.oggpack_bytes.restype = c_long - libogg.oggpack_bytes.argtypes = [b_p] - - def oggpack_bytes(b): - return libogg.oggpack_bytes(b) - - libogg.oggpack_bits.restype = c_long - libogg.oggpack_bits.argtypes = [b_p] - - def oggpack_bits(b): - return libogg.oggpack_bits(b) - - libogg.oggpack_get_buffer.restype = c_uchar_p - libogg.oggpack_get_buffer.argtypes = [b_p] - - def oggpack_get_buffer(b): - return libogg.oggpack_get_buffer(b) - - - - libogg.oggpackB_writeinit.restype = None - libogg.oggpackB_writeinit.argtypes = [b_p] - - def oggpackB_writeinit(b): - libogg.oggpackB_writeinit(b) - - try: - libogg.oggpackB_writecheck.restype = c_int - libogg.oggpackB_writecheck.argtypes = [b_p] - - def oggpackB_writecheck(b): - return libogg.oggpackB_writecheck(b) - except: - pass - - libogg.oggpackB_writetrunc.restype = None - libogg.oggpackB_writetrunc.argtypes = [b_p, c_long] - - def oggpackB_writetrunc(b, bits): - libogg.oggpackB_writetrunc(b, bits) - - libogg.oggpackB_writealign.restype = None - libogg.oggpackB_writealign.argtypes = [b_p] - - def oggpackB_writealign(b): - libogg.oggpackB_writealign(b) - - libogg.oggpackB_writecopy.restype = None - libogg.oggpackB_writecopy.argtypes = [b_p, c_void_p, c_long] - - def oggpackB_writecopy(b, source, bits): - libogg.oggpackB_writecopy(b, source, bits) - - libogg.oggpackB_reset.restype = None - libogg.oggpackB_reset.argtypes = [b_p] - - def oggpackB_reset(b): - libogg.oggpackB_reset(b) - - libogg.oggpackB_reset.restype = None - libogg.oggpackB_writeclear.argtypes = [b_p] - - def oggpackB_reset(b): - libogg.oggpackB_reset(b) - - libogg.oggpackB_readinit.restype = None - libogg.oggpackB_readinit.argtypes = [b_p, c_uchar_p, c_int] - - def oggpackB_readinit(b, buf, bytes): - libogg.oggpackB_readinit(b, buf, bytes) - - libogg.oggpackB_write.restype = None - libogg.oggpackB_write.argtypes = [b_p, c_ulong, c_int] - - def oggpackB_write(b, value, bits): - libogg.oggpackB_write(b, value, bits) - - libogg.oggpackB_look.restype = c_long - libogg.oggpackB_look.argtypes = [b_p, c_int] - - def oggpackB_look(b, bits): - return libogg.oggpackB_look(b, bits) - - libogg.oggpackB_look1.restype = c_long - libogg.oggpackB_look1.argtypes = [b_p] - - def oggpackB_look1(b): - return libogg.oggpackB_look1(b) - - libogg.oggpackB_adv.restype = None - libogg.oggpackB_adv.argtypes = [b_p, c_int] - - def oggpackB_adv(b, bits): - libogg.oggpackB_adv(b, bits) - - libogg.oggpackB_adv1.restype = None - libogg.oggpackB_adv1.argtypes = [b_p] - - def oggpackB_adv1(b): - libogg.oggpackB_adv1(b) - - libogg.oggpackB_read.restype = c_long - libogg.oggpackB_read.argtypes = [b_p, c_int] - - def oggpackB_read(b, bits): - return libogg.oggpackB_read(b, bits) - - libogg.oggpackB_read1.restype = c_long - libogg.oggpackB_read1.argtypes = [b_p] - - def oggpackB_read1(b): - return libogg.oggpackB_read1(b) - - libogg.oggpackB_bytes.restype = c_long - libogg.oggpackB_bytes.argtypes = [b_p] - - def oggpackB_bytes(b): - return libogg.oggpackB_bytes(b) - - libogg.oggpackB_bits.restype = c_long - libogg.oggpackB_bits.argtypes = [b_p] - - def oggpackB_bits(b): - return libogg.oggpackB_bits(b) - - libogg.oggpackB_get_buffer.restype = c_uchar_p - libogg.oggpackB_get_buffer.argtypes = [b_p] - - def oggpackB_get_buffer(b): - return libogg.oggpackB_get_buffer(b) - - - - libogg.ogg_stream_packetin.restype = c_int - libogg.ogg_stream_packetin.argtypes = [os_p, op_p] - - def ogg_stream_packetin(os, op): - return libogg.ogg_stream_packetin(os, op) - - try: - libogg.ogg_stream_iovecin.restype = c_int - libogg.ogg_stream_iovecin.argtypes = [os_p, iov_p, c_int, c_long, ogg_int64_t] - - def ogg_stream_iovecin(os, iov, count, e_o_s, granulepos): - return libogg.ogg_stream_iovecin(os, iov, count, e_o_s, granulepos) - except: - pass - - libogg.ogg_stream_pageout.restype = c_int - libogg.ogg_stream_pageout.argtypes = [os_p, og_p] - - def ogg_stream_pageout(os, og): - return libogg.ogg_stream_pageout(os, og) - - try: - libogg.ogg_stream_pageout_fill.restype = c_int - libogg.ogg_stream_pageout_fill.argtypes = [os_p, og_p, c_int] - def ogg_stream_pageout_fill(os, og, nfill): - return libogg.ogg_stream_pageout_fill(os, og, nfill) - except: - pass - - libogg.ogg_stream_flush.restype = c_int - libogg.ogg_stream_flush.argtypes = [os_p, og_p] - - def ogg_stream_flush(os, og): - return libogg.ogg_stream_flush(os, og) - - try: - libogg.ogg_stream_flush_fill.restype = c_int - libogg.ogg_stream_flush_fill.argtypes = [os_p, og_p, c_int] - def ogg_stream_flush_fill(os, og, nfill): - return libogg.ogg_stream_flush_fill(os, og, nfill) - except: - pass - - - - libogg.ogg_sync_init.restype = c_int - libogg.ogg_sync_init.argtypes = [oy_p] - - def ogg_sync_init(oy): - return libogg.ogg_sync_init(oy) - - libogg.ogg_sync_clear.restype = c_int - libogg.ogg_sync_clear.argtypes = [oy_p] - - def ogg_sync_clear(oy): - return libogg.ogg_sync_clear(oy) - - libogg.ogg_sync_reset.restype = c_int - libogg.ogg_sync_reset.argtypes = [oy_p] - - def ogg_sync_reset(oy): - return libogg.ogg_sync_reset(oy) - - libogg.ogg_sync_destroy.restype = c_int - libogg.ogg_sync_destroy.argtypes = [oy_p] - - def ogg_sync_destroy(oy): - return libogg.ogg_sync_destroy(oy) - - try: - libogg.ogg_sync_check.restype = c_int - libogg.ogg_sync_check.argtypes = [oy_p] - def ogg_sync_check(oy): - return libogg.ogg_sync_check(oy) - except: - pass - - - - libogg.ogg_sync_buffer.restype = c_char_p - libogg.ogg_sync_buffer.argtypes = [oy_p, c_long] - - def ogg_sync_buffer(oy, size): - return libogg.ogg_sync_buffer(oy, size) - - libogg.ogg_sync_wrote.restype = c_int - libogg.ogg_sync_wrote.argtypes = [oy_p, c_long] - - def ogg_sync_wrote(oy, bytes): - return libogg.ogg_sync_wrote(oy, bytes) - - libogg.ogg_sync_pageseek.restype = c_int - libogg.ogg_sync_pageseek.argtypes = [oy_p, og_p] - - def ogg_sync_pageseek(oy, og): - return libogg.ogg_sync_pageseek(oy, og) - - libogg.ogg_sync_pageout.restype = c_long - libogg.ogg_sync_pageout.argtypes = [oy_p, og_p] - - def ogg_sync_pageout(oy, og): - return libogg.ogg_sync_pageout(oy, og) - - libogg.ogg_stream_pagein.restype = c_int - libogg.ogg_stream_pagein.argtypes = [os_p, og_p] - - def ogg_stream_pagein(os, og): - return libogg.ogg_stream_pagein(oy, og) - - libogg.ogg_stream_packetout.restype = c_int - libogg.ogg_stream_packetout.argtypes = [os_p, op_p] - - def ogg_stream_packetout(os, op): - return libogg.ogg_stream_packetout(oy, op) - - libogg.ogg_stream_packetpeek.restype = c_int - libogg.ogg_stream_packetpeek.argtypes = [os_p, op_p] - - def ogg_stream_packetpeek(os, op): - return libogg.ogg_stream_packetpeek(os, op) - - - - libogg.ogg_stream_init.restype = c_int - libogg.ogg_stream_init.argtypes = [os_p, c_int] - - def ogg_stream_init(os, serialno): - return libogg.ogg_stream_init(os, serialno) - - libogg.ogg_stream_clear.restype = c_int - libogg.ogg_stream_clear.argtypes = [os_p] - - def ogg_stream_clear(os): - return libogg.ogg_stream_clear(os) - - libogg.ogg_stream_reset.restype = c_int - libogg.ogg_stream_reset.argtypes = [os_p] - - def ogg_stream_reset(os): - return libogg.ogg_stream_reset(os) - - libogg.ogg_stream_reset_serialno.restype = c_int - libogg.ogg_stream_reset_serialno.argtypes = [os_p, c_int] - - def ogg_stream_reset_serialno(os, serialno): - return libogg.ogg_stream_reset_serialno(os, serialno) - - libogg.ogg_stream_destroy.restype = c_int - libogg.ogg_stream_destroy.argtypes = [os_p] - - def ogg_stream_destroy(os): - return libogg.ogg_stream_destroy(os) - - try: - libogg.ogg_stream_check.restype = c_int - libogg.ogg_stream_check.argtypes = [os_p] - def ogg_stream_check(os): - return libogg.ogg_stream_check(os) - except: - pass - - libogg.ogg_stream_eos.restype = c_int - libogg.ogg_stream_eos.argtypes = [os_p] - - def ogg_stream_eos(os): - return libogg.ogg_stream_eos(os) - - - - libogg.ogg_page_checksum_set.restype = None - libogg.ogg_page_checksum_set.argtypes = [og_p] - - def ogg_page_checksum_set(og): - libogg.ogg_page_checksum_set(og) - - - - libogg.ogg_page_version.restype = c_int - libogg.ogg_page_version.argtypes = [og_p] - - def ogg_page_version(og): - return libogg.ogg_page_version(og) - - libogg.ogg_page_continued.restype = c_int - libogg.ogg_page_continued.argtypes = [og_p] - - def ogg_page_continued(og): - return libogg.ogg_page_continued(og) - - libogg.ogg_page_bos.restype = c_int - libogg.ogg_page_bos.argtypes = [og_p] - - def ogg_page_bos(og): - return libogg.ogg_page_bos(og) - - libogg.ogg_page_eos.restype = c_int - libogg.ogg_page_eos.argtypes = [og_p] - - def ogg_page_eos(og): - return libogg.ogg_page_eos(og) - - libogg.ogg_page_granulepos.restype = ogg_int64_t - libogg.ogg_page_granulepos.argtypes = [og_p] - - def ogg_page_granulepos(og): - return libogg.ogg_page_granulepos(og) - - libogg.ogg_page_serialno.restype = c_int - libogg.ogg_page_serialno.argtypes = [og_p] - - def ogg_page_serialno(og): - return libogg.ogg_page_serialno(og) - - libogg.ogg_page_pageno.restype = c_long - libogg.ogg_page_pageno.argtypes = [og_p] - - def ogg_page_pageno(og): - return libogg.ogg_page_pageno(og) - - libogg.ogg_page_packets.restype = c_int - libogg.ogg_page_packets.argtypes = [og_p] - - def ogg_page_packets(og): - return libogg.ogg_page_packets(og) - - - - libogg.ogg_packet_clear.restype = None - libogg.ogg_packet_clear.argtypes = [op_p] - - def ogg_packet_clear(op): - libogg.ogg_packet_clear(op) diff --git a/sbapp/pyogg/ogg_opus_writer.py b/sbapp/pyogg/ogg_opus_writer.py deleted file mode 100644 index 547d0f5..0000000 --- a/sbapp/pyogg/ogg_opus_writer.py +++ /dev/null @@ -1,421 +0,0 @@ -import builtins -import copy -import ctypes -import random -import struct -from typing import ( - Optional, - Union, - BinaryIO -) - -from . import ogg -from . import opus -from .opus_buffered_encoder import OpusBufferedEncoder -#from .opus_encoder import OpusEncoder -from .pyogg_error import PyOggError - -class OggOpusWriter(): - """Encodes PCM data into an OggOpus file.""" - - def __init__(self, - f: Union[BinaryIO, str], - encoder: OpusBufferedEncoder, - custom_pre_skip: Optional[int] = None) -> None: - """Construct an OggOpusWriter. - - f may be either a string giving the path to the file, or - an already-opened file handle. - - If f is an already-opened file handle, then it is the - user's responsibility to close the file when they are - finished with it. The file should be opened for writing - in binary (not text) mode. - - The encoder should be a - OpusBufferedEncoder and should be fully configured before the - first call to the `write()` method. - - The Opus encoder requires an amount of "warm up" and when - stored in an Ogg container that warm up can be skipped. When - `custom_pre_skip` is None, the required amount of warm up - silence is automatically calculated and inserted. If a custom - (non-silent) pre-skip is desired, then `custom_pre_skip` - should be specified as the number of samples (per channel). - It is then the user's responsibility to pass the non-silent - pre-skip samples to `encode()`. - - """ - # Store the Opus encoder - self._encoder = encoder - - # Store the custom pre skip - self._custom_pre_skip = custom_pre_skip - - # Create a new stream state with a random serial number - self._stream_state = self._create_stream_state() - - # Create a packet (reused for each pass) - self._ogg_packet = ogg.ogg_packet() - self._packet_valid = False - - # Create a page (reused for each pass) - self._ogg_page = ogg.ogg_page() - - # Counter for the number of packets written into Ogg stream - self._count_packets = 0 - - # Counter for the number of samples encoded into Opus - # packets - self._count_samples = 0 - - # Flag to indicate if the headers have been written - self._headers_written = False - - # Flag to indicate that the stream has been finished (the - # EOS bit was set in a final packet) - self._finished = False - - # Reference to the current encoded packet (written only - # when we know if it the last) - self._current_encoded_packet: Optional[bytes] = None - - # Open file if required. Given this may raise an exception, - # it should be the last step of initialisation. - self._i_opened_the_file = False - if isinstance(f, str): - self._file = builtins.open(f, 'wb') - self._i_opened_the_file = True - else: - # Assume it's already opened file - self._file = f - - def __del__(self) -> None: - if not self._finished: - self.close() - - # - # User visible methods - # - - def write(self, pcm: memoryview) -> None: - """Encode the PCM and write out the Ogg Opus stream. - - Encoders the PCM using the provided encoder. - - """ - # Check that the stream hasn't already been finished - if self._finished: - raise PyOggError( - "Stream has already ended. Perhaps close() was "+ - "called too early?") - - # If we haven't already written out the headers, do so - # now. Then, write a frame of silence to warm up the - # encoder. - if not self._headers_written: - pre_skip = self._write_headers(self._custom_pre_skip) - if self._custom_pre_skip is None: - self._write_silence(pre_skip) - - # Call the internal method to encode the bytes - self._write_to_oggopus(pcm) - - - def _write_to_oggopus(self, pcm: memoryview, flush: bool = False) -> None: - assert self._encoder is not None - - def handle_encoded_packet(encoded_packet: memoryview, - samples: int, - end_of_stream: bool) -> None: - # Cast memoryview to ctypes Array - Buffer = ctypes.c_ubyte * len(encoded_packet) - encoded_packet_ctypes = Buffer.from_buffer(encoded_packet) - - # Obtain a pointer to the encoded packet - encoded_packet_ptr = ctypes.cast( - encoded_packet_ctypes, - ctypes.POINTER(ctypes.c_ubyte) - ) - - # Increase the count of the number of samples written - self._count_samples += samples - - # Place data into the packet - self._ogg_packet.packet = encoded_packet_ptr - self._ogg_packet.bytes = len(encoded_packet) - self._ogg_packet.b_o_s = 0 - self._ogg_packet.e_o_s = end_of_stream - self._ogg_packet.granulepos = self._count_samples - self._ogg_packet.packetno = self._count_packets - - # Increase the counter of the number of packets - # in the stream - self._count_packets += 1 - - # Write the packet into the stream - self._write_packet() - - - # Encode the PCM data into an Opus packet - self._encoder.buffered_encode( - pcm, - flush=flush, - callback=handle_encoded_packet - ) - - def close(self) -> None: - # Check we haven't already closed this stream - if self._finished: - # We're attempting to close an already closed stream, - # do nothing more. - return - - # Flush the underlying buffered encoder - self._write_to_oggopus(memoryview(bytearray(b"")), flush=True) - - # The current packet must be the end of the stream, update - # the packet's details - self._ogg_packet.e_o_s = 1 - - # Write the packet to the stream - if self._packet_valid: - self._write_packet() - - # Flush the stream of any unwritten pages - self._flush() - - # Mark the stream as finished - self._finished = True - - # Close the file if we opened it - if self._i_opened_the_file: - self._file.close() - self._i_opened_the_file = False - - # Clean up the Ogg-related memory - ogg.ogg_stream_clear(self._stream_state) - - # Clean up the reference to the encoded packet (as it must - # now have been written) - del self._current_encoded_packet - - # - # Internal methods - # - - def _create_random_serial_no(self) -> ctypes.c_int: - sizeof_c_int = ctypes.sizeof(ctypes.c_int) - min_int = -2**(sizeof_c_int*8-1) - max_int = 2**(sizeof_c_int*8-1)-1 - serial_no = ctypes.c_int(random.randint(min_int, max_int)) - - return serial_no - - def _create_stream_state(self) -> ogg.ogg_stream_state: - # Create a random serial number - serial_no = self._create_random_serial_no() - - # Create an ogg_stream_state - ogg_stream_state = ogg.ogg_stream_state() - - # Initialise the stream state - ogg.ogg_stream_init( - ctypes.pointer(ogg_stream_state), - serial_no - ) - - return ogg_stream_state - - def _make_identification_header(self, pre_skip: int, input_sampling_rate: int = 0) -> bytes: - """Make the OggOpus identification header. - - An input_sampling rate may be set to zero to mean 'unspecified'. - - Only channel mapping family 0 is currently supported. - This allows mono and stereo signals. - - See https://tools.ietf.org/html/rfc7845#page-12 for more - details. - - """ - signature = b"OpusHead" - version = 1 - output_channels = self._encoder._channels - output_gain = 0 - channel_mapping_family = 0 - data = struct.pack( - " int: - """ Returns pre-skip. """ - if custom_pre_skip is not None: - # Use the user-specified amount of pre-skip - pre_skip = custom_pre_skip - else: - # Obtain the algorithmic delay of the Opus encoder. See - # https://tools.ietf.org/html/rfc7845#page-27 - delay_samples = self._encoder.get_algorithmic_delay() - - # Extra samples are recommended. See - # https://tools.ietf.org/html/rfc7845#page-27 - extra_samples = 120 - - # We will just fill a whole frame with silence. Calculate - # the minimum frame length, which we'll use as the - # pre-skip. - frame_durations = [2.5, 5, 10, 20, 40, 60] # milliseconds - frame_lengths = [ - x * self._encoder._samples_per_second // 1000 - for x in frame_durations - ] - for frame_length in frame_lengths: - if frame_length > delay_samples + extra_samples: - pre_skip = frame_length - break - - # Create the identification header - id_header = self._make_identification_header( - pre_skip = pre_skip - ) - - # Specify the packet containing the identification header - self._ogg_packet.packet = ctypes.cast(id_header, ogg.c_uchar_p) # type: ignore - self._ogg_packet.bytes = len(id_header) - self._ogg_packet.b_o_s = 1 - self._ogg_packet.e_o_s = 0 - self._ogg_packet.granulepos = 0 - self._ogg_packet.packetno = self._count_packets - self._count_packets += 1 - - # Write the identification header - result = ogg.ogg_stream_packetin( - self._stream_state, - self._ogg_packet - ) - - if result != 0: - raise PyOggError( - "Failed to write Opus identification header" - ) - - return pre_skip - - def _make_comment_header(self): - """Make the OggOpus comment header. - - See https://tools.ietf.org/html/rfc7845#page-22 for more - details. - - """ - signature = b"OpusTags" - vendor_string = b"ENCODER=PyOgg" - vendor_string_length = struct.pack(" None: - super().__init__() - - self._frame_size_ms: Optional[float] = None - self._frame_size_bytes: Optional[int] = None - - # Buffer contains the bytes required for the next - # frame. - self._buffer: Optional[ctypes.Array] = None - - # Location of the next free byte in the buffer - self._buffer_index = 0 - - - def set_frame_size(self, frame_size: float) -> None: - """ Set the desired frame duration (in milliseconds). - - Valid options are 2.5, 5, 10, 20, 40, or 60ms. - - """ - - # Ensure the frame size is valid. Compare frame size in - # units of 0.1ms to avoid floating point comparison - if int(frame_size*10) not in [25, 50, 100, 200, 400, 600]: - raise PyOggError( - "Frame size ({:f}) not one of ".format(frame_size)+ - "the acceptable values" - ) - - self._frame_size_ms = frame_size - - self._calc_frame_size() - - - def set_sampling_frequency(self, samples_per_second: int) -> None: - super().set_sampling_frequency(samples_per_second) - self._calc_frame_size() - - - def buffered_encode(self, - pcm_bytes: memoryview, - flush: bool = False, - callback: Callable[[memoryview,int,bool],None] = None - ) -> List[Tuple[memoryview, int, bool]]: - """Gets encoded packets and their number of samples. - - This method returns a list, where each item in the list is - a tuple. The first item in the tuple is an Opus-encoded - frame stored as a bytes-object. The second item in the - tuple is the number of samples encoded (excluding - silence). - - If `callback` is supplied then this method will instead - return an empty list but call the callback for every - Opus-encoded frame that would have been returned as a - list. This option has the desireable property of - eliminating the copying of the encoded packets, which is - required in order to form a list. The callback should - take two arguments, the encoded frame (a Python bytes - object) and the number of samples encoded per channel (an - int). The user must either process or copy the data as - the data may be overwritten once the callback terminates. - - """ - # If there's no work to do return immediately - if len(pcm_bytes) == 0 and flush == False: - return [] # no work to do - - # Sanity checks - if self._frame_size_ms is None: - raise PyOggError("Frame size must be set before encoding") - assert self._frame_size_bytes is not None - assert self._channels is not None - assert self._buffer is not None - assert self._buffer_index is not None - - # Local variable initialisation - results = [] - pcm_index = 0 - pcm_len = len(pcm_bytes) - - # 'Cast' memoryview of PCM to ctypes Array - Buffer = ctypes.c_ubyte * len(pcm_bytes) - try: - pcm_ctypes = Buffer.from_buffer(pcm_bytes) - except TypeError: - warnings.warn( - "Because PCM was read-only, an extra memory "+ - "copy was required; consider storing PCM in "+ - "writable memory (for example, bytearray "+ - "rather than bytes)." - ) - pcm_ctypes = Buffer.from_buffer(pcm_bytes) - - # Either store the encoded packet to return at the end of the - # method or immediately call the callback with the encoded - # packet. - def store_or_callback(encoded_packet: memoryview, - samples: int, - end_of_stream: bool = False) -> None: - if callback is None: - # Store the result - results.append(( - encoded_packet, - samples, - end_of_stream - )) - else: - # Call the callback - callback( - encoded_packet, - samples, - end_of_stream - ) - - # Fill the remainder of the buffer with silence and encode it. - # The associated number of samples are only that of actual - # data, not the added silence. - def flush_buffer() -> None: - # Sanity checks to satisfy mypy - assert self._buffer_index is not None - assert self._channels is not None - assert self._buffer is not None - - # If the buffer is already empty, we have no work to do - if self._buffer_index == 0: - return - - # Store the number of samples currently in the buffer - samples = ( - self._buffer_index - // self._channels - // ctypes.sizeof(opus.opus_int16) - ) - - # Fill the buffer with silence - ctypes.memset( - # destination - ctypes.byref(self._buffer, self._buffer_index), - # value - 0, - # count - len(self._buffer) - self._buffer_index - ) - - # Encode the PCM - # As at 2020-11-05, mypy is unaware that ctype Arrays - # support the buffer protocol. - encoded_packet = self.encode(memoryview(self._buffer)) # type: ignore - - # Either store the encoded packet or call the - # callback - store_or_callback(encoded_packet, samples, True) - - - # Copy the data remaining from the provided PCM into the - # buffer. Flush if required. - def copy_insufficient_data() -> None: - # Sanity checks to satisfy mypy - assert self._buffer is not None - - # Calculate remaining data - remaining_data = len(pcm_bytes) - pcm_index - - # Copy the data into the buffer. - ctypes.memmove( - # destination - ctypes.byref(self._buffer, self._buffer_index), - # source - ctypes.byref(pcm_ctypes, pcm_index), - # count - remaining_data - ) - - self._buffer_index += remaining_data - - # If we've been asked to flush the buffer then do so - if flush: - flush_buffer() - - # Loop through the provided PCM and the current buffer, - # encoding as we have full packets. - while True: - # There are two possibilities at this point: either we - # have previously unencoded data still in the buffer or we - # do not - if self._buffer_index == 0: - # We do not have unencoded data - - # We are free to progress through the PCM that has - # been provided encoding frames without copying any - # bytes. Once there is insufficient data remaining - # for a complete frame, that data should be copied - # into the buffer and we have finished. - if pcm_len - pcm_index > self._frame_size_bytes: - # We have enough data remaining in the provided - # PCM to encode more than an entire frame without - # copying any data. Unfortunately, splicing a - # ctypes array copies the array. To avoid the - # copy we use memoryview see - # https://mattgwwalker.wordpress.com/2020/12/12/python-ctypes-slicing/ - frame_data = memoryview(pcm_bytes)[ - pcm_index:pcm_index+self._frame_size_bytes - ] - - # Update the PCM index - pcm_index += self._frame_size_bytes - - # Store number of samples (per channel) of actual - # data - samples = ( - len(frame_data) - // self._channels - // ctypes.sizeof(opus.opus_int16) - ) - - # Encode the PCM - encoded_packet = super().encode(frame_data) - - # Either store the encoded packet or call the - # callback - store_or_callback(encoded_packet, samples) - - else: - # We do not have enough data to fill a frame while - # still having data left over. Copy the data into - # the buffer. - copy_insufficient_data() - return results - - else: - # We have unencoded data. - - # Copy the provided PCM into the buffer (up until the - # buffer is full). If we can fill it, then we can - # encode the filled buffer and continue. If we can't - # fill it then we've finished. - data_required = len(self._buffer) - self._buffer_index - if pcm_len > data_required: - # We have sufficient data to fill the buffer and - # have data left over. Copy data into the buffer. - assert pcm_index == 0 - remaining = len(self._buffer) - self._buffer_index - ctypes.memmove( - # destination - ctypes.byref(self._buffer, self._buffer_index), - # source - pcm_ctypes, - # count - remaining - ) - pcm_index += remaining - self._buffer_index += remaining - assert self._buffer_index == len(self._buffer) - - # Encode the PCM - encoded_packet = super().encode( - # Memoryviews of ctypes do work, even though - # mypy complains. - memoryview(self._buffer) # type: ignore - ) - - # Store number of samples (per channel) of actual - # data - samples = ( - self._buffer_index - // self._channels - // ctypes.sizeof(opus.opus_int16) - ) - - # We've now processed the buffer - self._buffer_index = 0 - - # Either store the encoded packet or call the - # callback - store_or_callback(encoded_packet, samples) - else: - # We have insufficient data to fill the buffer - # while still having data left over. Copy the - # data into the buffer. - copy_insufficient_data() - return results - - - def _calc_frame_size(self): - """Calculates the number of bytes in a frame. - - If the frame size (in milliseconds) and the number of - samples per seconds have already been specified, then the - frame size in bytes is set. Otherwise, this method does - nothing. - - The frame size is measured in bytes required to store the - sample. - - """ - if (self._frame_size_ms is None - or self._samples_per_second is None): - return - - self._frame_size_bytes = ( - self._frame_size_ms - * self._samples_per_second - // 1000 - * ctypes.sizeof(opus.opus_int16) - * self._channels - ) - - # Allocate space for the buffer - Buffer = ctypes.c_ubyte * self._frame_size_bytes - self._buffer = Buffer() - - - def _get_next_frame(self, add_silence=False): - """Gets the next Opus-encoded frame. - - Returns a tuple where the first item is the Opus-encoded - frame and the second item is the number of encoded samples - (per channel). - - Returns None if insufficient data is available. - - """ - next_frame = bytes() - samples = 0 - - # Ensure frame size has been specified - if self._frame_size_bytes is None: - raise PyOggError( - "Desired frame size hasn't been set. Perhaps "+ - "encode() was called before set_frame_size() "+ - "and set_sampling_frequency()?" - ) - - # Check if there's insufficient data in the buffer to fill - # a frame. - if self._frame_size_bytes > self._buffer_size: - if len(self._buffer) == 0: - # No data at all in buffer - return None - if add_silence: - # Get all remaining data - while len(self._buffer) != 0: - next_frame += self._buffer.popleft() - self._buffer_size = 0 - # Store number of samples (per channel) of actual - # data - samples = ( - len(next_frame) - // self._channels - // ctypes.sizeof(opus.opus_int16) - ) - # Fill remainder of frame with silence - bytes_remaining = self._frame_size_bytes - len(next_frame) - next_frame += b'\x00' * bytes_remaining - return (next_frame, samples) - else: - # Insufficient data to fill a frame and we're not - # adding silence - return None - - bytes_remaining = self._frame_size_bytes - while bytes_remaining > 0: - if len(self._buffer[0]) <= bytes_remaining: - # Take the whole first item - buffer_ = self._buffer.popleft() - next_frame += buffer_ - bytes_remaining -= len(buffer_) - self._buffer_size -= len(buffer_) - else: - # Take only part of the buffer - - # TODO: This could be more efficiently - # implemented. Rather than appending back the - # remaining data, we could just update an index - # saying where we were up to in regards to the - # first entry of the buffer. - buffer_ = self._buffer.popleft() - next_frame += buffer_[:bytes_remaining] - self._buffer_size -= bytes_remaining - # And put the unused part back into the buffer - self._buffer.appendleft(buffer_[bytes_remaining:]) - bytes_remaining = 0 - - # Calculate number of samples (per channel) - samples = ( - len(next_frame) - // self._channels - // ctypes.sizeof(opus.opus_int16) - ) - - return (next_frame, samples) diff --git a/sbapp/pyogg/opus_decoder.py b/sbapp/pyogg/opus_decoder.py deleted file mode 100644 index 8a1f4dd..0000000 --- a/sbapp/pyogg/opus_decoder.py +++ /dev/null @@ -1,273 +0,0 @@ -import ctypes - -from . import opus -from .pyogg_error import PyOggError - -class OpusDecoder: - def __init__(self): - self._decoder = None - self._channels = None - self._samples_per_second = None - self._pcm_buffer = None - self._pcm_buffer_ptr = None - self._pcm_buffer_size_int = None - - # TODO: Check if there is clean up that we need to do when - # closing a decoder. - - # - # User visible methods - # - - def set_channels(self, n): - - """Set the number of channels. - - n must be either 1 or 2. - - The decoder is capable of filling in either mono or - interleaved stereo pcm buffers. - - """ - if self._decoder is None: - if n < 0 or n > 2: - raise PyOggError( - "Invalid number of channels in call to "+ - "set_channels()" - ) - self._channels = n - else: - raise PyOggError( - "Cannot change the number of channels after "+ - "the decoder was created. Perhaps "+ - "set_channels() was called after decode()?" - ) - self._create_pcm_buffer() - - def set_sampling_frequency(self, samples_per_second): - """Set the number of samples (per channel) per second. - - samples_per_second must be one of 8000, 12000, 16000, - 24000, or 48000. - - Internally Opus stores data at 48000 Hz, so that should be - the default value for Fs. However, the decoder can - efficiently decode to buffers at 8, 12, 16, and 24 kHz so - if for some reason the caller cannot use data at the full - sample rate, or knows the compressed data doesn't use the - full frequency range, it can request decoding at a reduced - rate. - - """ - if self._decoder is None: - if samples_per_second in [8000, 12000, 16000, 24000, 48000]: - self._samples_per_second = samples_per_second - else: - raise PyOggError( - "Specified sampling frequency "+ - "({:d}) ".format(samples_per_second)+ - "was not one of the accepted values" - ) - else: - raise PyOggError( - "Cannot change the sampling frequency after "+ - "the decoder was created. Perhaps "+ - "set_sampling_frequency() was called after decode()?" - ) - self._create_pcm_buffer() - - def decode(self, encoded_bytes: memoryview): - """Decodes an Opus-encoded packet into PCM. - - """ - # If we haven't already created a decoder, do so now - if self._decoder is None: - self._decoder = self._create_decoder() - - # Create a ctypes array from the memoryview (without copying - # data) - Buffer = ctypes.c_char * len(encoded_bytes) - encoded_bytes_ctypes = Buffer.from_buffer(encoded_bytes) - - # Create pointer to encoded bytes - encoded_bytes_ptr = ctypes.cast( - encoded_bytes_ctypes, - ctypes.POINTER(ctypes.c_ubyte) - ) - - # Store length of encoded bytes into int32 - len_int32 = opus.opus_int32( - len(encoded_bytes) - ) - - # Check that we have a PCM buffer - if self._pcm_buffer is None: - raise PyOggError("PCM buffer was not configured.") - - # Decode the encoded frame - result = opus.opus_decode( - self._decoder, - encoded_bytes_ptr, - len_int32, - self._pcm_buffer_ptr, - self._pcm_buffer_size_int, - 0 # TODO: What's Forward Error Correction about? - ) - - # Check for any errors - if result < 0: - raise PyOggError( - "An error occurred while decoding an Opus-encoded "+ - "packet: "+ - opus.opus_strerror(result).decode("utf") - ) - - # Extract just the valid data as bytes - end_valid_data = ( - result - * ctypes.sizeof(opus.opus_int16) - * self._channels - ) - - # Create memoryview of PCM buffer to avoid copying data during slice. - mv = memoryview(self._pcm_buffer) - - # Cast memoryview to chars - mv = mv.cast('c') - - # Slice memoryview to extract only valid data - mv = mv[:end_valid_data] - - return mv - - - def decode_missing_packet(self, frame_duration): - """ Obtain PCM data despite missing a frame. - - frame_duration is in milliseconds. - - """ - - # Consider frame duration in units of 0.1ms in order to - # avoid floating-point comparisons. - if int(frame_duration*10) not in [25, 50, 100, 200, 400, 600]: - raise PyOggError( - "Frame duration ({:f}) is not one of the accepted values".format(frame_duration) - ) - - # Calculate frame size - frame_size = int( - frame_duration - * self._samples_per_second - // 1000 - ) - - # Store frame size as int - frame_size_int = ctypes.c_int(frame_size) - - # Decode missing packet - result = opus.opus_decode( - self._decoder, - None, - 0, - self._pcm_buffer_ptr, - frame_size_int, - 0 # TODO: What is this Forward Error Correction about? - ) - - # Check for any errors - if result < 0: - raise PyOggError( - "An error occurred while decoding an Opus-encoded "+ - "packet: "+ - opus.opus_strerror(result).decode("utf") - ) - - # Extract just the valid data as bytes - end_valid_data = ( - result - * ctypes.sizeof(opus.opus_int16) - * self._channels - ) - return bytes(self._pcm_buffer)[:end_valid_data] - - # - # Internal methods - # - - def _create_pcm_buffer(self): - if (self._samples_per_second is None - or self._channels is None): - # We cannot define the buffer yet - return - - # Create buffer to hold 120ms of samples. See "opus_decode()" at - # https://opus-codec.org/docs/opus_api-1.3.1/group__opus__decoder.html - max_duration = 120 # milliseconds - max_samples = max_duration * self._samples_per_second // 1000 - PCMBuffer = opus.opus_int16 * (max_samples * self._channels) - self._pcm_buffer = PCMBuffer() - self._pcm_buffer_ptr = ( - ctypes.cast(ctypes.pointer(self._pcm_buffer), - ctypes.POINTER(opus.opus_int16)) - ) - - # Store samples per channel in an int - self._pcm_buffer_size_int = ctypes.c_int(max_samples) - - def _create_decoder(self): - # To create a decoder, we must first allocate resources for it. - # We want Python to be responsible for the memory deallocation, - # and thus Python must be responsible for the initial memory - # allocation. - - # Check that the sampling frequency has been defined - if self._samples_per_second is None: - raise PyOggError( - "The sampling frequency was not specified before "+ - "attempting to create an Opus decoder. Perhaps "+ - "decode() was called before set_sampling_frequency()?" - ) - - # The sampling frequency must be passed in as a 32-bit int - samples_per_second = opus.opus_int32(self._samples_per_second) - - # Check that the number of channels has been defined - if self._channels is None: - raise PyOggError( - "The number of channels were not specified before "+ - "attempting to create an Opus decoder. Perhaps "+ - "decode() was called before set_channels()?" - ) - - # The number of channels must also be passed in as a 32-bit int - channels = opus.opus_int32(self._channels) - - # Obtain the number of bytes of memory required for the decoder - size = opus.opus_decoder_get_size(channels); - - # Allocate the required memory for the decoder - memory = ctypes.create_string_buffer(size) - - # Cast the newly-allocated memory as a pointer to a decoder. We - # could also have used opus.od_p as the pointer type, but writing - # it out in full may be clearer. - decoder = ctypes.cast(memory, ctypes.POINTER(opus.OpusDecoder)) - - # Initialise the decoder - error = opus.opus_decoder_init( - decoder, - samples_per_second, - channels - ); - - # Check that there hasn't been an error when initialising the - # decoder - if error != opus.OPUS_OK: - raise PyOggError( - "An error occurred while creating the decoder: "+ - opus.opus_strerror(error).decode("utf") - ) - - # Return our newly-created decoder - return decoder diff --git a/sbapp/pyogg/opus_encoder.py b/sbapp/pyogg/opus_encoder.py deleted file mode 100644 index 1da82da..0000000 --- a/sbapp/pyogg/opus_encoder.py +++ /dev/null @@ -1,358 +0,0 @@ -import ctypes -from typing import Optional, Union, ByteString - -from . import opus -from .pyogg_error import PyOggError - -class OpusEncoder: - """Encodes PCM data into Opus frames.""" - def __init__(self) -> None: - self._encoder: Optional[ctypes.pointer] = None - self._channels: Optional[int] = None - self._samples_per_second: Optional[int] = None - self._application: Optional[int] = None - self._max_bytes_per_frame: Optional[opus.opus_int32] = None - self._output_buffer: Optional[ctypes.Array] = None - self._output_buffer_ptr: Optional[ctypes.pointer] = None - - # An output buffer of 4,000 bytes is recommended in - # https://opus-codec.org/docs/opus_api-1.3.1/group__opus__encoder.html - self.set_max_bytes_per_frame(4000) - - # - # User visible methods - # - - def set_channels(self, n: int) -> None: - """Set the number of channels. - - n must be either 1 or 2. - - """ - if self._encoder is None: - if n < 0 or n > 2: - raise PyOggError( - "Invalid number of channels in call to "+ - "set_channels()" - ) - self._channels = n - else: - raise PyOggError( - "Cannot change the number of channels after "+ - "the encoder was created. Perhaps "+ - "set_channels() was called after encode()?" - ) - - def set_sampling_frequency(self, samples_per_second: int) -> None: - """Set the number of samples (per channel) per second. - - This must be one of 8000, 12000, 16000, 24000, or 48000. - - Regardless of the sampling rate and number of channels - selected, the Opus encoder can switch to a lower audio - bandwidth or number of channels if the bitrate selected is - too low. This also means that it is safe to always use 48 - kHz stereo input and let the encoder optimize the - encoding. - - """ - if self._encoder is None: - if samples_per_second in [8000, 12000, 16000, 24000, 48000]: - self._samples_per_second = samples_per_second - else: - raise PyOggError( - "Specified sampling frequency "+ - "({:d}) ".format(samples_per_second)+ - "was not one of the accepted values" - ) - else: - raise PyOggError( - "Cannot change the sampling frequency after "+ - "the encoder was created. Perhaps "+ - "set_sampling_frequency() was called after encode()?" - ) - - def set_application(self, application: str) -> None: - """Set the encoding mode. - - This must be one of 'voip', 'audio', or 'restricted_lowdelay'. - - 'voip': Gives best quality at a given bitrate for voice - signals. It enhances the input signal by high-pass - filtering and emphasizing formants and - harmonics. Optionally it includes in-band forward error - correction to protect against packet loss. Use this mode - for typical VoIP applications. Because of the enhancement, - even at high bitrates the output may sound different from - the input. - - 'audio': Gives best quality at a given bitrate for most - non-voice signals like music. Use this mode for music and - mixed (music/voice) content, broadcast, and applications - requiring less than 15 ms of coding delay. - - 'restricted_lowdelay': configures low-delay mode that - disables the speech-optimized mode in exchange for - slightly reduced delay. This mode can only be set on an - newly initialized encoder because it changes the codec - delay. - """ - if self._encoder is not None: - raise PyOggError( - "Cannot change the application after "+ - "the encoder was created. Perhaps "+ - "set_application() was called after encode()?" - ) - if application == "voip": - self._application = opus.OPUS_APPLICATION_VOIP - elif application == "audio": - self._application = opus.OPUS_APPLICATION_AUDIO - elif application == "restricted_lowdelay": - self._application = opus.OPUS_APPLICATION_RESTRICTED_LOWDELAY - else: - raise PyOggError( - "The application specification '{:s}' ".format(application)+ - "wasn't one of the accepted values." - ) - - def set_max_bytes_per_frame(self, max_bytes: int) -> None: - """Set the maximum number of bytes in an encoded frame. - - Size of the output payload. This may be used to impose an - upper limit on the instant bitrate, but should not be used - as the only bitrate control. - - TODO: Use OPUS_SET_BITRATE to control the bitrate. - - """ - self._max_bytes_per_frame = opus.opus_int32(max_bytes) - OutputBuffer = ctypes.c_ubyte * max_bytes - self._output_buffer = OutputBuffer() - self._output_buffer_ptr = ( - ctypes.cast(ctypes.pointer(self._output_buffer), - ctypes.POINTER(ctypes.c_ubyte)) - ) - - - def encode(self, pcm: Union[bytes, bytearray, memoryview]) -> memoryview: - """Encodes PCM data into an Opus frame. - - `pcm` must be formatted as bytes-like, with each sample taking - two bytes (signed 16-bit integers; interleaved left, then - right channels if in stereo). - - If `pcm` is not writeable, a copy of the array will be made. - - """ - # If we haven't already created an encoder, do so now - if self._encoder is None: - self._encoder = self._create_encoder() - - # Sanity checks also satisfy mypy type checking - assert self._channels is not None - assert self._samples_per_second is not None - assert self._output_buffer is not None - - # Calculate the effective frame duration of the given PCM - # data. Calculate it in units of 0.1ms in order to avoid - # floating point comparisons. - bytes_per_sample = 2 - frame_size = ( - len(pcm) # bytes - // bytes_per_sample - // self._channels - ) - frame_duration = ( - (10*frame_size) - // (self._samples_per_second//1000) - ) - - # Check that we have a valid frame size - if int(frame_duration) not in [25, 50, 100, 200, 400, 600]: - raise PyOggError( - "The effective frame duration ({:.1f} ms) " - .format(frame_duration/10)+ - "was not one of the acceptable values." - ) - - # Create a ctypes object sharing the memory of the PCM data - PcmCtypes = ctypes.c_ubyte * len(pcm) - try: - # Attempt to share the PCM memory - - # Unfortunately, as at 2020-09-27, the type hinting for - # read-only and writeable buffer protocols was a - # work-in-progress. The following only works for writable - # cases, but the method's parameters include a read-only - # possibility (bytes), thus we ignore mypy's error. - pcm_ctypes = PcmCtypes.from_buffer(pcm) # type: ignore[arg-type] - except TypeError: - # The data must be copied if it's not writeable - pcm_ctypes = PcmCtypes.from_buffer_copy(pcm) - - # Create a pointer to the PCM data - pcm_ptr = ctypes.cast( - pcm_ctypes, - ctypes.POINTER(opus.opus_int16) - ) - - # Create an int giving the frame size per channel - frame_size_int = ctypes.c_int(frame_size) - - # Encode PCM - result = opus.opus_encode( - self._encoder, - pcm_ptr, - frame_size_int, - self._output_buffer_ptr, - self._max_bytes_per_frame - ) - - # Check for any errors - if result < 0: - raise PyOggError( - "An error occurred while encoding to Opus format: "+ - opus.opus_strerror(result).decode("utf") - ) - - # Get memoryview of buffer so that the slice operation doesn't - # copy the data. - # - # Unfortunately, as at 2020-09-27, the type hints for - # memoryview do not include ctype arrays. This is because - # there is no currently accepted manner to label a class as - # supporting the buffer protocol. However, it's clearly a - # work in progress. For more information, see: - # * https://bugs.python.org/issue27501 - # * https://github.com/python/typing/issues/593 - # * https://github.com/python/typeshed/pull/4232 - mv = memoryview(self._output_buffer) # type: ignore - - # Cast the memoryview to char - mv = mv.cast('c') - - # Slice just the valid data from the memoryview - valid_data_as_bytes = mv[:result] - - # DEBUG - # Convert memoryview back to ctypes instance - Buffer = ctypes.c_ubyte * len(valid_data_as_bytes) - buf = Buffer.from_buffer( valid_data_as_bytes ) - - # Convert PCM back to pointer and dump 4,000-byte buffer - ptr = ctypes.cast( - buf, - ctypes.POINTER(ctypes.c_ubyte) - ) - - return valid_data_as_bytes - - - def get_algorithmic_delay(self): - """Gets the total samples of delay added by the entire codec. - - This can be queried by the encoder and then the provided - number of samples can be skipped on from the start of the - decoder's output to provide time aligned input and - output. From the perspective of a decoding application the - real data begins this many samples late. - - The decoder contribution to this delay is identical for all - decoders, but the encoder portion of the delay may vary from - implementation to implementation, version to version, or even - depend on the encoder's initial configuration. Applications - needing delay compensation should call this method rather than - hard-coding a value. - - """ - # If we haven't already created an encoder, do so now - if self._encoder is None: - self._encoder = self._create_encoder() - - # Obtain the algorithmic delay of the Opus encoder. See - # https://tools.ietf.org/html/rfc7845#page-27 - delay = opus.opus_int32() - - result = opus.opus_encoder_ctl( - self._encoder, - opus.OPUS_GET_LOOKAHEAD_REQUEST, - ctypes.pointer(delay) - ) - if result != opus.OPUS_OK: - raise PyOggError( - "Failed to obtain the algorithmic delay of "+ - "the Opus encoder: "+ - opus.opus_strerror(result).decode("utf") - ) - delay_samples = delay.value - return delay_samples - - - # - # Internal methods - # - - def _create_encoder(self) -> ctypes.pointer: - # To create an encoder, we must first allocate resources for it. - # We want Python to be responsible for the memory deallocation, - # and thus Python must be responsible for the initial memory - # allocation. - - # Check that the application has been defined - if self._application is None: - raise PyOggError( - "The application was not specified before "+ - "attempting to create an Opus encoder. Perhaps "+ - "encode() was called before set_application()?" - ) - application = self._application - - # Check that the sampling frequency has been defined - if self._samples_per_second is None: - raise PyOggError( - "The sampling frequency was not specified before "+ - "attempting to create an Opus encoder. Perhaps "+ - "encode() was called before set_sampling_frequency()?" - ) - - # The frequency must be passed in as a 32-bit int - samples_per_second = opus.opus_int32(self._samples_per_second) - - # Check that the number of channels has been defined - if self._channels is None: - raise PyOggError( - "The number of channels were not specified before "+ - "attempting to create an Opus encoder. Perhaps "+ - "encode() was called before set_channels()?" - ) - channels = self._channels - - # Obtain the number of bytes of memory required for the encoder - size = opus.opus_encoder_get_size(channels); - - # Allocate the required memory for the encoder - memory = ctypes.create_string_buffer(size) - - # Cast the newly-allocated memory as a pointer to an encoder. We - # could also have used opus.oe_p as the pointer type, but writing - # it out in full may be clearer. - encoder = ctypes.cast(memory, ctypes.POINTER(opus.OpusEncoder)) - - # Initialise the encoder - error = opus.opus_encoder_init( - encoder, - samples_per_second, - channels, - application - ) - - # Check that there hasn't been an error when initialising the - # encoder - if error != opus.OPUS_OK: - raise PyOggError( - "An error occurred while creating the encoder: "+ - opus.opus_strerror(error).decode("utf") - ) - - # Return our newly-created encoder - return encoder diff --git a/sbapp/pyogg/opus_file.py b/sbapp/pyogg/opus_file.py deleted file mode 100644 index f8519f4..0000000 --- a/sbapp/pyogg/opus_file.py +++ /dev/null @@ -1,106 +0,0 @@ -import ctypes - -from . import ogg -from . import opus -from .pyogg_error import PyOggError -from .audio_file import AudioFile - -class OpusFile(AudioFile): - def __init__(self, path: str) -> None: - # Open the file - error = ctypes.c_int() - of = opus.op_open_file( - ogg.to_char_p(path), - ctypes.pointer(error) - ) - - # Check for errors - if error.value != 0: - raise PyOggError( - ("File '{}' couldn't be opened or doesn't exist. "+ - "Error code: {}").format(path, error.value) - ) - - # Extract the number of channels in the newly opened file - #: Number of channels in audio file. - self.channels = opus.op_channel_count(of, -1) - - # Allocate sufficient memory to store the entire PCM - pcm_size = opus.op_pcm_total(of, -1) - Buf = opus.opus_int16*(pcm_size*self.channels) - buf = Buf() - - # Create a pointer to the newly allocated memory. It - # seems we can only do pointer arithmetic on void - # pointers. See - # https://mattgwwalker.wordpress.com/2020/05/30/pointer-manipulation-in-python/ - buf_ptr = ctypes.cast( - ctypes.pointer(buf), - ctypes.c_void_p - ) - assert buf_ptr.value is not None # for mypy - buf_ptr_zero = buf_ptr.value - - #: Bytes per sample - self.bytes_per_sample = ctypes.sizeof(opus.opus_int16) - - # Read through the entire file, copying the PCM into the - # buffer - samples = 0 - while True: - # Calculate remaining buffer size - remaining_buffer = ( - len(buf) # int - - (buf_ptr.value - - buf_ptr_zero) // self.bytes_per_sample - ) - - # Convert buffer pointer to the desired type - ptr = ctypes.cast( - buf_ptr, - ctypes.POINTER(opus.opus_int16) - ) - - # Read the next section of PCM - ns = opus.op_read( - of, - ptr, - remaining_buffer, - ogg.c_int_p() - ) - - # Check for errors - if ns<0: - raise PyOggError( - "Error while reading OggOpus file. "+ - "Error code: {}".format(ns) - ) - - # Increment the pointer - buf_ptr.value += ( - ns - * self.bytes_per_sample - * self.channels - ) - assert buf_ptr.value is not None # for mypy - - samples += ns - - # Check if we've finished - if ns==0: - break - - # Close the open file - opus.op_free(of) - - # Opus files are always stored at 48k samples per second - #: Number of samples per second (per channel). Always 48,000. - self.frequency = 48000 - - # Cast buffer to a one-dimensional array of chars - #: Raw PCM data from audio file. - CharBuffer = ( - ctypes.c_byte - * (self.bytes_per_sample * self.channels * pcm_size) - ) - self.buffer = CharBuffer.from_buffer(buf) diff --git a/sbapp/pyogg/opus_file_stream.py b/sbapp/pyogg/opus_file_stream.py deleted file mode 100644 index b3e1723..0000000 --- a/sbapp/pyogg/opus_file_stream.py +++ /dev/null @@ -1,127 +0,0 @@ -import ctypes - -from . import ogg -from . import opus -from .pyogg_error import PyOggError - -class OpusFileStream: - def __init__(self, path): - """Opens an OggOpus file as a stream. - - path should be a string giving the filename of the file to - open. Unicode file names may not work correctly. - - An exception will be raised if the file cannot be opened - correctly. - - """ - error = ctypes.c_int() - - self.of = opus.op_open_file(ogg.to_char_p(path), ctypes.pointer(error)) - - if error.value != 0: - self.of = None - raise PyOggError("file couldn't be opened or doesn't exist. Error code : {}".format(error.value)) - - #: Number of channels in audio file - self.channels = opus.op_channel_count(self.of, -1) - - #: Total PCM Length - self.pcm_size = opus.op_pcm_total(self.of, -1) - - #: Number of samples per second (per channel) - self.frequency = 48000 - - # The buffer size should be (per channel) large enough to - # hold 120ms (the largest possible Opus frame) at 48kHz. - # See https://opus-codec.org/docs/opusfile_api-0.7/group__stream__decoding.html#ga963c917749335e29bb2b698c1cb20a10 - self.buffer_size = self.frequency // 1000 * 120 * self.channels - self.Buf = opus.opus_int16 * self.buffer_size - self._buf = self.Buf() - self.buffer_ptr = ctypes.cast( - ctypes.pointer(self._buf), - opus.opus_int16_p - ) - - #: Bytes per sample - self.bytes_per_sample = ctypes.sizeof(opus.opus_int16) - - def __del__(self): - if self.of is not None: - opus.op_free(self.of) - - def get_buffer(self): - """Obtains the next frame of PCM samples. - - Returns an array of signed 16-bit integers. If the file - is in stereo, the left and right channels are interleaved. - - Returns None when all data has been read. - - The array that is returned should be either processed or - copied before the next call to :meth:`~get_buffer` or - :meth:`~get_buffer_as_array` as the array's memory is reused for - each call. - - """ - # Read the next frame - samples_read = opus.op_read( - self.of, - self.buffer_ptr, - self.buffer_size, - None - ) - - # Check for errors - if samples_read < 0: - raise PyOggError( - "Failed to read OpusFileStream. Error {:d}".format(samples_read) - ) - - # Check if we've reached the end of the stream - if samples_read == 0: - return None - - # Cast the pointer to opus_int16 to an array of the - # correct size - result_ptr = ctypes.cast( - self.buffer_ptr, - ctypes.POINTER(opus.opus_int16 * (samples_read*self.channels)) - ) - - # Convert the array to Python bytes - return bytes(result_ptr.contents) - - def get_buffer_as_array(self): - """Provides the buffer as a NumPy array. - - Note that the underlying data type is 16-bit signed - integers. - - Does not copy the underlying data, so the returned array - should either be processed or copied before the next call - to :meth:`~get_buffer` or :meth:`~get_buffer_as_array`. - - """ - import numpy # type: ignore - - # Read the next samples from the stream - buf = self.get_buffer() - - # Check if we've come to the end of the stream - if buf is None: - return None - - # Convert the bytes buffer to a NumPy array - array = numpy.frombuffer( - buf, - dtype=numpy.int16 - ) - - # Reshape the array - return array.reshape( - (len(buf) - // self.bytes_per_sample - // self.channels, - self.channels) - ) diff --git a/sbapp/pyogg/py.typed b/sbapp/pyogg/py.typed deleted file mode 100644 index d4defd9..0000000 --- a/sbapp/pyogg/py.typed +++ /dev/null @@ -1 +0,0 @@ -# Marker file for PEP 561. This package uses inline types. \ No newline at end of file diff --git a/sbapp/pyogg/pyogg_error.py b/sbapp/pyogg/pyogg_error.py deleted file mode 100644 index 35f28bf..0000000 --- a/sbapp/pyogg/pyogg_error.py +++ /dev/null @@ -1,2 +0,0 @@ -class PyOggError(Exception): - pass diff --git a/sbapp/pyogg/vorbis.py b/sbapp/pyogg/vorbis.py deleted file mode 100644 index a8432ba..0000000 --- a/sbapp/pyogg/vorbis.py +++ /dev/null @@ -1,855 +0,0 @@ -############################################################ -# Vorbis license: # -############################################################ -""" -Copyright (c) 2002-2015 Xiph.org Foundation - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiph.org Foundation nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -import ctypes -import ctypes.util -from traceback import print_exc as _print_exc -import os - -OV_EXCLUDE_STATIC_CALLBACKS = False - -__MINGW32__ = False - -_WIN32 = False - -from .ogg import * - -from .library_loader import ExternalLibrary, ExternalLibraryError - -__here = os.getcwd() - -libvorbis = None - -try: - names = { - "Windows": "libvorbis.dll", - "Darwin": "libvorbis.0.dylib", - "external": "vorbis" - } - libvorbis = Library.load(names, tests = [lambda lib: hasattr(lib, "vorbis_info_init")]) -except ExternalLibraryError: - pass -except: - _print_exc() - -libvorbisfile = None - -try: - names = { - "Windows": "libvorbisfile.dll", - "Darwin": "libvorbisfile.3.dylib", - "external": "vorbisfile" - } - libvorbisfile = Library.load(names, tests = [lambda lib: hasattr(lib, "ov_clear")]) -except ExternalLibraryError: - pass -except: - _print_exc() - -libvorbisenc = None - -# In some cases, libvorbis may also have the libvorbisenc functionality. -libvorbis_is_also_libvorbisenc = True - -for f in ("vorbis_encode_ctl", - "vorbis_encode_init", - "vorbis_encode_init_vbr", - "vorbis_encode_setup_init", - "vorbis_encode_setup_managed", - "vorbis_encode_setup_vbr"): - if not hasattr(libvorbis, f): - libvorbis_is_also_libvorbisenc = False - break - -if libvorbis_is_also_libvorbisenc: - libvorbisenc = libvorbis -else: - try: - names = { - "Windows": "libvorbisenc.dll", - "Darwin": "libvorbisenc.2.dylib", - "external": "vorbisenc" - } - libvorbisenc = Library.load(names, tests = [lambda lib: hasattr(lib, "vorbis_encode_init")]) - except ExternalLibraryError: - pass - except: - _print_exc() - -if libvorbis is None: - PYOGG_VORBIS_AVAIL = False -else: - PYOGG_VORBIS_AVAIL = True - -if libvorbisfile is None: - PYOGG_VORBIS_FILE_AVAIL = False -else: - PYOGG_VORBIS_FILE_AVAIL = True - -if libvorbisenc is None: - PYOGG_VORBIS_ENC_AVAIL = False -else: - PYOGG_VORBIS_ENC_AVAIL = True - -# FIXME: What's the story with the lack of checking for PYOGG_VORBIS_ENC_AVAIL? -# We just seem to assume that it's available. - -if PYOGG_OGG_AVAIL and PYOGG_VORBIS_AVAIL and PYOGG_VORBIS_FILE_AVAIL: - # Sanity check also satisfies mypy type checking - assert libogg is not None - assert libvorbis is not None - assert libvorbisfile is not None - - - # codecs - class vorbis_info(ctypes.Structure): - """ - Wrapper for: - typedef struct vorbis_info vorbis_info; - """ - _fields_ = [("version", c_int), - ("channels", c_int), - ("rate", c_long), - - ("bitrate_upper", c_long), - ("bitrate_nominal", c_long), - ("bitrate_lower", c_long), - ("bitrate_window", c_long), - ("codec_setup", c_void_p)] - - - - class vorbis_dsp_state(ctypes.Structure): - """ - Wrapper for: - typedef struct vorbis_dsp_state vorbis_dsp_state; - """ - _fields_ = [("analysisp", c_int), - ("vi", POINTER(vorbis_info)), - ("pcm", c_float_p_p), - ("pcmret", c_float_p_p), - ("pcm_storage", c_int), - ("pcm_current", c_int), - ("pcm_returned", c_int), - - ("preextrapolate", c_int), - ("eofflag", c_int), - - ("lW", c_long), - ("W", c_long), - ("nW", c_long), - ("centerW", c_long), - - ("granulepos", ogg_int64_t), - ("sequence", ogg_int64_t), - - ("glue_bits", ogg_int64_t), - ("time_bits", ogg_int64_t), - ("floor_bits", ogg_int64_t), - ("res_bits", ogg_int64_t), - - ("backend_state", c_void_p)] - - class alloc_chain(ctypes.Structure): - """ - Wrapper for: - typedef struct alloc_chain; - """ - pass - - alloc_chain._fields_ = [("ptr", c_void_p), - ("next", POINTER(alloc_chain))] - - class vorbis_block(ctypes.Structure): - """ - Wrapper for: - typedef struct vorbis_block vorbis_block; - """ - _fields_ = [("pcm", c_float_p_p), - ("opb", oggpack_buffer), - ("lW", c_long), - ("W", c_long), - ("nW", c_long), - ("pcmend", c_int), - ("mode", c_int), - - ("eofflag", c_int), - ("granulepos", ogg_int64_t), - ("sequence", ogg_int64_t), - ("vd", POINTER(vorbis_dsp_state)), - - ("localstore", c_void_p), - ("localtop", c_long), - ("localalloc", c_long), - ("totaluse", c_long), - ("reap", POINTER(alloc_chain)), - - ("glue_bits", c_long), - ("time_bits", c_long), - ("floor_bits", c_long), - ("res_bits", c_long), - - ("internal", c_void_p)] - - class vorbis_comment(ctypes.Structure): - """ - Wrapper for: - typedef struct vorbis_comment vorbis_comment; - """ - _fields_ = [("user_comments", c_char_p_p), - ("comment_lengths", c_int_p), - ("comments", c_int), - ("vendor", c_char_p)] - - - - vi_p = POINTER(vorbis_info) - vc_p = POINTER(vorbis_comment) - vd_p = POINTER(vorbis_dsp_state) - vb_p = POINTER(vorbis_block) - - libvorbis.vorbis_info_init.restype = None - libvorbis.vorbis_info_init.argtypes = [vi_p] - def vorbis_info_init(vi): - libvorbis.vorbis_info_init(vi) - - libvorbis.vorbis_info_clear.restype = None - libvorbis.vorbis_info_clear.argtypes = [vi_p] - def vorbis_info_clear(vi): - libvorbis.vorbis_info_clear(vi) - - libvorbis.vorbis_info_blocksize.restype = c_int - libvorbis.vorbis_info_blocksize.argtypes = [vi_p, c_int] - def vorbis_info_blocksize(vi, zo): - return libvorbis.vorbis_info_blocksize(vi, zo) - - libvorbis.vorbis_comment_init.restype = None - libvorbis.vorbis_comment_init.argtypes = [vc_p] - def vorbis_comment_init(vc): - libvorbis.vorbis_comment_init(vc) - - libvorbis.vorbis_comment_add.restype = None - libvorbis.vorbis_comment_add.argtypes = [vc_p, c_char_p] - def vorbis_comment_add(vc, comment): - libvorbis.vorbis_comment_add(vc, comment) - - libvorbis.vorbis_comment_add_tag.restype = None - libvorbis.vorbis_comment_add_tag.argtypes = [vc_p, c_char_p, c_char_p] - def vorbis_comment_add_tag(vc, tag, comment): - libvorbis.vorbis_comment_add_tag(vc, tag, comment) - - libvorbis.vorbis_comment_query.restype = c_char_p - libvorbis.vorbis_comment_query.argtypes = [vc_p, c_char_p, c_int] - def vorbis_comment_query(vc, tag, count): - libvorbis.vorbis_comment_query(vc, tag, count) - - libvorbis.vorbis_comment_query_count.restype = c_int - libvorbis.vorbis_comment_query_count.argtypes = [vc_p, c_char_p] - def vorbis_comment_query_count(vc, tag): - libvorbis.vorbis_comment_query_count(vc, tag) - - libvorbis.vorbis_comment_clear.restype = None - libvorbis.vorbis_comment_clear.argtypes = [vc_p] - def vorbis_comment_clear(vc): - libvorbis.vorbis_comment_clear(vc) - - - - libvorbis.vorbis_block_init.restype = c_int - libvorbis.vorbis_block_init.argtypes = [vd_p, vb_p] - def vorbis_block_init(v,vb): - return libvorbis.vorbis_block_init(v,vb) - - libvorbis.vorbis_block_clear.restype = c_int - libvorbis.vorbis_block_clear.argtypes = [vb_p] - def vorbis_block_clear(vb): - return libvorbis.vorbis_block_clear(vb) - - libvorbis.vorbis_dsp_clear.restype = None - libvorbis.vorbis_dsp_clear.argtypes = [vd_p] - def vorbis_dsp_clear(v): - return libvorbis.vorbis_dsp_clear(v) - - libvorbis.vorbis_granule_time.restype = c_double - libvorbis.vorbis_granule_time.argtypes = [vd_p, ogg_int64_t] - def vorbis_granule_time(v, granulepos): - return libvorbis.vorbis_granule_time(v, granulepos) - - - - libvorbis.vorbis_version_string.restype = c_char_p - libvorbis.vorbis_version_string.argtypes = [] - def vorbis_version_string(): - return libvorbis.vorbis_version_string() - - - - - - libvorbis.vorbis_analysis_init.restype = c_int - libvorbis.vorbis_analysis_init.argtypes = [vd_p, vi_p] - def vorbis_analysis_init(v, vi): - return libvorbis.vorbis_analysis_init(v, vi) - - libvorbis.vorbis_commentheader_out.restype = c_int - libvorbis.vorbis_commentheader_out.argtypes = [vc_p, op_p] - def vorbis_commentheader_out(vc, op): - return libvorbis.vorbis_commentheader_out(vc, op) - - libvorbis.vorbis_analysis_headerout.restype = c_int - libvorbis.vorbis_analysis_headerout.argtypes = [vd_p, vc_p, op_p, op_p, op_p] - def vorbis_analysis_headerout(v,vc, op, op_comm, op_code): - return libvorbis.vorbis_analysis_headerout(v,vc, op, op_comm, op_code) - - libvorbis.vorbis_analysis_buffer.restype = c_float_p_p - libvorbis.vorbis_analysis_buffer.argtypes = [vd_p, c_int] - def vorbis_analysis_buffer(v, vals): - return libvorbis.vorbis_analysis_buffer(v, vals) - - libvorbis.vorbis_analysis_wrote.restype = c_int - libvorbis.vorbis_analysis_wrote.argtypes = [vd_p, c_int] - def vorbis_analysis_wrote(v, vals): - return libvorbis.vorbis_analysis_wrote(v, vals) - - libvorbis.vorbis_analysis_blockout.restype = c_int - libvorbis.vorbis_analysis_blockout.argtypes = [vd_p, vb_p] - def vorbis_analysis_blockout(v, vb): - return libvorbis.vorbis_analysis_blockout(v, vb) - - libvorbis.vorbis_analysis.restype = c_int - libvorbis.vorbis_analysis.argtypes = [vb_p, op_p] - def vorbis_analysis(vb, op): - return libvorbis.vorbis_analysis(vb, op) - - - - - libvorbis.vorbis_bitrate_addblock.restype = c_int - libvorbis.vorbis_bitrate_addblock.argtypes = [vb_p] - def vorbis_bitrate_addblock(vb): - return libvorbis.vorbis_bitrate_addblock(vb) - - libvorbis.vorbis_bitrate_flushpacket.restype = c_int - libvorbis.vorbis_bitrate_flushpacket.argtypes = [vd_p, op_p] - def vorbis_bitrate_flushpacket(vd, op): - return libvorbis.vorbis_bitrate_flushpacket(vd, op) - - - - - libvorbis.vorbis_synthesis_idheader.restype = c_int - libvorbis.vorbis_synthesis_idheader.argtypes = [op_p] - def vorbis_synthesis_idheader(op): - return libvorbis.vorbis_synthesis_idheader(op) - - libvorbis.vorbis_synthesis_headerin.restype = c_int - libvorbis.vorbis_synthesis_headerin.argtypes = [vi_p, vc_p, op_p] - def vorbis_synthesis_headerin(vi, vc, op): - return libvorbis.vorbis_synthesis_headerin(vi, vc, op) - - - - - libvorbis.vorbis_synthesis_init.restype = c_int - libvorbis.vorbis_synthesis_init.argtypes = [vd_p, vi_p] - def vorbis_synthesis_init(v,vi): - return libvorbis.vorbis_synthesis_init(v,vi) - - libvorbis.vorbis_synthesis_restart.restype = c_int - libvorbis.vorbis_synthesis_restart.argtypes = [vd_p] - def vorbis_synthesis_restart(v): - return libvorbis.vorbis_synthesis_restart(v) - - libvorbis.vorbis_synthesis.restype = c_int - libvorbis.vorbis_synthesis.argtypes = [vb_p, op_p] - def vorbis_synthesis(vb, op): - return libvorbis.vorbis_synthesis(vb, op) - - libvorbis.vorbis_synthesis_trackonly.restype = c_int - libvorbis.vorbis_synthesis_trackonly.argtypes = [vb_p, op_p] - def vorbis_synthesis_trackonly(vb, op): - return libvorbis.vorbis_synthesis_trackonly(vb, op) - - libvorbis.vorbis_synthesis_blockin.restype = c_int - libvorbis.vorbis_synthesis_blockin.argtypes = [vd_p, vb_p] - def vorbis_synthesis_blockin(v, vb): - return libvorbis.vorbis_synthesis_blockin(v, vb) - - libvorbis.vorbis_synthesis_pcmout.restype = c_int - libvorbis.vorbis_synthesis_pcmout.argtypes = [vd_p, c_float_p_p_p] - def vorbis_synthesis_pcmout(v, pcm): - return libvorbis.vorbis_synthesis_pcmout(v, pcm) - - libvorbis.vorbis_synthesis_lapout.restype = c_int - libvorbis.vorbis_synthesis_lapout.argtypes = [vd_p, c_float_p_p_p] - def vorbis_synthesis_lapout(v, pcm): - return libvorbis.vorbis_synthesis_lapout(v, pcm) - - libvorbis.vorbis_synthesis_read.restype = c_int - libvorbis.vorbis_synthesis_read.argtypes = [vd_p, c_int] - def vorbis_synthesis_read(v, samples): - return libvorbis.vorbis_synthesis_read(v, samples) - - libvorbis.vorbis_packet_blocksize.restype = c_long - libvorbis.vorbis_packet_blocksize.argtypes = [vi_p, op_p] - def vorbis_packet_blocksize(vi, op): - return libvorbis.vorbis_packet_blocksize(vi, op) - - - - libvorbis.vorbis_synthesis_halfrate.restype = c_int - libvorbis.vorbis_synthesis_halfrate.argtypes = [vi_p, c_int] - def vorbis_synthesis_halfrate(v, flag): - return libvorbis.vorbis_synthesis_halfrate(v, flag) - - libvorbis.vorbis_synthesis_halfrate_p.restype = c_int - libvorbis.vorbis_synthesis_halfrate_p.argtypes = [vi_p] - def vorbis_synthesis_halfrate_p(vi): - return libvorbis.vorbis_synthesis_halfrate_p(vi) - - OV_FALSE = -1 - OV_EOF = -2 - OV_HOLE = -3 - - OV_EREAD = -128 - OV_EFAULT = -129 - OV_EIMPL =-130 - OV_EINVAL =-131 - OV_ENOTVORBIS =-132 - OV_EBADHEADER =-133 - OV_EVERSION =-134 - OV_ENOTAUDIO =-135 - OV_EBADPACKET =-136 - OV_EBADLINK =-137 - OV_ENOSEEK =-138 - # end of codecs - - # vorbisfile - read_func = ctypes.CFUNCTYPE(c_size_t, - c_void_p, - c_size_t, - c_size_t, - c_void_p) - - seek_func = ctypes.CFUNCTYPE(c_int, - c_void_p, - ogg_int64_t, - c_int) - - close_func = ctypes.CFUNCTYPE(c_int, - c_void_p) - - tell_func = ctypes.CFUNCTYPE(c_long, - c_void_p) - - class ov_callbacks(ctypes.Structure): - """ - Wrapper for: - typedef struct ov_callbacks; - """ - - _fields_ = [("read_func", read_func), - ("seek_func", seek_func), - ("close_func", close_func), - ("tell_func", tell_func)] - - NOTOPEN = 0 - PARTOPEN = 1 - OPENED = 2 - STREAMSET = 3 - INITSET = 4 - - class OggVorbis_File(ctypes.Structure): - """ - Wrapper for: - typedef struct OggVorbis_File OggVorbis_File; - """ - - _fields_ = [("datasource", c_void_p), - ("seekable", c_int), - ("offset", ogg_int64_t), - ("end", ogg_int64_t), - ("oy", ogg_sync_state), - - ("links", c_int), - ("offsets", ogg_int64_t_p), - ("dataoffsets", ogg_int64_t_p), - ("serialnos", c_long_p), - ("pcmlengths", ogg_int64_t_p), - ("vi", vi_p), - ("vc", vc_p), - - ("pcm_offset", ogg_int64_t), - ("ready_state", c_int), - ("current_serialno", c_long), - ("current_link", c_int), - - ("bittrack", c_double), - ("samptrack", c_double), - - ("os", ogg_stream_state), - - ("vd", vorbis_dsp_state), - ("vb", vorbis_block), - - ("callbacks", ov_callbacks)] - vf_p = POINTER(OggVorbis_File) - - libvorbisfile.ov_clear.restype = c_int - libvorbisfile.ov_clear.argtypes = [vf_p] - - def ov_clear(vf): - return libvorbisfile.ov_clear(vf) - - libvorbisfile.ov_fopen.restype = c_int - libvorbisfile.ov_fopen.argtypes = [c_char_p, vf_p] - - def ov_fopen(path, vf): - return libvorbisfile.ov_fopen(to_char_p(path), vf) - - libvorbisfile.ov_open_callbacks.restype = c_int - libvorbisfile.ov_open_callbacks.argtypes = [c_void_p, vf_p, c_char_p, c_long, ov_callbacks] - - def ov_open_callbacks(datasource, vf, initial, ibytes, callbacks): - return libvorbisfile.ov_open_callbacks(datasource, vf, initial, ibytes, callbacks) - - def ov_open(*args, **kw): - raise PyOggError("ov_open is not supported, please use ov_fopen instead") - - def ov_test(*args, **kw): - raise PyOggError("ov_test is not supported") - - libvorbisfile.ov_test_callbacks.restype = c_int - libvorbisfile.ov_test_callbacks.argtypes = [c_void_p, vf_p, c_char_p, c_long, ov_callbacks] - - def ov_test_callbacks(datasource, vf, initial, ibytes, callbacks): - return libvorbisfile.ov_test_callbacks(datasource, vf, initial, ibytes, callbacks) - - libvorbisfile.ov_test_open.restype = c_int - libvorbisfile.ov_test_open.argtypes = [vf_p] - - def ov_test_open(vf): - return libvorbisfile.ov_test_open(vf) - - - - - libvorbisfile.ov_bitrate.restype = c_long - libvorbisfile.ov_bitrate.argtypes = [vf_p, c_int] - - def ov_bitrate(vf, i): - return libvorbisfile.ov_bitrate(vf, i) - - libvorbisfile.ov_bitrate_instant.restype = c_long - libvorbisfile.ov_bitrate_instant.argtypes = [vf_p] - - def ov_bitrate_instant(vf): - return libvorbisfile.ov_bitrate_instant(vf) - - libvorbisfile.ov_streams.restype = c_long - libvorbisfile.ov_streams.argtypes = [vf_p] - - def ov_streams(vf): - return libvorbisfile.ov_streams(vf) - - libvorbisfile.ov_seekable.restype = c_long - libvorbisfile.ov_seekable.argtypes = [vf_p] - - def ov_seekable(vf): - return libvorbisfile.ov_seekable(vf) - - libvorbisfile.ov_serialnumber.restype = c_long - libvorbisfile.ov_serialnumber.argtypes = [vf_p, c_int] - - def ov_serialnumber(vf, i): - return libvorbisfile.ov_serialnumber(vf, i) - - - - libvorbisfile.ov_raw_total.restype = ogg_int64_t - libvorbisfile.ov_raw_total.argtypes = [vf_p, c_int] - - def ov_raw_total(vf, i): - return libvorbisfile.ov_raw_total(vf, i) - - libvorbisfile.ov_pcm_total.restype = ogg_int64_t - libvorbisfile.ov_pcm_total.argtypes = [vf_p, c_int] - - def ov_pcm_total(vf, i): - return libvorbisfile.ov_pcm_total(vf, i) - - libvorbisfile.ov_time_total.restype = c_double - libvorbisfile.ov_time_total.argtypes = [vf_p, c_int] - - def ov_time_total(vf, i): - return libvorbisfile.ov_time_total(vf, i) - - - - - libvorbisfile.ov_raw_seek.restype = c_int - libvorbisfile.ov_raw_seek.argtypes = [vf_p, ogg_int64_t] - - def ov_raw_seek(vf, pos): - return libvorbisfile.ov_raw_seek(vf, pos) - - libvorbisfile.ov_pcm_seek.restype = c_int - libvorbisfile.ov_pcm_seek.argtypes = [vf_p, ogg_int64_t] - - def ov_pcm_seek(vf, pos): - return libvorbisfile.ov_pcm_seek(vf, pos) - - libvorbisfile.ov_pcm_seek_page.restype = c_int - libvorbisfile.ov_pcm_seek_page.argtypes = [vf_p, ogg_int64_t] - - def ov_pcm_seek_page(vf, pos): - return libvorbisfile.ov_pcm_seek_page(vf, pos) - - libvorbisfile.ov_time_seek.restype = c_int - libvorbisfile.ov_time_seek.argtypes = [vf_p, c_double] - - def ov_time_seek(vf, pos): - return libvorbisfile.ov_time_seek(vf, pos) - - libvorbisfile.ov_time_seek_page.restype = c_int - libvorbisfile.ov_time_seek_page.argtypes = [vf_p, c_double] - - def ov_time_seek_page(vf, pos): - return libvorbisfile.ov_time_seek_page(vf, pos) - - - - - libvorbisfile.ov_raw_seek_lap.restype = c_int - libvorbisfile.ov_raw_seek_lap.argtypes = [vf_p, ogg_int64_t] - - def ov_raw_seek_lap(vf, pos): - return libvorbisfile.ov_raw_seek_lap(vf, pos) - - libvorbisfile.ov_pcm_seek_lap.restype = c_int - libvorbisfile.ov_pcm_seek_lap.argtypes = [vf_p, ogg_int64_t] - - def ov_pcm_seek_lap(vf, pos): - return libvorbisfile.ov_pcm_seek_lap(vf, pos) - - libvorbisfile.ov_pcm_seek_page_lap.restype = c_int - libvorbisfile.ov_pcm_seek_page_lap.argtypes = [vf_p, ogg_int64_t] - - def ov_pcm_seek_page_lap(vf, pos): - return libvorbisfile.ov_pcm_seek_page_lap(vf, pos) - - libvorbisfile.ov_time_seek_lap.restype = c_int - libvorbisfile.ov_time_seek_lap.argtypes = [vf_p, c_double] - - def ov_time_seek_lap(vf, pos): - return libvorbisfile.ov_time_seek_lap(vf, pos) - - libvorbisfile.ov_time_seek_page_lap.restype = c_int - libvorbisfile.ov_time_seek_page_lap.argtypes = [vf_p, c_double] - - def ov_time_seek_page_lap(vf, pos): - return libvorbisfile.ov_time_seek_page_lap(vf, pos) - - - - libvorbisfile.ov_raw_tell.restype = ogg_int64_t - libvorbisfile.ov_raw_tell.argtypes = [vf_p] - - def ov_raw_tell(vf): - return libvorbisfile.ov_raw_tell(vf) - - libvorbisfile.ov_pcm_tell.restype = ogg_int64_t - libvorbisfile.ov_pcm_tell.argtypes = [vf_p] - - def ov_pcm_tell(vf): - return libvorbisfile.ov_pcm_tell(vf) - - libvorbisfile.ov_time_tell.restype = c_double - libvorbisfile.ov_time_tell.argtypes = [vf_p] - - def ov_time_tell(vf): - return libvorbisfile.ov_time_tell(vf) - - - - libvorbisfile.ov_info.restype = vi_p - libvorbisfile.ov_info.argtypes = [vf_p, c_int] - - def ov_info(vf, link): - return libvorbisfile.ov_info(vf, link) - - libvorbisfile.ov_comment.restype = vc_p - libvorbisfile.ov_comment.argtypes = [vf_p, c_int] - - def ov_comment(vf, link): - return libvorbisfile.ov_comment(vf, link) - - - - libvorbisfile.ov_read_float.restype = c_long - libvorbisfile.ov_read_float.argtypes = [vf_p, c_float_p_p_p, c_int, c_int_p] - - def ov_read_float(vf, pcm_channels, samples, bitstream): - return libvorbisfile.ov_read_float(vf, pcm_channels, samples, bitstream) - - filter_ = ctypes.CFUNCTYPE(None, - c_float_p_p, - c_long, - c_long, - c_void_p) - - try: - libvorbisfile.ov_read_filter.restype = c_long - libvorbisfile.ov_read_filter.argtypes = [vf_p, c_char_p, c_int, c_int, c_int, c_int, c_int_p, filter_, c_void_p] - - def ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, filter_, filter_param): - return libvorbisfile.ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, filter_, filter_param) - except: - pass - - libvorbisfile.ov_read.restype = c_long - libvorbisfile.ov_read.argtypes = [vf_p, c_char_p, c_int, c_int, c_int, c_int, c_int_p] - - def ov_read(vf, buffer, length, bigendianp, word, sgned, bitstream): - return libvorbisfile.ov_read(vf, buffer, length, bigendianp, word, sgned, bitstream) - - libvorbisfile.ov_crosslap.restype = c_int - libvorbisfile.ov_crosslap.argtypes = [vf_p, vf_p] - - def ov_crosslap(vf1, cf2): - return libvorbisfile.ov_crosslap(vf1, vf2) - - - - - libvorbisfile.ov_halfrate.restype = c_int - libvorbisfile.ov_halfrate.argtypes = [vf_p, c_int] - - def ov_halfrate(vf, flag): - return libvorbisfile.ov_halfrate(vf, flag) - - libvorbisfile.ov_halfrate_p.restype = c_int - libvorbisfile.ov_halfrate_p.argtypes = [vf_p] - - def ov_halfrate_p(vf): - return libvorbisfile.ov_halfrate_p(vf) - # end of vorbisfile - - try: - # vorbisenc - - # Sanity check also satisfies mypy type checking - assert libvorbisenc is not None - - libvorbisenc.vorbis_encode_init.restype = c_int - libvorbisenc.vorbis_encode_init.argtypes = [vi_p, c_long, c_long, c_long, c_long, c_long] - - def vorbis_encode_init(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate): - return libvorbisenc.vorbis_encode_init(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate) - - libvorbisenc.vorbis_encode_setup_managed.restype = c_int - libvorbisenc.vorbis_encode_setup_managed.argtypes = [vi_p, c_long, c_long, c_long, c_long, c_long] - - def vorbis_encode_setup_managed(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate): - return libvorbisenc.vorbis_encode_setup_managed(vi, channels, rate, max_bitrate, nominal_bitrate, min_bitrate) - - libvorbisenc.vorbis_encode_setup_vbr.restype = c_int - libvorbisenc.vorbis_encode_setup_vbr.argtypes = [vi_p, c_long, c_long, c_float] - - def vorbis_encode_setup_vbr(vi, channels, rate, quality): - return libvorbisenc.vorbis_encode_setup_vbr(vi, channels, rate, quality) - - libvorbisenc.vorbis_encode_init_vbr.restype = c_int - libvorbisenc.vorbis_encode_init_vbr.argtypes = [vi_p, c_long, c_long, c_float] - - def vorbis_encode_init_vbr(vi, channels, rate, quality): - return libvorbisenc.vorbis_encode_init_vbr(vi, channels, rate, quality) - - libvorbisenc.vorbis_encode_setup_init.restype = c_int - libvorbisenc.vorbis_encode_setup_init.argtypes = [vi_p] - - def vorbis_encode_setup_init(vi): - return libvorbisenc.vorbis_encode_setup_init(vi) - - libvorbisenc.vorbis_encode_ctl.restype = c_int - libvorbisenc.vorbis_encode_ctl.argtypes = [vi_p, c_int, c_void_p] - - def vorbis_encode_ctl(vi, number, arg): - return libvorbisenc.vorbis_encode_ctl(vi, number, arg) - - class ovectl_ratemanage_arg(ctypes.Structure): - _fields_ = [("management_active", c_int), - ("bitrate_hard_min", c_long), - ("bitrate_hard_max", c_long), - ("bitrate_hard_window", c_double), - ("bitrate_av_lo", c_long), - ("bitrate_av_hi", c_long), - ("bitrate_av_window", c_double), - ("bitrate_av_window_center", c_double)] - - class ovectl_ratemanage2_arg(ctypes.Structure): - _fields_ = [("management_active", c_int), - ("bitrate_limit_min_kbps", c_long), - ("bitrate_limit_max_kbps", c_long), - ("bitrate_limit_reservoir_bits", c_long), - ("bitrate_limit_reservoir_bias", c_double), - ("bitrate_average_kbps", c_long), - ("bitrate_average_damping", c_double)] - - OV_ECTL_RATEMANAGE2_GET =0x14 - - OV_ECTL_RATEMANAGE2_SET =0x15 - - OV_ECTL_LOWPASS_GET =0x20 - - OV_ECTL_LOWPASS_SET =0x21 - - OV_ECTL_IBLOCK_GET =0x30 - - OV_ECTL_IBLOCK_SET =0x31 - - OV_ECTL_COUPLING_GET =0x40 - - OV_ECTL_COUPLING_SET =0x41 - - OV_ECTL_RATEMANAGE_GET =0x10 - - OV_ECTL_RATEMANAGE_SET =0x11 - - OV_ECTL_RATEMANAGE_AVG =0x12 - - OV_ECTL_RATEMANAGE_HARD =0x13 - # end of vorbisenc - except: - pass diff --git a/sbapp/pyogg/vorbis_file.py b/sbapp/pyogg/vorbis_file.py deleted file mode 100644 index 918f1e8..0000000 --- a/sbapp/pyogg/vorbis_file.py +++ /dev/null @@ -1,161 +0,0 @@ -import ctypes - -from . import vorbis -from .audio_file import AudioFile -from .pyogg_error import PyOggError - -# TODO: Issue #70: Vorbis files with multiple logical bitstreams could -# be supported by chaining VorbisFile instances (with say a 'next' -# attribute that points to the next VorbisFile that would contain the -# PCM for the next logical bitstream). A considerable constraint to -# implementing this was that examples files that demonstrated multiple -# logical bitstreams couldn't be found or created. Note that even -# Audacity doesn't handle multiple logical bitstreams (see -# https://wiki.audacityteam.org/wiki/OGG#Importing_multiple_stream_files). - -# TODO: Issue #53: Unicode file names are not well supported. -# They may work in macOS and Linux, they don't work under Windows. - -class VorbisFile(AudioFile): - def __init__(self, - path: str, - bytes_per_sample: int = 2, - signed:bool = True) -> None: - """Load an OggVorbis File. - - path specifies the location of the Vorbis file. Unicode - filenames may not work correctly under Windows. - - bytes_per_sample specifies the word size of the PCM. It may - be either 1 or 2. Specifying one byte per sample will save - memory but will likely decrease the quality of the decoded - audio. - - Only Vorbis files with a single logical bitstream are - supported. - - """ - # Sanity check the number of bytes per sample - assert bytes_per_sample==1 or bytes_per_sample==2 - - # Sanity check that the vorbis library is available (for mypy) - assert vorbis.libvorbisfile is not None - - #: Bytes per sample - self.bytes_per_sample = bytes_per_sample - - #: Samples are signed (rather than unsigned) - self.signed = signed - - # Create a Vorbis File structure - vf = vorbis.OggVorbis_File() - - # Attempt to open the Vorbis file - error = vorbis.libvorbisfile.ov_fopen( - vorbis.to_char_p(path), - ctypes.byref(vf) - ) - - # Check for errors during opening - if error != 0: - raise PyOggError( - ("File '{}' couldn't be opened or doesn't exist. "+ - "Error code : {}").format(path, error) - ) - - # Extract info from the Vorbis file - info = vorbis.libvorbisfile.ov_info( - ctypes.byref(vf), - -1 # the current logical bitstream - ) - - #: Number of channels in audio file. - self.channels = info.contents.channels - - #: Number of samples per second (per channel), 44100 for - # example. - self.frequency = info.contents.rate - - # Extract the total number of PCM samples for the first - # logical bitstream - pcm_length_samples = vorbis.libvorbisfile.ov_pcm_total( - ctypes.byref(vf), - 0 # to extract the length of the first logical bitstream - ) - - # Create a memory block to store the entire PCM - Buffer = ( - ctypes.c_char - * ( - pcm_length_samples - * self.bytes_per_sample - * self.channels - ) - ) - self.buffer = Buffer() - - # Create a pointer to the newly allocated memory. It - # seems we can only do pointer arithmetic on void - # pointers. See - # https://mattgwwalker.wordpress.com/2020/05/30/pointer-manipulation-in-python/ - buf_ptr = ctypes.cast( - ctypes.pointer(self.buffer), - ctypes.c_void_p - ) - - # Storage for the index of the logical bitstream - bitstream_previous = None - bitstream = ctypes.c_int() - - # Set bytes remaining to read into PCM - read_size = len(self.buffer) - - while True: - # Convert buffer pointer to the desired type - ptr = ctypes.cast( - buf_ptr, - ctypes.POINTER(ctypes.c_char) - ) - - # Attempt to decode PCM from the Vorbis file - result = vorbis.libvorbisfile.ov_read( - ctypes.byref(vf), - ptr, - read_size, - 0, # Little endian - self.bytes_per_sample, - int(self.signed), - ctypes.byref(bitstream) - ) - - # Check for errors - if result < 0: - raise PyOggError( - "An error occurred decoding the Vorbis file: "+ - f"Error code: {result}" - ) - - # Check that the bitstream hasn't changed as we only - # support Vorbis files with a single logical bitstream. - if bitstream_previous is None: - bitstream_previous = bitstream - else: - if bitstream_previous != bitstream: - raise PyOggError( - "PyOgg currently supports Vorbis files "+ - "with only one logical stream" - ) - - # Check for end of file - if result == 0: - break - - # Calculate the number of bytes remaining to read into PCM - read_size -= result - - # Update the pointer into the buffer - buf_ptr.value += result - - - # Close the file and clean up memory - vorbis.libvorbisfile.ov_clear(ctypes.byref(vf)) diff --git a/sbapp/pyogg/vorbis_file_stream.py b/sbapp/pyogg/vorbis_file_stream.py deleted file mode 100644 index 57677ba..0000000 --- a/sbapp/pyogg/vorbis_file_stream.py +++ /dev/null @@ -1,110 +0,0 @@ -import ctypes - -from . import vorbis -from .pyogg_error import PyOggError - -class VorbisFileStream: - def __init__(self, path, buffer_size=8192): - self.exists = False - self._buffer_size = buffer_size - - self.vf = vorbis.OggVorbis_File() - error = vorbis.ov_fopen(path, ctypes.byref(self.vf)) - if error != 0: - raise PyOggError("file couldn't be opened or doesn't exist. Error code : {}".format(error)) - - info = vorbis.ov_info(ctypes.byref(self.vf), -1) - - #: Number of channels in audio file. - self.channels = info.contents.channels - - #: Number of samples per second (per channel). Always - # 48,000. - self.frequency = info.contents.rate - - array = (ctypes.c_char*(self._buffer_size*self.channels))() - - self.buffer_ = ctypes.cast(ctypes.pointer(array), ctypes.c_char_p) - - self.bitstream = ctypes.c_int() - self.bitstream_pointer = ctypes.pointer(self.bitstream) - - self.exists = True # TODO: is this the best place for this statement? - - #: Bytes per sample - self.bytes_per_sample = 2 # TODO: Where is this defined? - - def __del__(self): - if self.exists: - vorbis.ov_clear(ctypes.byref(self.vf)) - self.exists = False - - def clean_up(self): - vorbis.ov_clear(ctypes.byref(self.vf)) - - self.exists = False - - def get_buffer(self): - """get_buffer() -> bytesBuffer, bufferLength - - Returns None when all data has been read from the file. - - """ - if not self.exists: - return None - buffer = [] - total_bytes_written = 0 - - while True: - new_bytes = vorbis.ov_read(ctypes.byref(self.vf), self.buffer_, self._buffer_size*self.channels - total_bytes_written, 0, 2, 1, self.bitstream_pointer) - - array_ = ctypes.cast(self.buffer_, ctypes.POINTER(ctypes.c_char*(self._buffer_size*self.channels))).contents - - buffer.append(array_.raw[:new_bytes]) - - total_bytes_written += new_bytes - - if new_bytes == 0 or total_bytes_written >= self._buffer_size*self.channels: - break - - out_buffer = b"".join(buffer) - - if total_bytes_written == 0: - self.clean_up() - return(None) - - return out_buffer - - def get_buffer_as_array(self): - """Provides the buffer as a NumPy array. - - Note that the underlying data type is 16-bit signed - integers. - - Does not copy the underlying data, so the returned array - should either be processed or copied before the next call - to get_buffer() or get_buffer_as_array(). - - """ - import numpy # type: ignore - - # Read the next samples from the stream - buf = self.get_buffer() - - # Check if we've come to the end of the stream - if buf is None: - return None - - # Convert the bytes buffer to a NumPy array - array = numpy.frombuffer( - buf, - dtype=numpy.int16 - ) - - # Reshape the array - return array.reshape( - (len(buf) - // self.bytes_per_sample - // self.channels, - self.channels) - )