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;