Attention: cette page se réfère à une ancienne version de SFML. Cliquez ici pour passer à la dernière version.

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 :

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.