Une bibliothèque de conversion de la fréquence d'échantillonnage audio pour la rouille.
Cette bibliothèque fournit des réampleurs pour traiter l'audio en morceaux.
Le rapport entre les taux d'échantillonnage d'entrée et de sortie est complètement gratuit. Des implémentations sont disponibles qui acceptent une entrée de longueur fixe lors du retour d'une sortie de longueur variable, et vice versa.
Rubato peut être utilisé dans des applications en temps réel sans aucune allocation pendant le traitement en préalant un [Resampleur] et en utilisant ses méthodes d'entrée_buffer_allocate et output_buffer_allocate avant de commencer le traitement. La fonction de fonction de journal doit être désactivée pour une utilisation en temps réel (elle est désactivée par défaut).
Les données d'entrée et de sortie sont stockées dans un format non interliné.
Les données d'entrée et de sortie sont stockées sous forme de tranches de références, &[AsRef<[f32]>]
ou &[AsRef<[f64]>]
. Les références intérieures ( AsRef<[f32]>
ou AsRef<[f64]>
) maintiennent chacune les valeurs d'échantillon pour un canal.
Étant donné que les vecteurs normaux implémentent le trait AsRef
, Vec<Vec<f32>>
et Vec<Vec<f64>>
peuvent être utilisés pour l'entrée et la sortie.
Les échantillonneurs asynchrones sont disponibles avec et sans filtres anti-aliasing.
Le rééchantillonnage avec l'anti-aliasing est basé sur une interpolation limitée par bande à l'aide de filtres d'interpolation SINC. L'interpolation SINC augmente les échantillons par un facteur réglable, puis les nouveaux points d'échantillonnage sont calculés en interpolant entre ces points. Le taux de rééchantillonnage peut être mis à jour à tout moment.
Le rééchantillonnage sans anti-aliasing omet l'interpolation SINC lourde du CPU. Cela fonctionne beaucoup plus rapidement mais produit un résultat de qualité inférieure.
Le rééchantillonnage synchrone est implémenté via FFT. Les données sont FFT: ED, le spectre modifié, puis FFT inverse: ED pour obtenir les données rééchantillonnées. Ce type de rééampleur est considérablement plus rapide mais ne prend pas en charge la modification du rapport de rééchantillonnage.
Les rééchantillonneurs fournis par cette bibliothèque sont destinés à traiter l'audio en morceaux. La taille optimale du morceau est déterminée par l'application, mais finira probablement par quelque chose entre quelques centaines à quelques milliers de trames. Cela donne un bon compromis entre l'efficacité et l'utilisation de la mémoire.
Rubato convient aux applications en temps réel lors de l'utilisation de la méthode Resampler::process_into_buffer()
. Cela stocke la sortie dans un tampon de sortie pré-alloué et n'effectue aucune allocation ou d'autres opérations qui peuvent bloquer le thread.
Un processus simple suggéré pour rééchantillonner un clip audio de la longueur connue à une nouvelle fréquence d'échantillonnage est le suivant. Ici, on suppose que les données source sont stockées dans un VEC, ou une autre structure qui prend en charge la lecture du nombre arbitraire de trames à la fois. Pour plus de simplicité, la sortie est stockée dans un tampon temporaire pendant le rééchantillonnage et a ensuite copié à la destination.
Préparations:
Resampler::output_delay()
pour savoir combien de trames de retard du Resampleur. Stockez le nombre comme delay
.new_length = original_length * new_rate / original_rate
.Il est maintenant temps de traiter la majeure partie du clip par des appels de procession répétés. Boucle:
Resampler::input_frames_next()
pour savoir combien de cadres a besoin de rééampleur.Resampler::process()
ou Resampler::process_into_buffer()
.L'étape suivante consiste à traiter les derniers trames restantes.
Resampler::process_partial()
ou Resampler::process_partial_into_buffer()
. À ce stade, toutes les cadres ont été envoyés au rééchantillon, mais en raison du retard à travers le réampleur, il peut encore avoir quelques cadres dans ses tampons internes. Lorsque toutes les trames recherchées ont été générées, la longueur du tampon de sortie temporaire doit être au moins new_length + delay
. Si ce n'est pas le cas, appelez Resampler::process_partial()
ou Resampler::process_partial_into_buffer()
avec None
comme entrée et ajoutez la sortie dans le tampon de sortie temporaire. Si nécessaire, répétez jusqu'à ce que la longueur soit suffisante.
Enfin, copiez les données du tampon de sortie temporaire vers la destination souhaitée. Ignorez les premiers trames delay
et copiez les cadres new_length
.
S'il y a plus d'un clip à rééchantillonner de et aux mêmes taux d'échantillonnage, le même rééchantillon doit être réutilisé. La création d'un nouveau rééchantillon est une tâche coûteuse et doit être évitée si possible. Commencez le processeur dès le début, mais au lieu de créer un nouveau rééchantillon, appelez Resampler::reset()
sur celui existant pour le préparer à un nouvel emploi.
Lors de la réévolution d'un flux, le processus est normalement effectué en temps réel, et soit l'entrée de sortie est une API qui fournit ou consomme des trames à un taux donné.
Les API audio telles que Coreaudio sur MacOS ou la caisse multiplateforme CPAL utilisent souvent des fonctions de rappel pour l'échange de données.
Un complet
Lorsque vous capturez l'audio, l'application transmet une fonction à l'API audio. L'API appelle ensuite cette fonction périodiquement, avec un pointeur vers un tampon de données contenant de nouvelles cadres audio. La taille du tampon de données est généralement la même sur chaque appel, mais cela varie entre les API. Il est important que la fonction ne bloque pas, car cela bloquerait une boucle interne de l'API et entraînerait la perte de certaines données audio. Il est recommandé de maintenir la lumière de la fonction de rappel. Idéalement, il doit lire les données audio fournies à partir du tampon fourni par l'API et effectuer éventuellement un traitement de la lumière tel que la conversion de format d'échantillon. Aucun traitement lourd tel que le rééchantillonnage ne doit être effectué ici. Il devrait ensuite stocker les données audio dans un tampon partagé. Le tampon peut être un Arc<Mutex<VecDeque<T>>>
, ou quelque chose de plus avancé comme RingBuf.
Une boucle séparée, en cours d'exécution dans le fil principal ou un thread séparé, doit ensuite lire à partir de ce tampon, rééchantillonner et enregistrer dans le fichier. Si l'API audio fournit une taille de tampon fixe, ce nombre de trames est un bon choix pour la taille du morceau de rééampleur. Si la taille varie, le tampon partagé peut être utilisé pour adapter les tailles de morceaux de l'API audio et du réampleur. Un bon point de départ pour la taille du morceau de rééampleur consiste à utiliser une valeur "facile" près de la taille moyenne de la partie de l'API audio. Assurez-vous que le tampon partagé est suffisamment grand pour ne pas être plein au cas où la boucle serait bloquée en attendant pour l'exemple pour l'accès au disque.
La boucle doit suivre un processus similaire au rééchantillonnage d'un clip, mais l'entrée est désormais le tampon partagé. La boucle doit attendre que le nombre nécessaire de trames soit disponible dans le tampon, avant de les lire et de les transmettre au rééampleur.
Il serait également approprié d'omettre le tampon de sortie temporaire et d'écrire la sortie directement sur la destination. La caisse de chasse est un choix populaire pour lire et écrire des formats audio non compressés.
Le rééampleur asynchrone prend en charge SIMD sur x86_64 et sur Aarch64. Les capacités SIMD du CPU sont déterminées au moment de l'exécution. Si aucun ensemble d'instructions SIMD pris en charge n'est disponible, il retombe à une implémentation scalaire.
Sur x86_64, il essaiera d'utiliser AVX. Si AVX n'est pas disponible, il essaiera plutôt SSE3.
Sur Aarch64 (bras 64 bits), il utilisera le néon si disponible.
Les échantillonneurs synchrones bénéficient de la prise en charge SIMD de la bibliothèque Rustfft.
fft_resampler
: Activez les rééchantillons synchrones basés sur FFTCette fonction est activée par défaut. Désactivez-le si les échantillonneurs FFT ne sont pas nécessaires, pour gagner du temps de compilation et réduire la taille binaire résultante.
log
: activer la journalisation Cette fonctionnalité permet la journalisation via la caisse log
. Ceci est destiné à des fins de débogage. Notez que la sortie des journaux alloue un [std :: string :: string] et que la plupart des implémentations de journalisation impliquent divers autres appels système. Ces appels peuvent prendre un temps (imprévisible) pour revenir, pendant lequel la demande est bloquée. Cela signifie que la journalisation doit être évitée si vous utilisez cette bibliothèque dans une application en temps réel.
La fonction log
peut être activée lors de l'exécution de tests, ce qui peut être très utile lors du débogage. Le niveau de journalisation peut être défini via la variable d'environnement RUST_LOG
.
Exemple:
RUST_LOG=trace cargo test --features log
Rééchantillonner un seul morceau d'un fichier audio factice de 44100 à 48000 Hz. Voir également l'exemple "process_f64" qui peut être utilisé pour traiter un fichier à partir du disque.
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 ( ) ;
Le répertoire examples
contient quelques échantillons d'applications pour tester les échantillonneurs. Il existe également des scripts Python pour générer des signaux de test simples ainsi que l'analyse des résultats rééchantillants.
Les exemples lisent et écrivent des données audio brutes au format flottant 64 bits. Ils peuvent être utilisés pour traiter les fichiers .wav si les fichiers sont d'abord convertis au bon format. Utilisez sox
pour convertir un .wav en échantillons bruts:
sox some_file.wav -e floating-point -b 64 some_file_f64.raw
Après le traitement, le résultat peut être reconverti en nouveau .wav. Ces exemples se transforment en 16 bits à 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
De nombreux éditeurs audio, par exemple Audacity, sont également en mesure d'importer et d'exporter directement les échantillons bruts.
La caisse rubato
nécessite Rustc version 1.61 ou plus récent.
fft_resampler
.log
.input/output_buffer_allocate()
éventuellement pré-remplir les tampons avec zéros.Licence: MIT