Skip to content

Commit a82109b

Browse files
committed
make portaudio input more robust
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
1 parent 622b9e2 commit a82109b

File tree

3 files changed

+64
-28
lines changed

3 files changed

+64
-28
lines changed

cava.c

+6-6
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
464464
case INPUT_PORTAUDIO:
465465
audio.format = 16;
466466
audio.rate = 44100;
467+
audio.threadparams = 1;
467468
if (!strcmp(audio.source, "list")) {
468469
input_portaudio((void *)&audio);
469470
} else {
@@ -501,7 +502,7 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
501502

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

659+
// force stereo if only one channel is available
660+
if (p.stereo && audio_channels == 1)
661+
p.stereo = 0;
662+
658663
// handle for user setting too many bars
659664
if (p.fixedbars) {
660665
p.autobars = 0;
@@ -679,11 +684,6 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co
679684

680685
int output_channels = 1;
681686
if (p.stereo) { // stereo must have even numbers of bars
682-
if (audio.channels == 1) {
683-
fprintf(stderr,
684-
"stereo output configured, but only one channel in audio input.\n");
685-
exit(1);
686-
}
687687
output_channels = 2;
688688
if (number_of_bars % 2 != 0)
689689
number_of_bars--;

config.c

+13-13
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,19 @@ bool validate_config(struct config_params *p, struct error_s *error) {
333333
p->stereo = -1;
334334
if (strcmp(channels, "mono") == 0) {
335335
p->stereo = 0;
336-
if (strcmp(monoOption, "average") == 0) {
337-
p->mono_opt = AVERAGE;
338-
} else if (strcmp(monoOption, "left") == 0) {
339-
p->mono_opt = LEFT;
340-
} else if (strcmp(monoOption, "right") == 0) {
341-
p->mono_opt = RIGHT;
342-
} else {
343-
write_errorf(error,
344-
"mono option %s is not supported, supported options are: 'average', "
345-
"'left' or 'right'\n",
346-
monoOption);
347-
return false;
348-
}
336+
}
337+
if (strcmp(monoOption, "average") == 0) {
338+
p->mono_opt = AVERAGE;
339+
} else if (strcmp(monoOption, "left") == 0) {
340+
p->mono_opt = LEFT;
341+
} else if (strcmp(monoOption, "right") == 0) {
342+
p->mono_opt = RIGHT;
343+
} else {
344+
write_errorf(error,
345+
"mono option %s is not supported, supported options are: 'average', "
346+
"'left' or 'right'\n",
347+
monoOption);
348+
return false;
349349
}
350350
if (strcmp(channels, "stereo") == 0)
351351
p->stereo = 1;

input/portaudio.c

+45-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include <portaudio.h>
55

66
#define SAMPLE_SILENCE -32767
7-
#define PA_SAMPLE_TYPE paInt16
87
typedef short SAMPLE;
98

109
typedef struct {
@@ -39,9 +38,9 @@ static int recordCallback(const void *inputBuffer, void *outputBuffer,
3938
}
4039

4140
if (inputBuffer == NULL)
42-
write_to_cava_input_buffers(framesToCalc * 2, silence_ptr, audio);
41+
write_to_cava_input_buffers(framesToCalc * audio->channels, silence_ptr, audio);
4342
else
44-
write_to_cava_input_buffers(framesToCalc * 2, rptr, audio);
43+
write_to_cava_input_buffers(framesToCalc * audio->channels, rptr, audio);
4544

4645
data->frameIndex += framesToCalc;
4746
if (finished == paComplete) {
@@ -92,6 +91,7 @@ void *input_portaudio(void *audiodata) {
9291
i + 1, deviceInfo->name, deviceInfo->maxInputChannels,
9392
deviceInfo->maxOutputChannels, deviceInfo->defaultSampleRate);
9493
}
94+
printf("See cava readme for more information on how to capture audio.\n");
9595
exit(EXIT_SUCCESS);
9696
} else if (!strcmp(audio->source, "auto")) {
9797
deviceNum = Pa_GetDefaultInputDevice();
@@ -120,30 +120,66 @@ void *input_portaudio(void *audiodata) {
120120
}
121121
}
122122
inputParameters.device = deviceNum;
123+
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(deviceNum);
124+
if (deviceInfo->maxInputChannels == 0) {
125+
fprintf(stderr, "Error: selected device has no input channels!\n Use \"list\" as source to "
126+
"get a list of available sources.\n");
127+
exit(EXIT_FAILURE);
128+
}
123129

124130
// set parameters
125-
data.maxFrameIndex = audio->input_buffer_size * 1024 / 2;
131+
inputParameters.channelCount = deviceInfo->maxInputChannels;
132+
audio->channels = deviceInfo->maxInputChannels;
133+
if (audio->channels > 2)
134+
audio->channels = 2;
135+
136+
data.maxFrameIndex = audio->input_buffer_size * 1024 / audio->channels;
126137
data.recordedSamples = (SAMPLE *)malloc(2 * data.maxFrameIndex * sizeof(SAMPLE));
127138
if (data.recordedSamples == NULL) {
128139
fprintf(stderr, "Error: failure in memory allocation!\n");
129140
exit(EXIT_FAILURE);
130141
} else
131142
memset(data.recordedSamples, 0x00, 2 * data.maxFrameIndex);
132143

133-
inputParameters.channelCount = 2;
134-
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
144+
double sampleRate = deviceInfo->defaultSampleRate;
145+
audio->rate = sampleRate;
146+
147+
PaSampleFormat sampleFormats[] = {paInt16, paInt24, paInt32, paFloat32,
148+
paInt8, paUInt8, paInt16};
149+
int sampleBits[] = {
150+
16, 24, 32, 32, 8, 8,
151+
};
152+
153+
for (int i = 0; i < 7; i++) {
154+
inputParameters.sampleFormat = sampleFormats[i];
155+
PaError err = Pa_IsFormatSupported(&inputParameters, NULL, sampleRate);
156+
if (err == paFormatIsSupported) {
157+
audio->format = sampleBits[i];
158+
if (i == 3)
159+
audio->IEEE_FLOAT = 1;
160+
break;
161+
}
162+
}
163+
135164
inputParameters.suggestedLatency =
136165
Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
137166
inputParameters.hostApiSpecificStreamInfo = NULL;
138167

139168
// set it to work
140-
err = Pa_OpenStream(&stream, &inputParameters, NULL, audio->rate, audio->input_buffer_size / 2,
141-
paClipOff, recordCallback, &data);
169+
err =
170+
Pa_OpenStream(&stream, &inputParameters, NULL, sampleRate,
171+
audio->input_buffer_size / audio->channels, paClipOff, recordCallback, &data);
142172
if (err != paNoError) {
143-
fprintf(stderr, "Error: failure in opening stream (%s)\n", Pa_GetErrorText(err));
173+
fprintf(stderr,
174+
"Error: failure in opening stream (device: %d), (error: %s). Use \"list\" as souce "
175+
"to get a list of "
176+
"available sources.\n",
177+
deviceNum + 1, Pa_GetErrorText(err));
144178
exit(EXIT_FAILURE);
145179
}
146180

181+
audio->threadparams = 0;
182+
147183
// main loop
148184
while (1) {
149185
// start recording

0 commit comments

Comments
 (0)