1 - Buffer
Comprendre l'audio
Quand on récupère les données microphone via un langage de bas niveau comme le C, on récupère le signal analogique en format digital, le Pulse Code Modulation (PCM). Ce format est utilisé par les CD audio par exemple.
Typiquement, le format WAV des fichiers audio n'est qu'une enveloppe qui contient du PCM dans ses données et un header qui permet à l'ordinateur d'avoir les informations sur le fichier audio comme sa durée, son format, etc.
[schema audio]
PortAudio est une bibliothèque en C qui permet de récupérer le flux audio du microphone en PCM. http://portaudio.com/docs.html
Fréquence, samples, page...
Il est important de déterminer les mots clés pour traiter le flux audio correctement.
La fréquence détermine le nombre de frames sample envoyés chaque seconde.
Un sample est une unité en bits de l'audio, selon le format : (char) 8-bit / (integer) 16-bit / (float) 32-bit
Un sample frame est un tronçon de données qui contient l'audio, elle fait la taille d'un sample * le nombre de canal (si mono = 1, si stéréo = 2). DOIT OBLIGATOIREMENT ÊTRE UN MULTIPLE DE 2.
Le canal correspond aux directions dans lesquelles le son est enregistré / projeté. Si 1 alors le son sera mono, si 2 il sera gauche/droite.
En stéréo, le son est en "interleaved" c'est-à-dire qu'il contient les données dans cet ordre : LRLRLRLRLR... où L = Left et R = Right.
EXEMPLE :
Pour ces données d'exemple :
- Fréquence : 48000 (équivalent à un enregistrement studio)
- Sample : 16-bit (Type short)
- Sample frame : 480 (l'idéal avec cette valeur c'est d'avoir un multiple pour réduire la latence, ici on aura 10ms de différence) * 2 (car stéréo)
- Channel : 2 (stéréo)
Pour ces valeurs, cela signifie que chaque seconde, on enregistrera :
Chaque buffer : 480 * 2 jusqu'à atteindre la fréquence : 48000 équivaut à une seconde.
Code exemple
#include "portaudio.h"
#include <iostream>
PaError paErr;
int const channels = 2; // Stereo
int const bufferSize = 480; // Sample frame size
int const sampleRate = 48000; // Frequency
int const durationSeconds = 10; // Total seconds of recording
/*
** Data structure
*/
typedef struct
{
int frameIndex; // Actual frame
int maxFrameIndex; // Total frame possible
short* recordedSamples; // data saved in a 16-bit array
} RecordingData;
/*
** Record callback
*/
static int recordCallback(const void* inputBuffer, void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData)
{
RecordingData* data = (RecordingData*)userData; // Save data
const short* rptr = (const short*)inputBuffer; // Read pointer
short* wptr = &data->recordedSamples[data->frameIndex * channels]; // Write pointer
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
// Remove warnings
(void)outputBuffer;
(void)timeInfo;
(void)statusFlags;
(void)userData;
if (framesLeft < framesPerBuffer)
{
framesToCalc = framesLeft;
finished = paComplete;
}
else
{
framesToCalc = framesPerBuffer;
finished = paContinue;
}
if (inputBuffer == NULL)
{
for (i = 0; i < framesToCalc; i++)
{
*wptr++ = 0; /* left */
if (channels == 2) *wptr++ = 0; /* right */
}
}
else
{
for (i = 0; i < framesToCalc; i++)
{
*wptr++ = *rptr++; /* left */
if (channels == 2) *wptr++ = *rptr++; /* right */
}
}
data->frameIndex += framesToCalc;
return finished;
}
int main()
{
/* Port Audio Setup */
PaStreamParameters inputParameters;
PaStream* stream;
RecordingData data;
int totalFrames;
int numSamples;
int numBytes;
data.maxFrameIndex = totalFrames = durationSeconds * sampleRate;
data.frameIndex = 0;
numSamples = totalFrames * channels;
numBytes = numSamples * sizeof(short);
data.recordedSamples = new short[numBytes]; // Alloc data
memset(data.recordedSamples, 0, numBytes);
if ((paErr = Pa_Initialize()) != paNoError)
{
std::cerr << "Pa_Initialize failed: " << Pa_GetErrorText(paErr) << "\n";
return 1;
}
inputParameters.device = Pa_GetDefaultInputDevice();
inputParameters.channelCount = 2;
inputParameters.sampleFormat = paInt16;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
paErr = Pa_OpenStream(&stream, &inputParameters, NULL, sampleRate, bufferSize, paClipOff, recordCallback, &data);
if ((paErr = Pa_StartStream(stream)) != paNoError)
{
std::cout << "Pa_StartStream failed: " << Pa_GetErrorText(paErr) << "\n";
return 1;
}
int i = 0;
while ((err = Pa_IsStreamActive(stream)) == 1)
{
if (i == durationSeconds)
break;
Pa_Sleep(1000);
i++;
}
if ((paErr = Pa_StopStream(stream)) != paNoError)
{
std::cout << "Pa_StopStream failed: " << Pa_GetErrorText(paErr) << "\n";
return 1;
}
if ((paErr = Pa_CloseStream(stream)) != paNoError)
{
std::cout << "Pa_CloseStream failed: " << Pa_GetErrorText(paErr) << "\n";
return 1;
}
if ((paErr = Pa_Terminate()) != paNoError)
{
std::cout << "Pa_Terminate failed: " << Pa_GetErrorText(paErr) << "\n";
return 1;
}
}