Una biblioteca de conversión de frecuencia de muestreo de audio para Rust.
Esta biblioteca proporciona resamplers para procesar audio en fragmentos.
La relación entre la entrada y las velocidades de muestra de salida es completamente gratuita. Las implementaciones están disponibles que aceptan una entrada de longitud fija al devolver una salida de longitud variable, y viceversa.
El rubato se puede usar en aplicaciones en tiempo real sin ninguna asignación durante el procesamiento preallocando un [resampler] y utilizando sus métodos input_buffer_allocate y output_buffer_allocate antes de comenzar a procesar. La función de registro de la función debe desactivarse para uso en tiempo real (está deshabilitado de forma predeterminada).
Los datos de entrada y salida se almacenan en un formato no interesado.
Los datos de entrada y salida se almacenan como cortes de referencias, &[AsRef<[f32]>]
o &[AsRef<[f64]>]
. Las referencias internas ( AsRef<[f32]>
o AsRef<[f64]>
) mantienen los valores de muestra para un canal cada uno.
Dado que los vectores normales implementan el rasgo AsRef
, se puede utilizar Vec<Vec<f32>>
y Vec<Vec<f64>>
tanto para la entrada como para la salida.
Los resamplers asíncronos están disponibles con y sin filtros antialiasing.
El remuestreo con anti-aliasing se basa en la interpolación limitada de banda utilizando filtros de interpolación SINC. La interpolación de SINC resalta por un factor ajustable, y luego los nuevos puntos de muestra se calculan interpolando entre estos puntos. La relación de remuestreo se puede actualizar en cualquier momento.
El remuestreo sin anti-alias omite la interpolación SINC-Hagavy CPU. Esto funciona mucho más rápido pero produce un resultado de menor calidad.
El remuestreo sincrónico se implementa a través de FFT. Los datos son FFT: ED, el espectro modificado y luego FFT inverso: ED para obtener los datos de muestreo. Este tipo de resampler es considerablemente más rápido, pero no admite cambiar la relación de remuestreo.
Los remuestreros proporcionados por esta biblioteca están destinados a procesar audio en fragmentos. La aplicación determina el tamaño óptimo de la fragmentación, pero probablemente terminará entre unos pocos cientos y unos pocos miles de cuadros. Esto brinda un buen compromiso entre eficiencia y uso de la memoria.
Rubato es adecuado para aplicaciones en tiempo real cuando se usa el método Resampler::process_into_buffer()
. Esto almacena la salida en un búfer de salida pre-asignado y no realiza asignaciones u otras operaciones que puedan bloquear el hilo.
Un proceso simple sugerido para volver a muestrear un clip de audio de longitud conocida a una nueva frecuencia de muestreo es el siguiente. Aquí se supone que los datos de origen se almacenan en un VEC, o en alguna otra estructura que admite la lectura de un número arbitrario de cuadros a la vez. Para simplificar, la salida se almacena en un búfer temporal durante el remuestreo y se copia al destino después.
Preparativos:
Resampler::output_delay()
para saber cuántos marcos de retraso da el resmpler. Almacene el número como delay
.new_length = original_length * new_rate / original_rate
.Ahora es el momento de procesar la mayor parte del clip mediante las repetidas llamadas de procedimiento. Bucle:
Resampler::input_frames_next()
para aprender cuántos marcos necesita el resampler.Resampler::process()
o Resampler::process_into_buffer()
.El siguiente paso es procesar los últimos cuadros restantes.
Resampler::process_partial()
o Resampler::process_partial_into_buffer()
. En este punto, todos los cuadros se han enviado al resampler, pero debido al retraso a través del resumen, aún puede tener algunos cuadros en sus buffers internos. Cuando se han generado todos los marcos buscados, la longitud del búfer de salida temporal debe ser al menos new_length + delay
. Si este no es el caso, llame a Resampler::process_partial()
o Resampler::process_partial_into_buffer()
con None
como entrada, y agregue la salida al búfer de salida temporal. Si es necesario, repita hasta que la longitud sea suficiente.
Finalmente, copie los datos del búfer de salida temporal al destino deseado. Omita los primeros marcos delay
y copie new_length
Frames.
Si hay más de un clip para volver a muestrear desde y a las mismas velocidades de muestra, se debe reutilizar el mismo resampler. Crear un nuevo resamper es una tarea costosa y debe evitarse si es posible. Comience el procedimiento desde el principio, pero en lugar de crear un nuevo resampler, llame a Resampler::reset()
en el existente para prepararlo para un nuevo trabajo.
Al rigurar una secuencia, el proceso normalmente se realiza en tiempo real, y la entrada de salida es una API que proporciona o consume marcos a una velocidad determinada.
Las API de audio, como Coreaudio en MacOS, o la Crate CPAL de la plataforma cruzada, a menudo utilizan funciones de devolución de llamada para el intercambio de datos.
Un completo
Al capturar audio de estos, la aplicación pasa una función a la API de audio. La API luego llama a esta función periódicamente, con un puntero a un búfer de datos que contiene nuevos marcos de audio. El tamaño del búfer de datos suele ser el mismo en cada llamada, pero eso varía entre las API. Es importante que la función no bloquee, ya que esto bloquearía algún bucle interno de la API y causaría la pérdida de algunos datos de audio. Se recomienda mantener la función de la función de devolución de llamada. Idealmente, debe leer los datos de audio proporcionados del búfer proporcionado por la API, y opcionalmente realizar un procesamiento de luz, como la conversión de formato de muestra. No se debe realizar un procesamiento pesado como el remuestreo aquí. Luego debe almacenar los datos de audio en un búfer compartido. El búfer puede ser un Arc<Mutex<VecDeque<T>>>
, o algo más avanzado como RingBuf.
Un bucle separado, que se ejecuta en el hilo principal o separado, debe leer desde ese búfer, volver a muestrear y guardar en el archivo. Si la API de audio proporciona un tamaño de búfer fijo, entonces este número de cuadros es una buena opción para el tamaño de la fragmentos de resumen. Si el tamaño varía, el búfer compartido se puede usar para adaptar los tamaños de fragmentos de la API de audio y el resumidor. Un buen punto de partida para el tamaño de la fragmentos de resumen es usar un valor "fácil" cerca del tamaño promedio de la fragmentación de la API de audio. Asegúrese de que el búfer compartido sea lo suficientemente grande como para no completarse en caso de que el bucle se bloquee esperando, por ejemplo, para el acceso al disco.
El bucle debe seguir un proceso similar al remuestreo de un clip, pero la entrada ahora es el búfer compartido. El bucle debe esperar a que el número necesario de cuadros esté disponible en el búfer, antes de leerlos y pasarlos al resampler.
También sería apropiado omitir el búfer de salida temporal y escribir la salida directamente al destino. The Hound Crate es una opción popular para leer y escribir formatos de audio sin comprimir.
El resampler asincrónico admite SIMD en x86_64 y en AARCH64. Las capacidades SIMD de la CPU se determinan en tiempo de ejecución. Si no hay un conjunto de instrucciones SIMD compatible disponible, recae en una implementación escalar.
En x86_64, intentará usar AVX. Si AVX no está disponible, en su lugar intentará SSE3.
En AARCH64 (brazo de 64 bits), usará neón si está disponible.
Los resamplers sincrónicos se benefician del soporte SIMD de la biblioteca Rustfft.
fft_resampler
: Habilite los resamplers sincrónicos basados en FFTEsta función está habilitada de forma predeterminada. Deshabilíelo si no se necesitan resamplers FFT, para ahorrar tiempo de compilación y reducir el tamaño binario resultante.
log
: habilitar el registro Esta característica habilita el registro a través de la caja log
. Esto está destinado a fines de depuración. Tenga en cuenta que la salida de registros asigna un [std :: string :: string] y la mayoría de las implementaciones de registro implican varias otras llamadas del sistema. Estas llamadas pueden tomar un tiempo (impredecible) para devolver, durante el cual se bloquea la aplicación. Esto significa que se debe evitar el registro si usa esta biblioteca en una aplicación en tiempo real.
La función log
se puede habilitar al ejecutar pruebas, lo que puede ser muy útil al depurar. El nivel de registro se puede establecer a través de la variable de entorno RUST_LOG
.
Ejemplo:
RUST_LOG=trace cargo test --features log
Vuelva a muestrear una sola parte de un archivo de audio ficticio de 44100 a 48000 Hz. Consulte también el ejemplo "Process_f64" que se puede usar para procesar un archivo desde el disco.
use rubato :: { Resampler , SincFixedIn , SincInterpolationType , SincInterpolationParameters , WindowFunction } ;
let params = SincInterpolationParameters {
sinc_len : 256 ,
f_cutoff : 0.95 ,
interpolation : SincInterpolationType :: Linear ,
oversampling_factor : 256 ,
window : WindowFunction :: BlackmanHarris2 ,
} ;
let mut resampler = SincFixedIn :: < f64 > :: new (
48000 as f64 / 44100 as f64 ,
2.0 ,
params ,
1024 ,
2 ,
) . unwrap ( ) ;
let waves_in = vec ! [ vec! [ 0.0f64 ; 1024 ] ; 2 ] ;
let waves_out = resampler . process ( & waves_in , None ) . unwrap ( ) ;
El directorio examples
contiene algunas aplicaciones de muestra para probar los resamplers. También hay scripts de Python para generar señales de prueba simples, así como para analizar los resultados muestreados.
Los ejemplos leen y escriben datos de audio sin procesar en formato flotante de 64 bits. Se pueden usar para procesar archivos .WAV si los archivos se convierten primero en el formato correcto. Use sox
para convertir un .wav a muestras sin procesar:
sox some_file.wav -e floating-point -b 64 some_file_f64.raw
Después del procesamiento, el resultado se puede convertir en nuevo .wav. Estos ejemplos se convierten en 16 bits a 44.1 kHz:
sox -e floating-point -b 64 -r 44100 -c 2 resampler_output.raw -e signed-integer -b 16 some_file_resampled.wav
Muchos editores de audio, por ejemplo, Audacity, también pueden importar y exportar directamente las muestras sin procesar.
La caja rubato
requiere Rustc versión 1.61 o más nueva.
fft_resampler
.log
.input/output_buffer_allocate()
opcionalmente se llene de buffers con ceros.Licencia: MIT