tech-kern archive

Call for testers - Audio improvements for NetBSD 8.1

To : tech-kern%netbsd.org@localhost

: Subject : Call for testers - Audio improvements for NetBSD 8.1

: From : Nathanial Sloss <nat%netbsd.org@localhost>

: Date: Sat, 23 Jun 2018 12:23:26 +1000

Hi, I have made changes to audio that improve it's performance and I'd like these changes to be in 8.1. Please test and report back with any problems. Please cc me as I am not subscribed to the list. The patch is also available at: ftp://ftp.netbsd.org/pub/NetBSD/misc/nat/audio-intr-final-v3.diff Summary of changes / improvements: It removes the complication of kthreads and conditional variables by turning the play/rec threads into softints. It also does not copy stream silence as there is already silence in the mixring - speed up should benefit older computers. It steadily caches three blocks of audio keeping the latency within spec as repoted via the sysctl latency variable. With this I was able to have stutter free playback on the x68k emulator even at 12 mhz (at 12mhz the mixing format had to be 8bit 1 ch 8000hz as to reduce the amount of data handled). It treats the second, third, to n number of streams differently mixing them into the mix rings output pointer signifinicantly reducing latency now that more data is cached. It should perform as good as mmapped audio. This patch should work better for slower computers slightly better performance, less stutter (even when switching from X11 to a text vt) due to better buffer utiliziation and near perfect negligable latency even though more data is buffered. Please test as I intend to commit this change in two weeks and it should make NetBSD 8.1. Best regards, Nat

Index: audiovar.h =================================================================== RCS file: /cvsroot/src/sys/dev/audiovar.h,v retrieving revision 1.68 diff -u -p -r1.68 audiovar.h --- audiovar.h 15 Nov 2017 04:28:45 -0000 1.68 +++ audiovar.h 23 Jun 2018 01:39:02 -0000 @@ -279,10 +279,8 @@ struct audio_softc { bool schedule_wih; bool schedule_rih; - lwp_t *sc_playthread; - kcondvar_t sc_condvar; - lwp_t *sc_recthread; - kcondvar_t sc_rcondvar; + void *sc_playmix_ih; + void *sc_recmix_ih; /* These are changeable by sysctl to set the vchan common format */ struct sysctllog *sc_log; /* sysctl log */ Index: audio.c =================================================================== RCS file: /cvsroot/src/sys/dev/audio.c,v retrieving revision 1.457 diff -u -p -r1.457 audio.c --- audio.c 22 May 2018 01:35:49 -0000 1.457 +++ audio.c 23 Jun 2018 01:39:03 -0000 @@ -182,7 +182,6 @@ __KERNEL_RCSID(0, "$NetBSD: audio.c,v 1. #include <sys/audioio.h> #include <sys/device.h> #include <sys/intr.h> -#include <sys/kthread.h> #include <sys/cpu.h> #include <sys/mman.h> @@ -227,6 +226,12 @@ int audio_idle_timeout = 30; mutex_exit(sc->sc_intr_lock); \ } while (0) +#define AUDIO_SOFTINT(x) do { \ + kpreempt_disable(); \ + softint_schedule(x); \ + kpreempt_enable(); \ +} while (0) + int audio_blk_ms = AUDIO_BLK_MS; int audiosetinfo(struct audio_softc *, struct audio_info *, bool, @@ -270,12 +275,12 @@ void audio_rint(void *); void audio_pint(void *); void audio_mix(void *); void audio_upmix(void *); -void audio_play_thread(void *); -void audio_rec_thread(void *); +void audio_play_mix(void *); +void audio_rec_mix(void *); void recswvol_func(struct audio_softc *, struct audio_ringbuffer *, size_t, struct virtual_channel *); void mix_func(struct audio_softc *, struct audio_ringbuffer *, - struct virtual_channel *); + struct virtual_channel *, uint8_t *, size_t, bool); int mix_write(void *); int mix_read(void *); int audio_check_params(struct audio_params *); @@ -285,6 +290,7 @@ static void audio_setblksize(struct audi struct virtual_channel *, int, int); int audio_calc_blksize(struct audio_softc *, const audio_params_t *); void audio_fill_silence(const struct audio_params *, uint8_t *, int); +void audio_wrap_silence(audio_stream_t *, int, int); int audio_silence_copyout(struct audio_softc *, int, struct uio *); static int audio_allocbufs(struct audio_softc *); @@ -546,8 +552,6 @@ audioattach(device_t parent, device_t se cv_init(&sc->sc_rchan, "audiord"); cv_init(&sc->sc_wchan, "audiowr"); cv_init(&sc->sc_lchan, "audiolk"); - cv_init(&sc->sc_condvar,"play"); - cv_init(&sc->sc_rcondvar,"record"); if (hwp == NULL || hwp->get_locks == NULL) { aprint_error(": missing method

"); @@ -855,10 +859,10 @@ audioattach(device_t parent, device_t se #ifdef AUDIO_PM_IDLE callout_schedule(&sc->sc_idle_counter, audio_idle_timeout * hz); #endif - kthread_create(PRI_SOFTSERIAL, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, - audio_rec_thread, sc, &sc->sc_recthread, "audiorec"); - kthread_create(PRI_SOFTSERIAL, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, - audio_play_thread, sc, &sc->sc_playthread, "audiomix"); + sc->sc_recmix_ih = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE, + audio_rec_mix, sc); + sc->sc_playmix_ih = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE, + audio_play_mix, sc); audiorescan(self, "audio", NULL); } @@ -872,8 +876,6 @@ audioactivate(device_t self, enum devact mutex_enter(sc->sc_lock); sc->sc_dying = true; mutex_enter(sc->sc_intr_lock); - cv_broadcast(&sc->sc_condvar); - cv_broadcast(&sc->sc_rcondvar); cv_broadcast(&sc->sc_wchan); cv_broadcast(&sc->sc_rchan); cv_broadcast(&sc->sc_lchan); @@ -902,16 +904,6 @@ audiodetach(device_t self, int flags) sc->sc_dying = true; cv_broadcast(&sc->sc_wchan); cv_broadcast(&sc->sc_rchan); - mutex_enter(sc->sc_intr_lock); - cv_broadcast(&sc->sc_condvar); - cv_broadcast(&sc->sc_rcondvar); - mutex_exit(sc->sc_intr_lock); - mutex_exit(sc->sc_lock); - kthread_join(sc->sc_playthread); - kthread_join(sc->sc_recthread); - mutex_enter(sc->sc_lock); - cv_destroy(&sc->sc_condvar); - cv_destroy(&sc->sc_rcondvar); mutex_exit(sc->sc_lock); /* delete sysctl nodes */ @@ -967,6 +959,14 @@ audiodetach(device_t self, int flags) auconv_delete_encodings(sc->sc_encodings); + if (sc->sc_recmix_ih) { + softint_disestablish(sc->sc_recmix_ih); + sc->sc_recmix_ih = NULL; + } + if (sc->sc_playmix_ih) { + softint_disestablish(sc->sc_playmix_ih); + sc->sc_playmix_ih = NULL; + } if (sc->sc_sih_rd) { softint_disestablish(sc->sc_sih_rd); sc->sc_sih_rd = NULL; @@ -2450,36 +2450,29 @@ audio_drain(struct audio_softc *sc, stru if (used <= 0) return 0; + int blksize; + if (sc->sc_usemixer) + blksize = sc->sc_mixring.sc_mpr.blksize; + else + blksize = cb->blksize; + if (hw == false && !vc->sc_pbus) { /* We've never started playing, probably because the * block was too short. Pad it and start now. */ uint8_t *inp = cb->s.inp; - int blksize = sc->sc_mixring.sc_mpr.blksize; cc = blksize - (inp - cb->s.start) % blksize; - audio_fill_silence(&cb->s.param, inp, cc); - cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc); + audio_wrap_silence(&cb->s, blksize, cc); mutex_exit(sc->sc_intr_lock); error = audiostartp(sc, vc); mutex_enter(sc->sc_intr_lock); if (error) return error; } else if (hw == true) { - used = cb->blksize - (sc->sc_mixring.sc_mpr.s.inp - - sc->sc_mixring.sc_mpr.s.start) % cb->blksize; - while (used > 0) { - cc = sc->sc_mixring.sc_mpr.s.end - - sc->sc_mixring.sc_mpr.s.inp; - if (cc > used) - cc = used; - audio_fill_silence(&cb->s.param, - sc->sc_mixring.sc_mpr.s.inp, cc); - sc->sc_mixring.sc_mpr.s.inp = - audio_stream_add_inp(&sc->sc_mixring.sc_mpr.s, - sc->sc_mixring.sc_mpr.s.inp, cc); - used -= cc; - } + cc = blksize - (sc->sc_mixring.sc_mpr.s.inp - + sc->sc_mixring.sc_mpr.s.start) % blksize; + audio_wrap_silence(&sc->sc_mixring.sc_mpr.s, blksize, cc); mix_write(sc); } /* @@ -2497,13 +2490,8 @@ audio_drain(struct audio_softc *sc, stru vc->sc_draining = true; drops = cb->drops; - if (vc == sc->sc_hwvc) - drops += cb->blksize; - else if (sc->sc_usemixer) - drops += sc->sc_mixring.sc_mpr.blksize * PREFILL_BLOCKS; - error = 0; - while (cb->drops <= drops && !error) { + while (cb->drops == drops && !error) { DPRINTF(("audio_drain: vc=%p used=%d, drops=%ld

", vc, audio_stream_get_used(&vc->sc_mpr.s), @@ -2798,6 +2786,22 @@ audio_calc_blksize(struct audio_softc *s } void +audio_wrap_silence(audio_stream_t *stream, int blksize, int n) +{ + int cc, total; + + total = n; + while (total > 0) { + cc = stream->end - stream->inp; + if (cc > total) + cc = total; + audio_fill_silence(&stream->param, stream->inp, cc); + stream->inp = audio_stream_add_inp(stream, stream->inp, cc); + total -= cc; + } +}; + +void audio_fill_silence(const struct audio_params *params, uint8_t *p, int n) { uint8_t auzero0, auzero1; @@ -3607,7 +3611,7 @@ audiostartr(struct audio_softc *sc, stru mutex_enter(sc->sc_intr_lock); error = mix_read(sc); if (sc->sc_usemixer) - cv_broadcast(&sc->sc_rcondvar); + AUDIO_SOFTINT(sc->sc_recmix_ih); mutex_exit(sc->sc_intr_lock); } vc->sc_rbus = true; @@ -3662,7 +3666,6 @@ audiostartp(struct audio_softc *sc, stru audio_stream_add_outp(&vc->sc_mpr.s, vc->sc_mpr.s.outp, vc->sc_mpr.blksize); error = mix_write(sc); - cv_broadcast(&sc->sc_condvar); } done: mutex_exit(sc->sc_intr_lock); @@ -3723,20 +3726,25 @@ audio_pint(void *v) struct audio_softc *sc; struct audio_ringbuffer *cb; struct virtual_channel *vc; - int blksize, cc, used; + audio_stream_t *stream; + int blksize, cc, silblksize, used; sc = v; vc = sc->sc_hwvc; blksize = vc->sc_mpr.blksize; + if (sc->sc_usemixer) { + stream = &sc->sc_mixring.sc_mpr.s; + silblksize = sc->sc_mixring.sc_mpr.blksize; + cb = &sc->sc_mixring.sc_mpr; + } else { + stream = vc->sc_pustream; + silblksize = blksize; + cb = &vc->sc_mpr; + } if (sc->sc_dying == true || sc->sc_trigger_started == false) return; - if (sc->sc_usemixer) - cb = &sc->sc_mixring.sc_mpr; - else - cb = &vc->sc_mpr; - if (vc->sc_draining && cb->drops != sc->sc_last_drops) { vc->sc_mpr.drops += blksize; cv_broadcast(&sc->sc_wchan); @@ -3747,27 +3755,22 @@ audio_pint(void *v) vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s, vc->sc_mpr.s.outp, blksize); - if (audio_stream_get_used(&cb->s) < blksize) { + used = audio_stream_get_used(stream); + + if (used < silblksize) { DPRINTFN(3, ("HW RING - INSERT SILENCE

")); - used = blksize; - while (used > 0) { - cc = cb->s.end - cb->s.inp; - if (cc > used) - cc = used; - audio_fill_silence(&cb->s.param, cb->s.inp, cc); - cb->s.inp = - audio_stream_add_inp(&cb->s, cb->s.inp, cc); - used -= cc; - } - vc->sc_mpr.drops += blksize; + cc = silblksize - used; + audio_wrap_silence(stream, silblksize, cc); + cb->drops += silblksize; } mix_write(sc); - if (sc->sc_usemixer) - cv_broadcast(&sc->sc_condvar); - else + if (!sc->sc_usemixer) cv_broadcast(&sc->sc_wchan); + else if (!vc->sc_draining) + AUDIO_SOFTINT(sc->sc_playmix_ih); + } void @@ -3778,9 +3781,13 @@ audio_mix(void *v) struct audio_chan *chan; struct virtual_channel *vc; struct audio_ringbuffer *cb; + audio_stream_t *hwstream; stream_fetcher_t *fetcher; uint8_t *inp; - int cc, cc1, used, blksize; + int cc, used, blksize; + size_t total; + unsigned int chancount; + bool first; sc = v; @@ -3792,7 +3799,13 @@ audio_mix(void *v) if (sc->sc_dying == true) return; + chancount = 0; blksize = sc->sc_mixring.sc_mpr.blksize; + hwstream = &sc->sc_mixring.sc_mpr.s; + + if (audio_stream_get_used(hwstream) > sc->sc_hwvc->sc_mpr.usedlow) + return; + SIMPLEQ_FOREACH(chan, &sc->sc_audiochan, entries) { vc = chan->vc; @@ -3804,15 +3817,27 @@ audio_mix(void *v) cb = &vc->sc_mpr; sc->sc_writeme = true; + chancount++; + if (chancount == 1) + first = true; + else + first = false; inp = cb->s.inp; cb->stamp += blksize; + total = blksize; + if (cb->mmapped) { DPRINTF(("audio_pint: vc=%p mmapped outp=%p cc=%d " "inp=%p

", vc, cb->s.outp, blksize, cb->s.inp)); mutex_enter(sc->sc_intr_lock); - mix_func(sc, cb, vc); + if (first) + mix_func(sc, cb, vc, hwstream->inp, total, + first); + else + mix_func(sc, cb, vc, __UNCONST(hwstream->outp), + total, first); cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize); mutex_exit(sc->sc_intr_lock); @@ -3861,7 +3886,7 @@ audio_mix(void *v) * at accurate timing. If used < blksize, uaudio(4) already * request transfer of garbage data. */ - if (used <= sc->sc_hwvc->sc_mpr.usedlow && !cb->copying && + if (used <= cb->usedlow && !cb->copying && vc->sc_npfilters > 0) { /* we might have data in filter pipeline */ null_fetcher.fetch_to = null_fetcher_fetch_to; @@ -3876,6 +3901,7 @@ audio_mix(void *v) cb->fstamp += used - audio_stream_get_used(vc->sc_pustream); used = audio_stream_get_used(&cb->s); + sc->schedule_wih = true; } if (used < blksize) { /* we don't have a full block to use */ @@ -3889,31 +3915,37 @@ audio_mix(void *v) "used=%d blksize=%d

", vc, used, blksize)); inp = cb->s.inp; - cc = blksize - (inp - cb->s.start) % blksize; + cc = blksize - used; if (cb->pause) cb->pdrops += cc; else { cb->drops += cc; vc->sc_playdrop += cc; } - - audio_fill_silence(&cb->s.param, inp, cc); - cb->s.inp = audio_stream_add_inp(&cb->s, inp, - cc); - - /* Clear next block to keep ahead of the DMA. */ - used = audio_stream_get_used(&cb->s); - if (used + blksize < cb->s.end - cb->s.start) { - audio_fill_silence(&cb->s.param, cb->s.inp, - blksize); + if (first) + audio_wrap_silence(&cb->s, blksize, cc); + else { + total = blksize - cc; + cb->s.inp = audio_stream_add_inp(&cb->s, + inp, cc); } } } DPRINTFN(5, ("audio_pint: vc=%p outp=%p used=%d cc=%d

", vc, cb->s.outp, used, blksize)); + + /* + * Mix at the tail of the ringbuffer if it's the first vc + * otherwize mix at the current playback position in the + * mix ring. This imroves latency for virtual channels + * greater than 1. + */ mutex_enter(sc->sc_intr_lock); - mix_func(sc, cb, vc); + if (first) + mix_func(sc, cb, vc, hwstream->inp, total, first); + else + mix_func(sc, cb, vc, __UNCONST(hwstream->outp), total, first); mutex_exit(sc->sc_intr_lock); cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize); @@ -3932,27 +3964,16 @@ audio_mix(void *v) mutex_enter(sc->sc_intr_lock); vc = sc->sc_hwvc; - cb = &sc->sc_mixring.sc_mpr; - inp = cb->s.inp; - cc = blksize - (inp - cb->s.start) % blksize; + inp = hwstream->inp; if (sc->sc_writeme == false) { + cc = blksize - audio_stream_get_used(hwstream); DPRINTFN(3, ("MIX RING EMPTY - INSERT SILENCE

")); - audio_fill_silence(&vc->sc_pustream->param, inp, cc); - sc->sc_mixring.sc_mpr.drops += cc; - } else + audio_wrap_silence(hwstream, blksize, cc); + vc->sc_mpr.drops += cc; + } else { cc = blksize; - cb->s.inp = audio_stream_add_inp(&cb->s, cb->s.inp, cc); - cc = blksize; - cc1 = sc->sc_mixring.sc_mpr.s.end - sc->sc_mixring.sc_mpr.s.inp; - if (cc1 < cc) { - audio_fill_silence(&vc->sc_pustream->param, - sc->sc_mixring.sc_mpr.s.inp, cc1); - cc -= cc1; - audio_fill_silence(&vc->sc_pustream->param, - sc->sc_mixring.sc_mpr.s.start, cc); - } else - audio_fill_silence(&vc->sc_pustream->param, - sc->sc_mixring.sc_mpr.s.inp, cc); + hwstream->inp = audio_stream_add_inp(hwstream, inp, cc); + } mutex_exit(sc->sc_intr_lock); kpreempt_disable(); @@ -3989,7 +4010,7 @@ audio_rint(void *v) mix_read(sc); if (sc->sc_usemixer) - cv_broadcast(&sc->sc_rcondvar); + AUDIO_SOFTINT(sc->sc_recmix_ih); else cv_broadcast(&sc->sc_rchan); } @@ -4114,9 +4135,7 @@ audio_upmix(void *v) blksize); } } - kpreempt_disable(); - softint_schedule(sc->sc_sih_rd); - kpreempt_enable(); + AUDIO_SOFTINT(sc->sc_sih_rd); } int @@ -4243,6 +4262,8 @@ audio_set_vchan_defaults(struct audio_so vc->sc_rparams = sc->sc_vchan_params; vc->sc_pparams = sc->sc_vchan_params; + vc->sc_pustream = &vc->sc_mpr.s; + vc->sc_rustream = &vc->sc_mrr.s; } return error; @@ -5586,19 +5607,24 @@ mix_write(void *arg) stream_filter_t *filter; stream_fetcher_t *fetcher; stream_fetcher_t null_fetcher; - int cc, cc1, cc2, error, used; + int blksize, cc, cc1, cc2, error, used; const uint8_t *orig; uint8_t *tocopy; vc = sc->sc_hwvc; error = 0; + if (sc->sc_usemixer) + blksize = sc->sc_mixring.sc_mpr.blksize; + else + blksize = vc->sc_mpr.blksize; + if (sc->sc_usemixer && - audio_stream_get_used(vc->sc_pustream) <= - sc->sc_mixring.sc_mpr.blksize) { + audio_stream_get_used(vc->sc_pustream) < + vc->sc_mpr.usedlow) { tocopy = vc->sc_pustream->inp; orig = sc->sc_mixring.sc_mpr.s.outp; - used = sc->sc_mixring.sc_mpr.blksize; + used = blksize; while (used > 0) { cc = used; @@ -5621,35 +5647,34 @@ mix_write(void *arg) } vc->sc_pustream->inp = audio_stream_add_inp(vc->sc_pustream, - vc->sc_pustream->inp, sc->sc_mixring.sc_mpr.blksize); + vc->sc_pustream->inp, blksize); sc->sc_mixring.sc_mpr.s.outp = audio_stream_add_outp(&sc->sc_mixring.sc_mpr.s, - sc->sc_mixring.sc_mpr.s.outp, - sc->sc_mixring.sc_mpr.blksize); + sc->sc_mixring.sc_mpr.s.outp, blksize); } + blksize = vc->sc_mpr.blksize; if (vc->sc_npfilters > 0 && (sc->sc_usemixer || sc->sc_trigger_started)) { null_fetcher.fetch_to = null_fetcher_fetch_to; filter = vc->sc_pfilters[0]; filter->set_fetcher(filter, &null_fetcher); fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base; - fetcher->fetch_to(sc, fetcher, &vc->sc_mpr.s, - vc->sc_mpr.blksize * 2); + fetcher->fetch_to(sc, fetcher, &vc->sc_mpr.s, blksize * 2); } if (sc->hw_if->trigger_output && sc->sc_trigger_started == false) { DPRINTF(("%s: call trigger_output

", __func__)); sc->sc_trigger_started = true; error = sc->hw_if->trigger_output(sc->hw_hdl, - vc->sc_mpr.s.start, vc->sc_mpr.s.end, vc->sc_mpr.blksize, + vc->sc_mpr.s.start, vc->sc_mpr.s.end, blksize, audio_pint, (void *)sc, &vc->sc_mpr.s.param); } else if (sc->hw_if->start_output) { DPRINTF(("%s: call start_output

", __func__)); sc->sc_trigger_started = true; error = sc->hw_if->start_output(sc->hw_hdl, - __UNCONST(vc->sc_mpr.s.outp), vc->sc_mpr.blksize, + __UNCONST(vc->sc_mpr.s.outp), blksize, audio_pint, (void *)sc); } @@ -5666,18 +5691,18 @@ mix_write(void *arg) #define DEF_MIX_FUNC(bits, type, bigger_type, MINVAL, MAXVAL) \ static void \ mix_func##bits(struct audio_softc *sc, struct audio_ringbuffer *cb, \ - struct virtual_channel *vc) \ + struct virtual_channel *vc, uint8_t *target, \ + size_t bytes, bool first) \ { \ - int blksize, cc, cc1, cc2, m, resid; \ + int cc, cc1, cc2, m, resid; \ bigger_type product; \ bigger_type result; \ type *orig, *tomix; \ \ - blksize = sc->sc_mixring.sc_mpr.blksize; \ - resid = blksize; \ + resid = bytes; \ \ tomix = __UNCONST(cb->s.outp); \ - orig = (type *)(sc->sc_mixring.sc_mpr.s.inp); \ + orig = (type *)(target); \ \ while (resid > 0) { \ cc = resid; \ @@ -5695,22 +5720,23 @@ mix_write(void *arg) tomix[m] = (bigger_type)tomix[m] * \ (bigger_type)(vc->sc_swvol) / 255; \ vol_done: \ + if (first) { \ + orig[m] = tomix[m]; \ + continue; \ + } \ result = (bigger_type)orig[m] + tomix[m]; \ - if (sc->sc_opens == 1) \ - goto adj_done; \ product = (bigger_type)orig[m] * tomix[m]; \ if (orig[m] > 0 && tomix[m] > 0) \ result -= product / MAXVAL; \ else if (orig[m] < 0 && tomix[m] < 0) \ result -= product / MINVAL; \ -adj_done: \ orig[m] = result; \ } \ \ if (&orig[m] >= \ (type *)sc->sc_mixring.sc_mpr.s.end) \ orig = \ - (type *)sc->sc_mixring.sc_mpr.s.start; \ + (type *)sc->sc_mixring.sc_mpr.s.start; \ if (&tomix[m] >= (type *)cb->s.end) \ tomix = (type *)cb->s.start; \ \ @@ -5724,18 +5750,18 @@ DEF_MIX_FUNC(32, int32_t, int64_t, INT32 void mix_func(struct audio_softc *sc, struct audio_ringbuffer *cb, - struct virtual_channel *vc) + struct virtual_channel *vc, uint8_t *target, size_t used, bool first) { switch (sc->sc_vchan_params.precision) { case 8: - mix_func8(sc, cb, vc); + mix_func8(sc, cb, vc, target, used, first); break; case 16: - mix_func16(sc, cb, vc); + mix_func16(sc, cb, vc, target, used, first); break; case 24: case 32: - mix_func32(sc, cb, vc); + mix_func32(sc, cb, vc, target, used, first); break; default: break; @@ -5967,59 +5993,43 @@ audio_query_encoding(struct audio_softc } void -audio_play_thread(void *v) +audio_play_mix(void *v) { struct audio_softc *sc; sc = (struct audio_softc *)v; - for (;;) { - mutex_enter(sc->sc_lock); - if (sc->sc_dying) { - mutex_exit(sc->sc_lock); - kthread_exit(0); - } - if (!sc->sc_trigger_started) - goto play_wait; - - while (!sc->sc_dying && sc->sc_usemixer && - audio_stream_get_used(&sc->sc_mixring.sc_mpr.s) < - sc->sc_mixring.sc_mpr.blksize) - audio_mix(sc); - -play_wait: + mutex_enter(sc->sc_lock); + if (sc->sc_dying) { mutex_exit(sc->sc_lock); - - mutex_enter(sc->sc_intr_lock); - cv_wait_sig(&sc->sc_condvar, sc->sc_intr_lock); - mutex_exit(sc->sc_intr_lock); + return; + } + + int i; + for (i = 0; i < PREFILL_BLOCKS; i++) { + int used = audio_stream_get_used(&sc->sc_mixring.sc_mpr.s); + if (!sc->sc_dying && !sc->sc_hwvc->sc_draining && + sc->sc_usemixer && used < sc->sc_mixring.sc_mpr.blksize * + PREFILL_BLOCKS) + audio_mix(sc); } + mutex_exit(sc->sc_lock); } void -audio_rec_thread(void *v) +audio_rec_mix(void *v) { struct audio_softc *sc; sc = (struct audio_softc *)v; - for (;;) { - mutex_enter(sc->sc_lock); - if (sc->sc_dying) { - mutex_exit(sc->sc_lock); - kthread_exit(0); - } - if (!sc->sc_rec_started) - goto rec_wait; - - audio_upmix(sc); -rec_wait: + mutex_enter(sc->sc_lock); + if (sc->sc_dying) { mutex_exit(sc->sc_lock); - - mutex_enter(sc->sc_intr_lock); - cv_wait_sig(&sc->sc_rcondvar, sc->sc_intr_lock); - mutex_exit(sc->sc_intr_lock); + return; } + audio_upmix(sc); + mutex_exit(sc->sc_lock); }