pedalboard_native/io/__init__.pyi (305 lines of code) (raw):

"""This module provides classes and functions for reading and writing audio files or streams. *Introduced in v0.5.1.*""" from __future__ import annotations import pedalboard_native.io import typing original_overload = typing.overload __OVERLOADED_DOCSTRINGS = {} def patch_overload(func): original_overload(func) if func.__doc__: __OVERLOADED_DOCSTRINGS[func.__qualname__] = func.__doc__ else: func.__doc__ = __OVERLOADED_DOCSTRINGS.get(func.__qualname__) if func.__doc__: # Work around the fact that pybind11-stubgen generates # duplicate docstrings sometimes, once for each overload: docstring = func.__doc__ if docstring[len(docstring) // 2 :].strip() == docstring[: -len(docstring) // 2].strip(): func.__doc__ = docstring[len(docstring) // 2 :].strip() return func typing.overload = patch_overload from typing_extensions import Literal from enum import Enum import threading import numpy import pedalboard_native.utils _Shape = typing.Tuple[int, ...] __all__ = [ "AudioFile", "AudioStream", "ReadableAudioFile", "ResampledReadableAudioFile", "StreamResampler", "WriteableAudioFile", "get_supported_read_formats", "get_supported_write_formats", ] class AudioFile: """ A base class for readable and writeable audio files. :class:`AudioFile` may be used just like a regular Python ``open`` function call, to open an audio file for reading (with the default ``"r"`` mode) or for writing (with the ``"w"`` mode). Unlike a typical ``open`` call: - :class:`AudioFile` objects can only be created in read (``"r"``) or write (``"w"``) mode. All audio files are binary (so a trailing ``b`` would be redundant) and appending to an existing audio file is not possible. - If opening an audio file in write mode (``"w"``), one additional argument is required: the sample rate of the file. - A file-like object can be provided to :class:`AudioFile`, allowing for reading and writing to in-memory streams or buffers. The provided file-like object must be seekable and must be opened in binary mode (i.e.: ``io.BytesIO`` instead of ``io.StringIO``). A :class:`memoryview` object may also be provided when reading audio. Examples -------- Opening an audio file on disk:: with AudioFile("my_file.mp3") as f: first_ten_seconds = f.read(int(f.samplerate * 10)) Opening a file-like object:: ogg_buffer: io.BytesIO = get_audio_buffer(...) with AudioFile(ogg_buffer) as f: first_ten_seconds = f.read(int(f.samplerate * 10)) Opening an audio file on disk, while resampling on-the-fly:: with AudioFile("my_file.mp3").resampled_to(22_050) as f: first_ten_seconds = f.read(int(f.samplerate * 10)) Writing an audio file on disk:: with AudioFile("white_noise.wav", "w", samplerate=44100, num_channels=2) as f: f.write(np.random.rand(2, 44100)) Writing encoded audio to a file-like object:: wav_buffer = io.BytesIO() with AudioFile(wav_buffer, "w", samplerate=44100, num_channels=2, format="wav") as f: f.write(np.random.rand(2, 44100)) wav_buffer.getvalue() # do something with the file-like object Encoding audio as ``wav``, ``ogg``, ``mp3``, or ``flac`` as a :class:`bytes` buffer in one line:: sr = 44100 num_channels = 2 audio = np.random.rand(num_channels, sr) wav_buffer = AudioFile.encode(audio, sr, num_channels, format="wav") ogg_buffer = AudioFile.encode(audio, sr, num_channels, format="ogg") mp3_buffer = AudioFile.encode(audio, sr, num_channels, format="mp3") flac_buffer = AudioFile.encode(audio, sr, num_channels, format="flac") Writing to an audio file while also specifying quality options for the codec:: with AudioFile( "white_noise.mp3", "w", samplerate=44100, num_channels=2, quality=160, # kilobits per second ) as f: f.write(np.random.rand(2, 44100)) Re-encoding a WAV file as an MP3 in four lines of Python:: with AudioFile("input.wav") as i: with AudioFile("output.mp3", "w", i.samplerate, i.num_channels) as o: while i.tell() < i.frames: o.write(i.read(1024)) .. note:: Calling the :class:`AudioFile` constructor does not actually return an :class:`AudioFile`. If opening an audio file in read ("r") mode, a :class:`ReadableAudioFile` will be returned. If opening an audio file in write ("w") mode, a :class:`WriteableAudioFile` will be returned. See those classes below for documentation. """ @classmethod @typing.overload def __new__(cls, filename: str, mode: Literal["r"] = "r") -> ReadableAudioFile: """ Open an audio file for reading. Open a file-like object for reading. The provided object must have ``read``, ``seek``, ``tell``, and ``seekable`` methods, and must return binary data (i.e.: ``open(..., "w")`` or ``io.BytesIO``, etc.). """ @classmethod @typing.overload def __new__( cls, file_like: typing.Union[typing.BinaryIO, memoryview], mode: Literal["r"] = "r" ) -> ReadableAudioFile: ... @classmethod @typing.overload def __new__( cls, filename: str, mode: Literal["w"], samplerate: typing.Optional[float] = None, num_channels: int = 1, bit_depth: int = 16, quality: typing.Optional[typing.Union[str, float]] = None, ) -> WriteableAudioFile: ... @classmethod @typing.overload def __new__( cls, file_like: typing.BinaryIO, mode: Literal["w"], samplerate: typing.Optional[float] = None, num_channels: int = 1, bit_depth: int = 16, quality: typing.Optional[typing.Union[str, float]] = None, format: typing.Optional[str] = None, ) -> WriteableAudioFile: ... @staticmethod def encode( samples: numpy.ndarray, samplerate: float, format: str, num_channels: int = 1, bit_depth: int = 16, quality: typing.Optional[typing.Union[str, float]] = None, ) -> bytes: """ Encode an audio buffer to a Python :class:`bytes` object. This function will encode an entire audio buffer at once and return a :class:`bytes` object representing the bytes of the resulting audio file. This function produces identical output to the following code:: buf = io.BytesIO() with AudioFile(buf, "w", samplerate, num_channels, bit_depth, format, quality) as f: f.write(samples) result = buf.getvalue() However, this function is much more efficient than the above code, as it writes to an in-memory buffer in C++ and avoids interacting with Python at all during the encoding process. This allows Python's Global Interpreter Lock (GIL) to be released, which also makes this method much more performant in multi-threaded programs. .. warning:: This function will encode the entire audio buffer at once, and may consume a large amount of memory if the input audio buffer is large. To avoid running out of memory with arbitrary-length inputs, it is recommended to stream the output into a file or file-like object by using :class:`AudioFile` class in write (``"w"``) mode instead. """ pass class AudioStream: """ A class that allows interacting with live streams of audio from an input audio device (i.e.: a microphone, audio interface, etc) and/or to an output device (speaker, headphones), allowing access to the audio stream from within Python code. Use :py:meth:`AudioStream.play` to play audio data to your speakers:: # Play a 10-second chunk of an audio file: with AudioFile("my_audio_file.mp3") as f: chunk = f.read(f.samplerate * 10) AudioStream.play(chunk, f.samplerate) Or use :py:meth:`AudioStream.write` to stream audio in chunks:: # Play an audio file by looping through it in chunks: with AudioStream(output_device="default") as stream: with AudioFile("my_audio_file.mp3") as f: while f.tell() < f.frames: # Decode and play 512 samples at a time: stream.write(f.read(512)) :class:`AudioStream` may also be used to pass live audio through a :class:`Pedalboard`:: # Pass both an input and output device name to connect both ends: input_device_name = AudioStream.default_input_device_name output_device_name = AudioStream.default_output_device_name with AudioStream(input_device_name, output_device_name) as stream: # In this block, audio is streaming through `stream`! # Audio will be coming out of your speakers at this point. # Add plugins to the live audio stream: reverb = Reverb() stream.plugins.append(reverb) # Change plugin properties as the stream is running: reverb.wet_level = 1.0 # Delete plugins: del stream.plugins[0] # Or use AudioStream synchronously: stream = AudioStream(input_device_name, output_device_name) stream.plugins.append(Reverb(wet_level=1.0)) stream.run() # Run the stream until Ctrl-C is received .. warning:: The :class:`AudioStream` class implements a context manager interface to ensure that audio streams are never left "dangling" (i.e.: running in the background without being stopped). While it is possible to call the :meth:`__enter__` method directly to run an audio stream in the background, this can have some nasty side effects. If the :class:`AudioStream` object is no longer reachable (not bound to a variable, not in scope, etc), the audio stream will continue to run forever, and won't stop until the Python interpreter exits. To run an :class:`AudioStream` in the background, use Python's :py:mod:`threading` module to run the stream object method on a background thread, allowing for easier cleanup. *Introduced in v0.7.0 for macOS and Windows. Linux support introduced in v0.9.14.* :py:meth:`read` *and* :py:meth:`write` *methods introduced in v0.9.12.* """ def __enter__(self) -> AudioStream: """ Use this :class:`AudioStream` as a context manager. Entering the context manager will immediately start the audio stream, sending audio through to the output device. """ def __exit__(self, arg0: object, arg1: object, arg2: object) -> None: """ Exit the context manager, ending the audio stream. Once called, the audio stream will be stopped (i.e.: :py:attr:`running` will be :py:const:`False`). """ def __init__( self, input_device_name: typing.Optional[str] = None, output_device_name: typing.Optional[str] = None, plugins: typing.Optional[pedalboard_native.utils.Chain] = None, sample_rate: typing.Optional[float] = None, buffer_size: typing.Optional[int] = None, allow_feedback: bool = False, num_input_channels: int = 1, num_output_channels: int = 2, ) -> None: ... def __repr__(self) -> str: ... def close(self) -> None: """ Close the audio stream, stopping the audio device and releasing any resources. After calling close, this AudioStream object is no longer usable. """ @staticmethod def play( audio: numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]], sample_rate: float, output_device_name: typing.Optional[str] = None, ) -> None: """ Play audio data to the speaker, headphones, or other output device. This method will block until the audio is finished playing. """ def read(self, num_samples: int = 0) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]: """ .. warning:: Recording audio is a **real-time** operation, so if your code doesn't call :py:meth:`read` quickly enough, some audio will be lost. To warn about this, :py:meth:`read` will throw an exception if audio data is dropped. This behavior can be disabled by setting :py:attr:`ignore_dropped_input` to :py:const:`True`. The number of dropped samples since the last call to :py:meth:`read` can be retrieved by accessing the :py:attr:`dropped_input_frame_count` property. """ def run(self) -> None: """ Start streaming audio from input to output, passing the audio stream through the :py:attr:`plugins` on this AudioStream object. This call will block the current thread until a :py:exc:`KeyboardInterrupt` (``Ctrl-C``) is received. """ def write( self, audio: numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]], sample_rate: float ) -> None: """ If the provided sample rate does not match the output device's sample rate, an error will be thrown. In this case, you can use :py:class:`StreamResampler` to resample the audio before calling :py:meth:`write`. """ @property def buffer_size(self) -> int: """ The size (in frames) of the buffer used between the audio hardware and Python. """ @property def buffered_input_sample_count(self) -> typing.Optional[int]: """ The number of frames of audio that are currently in the input buffer. This number will change rapidly, and will be :py:const:`None` if no input device is connected. """ @property def dropped_input_frame_count(self) -> int: """ The number of frames of audio that were dropped since the last call to :py:meth:`read`. To prevent audio from being dropped during recording, ensure that you call :py:meth:`read` as often as possible or increase your buffer size. """ @property def ignore_dropped_input(self) -> bool: """ Controls the behavior of the :py:meth:`read` method when audio data is dropped. If this property is false (the default), the :py:meth:`read` method will raise a :py:exc:`RuntimeError` if any audio data is dropped in between calls. If this property is true, the :py:meth:`read` method will return the most recent audio data available, even if some audio data was dropped. .. note:: The :py:attr:`dropped_input_frame_count` property is unaffected by this setting. """ @ignore_dropped_input.setter def ignore_dropped_input(self, arg1: bool) -> None: """ Controls the behavior of the :py:meth:`read` method when audio data is dropped. If this property is false (the default), the :py:meth:`read` method will raise a :py:exc:`RuntimeError` if any audio data is dropped in between calls. If this property is true, the :py:meth:`read` method will return the most recent audio data available, even if some audio data was dropped. .. note:: The :py:attr:`dropped_input_frame_count` property is unaffected by this setting. """ @property def num_input_channels(self) -> int: """ The number of input channels on the input device. Will be ``0`` if no input device is connected. """ @property def num_output_channels(self) -> int: """ The number of output channels on the output device. Will be ``0`` if no output device is connected. """ @property def plugins(self) -> pedalboard_native.utils.Chain: """ The Pedalboard object that this AudioStream will use to process audio. """ @plugins.setter def plugins(self, arg1: pedalboard_native.utils.Chain) -> None: """ The Pedalboard object that this AudioStream will use to process audio. """ @property def running(self) -> bool: """ :py:const:`True` if this stream is currently streaming live audio, :py:const:`False` otherwise. """ @property def sample_rate(self) -> float: """ The sample rate that this stream is operating at. """ default_input_device_name: Optional[str] = None default_output_device_name: Optional[str] = None input_device_names: typing.List[str] = [] output_device_names: typing.List[str] = [] pass class ReadableAudioFile(AudioFile): """ A class that wraps an audio file for reading, with native support for Ogg Vorbis, MP3, WAV, FLAC, and AIFF files on all operating systems. Other formats may also be readable depending on the operating system and installed system libraries: - macOS: ``.3g2``, ``.3gp``, ``.aac``, ``.ac3``, ``.adts``, ``.aif``, ``.aifc``, ``.aiff``, ``.amr``, ``.au``, ``.bwf``, ``.caf``, ``.ec3``, ``.flac``, ``.latm``, ``.loas``, ``.m4a``, ``.m4b``, ``.m4r``, ``.mov``, ``.mp1``, ``.mp2``, ``.mp3``, ``.mp4``, ``.mpa``, ``.mpeg``, ``.ogg``, ``.qt``, ``.sd2``, ``.snd``, ``.w64``, ``.wav``, ``.xhe`` - Windows: ``.aif``, ``.aiff``, ``.flac``, ``.mp3``, ``.ogg``, ``.wav``, ``.wma`` - Linux: ``.aif``, ``.aiff``, ``.flac``, ``.mp3``, ``.ogg``, ``.wav`` Use :meth:`pedalboard.io.get_supported_read_formats()` to see which formats or file extensions are supported on the current platform. (Note that although an audio file may have a certain file extension, its contents may be encoded with a compression algorithm unsupported by Pedalboard.) .. note:: You probably don't want to use this class directly: passing the same arguments to :class:`AudioFile` will work too, and allows using :class:`AudioFile` just like you'd use ``open(...)`` in Python. """ def __enter__(self) -> ReadableAudioFile: """ Use this :class:`ReadableAudioFile` as a context manager, automatically closing the file and releasing resources when the context manager exits. """ def __exit__(self, arg0: object, arg1: object, arg2: object) -> None: """ Stop using this :class:`ReadableAudioFile` as a context manager, close the file, release its resources. """ @typing.overload def __init__(self, filename: str) -> None: ... @typing.overload def __init__(self, file_like: typing.BinaryIO) -> None: ... @classmethod @typing.overload def __new__(cls, filename: str) -> ReadableAudioFile: ... @classmethod @typing.overload def __new__(cls, file_like: typing.Union[typing.BinaryIO, memoryview]) -> ReadableAudioFile: ... def __repr__(self) -> str: ... def close(self) -> None: """ Close this file, rendering this object unusable. """ def read( self, num_frames: typing.Union[float, int] = 0 ) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]: """ Read the given number of frames (samples in each channel) from this audio file at its current position. ``num_frames`` is a required argument, as audio files can be deceptively large. (Consider that an hour-long ``.ogg`` file may be only a handful of megabytes on disk, but may decompress to nearly a gigabyte in memory.) Audio files should be read in chunks, rather than all at once, to avoid hard-to-debug memory problems and out-of-memory crashes. Audio samples are returned as a multi-dimensional :class:`numpy.array` with the shape ``(channels, samples)``; i.e.: a stereo audio file will have shape ``(2, <length>)``. Returned data is always in the ``float32`` datatype. If the file does not contain enough audio data to fill ``num_frames``, the returned :class:`numpy.array` will contain as many frames as could be read from the file. (In some cases, passing :py:attr:`frames` as ``num_frames`` may still return less data than expected. See documentation for :py:attr:`frames` and :py:attr:`exact_duration_known` for more information about situations in which this may occur.) For most (but not all) audio files, the minimum possible sample value will be ``-1.0f`` and the maximum sample value will be ``+1.0f``. .. note:: For convenience, the ``num_frames`` argument may be a floating-point number. However, if the provided number of frames contains a fractional part (i.e.: ``1.01`` instead of ``1.00``) then an exception will be thrown, as a fractional number of samples cannot be returned. """ def read_raw(self, num_frames: typing.Union[float, int] = 0) -> numpy.ndarray: """ Read the given number of frames (samples in each channel) from this audio file at its current position. ``num_frames`` is a required argument, as audio files can be deceptively large. (Consider that an hour-long ``.ogg`` file may be only a handful of megabytes on disk, but may decompress to nearly a gigabyte in memory.) Audio files should be read in chunks, rather than all at once, to avoid hard-to-debug memory problems and out-of-memory crashes. Audio samples are returned as a multi-dimensional :class:`numpy.array` with the shape ``(channels, samples)``; i.e.: a stereo audio file will have shape ``(2, <length>)``. Returned data is in the raw format stored by the underlying file (one of ``int8``, ``int16``, ``int32``, or ``float32``) and may have any magnitude. If the file does not contain enough audio data to fill ``num_frames``, the returned :class:`numpy.array` will contain as many frames as could be read from the file. (In some cases, passing :py:attr:`frames` as ``num_frames`` may still return less data than expected. See documentation for :py:attr:`frames` and :py:attr:`exact_duration_known` for more information about situations in which this may occur.) .. note:: For convenience, the ``num_frames`` argument may be a floating-point number. However, if the provided number of frames contains a fractional part (i.e.: ``1.01`` instead of ``1.00``) then an exception will be thrown, as a fractional number of samples cannot be returned. """ def resampled_to( self, target_sample_rate: float, quality: pedalboard_native.Resample.Quality = pedalboard_native.Resample.Quality.WindowedSinc32, ) -> typing.Union[ReadableAudioFile, ResampledReadableAudioFile]: """ Return a :class:`ResampledReadableAudioFile` that will automatically resample this :class:`ReadableAudioFile` to the provided `target_sample_rate`, using a constant amount of memory. If `target_sample_rate` matches the existing sample rate of the file, the original file will be returned. *Introduced in v0.6.0.* """ def seek(self, position: int) -> None: """ Seek this file to the provided location in frames. Future reads will start from this position. """ def seekable(self) -> bool: """ Returns True if this file is currently open and calls to seek() will work. """ def tell(self) -> int: """ Return the current position of the read pointer in this audio file, in frames. This value will increase as :meth:`read` is called, and may decrease if :meth:`seek` is called. """ @property def closed(self) -> bool: """ True iff this file is closed (and no longer usable), False otherwise. """ @property def duration(self) -> float: """ The duration of this file in seconds (``frames`` divided by ``samplerate``). .. warning:: :py:attr:`duration` may be an overestimate for certain MP3 files. Use :py:attr:`exact_duration_known` property to determine if :py:attr:`duration` is accurate. (See the documentation for the :py:attr:`frames` attribute for more details.) """ @property def exact_duration_known(self) -> bool: """ Returns :py:const:`True` if this file's :py:attr:`frames` and :py:attr:`duration` attributes are exact values, or :py:const:`False` if the :py:attr:`frames` and :py:attr:`duration` attributes are estimates based on the file's size and bitrate. If :py:attr:`exact_duration_known` is :py:const:`False`, this value will change to :py:const:`True` once the file is read to completion. Once :py:const:`True`, this value will not change back to :py:const:`False` for the same :py:class:`AudioFile` object (even after calls to :meth:`seek`). .. note:: :py:attr:`exact_duration_known` will only ever be :py:const:`False` when reading certain MP3 files. For files in other formats than MP3, :py:attr:`exact_duration_known` will always be equal to :py:const:`True`. *Introduced in v0.7.2.* """ @property def file_dtype(self) -> str: """ The data type (``"int16"``, ``"float32"``, etc) stored natively by this file. Note that :meth:`read` will always return a ``float32`` array, regardless of the value of this property. Use :meth:`read_raw` to read data from the file in its ``file_dtype``. """ @property def frames(self) -> int: """ The total number of frames (samples per channel) in this file. For example, if this file contains 10 seconds of stereo audio at sample rate of 44,100 Hz, ``frames`` will return ``441,000``. .. warning:: When reading certain MP3 files that have been encoded in constant bitrate mode, the :py:attr:`frames` and :py:attr:`duration` properties may initially be estimates and **may change as the file is read**. The :py:attr:`exact_duration_known` property indicates if the values of :py:attr:`frames` and :py:attr:`duration` are estimates or exact values. This discrepancy is due to the fact that MP3 files are not required to have headers that indicate the duration of the file. If an MP3 file is opened and a ``Xing`` or ``Info`` header frame is not found, the initial value of the :py:attr:`frames` and :py:attr:`duration` attributes are estimates based on the file's bitrate and size. This may result in an overestimate of the file's duration if there is additional data present in the file after the audio stream is finished. If the exact number of frames in the file is required, read the entire file first before accessing the :py:attr:`frames` or :py:attr:`duration` properties. This operation forces each frame to be parsed and guarantees that :py:attr:`frames` and :py:attr:`duration` are correct, at the expense of scanning the entire file:: with AudioFile("my_file.mp3") as f: while f.tell() < f.frames: f.read(f.samplerate * 60) # f.frames is now guaranteed to be exact, as the entire file has been read: assert f.exact_duration_known == True f.seek(0) num_channels, num_samples = f.read(f.frames).shape assert num_samples == f.frames This behaviour is present in v0.7.2 and later; prior versions would raise an exception when trying to read the ends of MP3 files that contained trailing non-audio data and lacked ``Xing`` or ``Info`` headers. """ @property def name(self) -> typing.Optional[str]: """ The name of this file. If this :class:`ReadableAudioFile` was opened from a file-like object, this will be ``None``. """ @property def num_channels(self) -> int: """ The number of channels in this file. """ @property def samplerate(self) -> typing.Union[float, int]: """ The sample rate of this file in samples (per channel) per second (Hz). Sample rates are represented as floating-point numbers by default, but this property will be an integer if the file's sample rate has no fractional part. """ pass class ResampledReadableAudioFile(AudioFile): """ A class that wraps an audio file for reading, while resampling the audio stream on-the-fly to a new sample rate. *Introduced in v0.6.0.* Reading, seeking, and all other basic file I/O operations are supported (except for :meth:`read_raw`). :class:`ResampledReadableAudioFile` should usually be used via the :meth:`resampled_to` method on :class:`ReadableAudioFile`: :: with AudioFile("my_file.mp3").resampled_to(22_050) as f: f.samplerate # => 22050 first_ten_seconds = f.read(int(f.samplerate * 10)) Fractional (real-valued, non-integer) sample rates are supported. Under the hood, :class:`ResampledReadableAudioFile` uses a stateful :class:`StreamResampler` instance, which uses a constant amount of memory to resample potentially-unbounded streams of audio. The audio output by :class:`ResampledReadableAudioFile` will always be identical to the result obtained by passing the entire audio file through a :class:`StreamResampler`, with the added benefits of allowing chunked reads, seeking through files, and using a constant amount of memory. """ def __enter__(self) -> ResampledReadableAudioFile: """ Use this :class:`ResampledReadableAudioFile` as a context manager, automatically closing the file and releasing resources when the context manager exits. """ def __exit__(self, arg0: object, arg1: object, arg2: object) -> None: """ Stop using this :class:`ResampledReadableAudioFile` as a context manager, close the file, release its resources. """ def __init__( self, audio_file: ReadableAudioFile, target_sample_rate: float, resampling_quality: pedalboard_native.Resample.Quality = pedalboard_native.Resample.Quality.WindowedSinc32, ) -> None: ... @classmethod def __new__( cls, audio_file: ReadableAudioFile, target_sample_rate: float, resampling_quality: pedalboard_native.Resample.Quality = pedalboard_native.Resample.Quality.WindowedSinc32, ) -> ResampledReadableAudioFile: ... def __repr__(self) -> str: ... def close(self) -> None: """ Close this file, rendering this object unusable. Note that the :class:`ReadableAudioFile` instance that is wrapped by this object will not be closed, and will remain usable. """ def read( self, num_frames: typing.Union[float, int] = 0 ) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]: """ Read the given number of frames (samples in each channel, at the target sample rate) from this audio file at its current position, automatically resampling on-the-fly to ``target_sample_rate``. ``num_frames`` is a required argument, as audio files can be deceptively large. (Consider that an hour-long ``.ogg`` file may be only a handful of megabytes on disk, but may decompress to nearly a gigabyte in memory.) Audio files should be read in chunks, rather than all at once, to avoid hard-to-debug memory problems and out-of-memory crashes. Audio samples are returned as a multi-dimensional :class:`numpy.array` with the shape ``(channels, samples)``; i.e.: a stereo audio file will have shape ``(2, <length>)``. Returned data is always in the ``float32`` datatype. If the file does not contain enough audio data to fill ``num_frames``, the returned :class:`numpy.array` will contain as many frames as could be read from the file. (In some cases, passing :py:attr:`frames` as ``num_frames`` may still return less data than expected. See documentation for :py:attr:`frames` and :py:attr:`exact_duration_known` for more information about situations in which this may occur.) For most (but not all) audio files, the minimum possible sample value will be ``-1.0f`` and the maximum sample value will be ``+1.0f``. .. note:: For convenience, the ``num_frames`` argument may be a floating-point number. However, if the provided number of frames contains a fractional part (i.e.: ``1.01`` instead of ``1.00``) then an exception will be thrown, as a fractional number of samples cannot be returned. """ def seek(self, position: int) -> None: """ Seek this file to the provided location in frames at the target sample rate. Future reads will start from this position. .. note:: Prior to version 0.7.3, this method operated in linear time with respect to the seek position (i.e.: the file was seeked to its beginning and pushed through the resampler) to ensure that the resampled audio output was sample-accurate. This was optimized in version 0.7.3 to operate in effectively constant time while retaining sample-accuracy. """ def seekable(self) -> bool: """ Returns True if this file is currently open and calls to seek() will work. """ def tell(self) -> int: """ Return the current position of the read pointer in this audio file, in frames at the target sample rate. This value will increase as :meth:`read` is called, and may decrease if :meth:`seek` is called. """ @property def closed(self) -> bool: """ True iff either this file or its wrapped :class:`ReadableAudioFile` instance are closed (and no longer usable), False otherwise. """ @property def duration(self) -> float: """ The duration of this file in seconds (``frames`` divided by ``samplerate``). .. warning:: When reading certain MP3 files, the :py:attr:`frames` and :py:attr:`duration` properties may initially be estimates and **may change as the file is read**. See the documentation for :py:attr:`.ReadableAudioFile.frames` for more details. """ @property def exact_duration_known(self) -> bool: """ Returns :py:const:`True` if this file's :py:attr:`frames` and :py:attr:`duration` attributes are exact values, or :py:const:`False` if the :py:attr:`frames` and :py:attr:`duration` attributes are estimates based on the file's size and bitrate. :py:attr:`exact_duration_known` will change from :py:const:`False` to :py:const:`True` as the file is read to completion. Once :py:const:`True`, this value will not change back to :py:const:`False` for the same :py:class:`AudioFile` object (even after calls to :meth:`seek`). .. note:: :py:attr:`exact_duration_known` will only ever be :py:const:`False` when reading certain MP3 files. For files in other formats than MP3, :py:attr:`exact_duration_known` will always be equal to :py:const:`True`. *Introduced in v0.7.2.* """ @property def file_dtype(self) -> str: """ The data type (``"int16"``, ``"float32"``, etc) stored natively by this file. Note that :meth:`read` will always return a ``float32`` array, regardless of the value of this property. """ @property def frames(self) -> int: """ The total number of frames (samples per channel) in this file, at the target sample rate. For example, if this file contains 10 seconds of stereo audio at sample rate of 44,100 Hz, and ``target_sample_rate`` is 22,050 Hz, ``frames`` will return ``22,050``. Note that different ``resampling_quality`` values used for resampling may cause ``frames`` to differ by ± 1 from its expected value. .. warning:: When reading certain MP3 files, the :py:attr:`frames` and :py:attr:`duration` properties may initially be estimates and **may change as the file is read**. See the documentation for :py:attr:`.ReadableAudioFile.frames` for more details. """ @property def name(self) -> typing.Optional[str]: """ The name of this file. If the :class:`ReadableAudioFile` wrapped by this :class:`ResampledReadableAudioFile` was opened from a file-like object, this will be ``None``. """ @property def num_channels(self) -> int: """ The number of channels in this file. """ @property def resampling_quality(self) -> pedalboard_native.Resample.Quality: """ The resampling algorithm used to resample from the original file's sample rate to the ``target_sample_rate``. """ @property def samplerate(self) -> typing.Union[float, int]: """ The sample rate of this file in samples (per channel) per second (Hz). This will be equal to the ``target_sample_rate`` parameter passed when this object was created. Sample rates are represented as floating-point numbers by default, but this property will be an integer if the file's target sample rate has no fractional part. """ pass class StreamResampler: """ A streaming resampler that can change the sample rate of multiple chunks of audio in series, while using constant memory. For a resampling plug-in that can be used in :class:`Pedalboard` objects, see :class:`pedalboard.Resample`. *Introduced in v0.6.0.* """ def __init__( self, source_sample_rate: float, target_sample_rate: float, num_channels: int, quality: pedalboard_native.Resample.Quality = pedalboard_native.Resample.Quality.WindowedSinc32, ) -> None: """ Create a new StreamResampler, capable of resampling a potentially-unbounded audio stream with a constant amount of memory. The source sample rate, target sample rate, quality, or number of channels cannot be changed once the resampler is instantiated. """ def __repr__(self) -> str: ... def process( self, input: typing.Optional[numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]] = None ) -> numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]: """ Resample a 32-bit floating-point audio buffer. The returned buffer may be smaller than the provided buffer depending on the quality method used. Call :meth:`process()` without any arguments to flush the internal buffers and return all remaining audio. """ def reset(self) -> None: """ Used to reset the internal state of this resampler. Call this method when resampling a new audio stream to prevent audio from leaking between streams. """ @property def input_latency(self) -> float: """ The number of samples (in the input sample rate) that must be supplied before this resampler will begin returning output. """ @property def num_channels(self) -> int: """ The number of channels expected to be passed in every call to :meth:`process()`. """ @property def quality(self) -> pedalboard_native.Resample.Quality: """ The resampling algorithm used by this resampler. """ @property def source_sample_rate(self) -> float: """ The source sample rate of the input audio that this resampler expects to be passed to :meth:`process()`. """ @property def target_sample_rate(self) -> float: """ The sample rate of the audio that this resampler will return from :meth:`process()`. """ pass class WriteableAudioFile(AudioFile): """ A class that wraps an audio file for writing, with native support for Ogg Vorbis, MP3, WAV, FLAC, and AIFF files on all operating systems. Use :meth:`pedalboard.io.get_supported_write_formats()` to see which formats or file extensions are supported on the current platform. Args: filename_or_file_like: The path to an output file to write to, or a seekable file-like binary object (like ``io.BytesIO``) to write to. samplerate: The sample rate of the audio that will be written to this file. All calls to the :meth:`write` method will assume this sample rate is used. num_channels: The number of channels in the audio that will be written to this file. All calls to the :meth:`write` method will expect audio with this many channels, and will throw an exception if the audio does not contain this number of channels. bit_depth: The bit depth (number of bits per sample) that will be written to this file. Used for raw formats like WAV and AIFF. Will have no effect on compressed formats like MP3 or Ogg Vorbis. quality: An optional string or number that indicates the quality level to use for the given audio compression codec. Different codecs have different compression quality values; numeric values like ``128`` and ``256`` will usually indicate the number of kilobits per second used by the codec. Some formats, like MP3, support more advanced options like ``V2`` (as specified by `the LAME encoder <https://lame.sourceforge.io/>`_) which may be passed as a string. The strings ``"best"``, ``"worst"``, ``"fastest"``, and ``"slowest"`` will also work for any codec. .. note:: You probably don't want to use this class directly: all of the parameters accepted by the :class:`WriteableAudioFile` constructor will be accepted by :class:`AudioFile` as well, as long as the ``"w"`` mode is passed as the second argument. """ def __enter__(self) -> WriteableAudioFile: ... def __exit__(self, arg0: object, arg1: object, arg2: object) -> None: ... @typing.overload def __init__( self, filename: str, samplerate: float, num_channels: int = 1, bit_depth: int = 16, quality: typing.Optional[typing.Union[str, float]] = None, ) -> None: ... @typing.overload def __init__( self, file_like: typing.BinaryIO, samplerate: float, num_channels: int = 1, bit_depth: int = 16, quality: typing.Optional[typing.Union[str, float]] = None, format: typing.Optional[str] = None, ) -> None: ... @classmethod @typing.overload def __new__( cls, filename: str, samplerate: typing.Optional[float] = None, num_channels: int = 1, bit_depth: int = 16, quality: typing.Optional[typing.Union[str, float]] = None, ) -> WriteableAudioFile: ... @classmethod @typing.overload def __new__( cls, file_like: typing.BinaryIO, samplerate: typing.Optional[float] = None, num_channels: int = 1, bit_depth: int = 16, quality: typing.Optional[typing.Union[str, float]] = None, format: typing.Optional[str] = None, ) -> WriteableAudioFile: ... def __repr__(self) -> str: ... def close(self) -> None: """ Close this file, flushing its contents to disk and rendering this object unusable for further writing. """ def flush(self) -> None: """ Attempt to flush this audio file's contents to disk. Not all formats support flushing, so this may throw a RuntimeError. (If this happens, closing the file will reliably force a flush to occur.) """ def tell(self) -> int: """ Return the current position of the write pointer in this audio file, in frames at the target sample rate. This value will increase as :meth:`write` is called, and will never decrease. """ def write(self, samples: numpy.ndarray) -> None: """ Encode an array of audio data and write it to this file. The number of channels in the array must match the number of channels used to open the file. The array may contain audio in any shape. If the file's bit depth or format does not match the provided data type, the audio will be automatically converted. Arrays of type int8, int16, int32, float32, and float64 are supported. If an array of an unsupported ``dtype`` is provided, a ``TypeError`` will be raised. .. warning:: If an array of shape ``(num_channels, num_channels)`` is passed to this method before any other audio data is provided, an exception will be thrown, as the method will not be able to infer which dimension of the input corresponds to the number of channels and which dimension corresponds to the number of samples. To avoid this, first call this method with an array where the number of samples does not match the number of channels. The channel layout from the most recently provided input will be cached on the :py:class:`WritableAudioFile` object and will be used if necessary to disambiguate the array layout: .. code-block:: python with AudioFile("my_file.mp3", "w", 44100, num_channels=2) as f: # This will throw an exception: f.write(np.zeros((2, 2))) # But this will work: f.write(np.zeros((2, 1))) # And now `f` expects an input shape of (num_channels, num_samples), so this works: f.write(np.zeros((2, 2))) # Also an option: pass (0, num_channels) or (num_channels, 0) first # to hint that the input will be in that shape without writing anything: with AudioFile("my_file.mp3", "w", 44100, num_channels=2) as f: # Pass a hint, but write nothing: f.write(np.zeros((2, 0))) # And now `f` expects an input shape of (num_channels, num_samples), so this works: f.write(np.zeros((2, 2))) """ @property def closed(self) -> bool: """ If this file has been closed, this property will be True. """ @property def file_dtype(self) -> str: """ The data type stored natively by this file. Note that write(...) will accept multiple datatypes, regardless of the value of this property. """ @property def frames(self) -> int: """ The total number of frames (samples per channel) written to this file so far. """ @property def num_channels(self) -> int: """ The number of channels in this file. """ @property def quality(self) -> typing.Optional[str]: """ The quality setting used to write this file. For many formats, this may be ``None``. Quality options differ based on the audio codec used in the file. Most codecs specify a number of bits per second in 16- or 32-bit-per-second increments (128 kbps, 160 kbps, etc). Some codecs provide string-like options for variable bit-rate encoding (i.e. "V0" through "V9" for MP3). The strings ``"best"``, ``"worst"``, ``"fastest"``, and ``"slowest"`` will also work for any codec. """ @property def samplerate(self) -> typing.Union[float, int]: """ The sample rate of this file in samples (per channel) per second (Hz). Sample rates are represented as floating-point numbers by default, but this property will be an integer if the file's sample rate has no fractional part. """ pass def get_supported_read_formats() -> typing.List[str]: pass def get_supported_write_formats() -> typing.List[str]: pass