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