Mercurial > audlegacy-plugins
comparison src/alsa/audio.c @ 3153:3a1d7d680816
More ALSA restructuring from Hans de Goede. (Closes #68)
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Tue, 12 May 2009 15:29:18 -0500 |
parents | 4b50e2a0ae1f |
children |
comparison
equal
deleted
inserted
replaced
3152:6f45c19b3f74 | 3153:3a1d7d680816 |
---|---|
61 /* for audio thread */ | 61 /* for audio thread */ |
62 static GThread *audio_thread; /* audio loop thread */ | 62 static GThread *audio_thread; /* audio loop thread */ |
63 static gint thread_buffer_size; /* size of intermediate buffer in bytes */ | 63 static gint thread_buffer_size; /* size of intermediate buffer in bytes */ |
64 static gchar *thread_buffer; /* audio intermediate buffer */ | 64 static gchar *thread_buffer; /* audio intermediate buffer */ |
65 static gint rd_index, wr_index; /* current read/write position in int-buffer */ | 65 static gint rd_index, wr_index; /* current read/write position in int-buffer */ |
66 static gint buffer_empty; /* is the buffer empty? */ | |
66 static gboolean pause_request; /* pause status currently requested */ | 67 static gboolean pause_request; /* pause status currently requested */ |
67 static gint flush_request; /* flush status (time) currently requested */ | 68 static gint flush_request; /* flush status (time) currently requested */ |
68 static gint prebuffer_size; | 69 static gint prebuffer_size; |
69 GStaticMutex alsa_mutex = G_STATIC_MUTEX_INIT; | 70 GStaticMutex alsa_mutex = G_STATIC_MUTEX_INIT; |
70 | 71 |
146 g_static_mutex_lock(&alsa_mutex); | 147 g_static_mutex_lock(&alsa_mutex); |
147 | 148 |
148 if (!going || paused || prebuffer || alsa_pcm == NULL) | 149 if (!going || paused || prebuffer || alsa_pcm == NULL) |
149 ret = FALSE; | 150 ret = FALSE; |
150 else | 151 else |
151 ret = snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING && | 152 ret = snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING || |
152 !paused && | 153 get_thread_buffer_filled(); |
153 !prebuffer && | |
154 get_thread_buffer_filled() > hw_period_size_in; | |
155 | 154 |
156 g_static_mutex_unlock(&alsa_mutex); | 155 g_static_mutex_unlock(&alsa_mutex); |
157 | 156 |
158 return ret; | 157 return ret; |
159 } | 158 } |
160 | 159 |
161 static gint | 160 /* update and get the available space on h/w buffer */ |
162 alsa_recovery(gint err) | 161 static gint alsa_get_avail(void) |
163 { | |
164 gint err2; | |
165 | |
166 /* if debug mode is enabled, dump ALSA state to console */ | |
167 if (alsa_cfg.debug) | |
168 { | |
169 snd_pcm_status_t *alsa_status = NULL; | |
170 snd_pcm_status_alloca(&alsa_status); | |
171 if (snd_pcm_status(alsa_pcm, alsa_status) < 0) | |
172 g_warning("xrun_recover(): snd_pcm_status() failed"); | |
173 else | |
174 { | |
175 printf("Status:\n"); | |
176 snd_pcm_status_dump(alsa_status, logs); | |
177 } | |
178 } | |
179 | |
180 /* | |
181 * specifically handle -EPIPE and -ESTRPIPE to recover | |
182 * PCM fragment periods without losing data. | |
183 */ | |
184 switch (err) | |
185 { | |
186 case -ESTRPIPE: | |
187 case ESTRPIPE: /* "suspend": wait until ALSA is "running" again. */ | |
188 while ((err2 = snd_pcm_resume(alsa_pcm)) == -EAGAIN) | |
189 g_usleep(100000); | |
190 | |
191 if (err2 < 0) | |
192 return snd_pcm_prepare(alsa_pcm); | |
193 | |
194 break; | |
195 | |
196 case -EPIPE: | |
197 case EPIPE: /* under-run and the I/O pipe closed on us */ | |
198 return snd_pcm_prepare(alsa_pcm); | |
199 break; | |
200 | |
201 case EINTR: | |
202 case -EINTR: | |
203 break; | |
204 | |
205 default: | |
206 g_warning("Unhandled ALSA exception code %d (%s), trying hard restart.", err, snd_strerror(err)); | |
207 return snd_pcm_prepare(alsa_pcm); | |
208 break; | |
209 } | |
210 | |
211 return 0; | |
212 } | |
213 | |
214 /* update and get the available space on h/w buffer (in frames) */ | |
215 static snd_pcm_sframes_t alsa_get_avail(void) | |
216 { | 162 { |
217 snd_pcm_sframes_t ret; | 163 snd_pcm_sframes_t ret; |
218 | 164 |
219 if (alsa_pcm == NULL) | 165 if (alsa_pcm == NULL) |
220 return 0; | 166 return 0; |
221 | 167 |
222 while ((ret = snd_pcm_avail_update(alsa_pcm)) < 0) | 168 while ((ret = snd_pcm_avail_update(alsa_pcm)) < 0) |
223 { | 169 { |
224 ret = alsa_recovery(ret); | 170 ret = snd_pcm_recover(alsa_pcm, ret, !alsa_cfg.debug); |
225 if (ret < 0) | 171 if (ret < 0) |
226 { | 172 { |
227 g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s", | 173 g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s", |
228 snd_strerror(ret)); | 174 snd_strerror(ret)); |
229 return 0; | 175 return 0; |
230 } | 176 } |
231 } | 177 } |
232 return ret; | 178 return snd_pcm_frames_to_bytes(alsa_pcm, ret); |
233 } | 179 } |
234 | 180 |
235 /* get the free space on buffer */ | 181 /* get the free space on buffer */ |
236 int alsa_free(void) | 182 int alsa_free(void) |
237 { | 183 { |
245 remove_prebuffer = FALSE; | 191 remove_prebuffer = FALSE; |
246 } | 192 } |
247 if (prebuffer) | 193 if (prebuffer) |
248 remove_prebuffer = TRUE; | 194 remove_prebuffer = TRUE; |
249 | 195 |
250 ret = thread_buffer_size - get_thread_buffer_filled() - 1; | 196 ret = thread_buffer_size - get_thread_buffer_filled(); |
251 | 197 |
252 g_static_mutex_unlock(&alsa_mutex); | 198 g_static_mutex_unlock(&alsa_mutex); |
253 | 199 |
254 return ret; | 200 return ret; |
255 } | 201 } |
342 } | 288 } |
343 /* correct the offset */ | 289 /* correct the offset */ |
344 output_time_offset = time; | 290 output_time_offset = time; |
345 alsa_total_written = (guint64) time * inputf->bps / 1000; | 291 alsa_total_written = (guint64) time * inputf->bps / 1000; |
346 rd_index = wr_index = alsa_hw_written = 0; | 292 rd_index = wr_index = alsa_hw_written = 0; |
293 buffer_empty = 1; | |
347 } | 294 } |
348 | 295 |
349 void alsa_flush(gint time) | 296 void alsa_flush(gint time) |
350 { | 297 { |
351 flush_request = time; | 298 flush_request = time; |
556 */ | 503 */ |
557 | 504 |
558 /* return the size of audio data filled in the audio thread buffer */ | 505 /* return the size of audio data filled in the audio thread buffer */ |
559 static int get_thread_buffer_filled(void) | 506 static int get_thread_buffer_filled(void) |
560 { | 507 { |
561 if (wr_index >= rd_index) | 508 if (buffer_empty) |
509 return 0; | |
510 | |
511 if (wr_index > rd_index) | |
562 return wr_index - rd_index; | 512 return wr_index - rd_index; |
513 | |
563 return thread_buffer_size - (rd_index - wr_index); | 514 return thread_buffer_size - (rd_index - wr_index); |
564 } | 515 } |
565 | 516 |
566 gint alsa_get_output_time(void) | 517 gint alsa_get_output_time(void) |
567 { | 518 { |
632 int wr; | 583 int wr; |
633 cnt = MIN(length, thread_buffer_size - wr_index); | 584 cnt = MIN(length, thread_buffer_size - wr_index); |
634 memcpy(thread_buffer + wr_index, src, cnt); | 585 memcpy(thread_buffer + wr_index, src, cnt); |
635 wr = (wr_index + cnt) % thread_buffer_size; | 586 wr = (wr_index + cnt) % thread_buffer_size; |
636 wr_index = wr; | 587 wr_index = wr; |
588 buffer_empty = 0; | |
637 length -= cnt; | 589 length -= cnt; |
638 src += cnt; | 590 src += cnt; |
639 } | 591 } |
640 g_static_mutex_unlock(&alsa_mutex); | 592 g_static_mutex_unlock(&alsa_mutex); |
641 } | 593 } |
658 data += written; | 610 data += written; |
659 alsa_hw_written += written; | 611 alsa_hw_written += written; |
660 } | 612 } |
661 else | 613 else |
662 { | 614 { |
663 int err = alsa_recovery((int)written_frames); | 615 int err = snd_pcm_recover(alsa_pcm, written_frames, !alsa_cfg.debug); |
664 if (err < 0) | 616 if (err < 0) |
665 { | 617 { |
666 g_warning("alsa_write_audio(): write error: %s", | 618 g_warning("alsa_write_audio(): write error: %s", |
667 snd_strerror(err)); | 619 snd_strerror(err)); |
668 break; | 620 break; |
670 } | 622 } |
671 } | 623 } |
672 } | 624 } |
673 | 625 |
674 /* transfer audio data from thread buffer to h/w */ | 626 /* transfer audio data from thread buffer to h/w */ |
675 static void alsa_write_out_thread_data(void) | 627 static void alsa_write_out_thread_data(gint avail) |
676 { | 628 { |
677 gint length, cnt, avail; | 629 gint length, cnt; |
678 | 630 |
679 length = MIN(hw_period_size_in, get_thread_buffer_filled()); | 631 length = MIN(avail, get_thread_buffer_filled()); |
680 avail = snd_pcm_frames_to_bytes(alsa_pcm, alsa_get_avail()); | |
681 length = MIN(length, avail); | |
682 while (length > 0) | 632 while (length > 0) |
683 { | 633 { |
684 int rd; | 634 int rd; |
685 cnt = MIN(length, thread_buffer_size - rd_index); | 635 cnt = MIN(length, thread_buffer_size - rd_index); |
686 alsa_do_write(thread_buffer + rd_index, cnt); | 636 alsa_do_write(thread_buffer + rd_index, cnt); |
687 rd = (rd_index + cnt) % thread_buffer_size; | 637 rd = (rd_index + cnt) % thread_buffer_size; |
688 rd_index = rd; | 638 rd_index = rd; |
639 if (rd_index == wr_index) | |
640 buffer_empty = 1; | |
689 length -= cnt; | 641 length -= cnt; |
690 } | 642 } |
691 } | 643 } |
692 | 644 |
693 /* audio thread loop */ | 645 /* audio thread loop */ |
694 /* FIXME: proper lock? */ | 646 /* FIXME: proper lock? */ |
695 static void *alsa_loop(void *arg) | 647 static void *alsa_loop(void *arg) |
696 { | 648 { |
697 struct pollfd *pfd; | 649 struct pollfd *pfd; |
698 unsigned short *revents; | 650 gint npfds, err, avail; |
699 gint i, npfds, err, wr; | |
700 | 651 |
701 g_static_mutex_lock(&alsa_mutex); | 652 g_static_mutex_lock(&alsa_mutex); |
702 | 653 |
703 npfds = snd_pcm_poll_descriptors_count(alsa_pcm); | 654 npfds = snd_pcm_poll_descriptors_count(alsa_pcm); |
704 if (npfds <= 0) | 655 if (npfds <= 0) |
705 goto _error; | 656 goto _error; |
706 | 657 |
707 pfd = alloca(sizeof(*pfd) * npfds); | 658 pfd = alloca(sizeof(*pfd) * npfds); |
708 revents = alloca(sizeof(*revents) * npfds); | |
709 err = snd_pcm_poll_descriptors(alsa_pcm, pfd, npfds); | 659 err = snd_pcm_poll_descriptors(alsa_pcm, pfd, npfds); |
710 if (err != npfds) | 660 if (err != npfds) |
711 goto _error; | 661 goto _error; |
712 | 662 |
713 while (going && alsa_pcm) | 663 while (going && alsa_pcm) |
714 { | 664 { |
715 if (get_thread_buffer_filled() > prebuffer_size) | 665 if (get_thread_buffer_filled() > prebuffer_size) |
716 prebuffer = FALSE; | 666 prebuffer = FALSE; |
717 if (!paused && !prebuffer && | 667 if (!paused && !prebuffer && get_thread_buffer_filled()) |
718 get_thread_buffer_filled() > hw_period_size_in) | |
719 { | 668 { |
720 g_static_mutex_unlock(&alsa_mutex); | 669 avail = alsa_get_avail(); |
721 err = poll(pfd, npfds, 10); | 670 |
722 g_static_mutex_lock(&alsa_mutex); | 671 if (avail == 0) { |
723 | 672 g_static_mutex_unlock(&alsa_mutex); |
724 if (err == 0) | 673 err = poll(pfd, npfds, 10); |
725 continue; | 674 g_static_mutex_lock(&alsa_mutex); |
726 | 675 |
727 if (err < 0) { | 676 if (err < 0 && errno != EINTR) |
728 if (errno == EINTR) | 677 goto _error; |
729 continue; | 678 |
730 goto _error; | 679 avail = alsa_get_avail(); |
731 } | 680 } |
732 | 681 |
733 err = snd_pcm_poll_descriptors_revents(alsa_pcm, pfd, npfds, revents); | 682 alsa_write_out_thread_data(avail); |
734 if (err < 0) | |
735 goto _error; | |
736 | |
737 wr = 0; | |
738 for (i = 0; i < npfds; i++) { | |
739 if (revents[i] & (POLLERR | POLLNVAL)) { | |
740 wr = -1; | |
741 break; | |
742 } | |
743 if (revents[i] & POLLOUT) | |
744 wr = 1; | |
745 } | |
746 | |
747 if (wr > 0) | |
748 { | |
749 alsa_write_out_thread_data(); | |
750 } | |
751 else if (wr < 0) | |
752 { | |
753 alsa_recovery(wr); | |
754 } | |
755 } | 683 } |
756 else /* XXX: why is this here? --nenolod */ | 684 else |
757 { | 685 { |
758 g_static_mutex_unlock(&alsa_mutex); | 686 g_static_mutex_unlock(&alsa_mutex); |
759 g_usleep(10000); | 687 g_usleep(10000); |
760 g_static_mutex_lock(&alsa_mutex); | 688 g_static_mutex_lock(&alsa_mutex); |
761 } | 689 } |
815 prebuffer_size = 8192; | 743 prebuffer_size = 8192; |
816 thread_buffer_size += hw_buffer_size; | 744 thread_buffer_size += hw_buffer_size; |
817 thread_buffer_size -= thread_buffer_size % hw_period_size; | 745 thread_buffer_size -= thread_buffer_size % hw_period_size; |
818 thread_buffer = g_malloc0(thread_buffer_size); | 746 thread_buffer = g_malloc0(thread_buffer_size); |
819 wr_index = rd_index = 0; | 747 wr_index = rd_index = 0; |
748 buffer_empty = 1; | |
820 pause_request = FALSE; | 749 pause_request = FALSE; |
821 flush_request = -1; | 750 flush_request = -1; |
822 | 751 |
823 audio_thread = g_thread_create((GThreadFunc)alsa_loop, NULL, TRUE, NULL); | 752 audio_thread = g_thread_create((GThreadFunc)alsa_loop, NULL, TRUE, NULL); |
824 return 1; | 753 return 1; |