Skip to content

LedFx/python-samplerate-ledfx

 
 

Repository files navigation

python-samplerate-ledfx

Note: This is a fork of the original python-samplerate maintained by the LedFx team.

Why this fork exists:

  • The original python-samplerate project is sporadically active
  • We need Python 3.14 support with pre-built wheels on PyPI
  • We require the latest fixes and improvements from the main branch of python-samplerate
  • LedFx depends on python-samplerate and needs a reliable, up-to-date release

All credit for python-samplerate goes to the original authors. This fork exists solely to provide maintained releases for projects that depend on python-samplerate.

Original project: https://github.com/tuxu/python-samplerate
This fork: https://github.com/LedFx/python-samplerate-ledfx imageimageimageimageDocumentation Status

This is a wrapper around Erik de Castro Lopo's libsamplerate (aka Secret Rabbit Code) for high-quality sample rate conversion.

It implements all three APIs available in libsamplerate:

  • Simple API: for resampling a large chunk of data with a single library call
  • Full API: for obtaining the resampled signal from successive chunks of data
  • Callback API: like Full API, but input samples are provided by a callback function

The libsamplerate library is statically built together with the python bindings using pybind11.

Installation

$ pip install samplerate-ledfx

Binary wheels of samplerate-ledfx are available. A C++ 14 or above compiler is required to build the package.

Usage

import numpy as np
import samplerate

# Synthesize data
fs = 1000.
t = np.arange(fs * 2) / fs
input_data = np.sin(2 * np.pi * 5 * t)

# Simple API
ratio = 1.5
converter = 'sinc_best'  # or 'sinc_fastest', ...
output_data_simple = samplerate.resample(input_data, ratio, converter)

# Full API
resampler = samplerate.Resampler(converter, channels=1)
output_data_full = resampler.process(input_data, ratio, end_of_input=True)

# The result is the same for both APIs.
assert np.allclose(output_data_simple, output_data_full)

# See `samplerate.CallbackResampler` for the Callback API, or
# `examples/play_modulation.py` for an example.

# Callback API Example
def producer():
    # Generate data in chunks
    for i in range(10):
        yield np.random.uniform(-1, 1, 1024).astype(np.float32)
    yield None # Signal end of stream

data_iter = producer()
callback = lambda: next(data_iter)

resampler = samplerate.CallbackResampler(callback, ratio, converter)
output_chunks = []
while True:
    # Read chunks of resampled data
    chunk = resampler.read(512) 
    if chunk.shape[0] == 0:
        break
    output_chunks.append(chunk)

Performance Tips

To get the maximum performance from samplerate:

  1. Use np.float32: The underlying libsamplerate library operates on 32-bit floats. Passing np.float64 (default numpy float) or integer arrays triggers an implicit copy and cast, which can be expensive.
    # Fast (no copy)
    data = np.zeros(1000, dtype=np.float32)
    samplerate.resample(data, 1.5)
    
    # Slower (implicit copy + cast)
    data = np.zeros(1000, dtype=np.float64) 
    samplerate.resample(data, 1.5)
  2. Use C-Contiguous Arrays: Ensure your input arrays are C-contiguous (row-major). Non-contiguous arrays (e.g., column slices) will also trigger a copy.
  3. Adjust GIL Threshold: If you are processing many small chunks in a multi-threaded application, the default "auto" GIL release threshold (1000 frames) might be too high or too low. You can tune it:
    # Release GIL even for small chunks (e.g. > 100 frames)
    samplerate.set_gil_release_threshold(100)

Multi-threading and GIL Control

All resampling methods support a release_gil parameter that controls Python's Global Interpreter Lock (GIL) during resampling operations. This is useful for optimizing performance in different scenarios:

import samplerate

# Default: "auto" mode - releases GIL only for large data (>= 1000 frames)
# Balances single-threaded performance with multi-threading capability
# The threshold is configurable: samplerate.set_gil_release_threshold(2000)
output = samplerate.resample(input_data, ratio)

# Force GIL release - best for multi-threaded applications
# Allows other Python threads to run during resampling
output = samplerate.resample(input_data, ratio, release_gil=True)

# Disable GIL release - best for single-threaded applications with small data
# Avoids the ~1-5µs overhead of GIL release/acquire
output = samplerate.resample(input_data, ratio, release_gil=False)

The same parameter is available on Resampler.process() and CallbackResampler.read():

resampler = samplerate.Resampler('sinc_best', channels=1)
output = resampler.process(input_data, ratio, release_gil=True)

See also

  • scikits.samplerate implements only the Simple API and uses Cython for extern calls. The resample function of scikits.samplerate and this package share the same function signature for compatiblity.
  • resampy: sample rate conversion in Python + Cython.

License

This project is licensed under the MIT license.

As of version 0.1.9, libsamplerate is licensed under the 2-clause BSD license.

About

Python bindings for libsamplerate based on CFFI and NumPy

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 63.1%
  • C++ 34.5%
  • CMake 2.4%