Skip to content

Commit

Permalink
make portaudio input more robust
Browse files Browse the repository at this point in the history
get rate, channels and format from device before starting audio thread

fix mono input, only 2 channels are used if more are reported

will try 16bit format if not able to find any supported

if no input channels are listed, exit with error

reference option to list devices if opening stream fails

reference readme in device list
  • Loading branch information
karlstav committed Nov 9, 2024
1 parent 622b9e2 commit a82109b
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 28 deletions.
12 changes: 6 additions & 6 deletions cava.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
case INPUT_PORTAUDIO:
audio.format = 16;
audio.rate = 44100;
audio.threadparams = 1;
if (!strcmp(audio.source, "list")) {
input_portaudio((void *)&audio);
} else {
Expand Down Expand Up @@ -501,7 +502,7 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co

pthread_mutex_unlock(&audio.lock);
timeout_counter++;
if (timeout_counter > 2000) {
if (timeout_counter > 5000) {
cleanup();
fprintf(stderr, "could not get rate and/or format, problems with audio thread? "
"quitting...\n");
Expand Down Expand Up @@ -655,6 +656,10 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
exit(EXIT_FAILURE); // Can't happen.
}

// force stereo if only one channel is available
if (p.stereo && audio_channels == 1)
p.stereo = 0;

// handle for user setting too many bars
if (p.fixedbars) {
p.autobars = 0;
Expand All @@ -679,11 +684,6 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co

int output_channels = 1;
if (p.stereo) { // stereo must have even numbers of bars
if (audio.channels == 1) {
fprintf(stderr,
"stereo output configured, but only one channel in audio input.\n");
exit(1);
}
output_channels = 2;
if (number_of_bars % 2 != 0)
number_of_bars--;
Expand Down
26 changes: 13 additions & 13 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,19 +333,19 @@ bool validate_config(struct config_params *p, struct error_s *error) {
p->stereo = -1;
if (strcmp(channels, "mono") == 0) {
p->stereo = 0;
if (strcmp(monoOption, "average") == 0) {
p->mono_opt = AVERAGE;
} else if (strcmp(monoOption, "left") == 0) {
p->mono_opt = LEFT;
} else if (strcmp(monoOption, "right") == 0) {
p->mono_opt = RIGHT;
} else {
write_errorf(error,
"mono option %s is not supported, supported options are: 'average', "
"'left' or 'right'\n",
monoOption);
return false;
}
}
if (strcmp(monoOption, "average") == 0) {
p->mono_opt = AVERAGE;
} else if (strcmp(monoOption, "left") == 0) {
p->mono_opt = LEFT;
} else if (strcmp(monoOption, "right") == 0) {
p->mono_opt = RIGHT;
} else {
write_errorf(error,
"mono option %s is not supported, supported options are: 'average', "
"'left' or 'right'\n",
monoOption);
return false;
}
if (strcmp(channels, "stereo") == 0)
p->stereo = 1;
Expand Down
54 changes: 45 additions & 9 deletions input/portaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <portaudio.h>

#define SAMPLE_SILENCE -32767
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;

typedef struct {
Expand Down Expand Up @@ -39,9 +38,9 @@ static int recordCallback(const void *inputBuffer, void *outputBuffer,
}

if (inputBuffer == NULL)
write_to_cava_input_buffers(framesToCalc * 2, silence_ptr, audio);
write_to_cava_input_buffers(framesToCalc * audio->channels, silence_ptr, audio);
else
write_to_cava_input_buffers(framesToCalc * 2, rptr, audio);
write_to_cava_input_buffers(framesToCalc * audio->channels, rptr, audio);

data->frameIndex += framesToCalc;
if (finished == paComplete) {
Expand Down Expand Up @@ -92,6 +91,7 @@ void *input_portaudio(void *audiodata) {
i + 1, deviceInfo->name, deviceInfo->maxInputChannels,
deviceInfo->maxOutputChannels, deviceInfo->defaultSampleRate);
}
printf("See cava readme for more information on how to capture audio.\n");
exit(EXIT_SUCCESS);
} else if (!strcmp(audio->source, "auto")) {
deviceNum = Pa_GetDefaultInputDevice();
Expand Down Expand Up @@ -120,30 +120,66 @@ void *input_portaudio(void *audiodata) {
}
}
inputParameters.device = deviceNum;
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(deviceNum);
if (deviceInfo->maxInputChannels == 0) {
fprintf(stderr, "Error: selected device has no input channels!\n Use \"list\" as source to "
"get a list of available sources.\n");
exit(EXIT_FAILURE);
}

// set parameters
data.maxFrameIndex = audio->input_buffer_size * 1024 / 2;
inputParameters.channelCount = deviceInfo->maxInputChannels;
audio->channels = deviceInfo->maxInputChannels;
if (audio->channels > 2)
audio->channels = 2;

data.maxFrameIndex = audio->input_buffer_size * 1024 / audio->channels;
data.recordedSamples = (SAMPLE *)malloc(2 * data.maxFrameIndex * sizeof(SAMPLE));
if (data.recordedSamples == NULL) {
fprintf(stderr, "Error: failure in memory allocation!\n");
exit(EXIT_FAILURE);
} else
memset(data.recordedSamples, 0x00, 2 * data.maxFrameIndex);

inputParameters.channelCount = 2;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
double sampleRate = deviceInfo->defaultSampleRate;
audio->rate = sampleRate;

PaSampleFormat sampleFormats[] = {paInt16, paInt24, paInt32, paFloat32,
paInt8, paUInt8, paInt16};
int sampleBits[] = {
16, 24, 32, 32, 8, 8,
};

for (int i = 0; i < 7; i++) {
inputParameters.sampleFormat = sampleFormats[i];
PaError err = Pa_IsFormatSupported(&inputParameters, NULL, sampleRate);
if (err == paFormatIsSupported) {
audio->format = sampleBits[i];
if (i == 3)
audio->IEEE_FLOAT = 1;
break;
}
}

inputParameters.suggestedLatency =
Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;

// set it to work
err = Pa_OpenStream(&stream, &inputParameters, NULL, audio->rate, audio->input_buffer_size / 2,
paClipOff, recordCallback, &data);
err =
Pa_OpenStream(&stream, &inputParameters, NULL, sampleRate,
audio->input_buffer_size / audio->channels, paClipOff, recordCallback, &data);
if (err != paNoError) {
fprintf(stderr, "Error: failure in opening stream (%s)\n", Pa_GetErrorText(err));
fprintf(stderr,
"Error: failure in opening stream (device: %d), (error: %s). Use \"list\" as souce "
"to get a list of "
"available sources.\n",
deviceNum + 1, Pa_GetErrorText(err));
exit(EXIT_FAILURE);
}

audio->threadparams = 0;

// main loop
while (1) {
// start recording
Expand Down

0 comments on commit a82109b

Please sign in to comment.