diff --git a/cava.c b/cava.c index 945bd39..86d90f0 100644 --- a/cava.c +++ b/cava.c @@ -54,6 +54,11 @@ #include "input/alsa.c" #endif +#ifdef PORTAUDIO +#include "input/portaudio.c" +#include "input/portaudio.h" +#endif + #ifdef PULSE #include "input/pulse.h" #include "input/pulse.c" @@ -471,6 +476,13 @@ as of 0.4.0 all options are specified in config file, see in '/home/username/.co } #endif + #ifdef PORTAUDIO + if (p.im == 6) { + thr_id = pthread_create(&p_thread, NULL, input_portaudio, (void*)&audio); + audio.rate = 44100; + } + #endif + if (p.highcf > audio.rate / 2) { cleanup(); fprintf(stderr, diff --git a/config.c b/config.c index b6ab500..71adae1 100644 --- a/config.c +++ b/config.c @@ -167,6 +167,13 @@ if (strcmp(inputMethod, "shmem") == 0) { return false; #endif } +if (strcmp(inputMethod, "portaudio") == 0) { + p->im = 6; + #ifndef PORTAUDIO + write_errorf(error, "cava was built without portaudio support\n"); + return false; + #endif +} if (p->im == 0) { write_errorf(error, "input method '%s' is not supported, supported methods are: %s\n", inputMethod, supportedInput); @@ -412,6 +419,11 @@ if (colorsOnly) { //setting fifo to defaualt if no other input modes supported inputMethod = (char *)iniparser_getstring(ini, "input:method", "fifo"); +//setting portaudio to default if supported +#ifdef ALSA + inputMethod = (char *)iniparser_getstring(ini, "input:method", "portaudio"); +#endif + //setting alsa to defaualt if supported #ifdef ALSA inputMethod = (char *)iniparser_getstring(ini, "input:method", "alsa"); @@ -506,6 +518,12 @@ if (strcmp(inputMethod, "shmem") == 0) { p->audio_source = (char *)iniparser_getstring(ini, "input:source", "/squeezelite-00:00:00:00:00:00"); } #endif +#ifdef PORTAUDIO +if (strcmp(inputMethod, "portaudio") == 0) { + p->im = 6; + p->audio_source = (char *)iniparser_getstring(ini, "input:source", "auto"); +} +#endif return validate_config(supportedInput, params, error); //iniparser_freedict(ini); diff --git a/configure.ac b/configure.ac index 2f4602f..cfc2d1f 100644 --- a/configure.ac +++ b/configure.ac @@ -69,6 +69,19 @@ AC_CHECK_LIB(pulse-simple, pa_simple_new, have_pulse=yes, have_pulse=no) AC_MSG_NOTICE([WARNING: No pusleaudio dev files found building without pulseaudio support]) fi +dnl ###################### +dnl checking for portaudio dev +dnl ###################### +AC_CHECK_LIB(portaudio, Pa_Initialize, have_portaudio=yes, have_portaudio=no) + if [[ $have_portaudio = "yes" ]] ; then + LIBS="$LIBS -lportaudio" + CPPFLAGS="$CPPFLAGS -DPORTAUDIO" + fi + + if [[ $have_portaudio = "no" ]] ; then + AC_MSG_NOTICE([WARNING: No portaudio dev files found building without portaudio support]) + fi + dnl ###################### dnl checking for sndio dev dnl ###################### diff --git a/example_files/config b/example_files/config index a46f8f9..a24c49f 100644 --- a/example_files/config +++ b/example_files/config @@ -68,6 +68,9 @@ ; method = shmem ; source = /squeezelite-AA:BB:CC:DD:EE:FF +; method = portaudio +; source = auto + [output] diff --git a/input/portaudio.c b/input/portaudio.c new file mode 100644 index 0000000..3289f8e --- /dev/null +++ b/input/portaudio.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include "portaudio.h" +#include "fifo.h" +#define BUFFERSIZE 4096 + +#define SAMPLE_SILENCE 32768 +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; + +typedef struct { + int frameIndex; /* Index into sample array. */ + int maxFrameIndex; + SAMPLE *recordedSamples; +} paTestData; + +static struct audio_data *audio; +static int n = 0; + +static int recordCallback(const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, void *userData) { + paTestData *data = (paTestData*)userData; + const SAMPLE *rptr = (const SAMPLE*)inputBuffer; + long framesToCalc; + long i; + int finished; + unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; + + (void) outputBuffer; // Prevent unused variable warnings. + (void) timeInfo; + (void) statusFlags; + (void) userData; + + if( framesLeft < framesPerBuffer ) { + framesToCalc = framesLeft; + finished = paComplete; + } else { + framesToCalc = framesPerBuffer; + finished = paContinue; + } + + if(inputBuffer == NULL) { + for(i=0; ichannels == 1) audio->audio_out_l[n] = SAMPLE_SILENCE; + if(audio->channels == 2) { + audio->audio_out_l[n] = SAMPLE_SILENCE; + audio->audio_out_r[n] = SAMPLE_SILENCE; + } + if(n == BUFSIZE-1) n = 0; + } + } else { + for(i=0; ichannels == 1) { + audio->audio_out_l[n] = (rptr[0] + rptr[1]) / 2; + rptr += 2; + } + if(audio->channels == 2) { + audio->audio_out_l[n] = *rptr++; + audio->audio_out_r[n] = *rptr++; + } + n++; + if(n == BUFSIZE-1) n = 0; + } + } + + data->frameIndex += framesToCalc; + if(finished == paComplete) { + data->frameIndex = 0; + finished = paContinue; + } + return finished; +} + + +void portaudio_simple_free(paTestData data) { + Pa_Terminate(); + free(data.recordedSamples); +} + +void* input_portaudio(void *audiodata) { + audio = (struct audio_data *)audiodata; + + PaStreamParameters inputParameters; + PaStream* stream; + PaError err = paNoError; + paTestData data; + + // start portaudio + err = Pa_Initialize(); + if(err != paNoError) { + fprintf(stderr, "Error: unable to initilize portaudio - %s\n", Pa_GetErrorText(err)); + exit(EXIT_FAILURE); + } + + // get portaudio device + int deviceNum = -1, numOfDevices = Pa_GetDeviceCount(); + if(!strcmp(audio->source, "list")) { + if(numOfDevices < 0) { + fprintf(stderr, "Error: portaudio was unable to find a audio device! Code: 0x%x\n", numOfDevices); + exit(EXIT_FAILURE); + } + for(int i = 0; i < numOfDevices; i++) { + const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i); + printf("Device #%d: %s\n" + "\tInput Channels: %d\n" + "\tOutput Channels: %d\n" + "\tDefault SampleRate: %lf\n", + i+1, deviceInfo->name, deviceInfo->maxInputChannels, + deviceInfo->maxOutputChannels, deviceInfo->defaultSampleRate); + } + exit(EXIT_SUCCESS); + } else if(!strcmp(audio->source, "auto")) { + deviceNum = Pa_GetDefaultInputDevice(); + + if(deviceNum == paNoDevice) { + fprintf(stderr, "Error: no portaudio input device found\n"); + exit(EXIT_FAILURE); + } + } else if(sscanf(audio->source,"%d", &deviceNum)) { + if(deviceNum > numOfDevices) { + fprintf(stderr, "Error: Invalid input device!\n"); + exit(EXIT_FAILURE); + } + deviceNum--; + } else { + for(int i = 0; i < numOfDevices; i++) { + const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i); + if(!strcmp(audio->source, deviceInfo->name)) { + deviceNum=i; + break; + } + } + if(deviceNum==-1) { + fprintf(stderr, "Error: No such device '%s'!\n", audio->source); + exit(EXIT_FAILURE); + } + } + inputParameters.device = deviceNum; + + // set parameters + data.maxFrameIndex = BUFSIZE; + data.recordedSamples = (SAMPLE *)malloc(2*BUFSIZE*sizeof(SAMPLE)); + if(data.recordedSamples == NULL) { + fprintf(stderr, "Error: failure in memory allocation!\n"); + exit(EXIT_FAILURE); + } else memset(data.recordedSamples, 0x00, 2*BUFSIZE); + + inputParameters.channelCount = 2; + inputParameters.sampleFormat = PA_SAMPLE_TYPE; + inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; + inputParameters.hostApiSpecificStreamInfo = NULL; + + // set it to work + err = Pa_OpenStream(&stream, &inputParameters, NULL, audio->rate, BUFSIZE, + paClipOff, recordCallback, &data); + if(err != paNoError) { + fprintf(stderr, "Error: failure in opening stream (%x)\n", err); + exit(EXIT_FAILURE); + } + + // main loop + while(1){ + // start recording + data.frameIndex = 0; + err = Pa_StartStream(stream); + if(err != paNoError) { + fprintf(stderr, "Error: failure in starting stream (%x)\n", err); + exit(EXIT_FAILURE); + } + + // record + while((err = Pa_IsStreamActive(stream)) == 1) { + Pa_Sleep(5); + if(audio->terminate == 1) break; + } + // check for errors + if(err < 0) { + fprintf(stderr, "Error: failure in recording audio (%x)\n", err); + exit(EXIT_FAILURE); + } + + // check if it bailed + if(audio->terminate == 1) break; + } + // close stream + if((err = Pa_CloseStream(stream)) != paNoError) { + fprintf(stderr, "Error: failure in closing stream (%x)\n", err); + exit(EXIT_FAILURE); + } + + portaudio_simple_free(data); + return 0; +} diff --git a/input/portaudio.h b/input/portaudio.h new file mode 100644 index 0000000..b6f17e1 --- /dev/null +++ b/input/portaudio.h @@ -0,0 +1 @@ +void* input_portaudio(void *audiodata);