1 /*
2 * ALSA streaming support
3 *
4 * Originally written by:
5 * Copyright (c) by Devin Heitmueller <dheitmueller@kernellabs.com>
6 * for usage at tvtime
7 * Derived from the alsa-driver test tool latency.c:
8 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
9 *
10 * Copyright (c) 2011 - Mauro Carvalho Chehab
11 * Ported to xawtv, with bug fixes and improvements
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 */
28
29 #include <config.h>
30
31 #ifdef HAVE_ALSA
32 #include "alsa_stream.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sched.h>
38 #include <errno.h>
39 #include <getopt.h>
40 #include <pthread.h>
41 #include <alsa/asoundlib.h>
42 #include <sys/time.h>
43 #include <math.h>
44
45 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
46
47 /* Private vars to control alsa thread status */
48 static int stop_alsa = 0;
49 static snd_htimestamp_t timestamp;
50
51 /* Error handlers */
52 snd_output_t *output = NULL;
53 FILE *error_fp;
54 int verbose = 0;
55
56 struct final_params {
57 int bufsize;
58 int rate;
59 int latency;
60 int channels;
61 };
62
setparams_stream(snd_pcm_t * handle,snd_pcm_hw_params_t * params,snd_pcm_format_t format,int * channels,const char * id)63 static int setparams_stream(snd_pcm_t *handle,
64 snd_pcm_hw_params_t *params,
65 snd_pcm_format_t format,
66 int *channels,
67 const char *id)
68 {
69 int err;
70
71 err = snd_pcm_hw_params_any(handle, params);
72 if (err < 0) {
73 fprintf(error_fp,
74 "alsa: Broken configuration for %s PCM: no configurations available: %s\n",
75 snd_strerror(err), id);
76 return err;
77 }
78
79 err = snd_pcm_hw_params_set_access(handle, params,
80 SND_PCM_ACCESS_RW_INTERLEAVED);
81 if (err < 0) {
82 fprintf(error_fp, "alsa: Access type not available for %s: %s\n", id,
83 snd_strerror(err));
84 return err;
85 }
86
87 err = snd_pcm_hw_params_set_format(handle, params, format);
88 if (err < 0) {
89 fprintf(error_fp, "alsa: Sample format not available for %s: %s\n", id,
90 snd_strerror(err));
91 return err;
92 }
93
94 retry:
95 err = snd_pcm_hw_params_set_channels(handle, params, *channels);
96 if (err < 0) {
97 if (strcmp(id, "capture") == 0 && *channels == 2) {
98 *channels = 1;
99 goto retry; /* Retry with mono capture */
100 }
101 fprintf(error_fp, "alsa: Channels count (%i) not available for %s: %s\n",
102 *channels, id, snd_strerror(err));
103 return err;
104 }
105
106 return 0;
107 }
108
getparams_periods(snd_pcm_t * handle,snd_pcm_hw_params_t * params,unsigned int * usecs,unsigned int * count,const char * id)109 static void getparams_periods(snd_pcm_t *handle,
110 snd_pcm_hw_params_t *params,
111 unsigned int *usecs,
112 unsigned int *count,
113 const char *id)
114 {
115 unsigned min = 0, max = 0;
116
117 snd_pcm_hw_params_get_periods_min(params, &min, 0);
118 snd_pcm_hw_params_get_periods_max(params, &max, 0);
119 if (min && max) {
120 if (verbose)
121 fprintf(error_fp, "alsa: %s periods range between %u and %u. Want: %u\n",
122 id, min, max, *count);
123 if (*count < min)
124 *count = min;
125 if (*count > max)
126 *count = max;
127 }
128
129 min = max = 0;
130 snd_pcm_hw_params_get_period_time_min(params, &min, 0);
131 snd_pcm_hw_params_get_period_time_max(params, &max, 0);
132 if (min && max) {
133 if (verbose)
134 fprintf(error_fp, "alsa: %s period time range between %u and %u. Want: %u\n",
135 id, min, max, *usecs);
136 if (*usecs < min)
137 *usecs = min;
138 if (*usecs > max)
139 *usecs = max;
140 }
141 }
142
setparams_periods(snd_pcm_t * handle,snd_pcm_hw_params_t * params,unsigned int * usecs,unsigned int * count,const char * id)143 static int setparams_periods(snd_pcm_t *handle,
144 snd_pcm_hw_params_t *params,
145 unsigned int *usecs,
146 unsigned int *count,
147 const char *id)
148 {
149 int err;
150
151 err = snd_pcm_hw_params_set_period_time_near(handle, params, usecs, 0);
152 if (err < 0) {
153 fprintf(error_fp, "alsa: Unable to set period time %u for %s: %s\n",
154 *usecs, id, snd_strerror(err));
155 return err;
156 }
157
158 err = snd_pcm_hw_params_set_periods_near(handle, params, count, 0);
159 if (err < 0) {
160 fprintf(error_fp, "alsa: Unable to set %u periods for %s: %s\n",
161 *count, id, snd_strerror(err));
162 return err;
163 }
164
165 if (verbose)
166 fprintf(error_fp, "alsa: %s period set to %u periods of %u time\n",
167 id, *count, *usecs);
168
169 return 0;
170 }
171
setparams_set(snd_pcm_t * handle,snd_pcm_hw_params_t * params,snd_pcm_sw_params_t * swparams,snd_pcm_uframes_t start_treshold,const char * id)172 static int setparams_set(snd_pcm_t *handle,
173 snd_pcm_hw_params_t *params,
174 snd_pcm_sw_params_t *swparams,
175 snd_pcm_uframes_t start_treshold,
176 const char *id)
177 {
178 int err;
179
180 err = snd_pcm_hw_params(handle, params);
181 if (err < 0) {
182 fprintf(error_fp, "alsa: Unable to set hw params for %s: %s\n",
183 id, snd_strerror(err));
184 return err;
185 }
186 err = snd_pcm_sw_params_current(handle, swparams);
187 if (err < 0) {
188 fprintf(error_fp, "alsa: Unable to determine current swparams for %s: %s\n",
189 id, snd_strerror(err));
190 return err;
191 }
192 err = snd_pcm_sw_params_set_start_threshold(handle, swparams,
193 start_treshold);
194 if (err < 0) {
195 fprintf(error_fp, "alsa: Unable to set start threshold mode for %s: %s\n",
196 id, snd_strerror(err));
197 return err;
198 }
199
200 err = snd_pcm_sw_params_set_avail_min(handle, swparams, 4);
201 if (err < 0) {
202 fprintf(error_fp, "alsa: Unable to set avail min for %s: %s\n",
203 id, snd_strerror(err));
204 return err;
205 }
206
207 err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams, SND_PCM_TSTAMP_ENABLE);
208 if (err < 0) {
209 fprintf(error_fp, "alsa: Unable to enable timestamps for %s: %s\n",
210 id, snd_strerror(err));
211 }
212
213 err = snd_pcm_sw_params(handle, swparams);
214 if (err < 0) {
215 fprintf(error_fp, "alsa: Unable to set sw params for %s: %s\n",
216 id, snd_strerror(err));
217 return err;
218 }
219 return 0;
220 }
221
alsa_try_rate(snd_pcm_t * phandle,snd_pcm_t * chandle,snd_pcm_hw_params_t * p_hwparams,snd_pcm_hw_params_t * c_hwparams,int allow_resample,unsigned * ratep,unsigned * ratec)222 static int alsa_try_rate(snd_pcm_t *phandle, snd_pcm_t *chandle,
223 snd_pcm_hw_params_t *p_hwparams,
224 snd_pcm_hw_params_t *c_hwparams,
225 int allow_resample, unsigned *ratep, unsigned *ratec)
226 {
227 int err;
228
229 err = snd_pcm_hw_params_set_rate_near(chandle, c_hwparams, ratec, 0);
230 if (err)
231 return err;
232
233 *ratep = *ratec;
234 err = snd_pcm_hw_params_set_rate_near(phandle, p_hwparams, ratep, 0);
235 if (err)
236 return err;
237
238 if (*ratep == *ratec)
239 return 0;
240
241 if (verbose)
242 fprintf(error_fp,
243 "alsa_try_rate: capture wanted %u, playback wanted %u%s\n",
244 *ratec, *ratep, allow_resample ? " with resample enabled": "");
245
246 return 1; /* No error, but also no match */
247 }
248
setparams(snd_pcm_t * phandle,snd_pcm_t * chandle,snd_pcm_format_t format,int latency,int allow_resample,struct final_params * negotiated)249 static int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle,
250 snd_pcm_format_t format,
251 int latency, int allow_resample,
252 struct final_params *negotiated)
253 {
254 int i;
255 unsigned ratep, ratec = 0;
256 unsigned ratemin = 32000, ratemax = 96000, val;
257 int err, channels = 2;
258 snd_pcm_hw_params_t *p_hwparams, *c_hwparams;
259 snd_pcm_sw_params_t *p_swparams, *c_swparams;
260 snd_pcm_uframes_t c_size, p_psize, c_psize;
261 /* Our latency is 2 periods (in usecs) */
262 unsigned int c_periods = 2, p_periods;
263 unsigned int c_periodtime, p_periodtime;
264 const unsigned int prefered_rates[] = { 44100, 48000, 32000 };
265
266 snd_pcm_hw_params_alloca(&p_hwparams);
267 snd_pcm_hw_params_alloca(&c_hwparams);
268 snd_pcm_sw_params_alloca(&p_swparams);
269 snd_pcm_sw_params_alloca(&c_swparams);
270
271 if (setparams_stream(chandle, c_hwparams, format, &channels, "capture"))
272 return 1;
273
274 if (setparams_stream(phandle, p_hwparams, format, &channels, "playback"))
275 return 1;
276
277 if (allow_resample) {
278 err = snd_pcm_hw_params_set_rate_resample(chandle, c_hwparams, 1);
279 if (err < 0) {
280 fprintf(error_fp, "alsa: Resample setup failed: %s\n", snd_strerror(err));
281 return 1;
282 } else if (verbose)
283 fprintf(error_fp, "alsa: Resample enabled.\n");
284 }
285
286 err = snd_pcm_hw_params_get_rate_min(c_hwparams, &ratemin, 0);
287 if (err >= 0 && verbose)
288 fprintf(error_fp, "alsa: Capture min rate is %d\n", ratemin);
289 err = snd_pcm_hw_params_get_rate_max(c_hwparams, &ratemax, 0);
290 if (err >= 0 && verbose)
291 fprintf(error_fp, "alsa: Capture max rate is %u\n", ratemax);
292
293 err = snd_pcm_hw_params_get_rate_min(p_hwparams, &val, 0);
294 if (err >= 0) {
295 if (verbose)
296 fprintf(error_fp, "alsa: Playback min rate is %u\n", val);
297 if (val > ratemin)
298 ratemin = val;
299 }
300 err = snd_pcm_hw_params_get_rate_max(p_hwparams, &val, 0);
301 if (err >= 0) {
302 if (verbose)
303 fprintf(error_fp, "alsa: Playback max rate is %u\n", val);
304 if (val < ratemax)
305 ratemax = val;
306 }
307
308 if (verbose)
309 fprintf(error_fp,
310 "alsa: Will search a common rate between %u and %u\n",
311 ratemin, ratemax);
312
313 /* First try a set of common rates */
314 err = -1;
315 for (i = 0; i < ARRAY_SIZE(prefered_rates); i++) {
316 if (prefered_rates[i] < ratemin || prefered_rates[i] > ratemax)
317 continue;
318 ratep = ratec = prefered_rates[i];
319 err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams,
320 allow_resample, &ratep, &ratec);
321 if (err == 0)
322 break;
323 }
324
325 if (err != 0) {
326 if (ratemin >= 44100) {
327 for (i = ratemin; i <= ratemax; i += 100) {
328 ratep = ratec = i;
329 err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams,
330 allow_resample, &ratep, &ratec);
331 if (err == 0)
332 break;
333 }
334 } else {
335 for (i = ratemax; i >= ratemin; i -= 100) {
336 ratep = ratec = i;
337 err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams,
338 allow_resample, &ratep, &ratec);
339 if (err == 0)
340 break;
341 }
342 }
343 }
344
345 if (err < 0) {
346 fprintf(error_fp, "alsa: Failed to set a supported rate: %s\n",
347 snd_strerror(err));
348 return 1;
349 }
350 if (ratep != ratec) {
351 if (verbose || allow_resample)
352 fprintf(error_fp,
353 "alsa: Couldn't find a rate that it is supported by both playback and capture\n");
354 return 2;
355 }
356 if (verbose)
357 fprintf(error_fp, "alsa: Using Rate %d\n", ratec);
358
359 /* Negotiate period parameters */
360
361 c_periodtime = latency * 1000 / c_periods;
362 getparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture");
363 p_periods = c_periods * 2;
364 p_periodtime = c_periodtime;
365 getparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback");
366 c_periods = p_periods / 2;
367
368 /*
369 * Some playback devices support a very limited periodtime range. If the user needs to
370 * use a higher latency to avoid overrun/underrun, use an alternate algorithm of incresing
371 * the number of periods, to archive the needed latency
372 */
373 if (p_periodtime < c_periodtime) {
374 c_periodtime = p_periodtime;
375 c_periods = round (latency * 1000.0 / c_periodtime + 0.5);
376 getparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture");
377 p_periods = c_periods * 2;
378 p_periodtime = c_periodtime;
379 getparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback");
380 c_periods = p_periods / 2;
381 }
382
383 if (setparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture"))
384 return 1;
385
386 /* Note we use twice as much periods for the playback buffer, since we
387 will get a period size near the requested time and we don't want it to
388 end up smaller than the capture buffer as then we could end up blocking
389 on writing to it. Note we will configure the playback dev to start
390 playing as soon as it has 2 capture periods worth of data, so this
391 won't influence latency */
392 if (setparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback"))
393 return 1;
394
395 snd_pcm_hw_params_get_period_size(p_hwparams, &p_psize, NULL);
396 snd_pcm_hw_params_get_period_size(c_hwparams, &c_psize, NULL);
397 snd_pcm_hw_params_get_buffer_size(c_hwparams, &c_size);
398
399 latency = c_periods * c_psize;
400 if (setparams_set(phandle, p_hwparams, p_swparams, latency, "playback"))
401 return 1;
402
403 if (setparams_set(chandle, c_hwparams, c_swparams, c_psize, "capture"))
404 return 1;
405
406 if ((err = snd_pcm_prepare(phandle)) < 0) {
407 fprintf(error_fp, "alsa: Prepare error: %s\n", snd_strerror(err));
408 return 1;
409 }
410
411 if (verbose) {
412 fprintf(error_fp, "alsa: Negotiated configuration:\n");
413 snd_pcm_dump_setup(phandle, output);
414 snd_pcm_dump_setup(chandle, output);
415 fprintf(error_fp, "alsa: Parameters are %iHz, %s, %i channels\n",
416 ratep, snd_pcm_format_name(format), channels);
417 fprintf(error_fp, "alsa: Set bitrate to %u%s, buffer size is %u\n", ratec,
418 allow_resample ? " with resample enabled at playback": "",
419 (unsigned int)c_size);
420 }
421
422 negotiated->bufsize = c_size;
423 negotiated->rate = ratep;
424 negotiated->channels = channels;
425 negotiated->latency = latency;
426 return 0;
427 }
428
429 /* Read up to len frames */
readbuf(snd_pcm_t * handle,char * buf,long len)430 static snd_pcm_sframes_t readbuf(snd_pcm_t *handle, char *buf, long len)
431 {
432 snd_pcm_sframes_t r;
433 snd_pcm_uframes_t frames;
434 snd_pcm_htimestamp(handle, &frames, ×tamp);
435 r = snd_pcm_readi(handle, buf, len);
436 if (r < 0 && !(r == -EAGAIN || r == -ENODEV)) {
437 r = snd_pcm_recover(handle, r, 0);
438 if (r < 0)
439 fprintf(error_fp, "alsa: overrun recover error: %s\n", snd_strerror(r));
440 }
441 return r;
442 }
443
444 /* Write len frames (note not up to len, but all of len!) */
writebuf(snd_pcm_t * handle,char * buf,long len)445 static snd_pcm_sframes_t writebuf(snd_pcm_t *handle, char *buf, long len)
446 {
447 snd_pcm_sframes_t r;
448
449 while (!stop_alsa) {
450 r = snd_pcm_writei(handle, buf, len);
451 if (r == len)
452 return 0;
453 if (r < 0) {
454 r = snd_pcm_recover(handle, r, 0);
455 if (r < 0) {
456 fprintf(error_fp, "alsa: underrun recover error: %s\n",
457 snd_strerror(r));
458 return r;
459 }
460 }
461 buf += r * 4;
462 len -= r;
463 snd_pcm_wait(handle, 100);
464 }
465 return -1;
466 }
467
alsa_stream(const char * pdevice,const char * cdevice,int latency)468 static int alsa_stream(const char *pdevice, const char *cdevice, int latency)
469 {
470 snd_pcm_t *phandle, *chandle;
471 char *buffer;
472 int err;
473 ssize_t r;
474 struct final_params negotiated;
475 snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
476 char pdevice_new[32];
477
478 err = snd_output_stdio_attach(&output, error_fp, 0);
479 if (err < 0) {
480 fprintf(error_fp, "alsa: Output failed: %s\n", snd_strerror(err));
481 return 0;
482 }
483
484 /* Open the devices */
485 if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK,
486 0)) < 0) {
487 fprintf(error_fp, "alsa: Cannot open playback device %s: %s\n",
488 pdevice, snd_strerror(err));
489 return 0;
490 }
491 if ((err = snd_pcm_open(&chandle, cdevice, SND_PCM_STREAM_CAPTURE,
492 SND_PCM_NONBLOCK)) < 0) {
493 fprintf(error_fp, "alsa: Cannot open capture device %s: %s\n",
494 cdevice, snd_strerror(err));
495 snd_pcm_close(phandle);
496 return 0;
497 }
498
499 err = setparams(phandle, chandle, format, latency, 0, &negotiated);
500
501 /* Try to use plughw instead, as it allows emulating speed */
502 if (err == 2 && strncmp(pdevice, "hw", 2) == 0) {
503
504 snd_pcm_close(phandle);
505
506 sprintf(pdevice_new, "plug%s", pdevice);
507 pdevice = pdevice_new;
508 if (verbose)
509 fprintf(error_fp, "alsa: Trying %s for playback\n", pdevice);
510 if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK,
511 0)) < 0) {
512 fprintf(error_fp, "alsa: Cannot open playback device %s: %s\n",
513 pdevice, snd_strerror(err));
514 snd_pcm_close(chandle);
515 return 0;
516 }
517
518 err = setparams(phandle, chandle, format, latency, 1, &negotiated);
519 }
520
521 if (err != 0) {
522 fprintf(error_fp, "alsa: setparams failed\n");
523 snd_pcm_close(phandle);
524 snd_pcm_close(chandle);
525 return 1;
526 }
527
528 buffer = malloc((negotiated.bufsize * snd_pcm_format_width(format) / 8)
529 * negotiated.channels);
530 if (buffer == NULL) {
531 fprintf(error_fp, "alsa: Failed allocating buffer for audio\n");
532 snd_pcm_close(phandle);
533 snd_pcm_close(chandle);
534 return 0;
535 }
536
537 if (verbose)
538 fprintf(error_fp,
539 "alsa: stream started from %s to %s (%i Hz, buffer delay = %.2f ms)\n",
540 cdevice, pdevice, negotiated.rate,
541 negotiated.latency * 1000.0 / negotiated.rate);
542
543 while (!stop_alsa) {
544 /* We start with a read and not a wait to auto(re)start the capture */
545 r = readbuf(chandle, buffer, negotiated.bufsize);
546 if (r == 0) /* Succesfully recovered from an overrun? */
547 continue; /* Force restart of capture stream */
548 if (r > 0)
549 writebuf(phandle, buffer, r);
550 /* use poll to wait for next event */
551 while (!stop_alsa && !snd_pcm_wait(chandle, 50))
552 ;
553 }
554
555 snd_pcm_drop(chandle);
556 snd_pcm_drop(phandle);
557
558 snd_pcm_unlink(chandle);
559 snd_pcm_hw_free(phandle);
560 snd_pcm_hw_free(chandle);
561
562 snd_pcm_close(phandle);
563 snd_pcm_close(chandle);
564
565 return 0;
566 }
567
568 struct input_params {
569 char *pdevice;
570 char *cdevice;
571 int latency;
572 };
573
alsa_thread_entry(void * whatever)574 static void *alsa_thread_entry(void *whatever)
575 {
576 struct input_params *inputs = (struct input_params *) whatever;
577
578 if (verbose)
579 fprintf(error_fp, "alsa: starting copying alsa stream from %s to %s\n",
580 inputs->cdevice, inputs->pdevice);
581 alsa_stream(inputs->pdevice, inputs->cdevice, inputs->latency);
582 if (verbose)
583 fprintf(error_fp, "alsa: stream stopped\n");
584
585 free(inputs->pdevice);
586 free(inputs->cdevice);
587 free(inputs);
588
589 return NULL;
590 }
591
592 /*************************************************************************
593 Public functions
594 *************************************************************************/
595
596 static int alsa_is_running = 0;
597 static pthread_t alsa_thread;
598
alsa_thread_startup(const char * pdevice,const char * cdevice,int latency,FILE * __error_fp,int __verbose)599 int alsa_thread_startup(const char *pdevice, const char *cdevice, int latency,
600 FILE *__error_fp, int __verbose)
601 {
602 int ret;
603 struct input_params *inputs;
604
605 if ((strcasecmp(pdevice, "disabled") == 0) ||
606 (strcasecmp(cdevice, "disabled") == 0))
607 return 0;
608
609 if (__error_fp)
610 error_fp = __error_fp;
611 else
612 error_fp = stderr;
613
614 verbose = __verbose;
615
616 if (alsa_is_running) {
617 fprintf(error_fp, "alsa: Already running\n");
618 return EBUSY;
619 }
620
621 inputs = malloc(sizeof(struct input_params));
622 if (inputs == NULL) {
623 fprintf(error_fp, "alsa: failed allocating memory for inputs\n");
624 return ENOMEM;
625 }
626
627 inputs->pdevice = strdup(pdevice);
628 inputs->cdevice = strdup(cdevice);
629 inputs->latency = latency;
630
631 stop_alsa = 0;
632 ret = pthread_create(&alsa_thread, NULL,
633 &alsa_thread_entry, (void *) inputs);
634 if (ret == 0)
635 alsa_is_running = 1;
636
637 return ret;
638 }
639
alsa_thread_stop(void)640 void alsa_thread_stop(void)
641 {
642 if (!alsa_is_running)
643 return;
644
645 stop_alsa = 1;
646 pthread_join(alsa_thread, NULL);
647 alsa_is_running = 0;
648 }
649
alsa_thread_is_running(void)650 int alsa_thread_is_running(void)
651 {
652 return alsa_is_running;
653 }
654
alsa_thread_timestamp(struct timeval * tv)655 void alsa_thread_timestamp(struct timeval *tv)
656 {
657 if (alsa_thread_is_running()) {
658 tv->tv_sec = timestamp.tv_sec;
659 tv->tv_usec = timestamp.tv_nsec / 1000;
660 } else {
661 tv->tv_sec = 0;
662 tv->tv_usec = 0;
663 }
664 }
665 #endif
666