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; |
