Hello,
I would like to build a small VU meter in C for monitoring my microphone.
I don't know anything about sound libraries that can be used to record audio, so I've turned my attention to Portaudio, only because I've heard about it.
I rewrote one of their examples to record audio (paex_record.c), in order to understand all of the steps, but also to do it in my programming style.
The example works, but it only fills the predefined buffer, then exits.
I've tried many things to try to restart the recording buffer, whenever by putting some part of the code into a loop, or using gotos; but nothing have worked, and despite reading the documentation, I'm starting to run around in circle.
My two questions are:
- Is Portaudio a good library for this? Are there better choices in terms of simplicity for newcomers?
- How can I modify my code to record continuously?
Here is my code, that you can compile under Linux, using:
Code:
$ gcc file.c -lm -lportaudio
Code:
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <portaudio.h>
#define SAMPLE_RATE 44100
#define FRAMES_PER_BUFFER 256
#define NUM_SECONDS 5
#define NUM_CHANNELS 2
#define DITHER_FLAG 0
#define WRITE_TO_FILE 0
#define SAMPLES (SAMPLE_RATE * NUM_CHANNELS)
typedef struct paTestData paTestData;
struct paTestData
{
int32_t frame_index;
int32_t frame_index_max;
float recorded_samples[SAMPLES];
};
static int32_t
record_callback(const void* input, void* output, size_t frames_count, const PaStreamCallbackTimeInfo* time_info,
PaStreamCallbackFlags status_flags, void* user_data)
{
(void)output;
(void)time_info;
(void)status_flags;
paTestData* data = (paTestData*)user_data;
const float* rptr = (const float*)input;
float* wptr = &data->recorded_samples[data->frame_index * NUM_CHANNELS];
long frames_to_calc = 0;
int32_t finished = 0;
size_t frames_left = data->frame_index_max - data->frame_index;
if (frames_left < frames_count)
{
frames_to_calc = frames_left;
finished = paComplete;
}
else
{
frames_to_calc = frames_count;
finished = paContinue;
}
if (!input)
{
for (size_t i = 0; i < frames_to_calc; ++i)
{
*wptr++ = 0.0f;
if (NUM_CHANNELS == 2)
{
*wptr++ = 0.0f;
}
}
}
else
{
for (size_t i = 0; i < frames_to_calc; ++i)
{
*wptr++ = *rptr++;
if (NUM_CHANNELS == 2)
{
*wptr++ = *rptr++;
}
}
}
data->frame_index += frames_to_calc;
return finished;
}
static void
fatal_error(PaError err)
{
Pa_Terminate();
exit(err);
}
static inline float
amplitude_to_db(float amplitude)
{
return 20.0f * log10(amplitude);
}
int32_t
main(int32_t argc, char* argv[])
{
(void)argc;
(void)argv;
fputs("PortAudio record test\n", stdout);
paTestData data = {
.frame_index_max = SAMPLE_RATE};
PaError err = Pa_Initialize();
if (err != paNoError)
{
fatal_error(err);
}
PaStreamParameters input_parameters = {
.device = Pa_GetDefaultInputDevice(),
.channelCount = NUM_CHANNELS,
.sampleFormat = paFloat32,
};
if (input_parameters.device == paNoDevice)
{
fatal_error(0);
}
else
{
input_parameters.suggestedLatency = Pa_GetDeviceInfo(input_parameters.device)->defaultLowInputLatency;
}
// Record audio
PaStream* stream = 0;
err = Pa_OpenStream(&stream, &input_parameters, 0, SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, record_callback, &data);
if (err != paNoError)
{
fatal_error(err);
}
err = Pa_StartStream(stream);
if (err != paNoError)
{
fatal_error(err);
}
fputs("Now recording.\n", stdout);
while ((err = Pa_IsStreamActive(stream)) == 1)
{
Pa_Sleep(16);
printf("index: %d %f\n", data.frame_index, amplitude_to_db(data.recorded_samples[data.frame_index]));
}
if (err != paNoError)
{
fatal_error(err);
}
err = Pa_CloseStream(stream);
if (err != paNoError)
{
fatal_error(err);
}
// Measure maximum peak amplitude
float max = 0.0f;
double average = 0.0;
for (size_t i = 0; i < SAMPLES; ++i)
{
float val = data.recorded_samples[i];
if (val < 0.0f)
{
val = -val; // abs()
}
if (val > max)
{
max = val;
}
average += val;
}
average /= (double)SAMPLES;
printf("Sample maximum amplitude: %f\n", max);
printf("Sample average amplitude: %lf\n", average);
printf("Sample maximum dB: %f\n", amplitude_to_db(max));
printf("Sample average dB: %lf\n", amplitude_to_db(average));
// Done
Pa_Terminate();
return EXIT_SUCCESS;
}
Thank you very much for your time.
Best regards.