Effectuer des captures audio
Effectuer une capture dans un buffer
Le destin le plus probable pour des données audio capturées, est d'être sauvegardées dans un buffer (sf::SoundBuffer
) de sorte
qu'elles puissent être jouées ou bien sauvegardées dans un fichier.
Ce type de capture peut être effectué via l'interface très simple de la classe sf::SoundBufferRecorder
:
// tout d'abord on vérifie si la capture est supportée par le système
if (!SoundBufferRecorder::isAvailable())
{
// erreur : la capture audio n'est pas supportée
...
}
// création de l'enregistreur
SoundBufferRecorder recorder;
// démarrage de l'enregistrement
recorder.start();
// on attend...
// fin de l'enregistrement
recorder.stop();
// récupération du buffer qui contient les données audio enregistrées
const sf::SoundBuffer& buffer = recorder.getBuffer();
La fonction statique SoundBufferRecorder::isAvailable
vérifie si la capture audio est supportée par le système. Si elle renvoie
false
, vous ne pourrez pas utiliser la classe sf::SoundBufferRecorder
.
Les fonctions start
et stop
sont assez explicites. La capture tourne dans son propre thread, ce qui signifie que vous
pouvez faire tout ce qui vous chante entre le début et la fin de l'enregistrement. Après la fin de la capture, les données audio sont disponibles
dans un buffer que vous pouvez récupérer avec la fonction getBuffer
.
Avec les données enregistrées, vous pouvez ensuite :
- Les sauvegarder dans un fichier
buffer.saveToFile("my_record.ogg");
- Les jouer directement
sf::Sound sound(buffer); sound.play();
-
Accéder aux données brutes et les analyser, transformer, etc.
const sf::Int16* samples = buffer.getSamples(); std::size_t count = buffer.getSampleCount(); doSomething(samples, count);
Si vous comptez utiliser les données capturées après que l'enregistreur a été détruit ou redémarré, n'oubliez pas de faire une copie du buffer.
Enregistrement personnalisé
Si stocker les données enregistrées dans un buffer audio n'est pas ce que vous voulez, vous pouvez tout aussi bien écrire votre propre enregistreur. Cela vous permettra de traiter les données audio pendant qu'elles sont en train d'être capturées, (presque) directement dès qu'elles arrivent du périphérique d'enregistrement. De cette façon vous pouvez, par exemple, streamer les données capturées sur le réseau, en effectuer une analyse temps-réel, etc.
Pour écrire votre propre enregistreur, vous devez hériter de la classe abstraite sf::SoundRecorder
. En fait,
sf::SoundBufferRecorder
est juste une spécialisation de cette classe, directement intégrée à SFML.
Vous n'avez qu'une fonction virtuelle à redéfinir dans votre classe dérivée : onProcessSamples
. Elle est appelée à chaque fois qu'un
nouveau lot d'échantillons audio ont été capturés, c'est donc là que vous devez implémenter votre traitement perso.
Des échantillons audio sont passés à la fonction onProcessSamples
toutes les 100 ms. Cette valeur est fixée dans le code de SFML et
vous ne pouvez pas la changer (à moins de modifier SFML directement). Ceci sera probablement amélioré dans une prochaine version.
Il existe deux autres fonctions virtuelles que vous pouvez redéfinir si vous le souhaitez : onStart
et onStop
. Elles sont
appelées respectivement lorsque la capture démarre/est arrêtée. Elles peuvent être utiles pour les tâches d'initialisation et de nettoyage.
Voici le squelette d'une classe dérivée complète :
class MyRecorder : public sf::SoundRecorder
{
virtual bool onStart() // optionnelle
{
// initialisez ce qui doit l'être avant que la capture démarre
...
// renvoyez true pour démarrer la capture, ou false pour l'annuler
return true;
}
virtual bool onProcessSamples(const Int16* samples, std::size_t sampleCount)
{
// faites ce que vous voulez des échantillons audio
...
// renvoyez true pour continuer la capture, ou false pour la stopper
return true;
}
virtual void onStop() // optionnelle
{
// nettoyez ce qui doit l'être après la fin de la capture
...
}
}
Les fonctions isAvailable
/start
/stop
sont définies dans la classe de base, sf::SoundRecorder
,
et donc par conséquent dans toutes les classes dérivées. Cela signifie que vous pouvez utiliser n'importe quelle enregistreur exactement de la même
manière que la classe sf::SoundBufferRecorder
ci-dessus.
if (!MyRecorder::isAvailable())
{
// erreur...
}
MyRecorder recorder;
recorder.start();
...
recorder.stop();
Attention aux threads
Comme la capture est effectuée dans un thread, il est important de savoir ce qui se passe exactement, et où.
onStart
sera appelée directement par la fonction start
, donc elle sera exécutée dans le thread qui l'a appelée. Par contre,
onProcessSample
et onStop
seront toujours appelées depuis le thread de capture interne que SFML crée.
Donc, si votre enregistreur utilise des données qui peuvent être accédées de manière concurrente (c'est-à-dire en même temps) à la fois par le thread appelant et par le thread d'enregistrement, il faudra les protéger (avec un mutex ou autre) afin d'éviter les accès concurrents, qui pourraient entraîner des comportements indéterminés -- données audio corrompues, crashs, etc.
Si vous n'êtes pas suffisamment à l'aise avec les problèmes liés aux threads, vous pouvez faire un saut par le tutoriel correspondant.