1 #include "input/portaudio.h"
2 #include "input/common.h"
3 
4 #include <portaudio.h>
5 
6 #define SAMPLE_SILENCE -32767
7 #define PA_SAMPLE_TYPE paInt16
8 typedef short SAMPLE;
9 
10 typedef struct {
11     int frameIndex; /* Index into sample array. */
12     int maxFrameIndex;
13     SAMPLE *recordedSamples;
14 } paTestData;
15 
16 static struct audio_data *audio;
17 int16_t silence_buffer[8092] = {SAMPLE_SILENCE};
18 
recordCallback(const void * inputBuffer,void * outputBuffer,unsigned long framesPerBuffer,const PaStreamCallbackTimeInfo * timeInfo,PaStreamCallbackFlags statusFlags,void * userData)19 static int recordCallback(const void *inputBuffer, void *outputBuffer,
20                           unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
21                           PaStreamCallbackFlags statusFlags, void *userData) {
22     paTestData *data = (paTestData *)userData;
23     SAMPLE *rptr = (SAMPLE *)inputBuffer;
24     long framesToCalc;
25     int finished;
26     unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
27     (void)outputBuffer; // Prevent unused variable warnings.
28     (void)timeInfo;
29     (void)statusFlags;
30     (void)userData;
31 
32     if (framesLeft < framesPerBuffer) {
33         framesToCalc = framesLeft;
34         finished = paComplete;
35     } else {
36         framesToCalc = framesPerBuffer;
37         finished = paContinue;
38     }
39 
40     pthread_mutex_lock(&lock);
41 
42     if (inputBuffer == NULL)
43         write_to_fftw_input_buffers(framesToCalc, silence_buffer, audio);
44     else
45         write_to_fftw_input_buffers(framesToCalc, rptr, audio);
46 
47     pthread_mutex_unlock(&lock);
48 
49     data->frameIndex += framesToCalc;
50     if (finished == paComplete) {
51         data->frameIndex = 0;
52         finished = paContinue;
53     }
54 
55     if (audio->terminate == 1)
56         finished = paComplete;
57 
58     return finished;
59 }
60 
portaudio_simple_free(paTestData data)61 void portaudio_simple_free(paTestData data) {
62     Pa_Terminate();
63     free(data.recordedSamples);
64 }
65 
input_portaudio(void * audiodata)66 void *input_portaudio(void *audiodata) {
67     audio = (struct audio_data *)audiodata;
68 
69     PaStreamParameters inputParameters;
70     PaStream *stream;
71     PaError err = paNoError;
72     paTestData data;
73 
74     // start portaudio
75     err = Pa_Initialize();
76     if (err != paNoError) {
77         fprintf(stderr, "Error: unable to initilize portaudio - %s\n", Pa_GetErrorText(err));
78         exit(EXIT_FAILURE);
79     }
80 
81     // get portaudio device
82     int deviceNum = -1, numOfDevices = Pa_GetDeviceCount();
83     if (!strcmp(audio->source, "list")) {
84         if (numOfDevices < 0) {
85             fprintf(stderr, "Error: portaudio was unable to find a audio device! Code: 0x%x\n",
86                     numOfDevices);
87             exit(EXIT_FAILURE);
88         }
89         for (int i = 0; i < numOfDevices; i++) {
90             const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
91             printf("Device #%d: %s\n"
92                    "\tInput Channels: %d\n"
93                    "\tOutput Channels: %d\n"
94                    "\tDefault SampleRate: %lf\n",
95                    i + 1, deviceInfo->name, deviceInfo->maxInputChannels,
96                    deviceInfo->maxOutputChannels, deviceInfo->defaultSampleRate);
97         }
98         exit(EXIT_SUCCESS);
99     } else if (!strcmp(audio->source, "auto")) {
100         deviceNum = Pa_GetDefaultInputDevice();
101 
102         if (deviceNum == paNoDevice) {
103             fprintf(stderr, "Error: no portaudio input device found\n");
104             exit(EXIT_FAILURE);
105         }
106     } else if (sscanf(audio->source, "%d", &deviceNum)) {
107         if (deviceNum > numOfDevices) {
108             fprintf(stderr, "Error: Invalid input device!\n");
109             exit(EXIT_FAILURE);
110         }
111         deviceNum--;
112     } else {
113         for (int i = 0; i < numOfDevices; i++) {
114             const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
115             if (!strcmp(audio->source, deviceInfo->name)) {
116                 deviceNum = i;
117                 break;
118             }
119         }
120         if (deviceNum == -1) {
121             fprintf(stderr, "Error: No such device '%s'!\n", audio->source);
122             exit(EXIT_FAILURE);
123         }
124     }
125     inputParameters.device = deviceNum;
126 
127     // set parameters
128     data.maxFrameIndex = audio->FFTtreblebufferSize * 1024;
129     data.recordedSamples = (SAMPLE *)malloc(2 * data.maxFrameIndex * sizeof(SAMPLE));
130     if (data.recordedSamples == NULL) {
131         fprintf(stderr, "Error: failure in memory allocation!\n");
132         exit(EXIT_FAILURE);
133     } else
134         memset(data.recordedSamples, 0x00, 2 * data.maxFrameIndex);
135 
136     inputParameters.channelCount = 2;
137     inputParameters.sampleFormat = PA_SAMPLE_TYPE;
138     inputParameters.suggestedLatency =
139         Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
140     inputParameters.hostApiSpecificStreamInfo = NULL;
141 
142     // set it to work
143     err = Pa_OpenStream(&stream, &inputParameters, NULL, audio->rate, audio->FFTtreblebufferSize,
144                         paClipOff, recordCallback, &data);
145     if (err != paNoError) {
146         fprintf(stderr, "Error: failure in opening stream (%x)\n", err);
147         exit(EXIT_FAILURE);
148     }
149 
150     // main loop
151     while (1) {
152         // start recording
153         data.frameIndex = 0;
154         err = Pa_StartStream(stream);
155         if (err != paNoError) {
156             fprintf(stderr, "Error: failure in starting stream (%x)\n", err);
157             exit(EXIT_FAILURE);
158         }
159 
160         //  record
161         while ((err = Pa_IsStreamActive(stream)) == 1) {
162             Pa_Sleep(1000);
163             if (audio->terminate == 1)
164                 break;
165         }
166         // check for errors
167         if (err < 0) {
168             fprintf(stderr, "Error: failure in recording audio (%x)\n", err);
169             exit(EXIT_FAILURE);
170         }
171 
172         // check if it bailed
173         if (audio->terminate == 1)
174             break;
175     }
176     // close stream
177     if ((err = Pa_CloseStream(stream)) != paNoError) {
178         fprintf(stderr, "Error: failure in closing stream (%x)\n", err);
179         exit(EXIT_FAILURE);
180     }
181 
182     portaudio_simple_free(data);
183     return 0;
184 }
185