1 /*
2 * lingot, a musical instrument tuner.
3 *
4 * Copyright (C) 2004-2018 Iban Cereijo.
5 * Copyright (C) 2004-2008 Jairo Chapela.
6 *
7 * This file is part of lingot.
8 *
9 * lingot is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * lingot is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with lingot; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include <assert.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/time.h>
29 #include <time.h>
30 #include <errno.h>
31
32 #include "lingot-defs.h"
33 #include "lingot-audio.h"
34
35 #include "lingot-core.h"
36 #include "lingot-audio-oss.h"
37 #include "lingot-audio-alsa.h"
38 #include "lingot-audio-jack.h"
39 #include "lingot-audio-pulseaudio.h"
40 #include "lingot-i18n.h"
41 #include "lingot-msg.h"
42
lingot_audio_new(LingotAudioHandler * result,audio_system_t audio_system,const char * device,int sample_rate,LingotAudioProcessCallback process_callback,void * process_callback_arg)43 void lingot_audio_new(LingotAudioHandler* result, audio_system_t audio_system, const char* device,
44 int sample_rate, LingotAudioProcessCallback process_callback,
45 void *process_callback_arg) {
46
47 # if !defined(OSS) && !defined(ALSA) && !defined(JACK) && !defined(PULSEAUDIO)
48 # error "No audio system has been defined"
49 # endif
50
51 switch (audio_system) {
52 # ifdef OSS
53 case AUDIO_SYSTEM_OSS:
54 lingot_audio_oss_new(result, device, sample_rate);
55 # else
56 lingot_msg_add_error(
57 _("The application has not been built with OSS support"));
58 result->audio_system = -1;
59 # endif
60 break;
61 case AUDIO_SYSTEM_ALSA:
62 # ifdef ALSA
63 lingot_audio_alsa_new(result, device, sample_rate);
64 # else
65 lingot_msg_add_error(
66 _("The application has not been built with ALSA support"));
67 result->audio_system = -1;
68 # endif
69 break;
70 case AUDIO_SYSTEM_JACK:
71 # ifdef JACK
72 lingot_audio_jack_new(result, device);
73 # else
74 lingot_msg_add_error(
75 _("The application has not been built with JACK support"));
76 result->audio_system = -1;
77 # endif
78 break;
79 # ifdef PULSEAUDIO
80 case AUDIO_SYSTEM_PULSEAUDIO:
81 lingot_audio_pulseaudio_new(result, device, sample_rate);
82 # else
83 lingot_msg_add_error(
84 _("The application has not been built with PULSEAUDIO support"));
85 result->audio_system = -1;
86 # endif
87 break;
88 default:
89 assert (0);
90 }
91
92 if (result->audio_system != -1 ) {
93 // audio source read in floating point format.
94 result->flt_read_buffer = malloc(
95 result->read_buffer_size_samples * sizeof(FLT));
96 memset(result->flt_read_buffer, 0,
97 result->read_buffer_size_samples * sizeof(FLT));
98 result->process_callback = process_callback;
99 result->process_callback_arg = process_callback_arg;
100 result->interrupted = 0;
101 result->running = 0;
102 }
103 }
104
lingot_audio_destroy(LingotAudioHandler * audio)105 void lingot_audio_destroy(LingotAudioHandler* audio) {
106 switch (audio->audio_system) {
107 # ifdef OSS
108 case AUDIO_SYSTEM_OSS:
109 lingot_audio_oss_destroy(audio);
110 break;
111 # endif
112 # ifdef ALSA
113 case AUDIO_SYSTEM_ALSA:
114 lingot_audio_alsa_destroy(audio);
115 break;
116 # endif
117 # ifdef JACK
118 case AUDIO_SYSTEM_JACK:
119 lingot_audio_jack_destroy(audio);
120 break;
121 # endif
122 # ifdef PULSEAUDIO
123 case AUDIO_SYSTEM_PULSEAUDIO:
124 lingot_audio_pulseaudio_destroy(audio);
125 break;
126 # endif
127 default:
128 assert (0);
129 }
130 if (audio->flt_read_buffer != 0x0) {
131 free(audio->flt_read_buffer);
132 audio->flt_read_buffer = 0x0;
133 }
134 audio->audio_system = -1;
135 }
136
lingot_audio_read(LingotAudioHandler * audio)137 int lingot_audio_read(LingotAudioHandler* audio) {
138 int samples_read = -1;
139
140 switch (audio->audio_system) {
141 # ifdef OSS
142 case AUDIO_SYSTEM_OSS:
143 samples_read = lingot_audio_oss_read(audio);
144 break;
145 # endif
146 # ifdef ALSA
147 case AUDIO_SYSTEM_ALSA:
148 samples_read = lingot_audio_alsa_read(audio);
149 break;
150 # endif
151 # ifdef PULSEAUDIO
152 case AUDIO_SYSTEM_PULSEAUDIO:
153 samples_read = lingot_audio_pulseaudio_read(audio);
154 break;
155 # endif
156 default:
157 assert (0);
158 }
159
160 //# define RATE_ESTIMATOR
161
162 # ifdef RATE_ESTIMATOR
163 static double samplerate_estimator = 0.0;
164 static unsigned long read_samples = 0;
165 static double elapsed_time = 0.0;
166
167 struct timeval tdiff, t_abs;
168 static struct timeval t_abs_old = { .tv_sec = 0, .tv_usec = 0 };
169 static FILE* fid = 0x0;
170
171 if (fid == 0x0) {
172 fid = fopen("/tmp/dump.txt", "w");
173 }
174
175 gettimeofday(&t_abs, NULL );
176
177 if ((t_abs_old.tv_sec != 0) || (t_abs_old.tv_usec != 0)) {
178
179 int i;
180 for (i = 0; i < samples_read; i++) {
181 fprintf(fid, "%f ", audio->flt_read_buffer[i]);
182 // printf("%f ", audio->flt_read_buffer[i]);
183 }
184 // printf("\n");
185 timersub(&t_abs, &t_abs_old, &tdiff);
186 read_samples = samples_read;
187 elapsed_time = tdiff.tv_sec + 1e-6 * tdiff.tv_usec;
188 static const double c = 0.9;
189 samplerate_estimator = c * samplerate_estimator
190 + (1 - c) * (read_samples / elapsed_time);
191 // printf("estimated sample rate %f (read %i samples in %f seconds)\n",
192 // samplerate_estimator, read_samples, elapsed_time);
193
194 }
195 t_abs_old = t_abs;
196 # endif
197
198 return samples_read;
199 }
200
lingot_audio_get_audio_system_properties(LingotAudioSystemProperties * properties,audio_system_t audio_system)201 int lingot_audio_get_audio_system_properties(
202 LingotAudioSystemProperties* properties,
203 audio_system_t audio_system) {
204
205 switch (audio_system) {
206 # ifdef OSS
207 case AUDIO_SYSTEM_OSS:
208 return lingot_audio_oss_get_audio_system_properties(properties);
209 # endif
210 # ifdef ALSA
211 case AUDIO_SYSTEM_ALSA:
212 return lingot_audio_alsa_get_audio_system_properties(properties);
213 # endif
214 # ifdef JACK
215 case AUDIO_SYSTEM_JACK:
216 return lingot_audio_jack_get_audio_system_properties(properties);
217 # endif
218 # ifdef PULSEAUDIO
219 case AUDIO_SYSTEM_PULSEAUDIO:
220 return lingot_audio_pulseaudio_get_audio_system_properties(properties);
221 # endif
222 default:
223 assert (0);
224 }
225
226 return -1;
227 }
228
lingot_audio_audio_system_properties_destroy(LingotAudioSystemProperties * properties)229 void lingot_audio_audio_system_properties_destroy(
230 LingotAudioSystemProperties* properties) {
231
232 int i;
233 if (properties->devices != NULL) {
234 for (i = 0; i < properties->n_devices; i++) {
235 if (properties->devices[i] != NULL ) {
236 free(properties->devices[i]);
237 }
238 }
239 free(properties->devices);
240 }
241 }
242
lingot_audio_run_reading_thread(LingotAudioHandler * audio)243 void lingot_audio_run_reading_thread(LingotAudioHandler* audio) {
244
245 int samples_read = 0;
246
247 while (audio->running) {
248 // process new data block.
249 samples_read = lingot_audio_read(audio);
250
251 if (samples_read < 0) {
252 audio->running = 0;
253 audio->interrupted = 1;
254 } else {
255 audio->process_callback(audio->flt_read_buffer, samples_read,
256 audio->process_callback_arg);
257 }
258 }
259
260 pthread_mutex_lock(&audio->thread_input_read_mutex);
261 pthread_cond_broadcast(&audio->thread_input_read_cond);
262 pthread_mutex_unlock(&audio->thread_input_read_mutex);
263
264 }
265
lingot_audio_start(LingotAudioHandler * audio)266 int lingot_audio_start(LingotAudioHandler* audio) {
267
268 int result = 0;
269
270 switch (audio->audio_system) {
271 case AUDIO_SYSTEM_JACK:
272 # ifdef JACK
273 result = lingot_audio_jack_start(audio);
274 # else
275 assert (0);
276 # endif
277 break;
278 default:
279 pthread_attr_init(&audio->thread_input_read_attr);
280
281 // detached thread.
282 // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
283 pthread_mutex_init(&audio->thread_input_read_mutex, NULL );
284 pthread_cond_init(&audio->thread_input_read_cond, NULL );
285 pthread_attr_init(&audio->thread_input_read_attr);
286 pthread_create(&audio->thread_input_read,
287 &audio->thread_input_read_attr,
288 (void* (*)(void*)) lingot_audio_run_reading_thread, audio);
289 break;
290 }
291
292 if (result == 0) {
293 audio->running = 1;
294 }
295
296 return result;
297 }
298
299 // function invoked when the audio thread must be cancelled
lingot_audio_cancel(LingotAudioHandler * audio)300 void lingot_audio_cancel(LingotAudioHandler* audio) {
301 // TODO: avoid
302 fprintf(stderr, "warning: canceling audio thread\n");
303 switch (audio->audio_system) {
304 # ifdef PULSEAUDIO
305 case AUDIO_SYSTEM_PULSEAUDIO:
306 lingot_audio_pulseaudio_cancel(audio);
307 break;
308 # endif
309 default:
310 break;
311 }
312 }
313
lingot_audio_stop(LingotAudioHandler * audio)314 void lingot_audio_stop(LingotAudioHandler* audio) {
315 void* thread_result;
316
317 int result;
318 struct timeval tout, tout_abs;
319 struct timespec tout_tspec;
320
321 gettimeofday(&tout_abs, NULL );
322 tout.tv_sec = 0;
323 tout.tv_usec = 500000;
324
325 if (audio->running == 1) {
326 audio->running = 0;
327 switch (audio->audio_system) {
328 case AUDIO_SYSTEM_JACK:
329 # ifdef JACK
330 lingot_audio_jack_stop(audio);
331 # else
332 assert (0);
333 # endif
334 break;
335 // case AUDIO_SYSTEM_PULSEAUDIO:
336 // pthread_join(audio->thread_input_read, &thread_result);
337 // pthread_attr_destroy(&audio->thread_input_read_attr);
338 // pthread_mutex_destroy(&audio->thread_input_read_mutex);
339 // pthread_cond_destroy(&audio->thread_input_read_cond);
340 // break;
341 default:
342 timeradd(&tout, &tout_abs, &tout_abs);
343 tout_tspec.tv_sec = tout_abs.tv_sec;
344 tout_tspec.tv_nsec = 1000 * tout_abs.tv_usec;
345
346 // watchdog timer
347 pthread_mutex_lock(&audio->thread_input_read_mutex);
348 result = pthread_cond_timedwait(&audio->thread_input_read_cond,
349 &audio->thread_input_read_mutex, &tout_tspec);
350 pthread_mutex_unlock(&audio->thread_input_read_mutex);
351
352 if (result == ETIMEDOUT) {
353 pthread_cancel(audio->thread_input_read);
354 lingot_audio_cancel(audio);
355 } else {
356 pthread_join(audio->thread_input_read, &thread_result);
357 }
358 pthread_attr_destroy(&audio->thread_input_read_attr);
359 pthread_mutex_destroy(&audio->thread_input_read_mutex);
360 pthread_cond_destroy(&audio->thread_input_read_cond);
361 break;
362 }
363 }
364 }
365
366