X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=audio%2Falsaaudio.c;h=cb45b49c2ae44cbf5087c14da59bee64311bfab4;hb=c01b2456238f73964876471452e27d4aad7900fb;hp=3fb6cbd41a1b1f55a6bcf639b88f8c4269efe67b;hpb=b4f763b86dda1de95d595e4630c6ea5485484580;p=qemu.git diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 3fb6cbd41..cb45b49c2 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -42,6 +42,8 @@ struct pollhlp { typedef struct ALSAVoiceOut { HWVoiceOut hw; + int wpos; + int pending; void *pcm_buf; snd_pcm_t *handle; struct pollhlp pollhlp; @@ -72,7 +74,8 @@ static struct { int period_size_out_overridden; int verbose; } conf = { - .buffer_size_out = 1024, + .buffer_size_out = 4096, + .period_size_out = 1024, .pcm_name_out = "default", .pcm_name_in = "default", }; @@ -133,7 +136,7 @@ static void alsa_fini_poll (struct pollhlp *hlp) for (i = 0; i < hlp->count; ++i) { qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); } - qemu_free (pfds); + g_free (pfds); } hlp->pfds = NULL; hlp->count = 0; @@ -210,6 +213,10 @@ static void alsa_poll_handler (void *opaque) state = snd_pcm_state (hlp->handle); switch (state) { + case SND_PCM_STATE_SETUP: + alsa_recover (hlp->handle); + break; + case SND_PCM_STATE_XRUN: alsa_recover (hlp->handle); break; @@ -253,7 +260,7 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) if (err < 0) { alsa_logerr (err, "Could not initialize poll mode\n" "Could not obtain poll descriptors\n"); - qemu_free (pfds); + g_free (pfds); return -1; } @@ -281,7 +288,7 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) while (i--) { qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); } - qemu_free (pfds); + g_free (pfds); return -1; } } @@ -311,7 +318,7 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt) +static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) { switch (fmt) { case AUD_FMT_S8: @@ -321,16 +328,36 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt) return SND_PCM_FORMAT_U8; case AUD_FMT_S16: - return SND_PCM_FORMAT_S16_LE; + if (endianness) { + return SND_PCM_FORMAT_S16_BE; + } + else { + return SND_PCM_FORMAT_S16_LE; + } case AUD_FMT_U16: - return SND_PCM_FORMAT_U16_LE; + if (endianness) { + return SND_PCM_FORMAT_U16_BE; + } + else { + return SND_PCM_FORMAT_U16_LE; + } case AUD_FMT_S32: - return SND_PCM_FORMAT_S32_LE; + if (endianness) { + return SND_PCM_FORMAT_S32_BE; + } + else { + return SND_PCM_FORMAT_S32_LE; + } case AUD_FMT_U32: - return SND_PCM_FORMAT_U32_LE; + if (endianness) { + return SND_PCM_FORMAT_U32_BE; + } + else { + return SND_PCM_FORMAT_U32_LE; + } default: dolog ("Internal logic error: Bad audio format %d\n", fmt); @@ -404,10 +431,11 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, } static void alsa_dump_info (struct alsa_params_req *req, - struct alsa_params_obt *obt) + struct alsa_params_obt *obt, + snd_pcm_format_t obtfmt) { dolog ("parameter | requested value | obtained value\n"); - dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); + dolog ("format | %10d | %10d\n", req->fmt, obtfmt); dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels); dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); @@ -592,7 +620,7 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - if ((req->override_mask & 1) && (obt - req->period_size)) + if (((req->override_mask & 1) && (obt - req->period_size))) dolog ("Requested period %s %u was rejected, using %lu\n", size_in_usec ? "time" : "size", req->period_size, obt); } @@ -659,15 +687,15 @@ static int alsa_open (int in, struct alsa_params_req *req, *handlep = handle; if (conf.verbose && - (obt->fmt != req->fmt || + (obtfmt != req->fmt || obt->nchannels != req->nchannels || obt->freq != req->freq)) { - dolog ("Audio paramters for %s\n", typ); - alsa_dump_info (req, obt); + dolog ("Audio parameters for %s\n", typ); + alsa_dump_info (req, obt, obtfmt); } #ifdef DEBUG - alsa_dump_info (req, obt); + alsa_dump_info (req, obt, obtfmt); #endif return 0; @@ -698,41 +726,19 @@ static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) return avail; } -static int alsa_run_out (HWVoiceOut *hw) +static void alsa_write_pending (ALSAVoiceOut *alsa) { - ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; - int rpos, live, decr; - int samples; - uint8_t *dst; - struct st_sample *src; - snd_pcm_sframes_t avail; + HWVoiceOut *hw = &alsa->hw; - live = audio_pcm_hw_get_live_out (hw); - if (!live) { - return 0; - } - - avail = alsa_get_avail (alsa->handle); - if (avail < 0) { - dolog ("Could not get number of available playback frames\n"); - return 0; - } - - decr = audio_MIN (live, avail); - samples = decr; - rpos = hw->rpos; - while (samples) { - int left_till_end_samples = hw->samples - rpos; - int len = audio_MIN (samples, left_till_end_samples); - snd_pcm_sframes_t written; - - src = hw->mix_buf + rpos; - dst = advance (alsa->pcm_buf, rpos << hw->info.shift); - - hw->clip (dst, src, len); + while (alsa->pending) { + int left_till_end_samples = hw->samples - alsa->wpos; + int len = audio_MIN (alsa->pending, left_till_end_samples); + char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift); while (len) { - written = snd_pcm_writei (alsa->handle, dst, len); + snd_pcm_sframes_t written; + + written = snd_pcm_writei (alsa->handle, src, len); if (written <= 0) { switch (written) { @@ -740,13 +746,13 @@ static int alsa_run_out (HWVoiceOut *hw) if (conf.verbose) { dolog ("Failed to write %d frames (wrote zero)\n", len); } - goto exit; + return; case -EPIPE: if (alsa_recover (alsa->handle)) { alsa_logerr (written, "Failed to write %d frames\n", len); - goto exit; + return; } if (conf.verbose) { dolog ("Recovering from playback xrun\n"); @@ -759,7 +765,7 @@ static int alsa_run_out (HWVoiceOut *hw) if (alsa_resume (alsa->handle)) { alsa_logerr (written, "Failed to write %d frames\n", len); - goto exit; + return; } if (conf.verbose) { dolog ("Resuming suspended output stream\n"); @@ -767,25 +773,38 @@ static int alsa_run_out (HWVoiceOut *hw) continue; case -EAGAIN: - goto exit; + return; default: - alsa_logerr (written, "Failed to write %d frames to %p\n", - len, dst); - goto exit; + alsa_logerr (written, "Failed to write %d frames from %p\n", + len, src); + return; } } - rpos = (rpos + written) % hw->samples; - samples -= written; + alsa->wpos = (alsa->wpos + written) % hw->samples; + alsa->pending -= written; len -= written; - dst = advance (dst, written << hw->info.shift); - src += written; } } +} - exit: - hw->rpos = rpos; +static int alsa_run_out (HWVoiceOut *hw, int live) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + int decr; + snd_pcm_sframes_t avail; + + avail = alsa_get_avail (alsa->handle); + if (avail < 0) { + dolog ("Could not get number of available playback frames\n"); + return 0; + } + + decr = audio_MIN (live, avail); + decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending); + alsa->pending += decr; + alsa_write_pending (alsa); return decr; } @@ -797,7 +816,7 @@ static void alsa_fini_out (HWVoiceOut *hw) alsa_anal_close (&alsa->handle, &alsa->pollhlp); if (alsa->pcm_buf) { - qemu_free (alsa->pcm_buf); + g_free (alsa->pcm_buf); alsa->pcm_buf = NULL; } } @@ -810,7 +829,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) snd_pcm_t *handle; struct audsettings obt_as; - req.fmt = aud_to_alsafmt (as->fmt); + req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; req.period_size = conf.period_size_out; @@ -844,11 +863,15 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) return 0; } -static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) +#define VOICE_CTL_PAUSE 0 +#define VOICE_CTL_PREPARE 1 +#define VOICE_CTL_START 2 + +static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) { int err; - if (pause) { + if (ctl == VOICE_CTL_PAUSE) { err = snd_pcm_drop (handle); if (err < 0) { alsa_logerr (err, "Could not stop %s\n", typ); @@ -861,6 +884,13 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) alsa_logerr (err, "Could not prepare handle for %s\n", typ); return -1; } + if (ctl == VOICE_CTL_START) { + err = snd_pcm_start(handle); + if (err < 0) { + alsa_logerr (err, "Could not start handle for %s\n", typ); + return -1; + } + } } return 0; @@ -868,26 +898,33 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) { - va_list ap; - int poll_mode; ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); - switch (cmd) { case VOICE_ENABLE: - ldebug ("enabling voice\n"); - if (poll_mode && alsa_poll_out (hw)) { - poll_mode = 0; + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && alsa_poll_out (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE); } - hw->poll_mode = poll_mode; - return alsa_voice_ctl (alsa->handle, "playback", 0); case VOICE_DISABLE: ldebug ("disabling voice\n"); - return alsa_voice_ctl (alsa->handle, "playback", 1); + if (hw->poll_mode) { + hw->poll_mode = 0; + alsa_fini_poll (&alsa->pollhlp); + } + return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE); } return -1; @@ -901,7 +938,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) snd_pcm_t *handle; struct audsettings obt_as; - req.fmt = aud_to_alsafmt (as->fmt); + req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; req.period_size = conf.period_size_in; @@ -942,7 +979,7 @@ static void alsa_fini_in (HWVoiceIn *hw) alsa_anal_close (&alsa->handle, &alsa->pollhlp); if (alsa->pcm_buf) { - qemu_free (alsa->pcm_buf); + g_free (alsa->pcm_buf); alsa->pcm_buf = NULL; } } @@ -1060,7 +1097,7 @@ static int alsa_run_in (HWVoiceIn *hw) } } - hw->conv (dst, src, nread, &nominal_volume); + hw->conv (dst, src, nread); src = advance (src, nread << hwshift); dst += nread; @@ -1082,23 +1119,26 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size) static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) { - va_list ap; - int poll_mode; ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); - switch (cmd) { case VOICE_ENABLE: - ldebug ("enabling voice\n"); - if (poll_mode && alsa_poll_in (hw)) { - poll_mode = 0; - } - hw->poll_mode = poll_mode; + { + va_list ap; + int poll_mode; - return alsa_voice_ctl (alsa->handle, "capture", 0); + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && alsa_poll_in (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + + return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START); + } case VOICE_DISABLE: ldebug ("disabling voice\n"); @@ -1106,7 +1146,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) hw->poll_mode = 0; alsa_fini_poll (&alsa->pollhlp); } - return alsa_voice_ctl (alsa->handle, "capture", 1); + return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE); } return -1;