### Install Target Source: https://github.com/pawel-glomski/pylibrb/blob/main/src/CMakeLists.txt Specifies the installation rule for the 'pylibrb_ext' target, ensuring it is installed as a library in the 'pylibrb' destination directory, intended for use with scikit-build. ```cmake # Install directive for scikit-build install(TARGETS pylibrb_ext LIBRARY DESTINATION pylibrb) ``` -------------------------------- ### Initialize and Process Audio with RubberBandStretcher Source: https://github.com/pawel-glomski/pylibrb/blob/main/README.md This example demonstrates how to create a RubberBandStretcher instance, configure it for real-time processing with specific options, and then feed it audio data in batches. It shows how to check for available output and retrieve processed audio. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array # create a stretcher stretcher = RubberBandStretcher(sample_rate=16000, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER, initial_time_ratio=0.5) stretcher.set_max_process_size(1024) # provide the audio to the stretcher, until some output is available audio_in = create_audio_array(channels_num=1, samples_num=1024) while not stretcher.available(): audio_in[:] = 0 # get the next batch of samples, here we just use silence stretcher.process(audio_in) # retrieve the available samples audio_out = stretcher.retrieve_available() ``` -------------------------------- ### Install pylibrb Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Install the pylibrb package using pip. ```bash pip install pylibrb ``` -------------------------------- ### Real-Time Audio Processing with RubberBandStretcher Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Demonstrates real-time audio processing by feeding blocks to `process()` and retrieving output. Includes handling start padding, dynamic ratio updates, and discarding initial delay samples. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array stretcher = RubberBandStretcher( sample_rate=16000, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER, initial_time_ratio=0.5, # slow down to half speed ) stretcher.set_max_process_size(1024) # Handle real-time start padding and delay pad_samples = stretcher.get_preferred_start_pad() if pad_samples > 0: silence = create_audio_array(1, pad_samples) stretcher.process(silence) start_delay = stretcher.get_start_delay() samples_to_discard = start_delay # Streaming loop for block_index in range(20): audio_in = create_audio_array(channels_num=1, samples_num=1024, init_value=0.1) # Optionally update ratios dynamically if block_index == 10: stretcher.time_ratio = 1.0 # switch back to normal speed stretcher.process(audio_in, final=(block_index == 19)) available = stretcher.available() if available > 0: audio_out = stretcher.retrieve(available) # Discard initial delay samples if samples_to_discard > 0: discard = min(samples_to_discard, audio_out.shape[1]) audio_out = audio_out[:, discard:] samples_to_discard -= discard print(f"Block {block_index}: retrieved {audio_out.shape[1]} samples") print("Done:", stretcher.is_done()) ``` -------------------------------- ### RubberBandStretcher: Fixed vs. Variable Block Size Processing Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Demonstrates two workflows for processing audio: fixed block size using `set_max_process_size` and variable block size guided by `get_samples_required`. Use `set_max_process_size` for applications with a known, constant block size. Use `get_samples_required` when the application can provide variable-sized blocks and needs guidance on the required input size. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array # Fixed block size workflow stretcher_fixed = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FASTER, ) stretcher_fixed.set_max_process_size(512) # must call before first process() print(f"Process size limit: {stretcher_fixed.get_process_size_limit()}") # 524288 # Variable block size workflow (guided by get_samples_required) stretcher_var = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FASTER, ) for _ in range(5): required = stretcher_var.get_samples_required() audio = create_audio_array(channels_num=1, samples_num=required) stretcher_var.process(audio) out = stretcher_var.retrieve_available() print(f"Required: {required}, Got: {out.shape[1] if out.size else 0}") ``` -------------------------------- ### RubberBandStretcher.set_max_process_size and get_samples_required Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Configure the stretcher for fixed or variable block processing. `set_max_process_size` is used for applications with a known, fixed block size, while `get_samples_required` guides variable-sized block processing. ```APIDOC ## RubberBandStretcher.set_max_process_size(samples) ### Description Sets the maximum number of samples the stretcher will process in a single call. This is useful for applications with a fixed block size. ### Method `set_max_process_size` ### Parameters - **samples** (int) - The maximum number of samples per process call. ## RubberBandStretcher.get_samples_required() ### Description Returns the number of samples required for the next processing step. This is useful for applications that can provide variable-sized blocks of audio data. ### Method `get_samples_required` ### Parameters None ### Response Example ```python # Example usage within a loop for variable block size processing required = stretcher.get_samples_required() audio = create_audio_array(channels_num=1, samples_num=required) stretcher.process(audio) out = stretcher.retrieve_available() print(f"Required: {required}, Got: {out.shape[1] if out.size else 0}") ``` ``` -------------------------------- ### RubberBandStretcher.time_ratio Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Gets or sets the time stretch ratio (stretched/original duration). A value of 2.0 doubles the duration (halves tempo); 0.5 halves it (doubles tempo). Freely changeable in real-time mode. ```APIDOC ## RubberBandStretcher.time_ratio (property) ### Description Gets or sets the time stretch ratio (stretched/original duration). A value of `2.0` doubles the duration (halves tempo); `0.5` halves it (doubles tempo). Freely changeable in real-time mode. ### Method ```python RubberBandStretcher.time_ratio = value value = RubberBandStretcher.time_ratio ``` ### Parameters #### Setters - **value** (float) - The time stretch ratio. ### Request Example ```python from pylibrb import RubberBandStretcher, Option, create_audio_array stretcher = RubberBandStretcher( sample_rate=16000, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FASTER, ) stretcher.set_max_process_size(1024) audio = create_audio_array(channels_num=1, samples_num=1024) # Start at normal speed stretcher.time_ratio = 1.0 stretcher.process(audio) out1 = stretcher.retrieve_available() # Switch to half speed stretcher.time_ratio = 2.0 stretcher.process(audio) out2 = stretcher.retrieve_available() print(f"Normal speed output: {out1.shape[1] if out1.size else 0} samples") print(f"Half speed output: {out2.shape[1] if out2.size else 0} samples") ``` ``` -------------------------------- ### RubberBandStretcher.pitch_scale Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Gets or sets the pitch scaling ratio. A value of 2.0 shifts pitch up one octave; 0.5 shifts down one octave. Can be freely changed during real-time processing. Fixed in offline mode after study() begins. ```APIDOC ## RubberBandStretcher.pitch_scale (property) ### Description Gets or sets the pitch scaling ratio. A value of `2.0` shifts pitch up one octave; `0.5` shifts down one octave. Can be freely changed during real-time processing. Fixed in offline mode after `study()` begins. ### Method ```python RubberBandStretcher.pitch_scale = value value = RubberBandStretcher.pitch_scale ``` ### Parameters #### Setters - **value** (float) - The pitch scaling ratio. ### Request Example ```python from pylibrb import RubberBandStretcher, Option import math stretcher = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER | Option.PITCH_HIGH_CONSISTENCY, ) stretcher.set_max_process_size(2048) # Shift up by 3 semitones semitones = 3 stretcher.pitch_scale = math.pow(2.0, semitones / 12.0) print(f"Pitch scale for +{semitones} semitones: {stretcher.pitch_scale:.4f}") # Process a block audio = create_audio_array(channels_num=1, samples_num=2048, init_value=0.3) stretcher.process(audio) # Dynamically change pitch mid-stream (PITCH_HIGH_CONSISTENCY required) stretcher.pitch_scale = math.pow(2.0, -5 / 12.0) # shift down 5 semitones stretcher.process(audio) print(f"New pitch scale: {stretcher.pitch_scale:.4f}") ``` ``` -------------------------------- ### Initialize RubberBandStretcher (R3, Real-Time, Short Window) Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Construct a RubberBandStretcher instance for real-time processing with the R3 engine and short window for low latency. Demonstrates accessing instance properties. ```python from pylibrb import RubberBandStretcher, Option # R3 engine, real-time mode, short window (low latency) stretcher = RubberBandStretcher( sample_rate=44100, channels=2, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER | Option.WINDOW_SHORT, initial_time_ratio=1.0, initial_pitch_scale=1.0, ) print(stretcher.channels) # 2 print(stretcher.engine_version) # 3 print(stretcher.time_ratio) # 1.0 print(stretcher.pitch_scale) # 1.0 print(stretcher.is_done()) # False ``` -------------------------------- ### Find and Configure Libraries Source: https://github.com/pawel-glomski/pylibrb/blob/main/src/CMakeLists.txt Finds required libraries like fmt and rubberband using CMake's find_package and pkg_check_modules commands. Includes platform-specific adjustments for library linking on Windows. ```cmake INCLUDE(CheckLibraryExists) #################################################################################################### # 3rdparty find_package(fmt CONFIG REQUIRED) find_package(PkgConfig) pkg_check_modules(RUBBERBAND REQUIRED rubberband) if (WIN32) # fftw adds `m` (math.h) to the dependencies for some reason message(STATUS "Rubberband's LIBRARIES before: ${RUBBERBAND_LIBRARIES}") string(REPLACE ";m;" ";" RUBBERBAND_LIBRARIES "${RUBBERBAND_LIBRARIES}") string(REGEX REPLACE ";m$" "" RUBBERBAND_LIBRARIES "${RUBBERBAND_LIBRARIES}") message(STATUS "Rubberband's LIBRARIES after: ${RUBBERBAND_LIBRARIES}") endif() ``` -------------------------------- ### Real-time Time Stretching with RubberBandStretcher Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Shows how to perform real-time time stretching using the `time_ratio` property. The time ratio can be modified during processing to change the playback speed. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array stretcher = RubberBandStretcher( sample_rate=16000, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FASTER, ) stretcher.set_max_process_size(1024) audio = create_audio_array(channels_num=1, samples_num=1024) # Start at normal speed stretcher.time_ratio = 1.0 stretcher.process(audio) out1 = stretcher.retrieve_available() # Switch to half speed stretcher.time_ratio = 2.0 stretcher.process(audio) out2 = stretcher.retrieve_available() print(f"Normal speed output: {out1.shape[1] if out1.size else 0} samples") print(f"Half speed output: {out2.shape[1] if out2.size else 0} samples") ``` -------------------------------- ### pylibrb: Reordering Audio Data Layouts Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Provides utility functions `reorder_to_rb` and `reorder_from_rb` to convert audio data between common layouts (e.g., `[samples, channels]`) and pylibrb's required `[channels, samples]` format. These functions return views, avoiding data copies unless memory contiguity issues arise during `process()`. ```python import numpy as np import pylibrb from pylibrb import RubberBandStretcher, Option, create_audio_array, reorder_to_rb, reorder_from_rb stretcher = RubberBandStretcher( sample_rate=44100, channels=2, options=Option.PROCESS_REALTIME | Option.ENGINE_FASTER, ) stretcher.set_max_process_size(1024) # Audio in [samples, channels] layout (common in many audio libraries) audio_samples_first = np.random.randn(1024, 2).astype(np.float32) # Convert to pylibrb layout: [channels, samples] rb_audio = reorder_to_rb(audio_samples_first, samples_axis=0) print(f"Original shape: {audio_samples_first.shape}") # (1024, 2) print(f"rb layout shape: {rb_audio.shape}") # (2, 1024) stretcher.process(rb_audio) out_rb = stretcher.retrieve_available() if out_rb.size: # Convert output back to [samples, channels] layout wanted_shape = [None, 2] # None marks the samples axis out_original = reorder_from_rb(out_rb, wanted_shape=wanted_shape) print(f"Output in original layout: {out_original.shape}") # (N, 2) ``` -------------------------------- ### RubberBandStretcher.process() - Real-Time Mode Source: https://context7.com/pawel-glomski/pylibrb/llms.txt In real-time mode, feed audio blocks directly to `process()` and retrieve output as it becomes available. No study pass is needed. Time ratio and pitch scale may be changed at any time between `process()` calls. ```APIDOC ## RubberBandStretcher.process(audio, final=False) - Real-Time Mode ### Description Processes an audio block in real-time mode. Audio is fed to the stretcher, and processed output is retrieved. Time ratio and pitch scale can be updated dynamically between calls. ### Method POST (conceptual, as it modifies internal state and returns data) ### Parameters - **audio** (numpy.ndarray) - The input audio block with shape `(channels, samples)`. - **final** (bool, optional) - Set to `True` to indicate this is the last block of audio. Defaults to `False`. ### Request Example ```python from pylibrb import RubberBandStretcher, Option, create_audio_array stretcher = RubberBandStretcher( sample_rate=16000, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER, initial_time_ratio=0.5, # slow down to half speed ) stretcher.set_max_process_size(1024) # Handle real-time start padding and delay pad_samples = stretcher.get_preferred_start_pad() if pad_samples > 0: silence = create_audio_array(1, pad_samples) stretcher.process(silence) start_delay = stretcher.get_start_delay() samples_to_discard = start_delay # Streaming loop for block_index in range(20): audio_in = create_audio_array(channels_num=1, samples_num=1024, init_value=0.1) # Optionally update ratios dynamically if block_index == 10: stretcher.time_ratio = 1.0 # switch back to normal speed stretcher.process(audio_in, final=(block_index == 19)) available = stretcher.available() if available > 0: audio_out = stretcher.retrieve(available) # Discard initial delay samples if samples_to_discard > 0: discard = min(samples_to_discard, audio_out.shape[1]) audio_out = audio_out[:, discard:] samples_to_discard -= discard print(f"Block {block_index}: retrieved {audio_out.shape[1]} samples") print("Done:", stretcher.is_done()) ``` ### Response #### Success Response (Implicitly returns processed audio via `retrieve()`) - The `process()` method itself does not directly return audio data. Instead, it prepares processed audio that can be retrieved using the `retrieve()` method. ### Additional Methods Used in Real-Time Mode - **`set_max_process_size(size)`**: Sets the maximum number of samples the stretcher can process at once. - **`get_preferred_start_pad()`**: Returns the number of silence samples to feed initially. - **`get_start_delay()`**: Returns the number of samples that will be delayed before output. - **`available()`**: Returns the number of processed samples ready for retrieval. - **`retrieve(num_samples)`**: Retrieves the specified number of processed audio samples. - **`is_done()`**: Returns `True` if processing is complete. ``` -------------------------------- ### Retrieving Processed Audio Samples Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Demonstrates how to retrieve processed audio samples using `retrieve(samples_num)` and `retrieve_available()`. `retrieve_available()` fetches all currently processed samples. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array import pylibrb stretcher = RubberBandStretcher( sample_rate=44100, channels=2, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER, ) stretcher.set_max_process_size(2048) audio = create_audio_array(channels_num=2, samples_num=2048, init_value=0.5) # Feed until output is available while stretcher.available() == 0: stretcher.process(audio) # Retrieve a fixed number of samples n = stretcher.available() // 2 partial = stretcher.retrieve(n) print(f"Partial retrieve: {partial.shape}") # (2, n) if stereo # Retrieve all remaining rest = stretcher.retrieve_available() print(f"Rest: {rest.shape}") # Returns empty array when nothing is available empty = stretcher.retrieve(1024) print(f"Empty: {empty.size}") # 0 ``` -------------------------------- ### Create Audio Array for pylibrb Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Create a NumPy ndarray with the correct shape and dtype for pylibrb using `create_audio_array`. This ensures layout compatibility. Shows creation of stereo and mono buffers, and confirmation of channel/sample axes. ```python import pylibrb from pylibrb import create_audio_array # Create a stereo buffer of 1024 samples, initialized to silence audio = create_audio_array(channels_num=2, samples_num=1024) print(audio.shape) # shape with channels and samples on CHANNELS_AXIS / SAMPLES_AXIS print(audio.dtype) # float32 # Create a buffer pre-filled with a specific value audio_ones = create_audio_array(channels_num=1, samples_num=512, init_value=1.0) # Confirm axes print(audio.shape[pylibrb.CHANNELS_AXIS]) # 2 print(audio.shape[pylibrb.SAMPLES_AXIS]) # 1024 ``` -------------------------------- ### Real-time Pitch Shifting with RubberBandStretcher Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Demonstrates real-time pitch shifting using the `pitch_scale` property. The pitch can be changed dynamically during processing. Requires `Option.PITCH_HIGH_CONSISTENCY` for mid-stream changes. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array import math stretcher = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER | Option.PITCH_HIGH_CONSISTENCY, ) stretcher.set_max_process_size(2048) # Shift up by 3 semitones semitones = 3 stretcher.pitch_scale = math.pow(2.0, semitones / 12.0) print(f"Pitch scale for +{semitones} semitones: {stretcher.pitch_scale:.4f}") # ~1.1892 # Process a block audio = create_audio_array(channels_num=1, samples_num=2048, init_value=0.3) stretcher.process(audio) # Dynamically change pitch mid-stream (PITCH_HIGH_CONSISTENCY required) stretcher.pitch_scale = math.pow(2.0, -5 / 12.0) # shift down 5 semitones stretcher.process(audio) print(f"New pitch scale: {stretcher.pitch_scale:.4f}") # ~0.7491 ``` -------------------------------- ### reorder_to_rb and reorder_from_rb Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Utility functions to convert audio data between arbitrary array layouts and the `[channels, samples]` layout required by pylibrb. These functions return views, avoiding data copies when possible. ```APIDOC ## reorder_to_rb(audio, samples_axis) ### Description Converts an audio array from a given layout to pylibrb's required `[channels, samples]` layout. This function returns a view of the original data, minimizing memory overhead. ### Parameters - **audio** (numpy.ndarray) - The input audio array. - **samples_axis** (int) - The axis in the input array that represents the samples. ### Returns - numpy.ndarray: A view of the audio data in `[channels, samples]` layout. ## reorder_from_rb(rb_audio, wanted_shape) ### Description Converts audio data from pylibrb's `[channels, samples]` layout back to a desired shape, typically the original layout. This function also returns a view if possible. ### Parameters - **rb_audio** (numpy.ndarray) - The input audio array in `[channels, samples]` layout. - **wanted_shape** (list) - The desired shape for the output array. Use `None` to indicate the samples axis (e.g., `[None, channels]`). ### Returns - numpy.ndarray: A view of the audio data in the specified `wanted_shape`. ### Usage Example ```python # Assuming audio_samples_first is in [samples, channels] layout rb_audio = reorder_to_rb(audio_samples_first, samples_axis=0) # Process audio with stretcher... # Convert output back to [samples, channels] layout wanted_shape = [None, 2] # None marks the samples axis out_original = reorder_from_rb(out_rb, wanted_shape=wanted_shape) ``` ``` -------------------------------- ### Link Libraries to Target Source: https://github.com/pawel-glomski/pylibrb/blob/main/src/CMakeLists.txt Links the 'pylibrb_ext' target to necessary libraries, including fmt and rubberband. Conditionally links the Accelerate framework on Apple platforms and specifies library directories and include paths. ```cmake target_link_libraries( pylibrb_ext PUBLIC fmt::fmt ${RUBBERBAND_LIBRARIES} ) if(APPLE) target_link_libraries(pylibrb_ext PRIVATE "-framework Accelerate") endif() target_link_directories( pylibrb_ext PUBLIC ${RUBBERBAND_LIBRARY_DIRS} ) target_include_directories( pylibrb_ext PUBLIC ${RUBBERBAND_INCLUDE_DIRS} ) ``` -------------------------------- ### Offline Audio Processing with RubberBandStretcher Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Illustrates offline audio processing where the entire input is first studied to build a profile, then processed to produce the stretched output. Time ratio and pitch scale are fixed during this process. ```python import numpy as np from pylibrb import RubberBandStretcher, Option, create_audio_array ``` -------------------------------- ### RubberBandStretcher.retrieve(samples_num) and retrieve_available() Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Fetches processed audio samples. `retrieve(n)` fetches up to `n` samples, while `retrieve_available()` fetches all currently available samples. Both return a NumPy ndarray. ```APIDOC ## RubberBandStretcher.retrieve(samples_num) and retrieve_available() ### Description `retrieve(n)` fetches up to `n` processed samples. `retrieve_available()` is a convenience method equivalent to `retrieve(available())`. Both return a NumPy ndarray with shape `[channels, samples]`. ### Method ```python retrieved_samples = stretcher.retrieve(samples_num) retrieved_samples = stretcher.retrieve_available() ``` ### Parameters #### retrieve(samples_num) - **samples_num** (int) - The maximum number of samples to retrieve. ### Response - **retrieved_samples** (numpy.ndarray) - A NumPy array containing the processed audio samples with shape `[channels, samples]`. Returns an empty array if no samples are available. ### Request Example ```python from pylibrb import RubberBandStretcher, Option, create_audio_array import pylibrb stretcher = RubberBandStretcher( sample_rate=44100, channels=2, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER, ) stretcher.set_max_process_size(2048) audio = create_audio_array(channels_num=2, samples_num=2048, init_value=0.5) # Feed until output is available while stretcher.available() == 0: stretcher.process(audio) # Retrieve a fixed number of samples n = stretcher.available() // 2 partial = stretcher.retrieve(n) print(f"Partial retrieve: {partial.shape}") # Retrieve all remaining rest = stretcher.retrieve_available() print(f"Rest: {rest.shape}") # Returns empty array when nothing is available empty = stretcher.retrieve(1024) print(f"Empty: {empty.size}") ``` ``` -------------------------------- ### Configure Rubber Band Stretcher with Option Flags Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Configures stretcher behavior using bitwise OR combinations of `Option` flags. Options control engine type, processing mode, transients, pitch, formant, window, and channel handling. Some options are fixed at construction, while others can be changed dynamically. ```python from pylibrb import RubberBandStretcher, Option # High-quality offline pitch shifting with formant preservation stretcher_hq = RubberBandStretcher( sample_rate=44100, channels=1, options=( Option.PROCESS_OFFLINE | Option.ENGINE_FINER | Option.FORMANT_PRESERVED | Option.WINDOW_STANDARD | Option.CHANNELS_TOGETHER ), initial_pitch_scale=1.5, ) # Low-latency real-time with percussive preset stretcher_perc = RubberBandStretcher( sample_rate=44100, channels=2, options=( Option.PROCESS_REALTIME | Option.ENGINE_FASTER | Option.PRESET_PERCUSSIVE | Option.WINDOW_SHORT ), ) # Dynamic option change at runtime stretcher_perc.set_transients_options(Option.TRANSIENTS_SMOOTH) stretcher_perc.set_formant_options(Option.FORMANT_PRESERVED) ``` -------------------------------- ### Offline Mode Processing with R2 Engine Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Demonstrates offline audio processing with the R2 engine, doubling the duration while maintaining original pitch. Requires setting expected input duration before study. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array import numpy as np stretcher = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_OFFLINE | Option.ENGINE_FASTER, initial_time_ratio=2.0, initial_pitch_scale=1.0, # keep original pitch ) # Simulate a 44100-sample (1 second) mono signal total_samples = 44100 audio_data = create_audio_array(channels_num=1, samples_num=total_samples, init_value=0.5) stretcher.set_expected_input_duration(total_samples) # --- Study pass --- block_size = 4096 for start in range(0, total_samples, block_size): end = min(start + block_size, total_samples) block = audio_data[:, start:end] is_final = (end == total_samples) stretcher.study(block, final=is_final) # --- Process pass --- output_blocks = [] for start in range(0, total_samples, block_size): end = min(start + block_size, total_samples) block = audio_data[:, start:end] is_final = (end == total_samples) stretcher.process(block, final=is_final) available = stretcher.available() if available > 0: output_blocks.append(stretcher.retrieve_available()) # Drain remaining output while not stretcher.is_done(): available = stretcher.available() if available > 0: output_blocks.append(stretcher.retrieve_available()) output = np.concatenate(output_blocks, axis=1) if output_blocks else np.array([]) print(f"Input samples: {total_samples}, Output samples: {output.shape[1]}") # Output samples: ~88200 (2x) ``` -------------------------------- ### Define Python Module Target Source: https://github.com/pawel-glomski/pylibrb/blob/main/src/CMakeLists.txt Defines the Python extension module 'pylibrb_ext' using nanobind, specifying stable ABI and static linking. It lists source and header files required for the module. ```cmake set(SOURCES_DIR "${CMAKE_SOURCE_DIR}/src") set(HEADER_LIST ) set(SOURCE_LIST "${SOURCES_DIR}/bindings.cpp" ) nanobind_add_module( pylibrb_ext STABLE_ABI NB_STATIC ${HEADER_LIST} ${SOURCE_LIST} ) ``` -------------------------------- ### create_audio_array Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Creates a NumPy ndarray with the correct shape and dtype for use with pylibrb. This is the recommended way to allocate audio buffers to ensure layout compatibility. ```APIDOC ## create_audio_array(channels_num, samples_num, init_value=0.0) ### Description Creates a NumPy ndarray with the correct shape and dtype for use with pylibrb. This is the recommended way to allocate audio buffers to ensure layout compatibility. ### Parameters - **channels_num** (int) - The number of audio channels. - **samples_num** (int) - The number of samples per channel. - **init_value** (float, optional) - The initial value to fill the array with. Defaults to 0.0. ### Request Example ```python import pylibrb from pylibrb import create_audio_array # Create a stereo buffer of 1024 samples, initialized to silence audio = create_audio_array(channels_num=2, samples_num=1024) # Create a buffer pre-filled with a specific value audio_ones = create_audio_array(channels_num=1, samples_num=512, init_value=1.0) ``` ### Response #### Success Response (numpy.ndarray) - **ndarray** - A NumPy array with the shape `(channels_num, samples_num)` and `dtype=float32`. ### Response Example ```python print(audio.shape) # shape with channels and samples on CHANNELS_AXIS / SAMPLES_AXIS print(audio.dtype) # float32 # Confirm axes print(audio.shape[pylibrb.CHANNELS_AXIS]) # 2 print(audio.shape[pylibrb.SAMPLES_AXIS]) # 1024 ``` ``` -------------------------------- ### RubberBandStretcher Constructor Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Constructs a time and pitch stretcher instance. The mode (offline vs. real-time) and engine (R2 vs. R3) must be chosen at construction time via `options` and cannot be changed afterward. Best quality is achieved at 44100 or 48000 Hz sample rates. ```APIDOC ## RubberBandStretcher(sample_rate, channels, options, initial_time_ratio, initial_pitch_scale) ### Description Constructs a time and pitch stretcher instance. The mode (offline vs. real-time) and engine (R2 vs. R3) must be chosen at construction time via `options` and cannot be changed afterward. Best quality is achieved at 44100 or 48000 Hz sample rates. ### Parameters - **sample_rate** (int) - The sample rate of the audio. - **channels** (int) - The number of audio channels. - **options** (Option) - Bitmask of options to configure the stretcher (e.g., `Option.PROCESS_REALTIME`, `Option.ENGINE_FINER`, `Option.WINDOW_SHORT`). - **initial_time_ratio** (float) - The initial time stretching ratio. - **initial_pitch_scale** (float) - The initial pitch scaling factor. ### Request Example ```python from pylibrb import RubberBandStretcher, Option # R3 engine, real-time mode, short window (low latency) stretcher = RubberBandStretcher( sample_rate=44100, channels=2, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER | Option.WINDOW_SHORT, initial_time_ratio=1.0, initial_pitch_scale=1.0, ) ``` ### Response #### Success Response (Instance of RubberBandStretcher) - **channels** (int) - The number of channels. - **engine_version** (int) - The version of the engine used (2 or 3). - **time_ratio** (float) - The current time stretching ratio. - **pitch_scale** (float) - The current pitch scaling factor. - **is_done()** (bool) - Returns True if the processing is complete. ### Response Example ```python print(stretcher.channels) # 2 print(stretcher.engine_version) # 3 print(stretcher.time_ratio) # 1.0 print(stretcher.pitch_scale) # 1.0 print(stretcher.is_done()) # False ``` ``` -------------------------------- ### RubberBandStretcher.study() and process() - Offline Mode Source: https://context7.com/pawel-glomski/pylibrb/llms.txt In offline mode, the entire input is passed through `study()` first to build a stretch profile, then through `process()` to produce the stretched output. Time ratio and pitch scale are fixed once `study()` begins. ```APIDOC ## RubberBandStretcher.study() and RubberBandStretcher.process() - Offline Mode ### Description Processes audio in offline mode. First, `study()` analyzes the entire input audio to build a processing profile. Then, `process()` generates the time-stretched and pitch-shifted output. Time ratio and pitch scale are fixed during the `study()` phase. ### Method POST (conceptual for `study()` and `process()`) ### Parameters for `study()` - **audio** (numpy.ndarray) - The entire input audio block with shape `(channels, samples)`. ### Parameters for `process()` (Offline Mode) - **audio** (numpy.ndarray) - The input audio block. In offline mode, this is typically called after `study()` has been completed on the entire dataset. The `final` parameter behaves similarly to real-time mode. - **final** (bool, optional) - Set to `True` to indicate this is the last block of audio. Defaults to `False`. ### Request Example (Conceptual - requires full audio input for study) ```python import numpy as np from pylibrb import RubberBandStretcher, Option, create_audio_array # Assume 'full_audio_input' is a numpy array containing the entire audio file # stretcher = RubberBandStretcher(sample_rate=..., channels=..., options=Option.ENGINE_FINER, ...) # Step 1: Study the entire audio input # stretcher.study(full_audio_input) # Step 2: Process the audio to get stretched output # This would typically involve iterating through blocks of the audio # For simplicity, showing a single process call assuming study is done # output_audio = stretcher.process(full_audio_input, final=True) # In a real scenario, you'd process in chunks after study # For example: # processed_blocks = [] # for audio_chunk in chunk_iterator(full_audio_input): # processed_chunk = stretcher.process(audio_chunk) # processed_blocks.append(processed_chunk) # final_output = np.concatenate(processed_blocks, axis=1) ``` ### Response #### Success Response (numpy.ndarray) - **ndarray** - The processed audio block with the time-stretched and/or pitch-shifted audio data. The shape will be `(channels, samples)`, where `samples` may differ from the input due to stretching. ### Note on Offline Mode - The `study()` method must be called with the complete audio data before `process()` can yield meaningful results for the entire piece. - Time ratio and pitch scale are set at construction and cannot be changed after `study()` is called. ``` -------------------------------- ### RubberBandStretcher: Offline Variable Stretching with Keyframes Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Demonstrates sample-accurate control over the stretch profile in offline mode using `set_keyframe_map`. This method maps source sample positions to target sample positions, ensuring precise alignment of critical points like bar boundaries. The keyframe map must be set before the first `process()` call. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array stretcher = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_OFFLINE | Option.ENGINE_FASTER, initial_time_ratio=2.0, ) total_samples = 44100 audio = create_audio_array(channels_num=1, samples_num=total_samples) # Study pass stretcher.study(audio, final=True) # Map key anchor points: source_sample -> target_sample # Ensures bar boundaries align exactly after stretching keyframe_map = { 0: 0, 11025: 22050, # 0.25s in -> 0.5s out 22050: 44100, # 0.5s in -> 1.0s out 44100: 88200, # 1.0s in -> 2.0s out } stretcher.set_keyframe_map(keyframe_map) # Process pass output = [] block_size = 4096 for start in range(0, total_samples, block_size): end = min(start + block_size, total_samples) stretcher.process(audio[:, start:end], final=(end == total_samples)) out = stretcher.retrieve_available() if out.size: output.append(out) print(f"Output blocks: {len(output)}") ``` -------------------------------- ### Platform-Specific Compile Definitions Source: https://github.com/pawel-glomski/pylibrb/blob/main/src/CMakeLists.txt Adds the NOMINMAX compile definition to the 'pylibrb_ext' target for Windows platforms to prevent potential macro conflicts. ```cmake if(WIN32) target_compile_definitions(pylibrb_ext PRIVATE NOMINMAX) endif() ``` -------------------------------- ### Reorder Audio Array to Rubber Band Layout Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Converts a 4D audio array (batch, channels, time, features) to the Rubber Band layout. Useful for integrating with other audio libraries that use non-standard array layouts. ```python from pylibrb import create_audio_array, reorder_to_rb, reorder_from_rb audio_4d = create_audio_array(1, 2 * 4 * 8 * 4).reshape(2, 4, 8, 4) rb_4d = reorder_to_rb(audio_4d, samples_axis=2) # axis 2 has 8 samples print(f"4D -> rb: {rb_4d.shape}") # (32, 8) - all other dims collapsed into channels # Round-trip back to original shape restored = reorder_from_rb(rb_4d, wanted_shape=[2, 4, None, 4]) print(f"Restored shape: {restored.shape}") # (2, 4, 8, 4) ``` -------------------------------- ### RubberBandStretcher.set_keyframe_map(mapping) Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Enables sample-accurate control over the stretch profile in offline mode by mapping source sample positions to target sample positions. This must be called before the first `process()` call. ```APIDOC ## RubberBandStretcher.set_keyframe_map(mapping) ### Description Provides sample-accurate control over the audio stretching process in offline mode. It allows users to define specific source sample positions and their corresponding target sample positions, ensuring precise alignment of critical points like bar boundaries after stretching. ### Method `set_keyframe_map` ### Parameters - **mapping** (dict) - A dictionary where keys are source sample indices and values are the corresponding target sample indices. ### Usage ```python # Define keyframe points for precise stretching keyframe_map = { 0: 0, 11025: 22050, # 0.25s in -> 0.5s out 22050: 44100, # 0.5s in -> 1.0s out 44100: 88200, # 1.0s in -> 2.0s out } stretcher.set_keyframe_map(keyframe_map) ``` ### Notes - Must be called before the first `process()` call. - Primarily used with `Option.PROCESS_OFFLINE`. ``` -------------------------------- ### Set Logging Level for Rubber Band Stretcher Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Controls debug output verbosity for Rubber Band stretchers. Use `set_default_logging_level` for all future instances or `stretcher.set_logging_level` to override for a specific instance. Level 0 is errors only, higher levels increase verbosity. ```python import pylibrb from pylibrb import RubberBandStretcher, Option, set_default_logging_level # Set default for all future stretchers set_default_logging_level(1) # log construction info stretcher = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FASTER, ) # Override for this specific instance stretcher.set_logging_level(0) # silence this stretcher ``` -------------------------------- ### RubberBandStretcher: Formant Scaling Control Source: https://context7.com/pawel-glomski/pylibrb/llms.txt Illustrates how to control independent formant scaling using the `formant_scale` property. This is useful for special effects where formants need to be shifted separately from pitch. The R3 engine (`Option.ENGINE_FINER`) is required for this feature. The default behavior is automatic formant compensation. ```python from pylibrb import RubberBandStretcher, Option, create_audio_array, AUTO_FORMANT_SCALE import math stretcher = RubberBandStretcher( sample_rate=44100, channels=1, options=Option.PROCESS_REALTIME | Option.ENGINE_FINER | Option.FORMANT_PRESERVED, initial_pitch_scale=math.pow(2.0, 4 / 12.0), # +4 semitones ) # Default: formant is automatically compensated print(stretcher.formant_scale == AUTO_FORMANT_SCALE) # True (AUTO_FORMANT_SCALE value) # Override: shift formant independently (special effects only) stretcher.formant_scale = 1.0 # keep formant unchanged while pitch shifts audio = create_audio_array(channels_num=1, samples_num=1024) stretcher.process(audio) # Reset to automatic stretcher.formant_scale = AUTO_FORMANT_SCALE ``` -------------------------------- ### Option Flags for RubberBandStretcher Source: https://context7.com/pawel-glomski/pylibrb/llms.txt The `Option` enum provides bitwise flags to configure various aspects of the RubberBandStretcher's behavior, including engine type, processing mode, transient handling, pitch, formant, window, and channel processing. ```APIDOC ## Option Flags ### Description The `Option` enum controls all aspects of stretcher behaviour via bitwise OR combination. Key groups include Engine, Mode, Presets, Transients, Pitch, Formant, Window, and Channels. ### Groups and Options * **Engine**: `ENGINE_FASTER` (R2), `ENGINE_FINER` (R3) - Fixed at construction. * **Mode**: `PROCESS_OFFLINE`, `PROCESS_REALTIME` - Fixed at construction. * **Presets**: `PRESET_DEFAULT`, `PRESET_PERCUSSIVE` - Convenience combinations. * **Transients**: `TRANSIENTS_CRISP`, `TRANSIENTS_MIXED`, `TRANSIENTS_SMOOTH` - R2 only; changeable in RT mode. * **Pitch**: `PITCH_HIGH_SPEED`, `PITCH_HIGH_QUALITY`, `PITCH_HIGH_CONSISTENCY` - RT mode only; R2 only (except construction). * **Formant**: `FORMANT_SHIFTED`, `FORMANT_PRESERVED` - Both engines; changeable anytime. * **Window**: `WINDOW_STANDARD`, `WINDOW_SHORT`, `WINDOW_LONG` - Fixed at construction. * **Channels**: `CHANNELS_APART`, `CHANNELS_TOGETHER` - Fixed at construction. ### Request Example ```python from pylibrb import RubberBandStretcher, Option # High-quality offline pitch shifting with formant preservation stretcher_hq = RubberBandStretcher( sample_rate=44100, channels=1, options=( Option.PROCESS_OFFLINE | Option.ENGINE_FINER | Option.FORMANT_PRESERVED | Option.WINDOW_STANDARD | Option.CHANNELS_TOGETHER ), initial_pitch_scale=1.5, ) # Low-latency real-time with percussive preset stretcher_perc = RubberBandStretcher( sample_rate=44100, channels=2, options=( Option.PROCESS_REALTIME | Option.ENGINE_FASTER | Option.PRESET_PERCUSSIVE | Option.WINDOW_SHORT ), ) # Dynamic option change at runtime stretcher_perc.set_transients_options(Option.TRANSIENTS_SMOOTH) stretcher_perc.set_formant_options(Option.FORMANT_PRESERVED) ``` ```