audio: documentation fixes.
audio: new backend api (first part of the surround sound patch series). -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJdibVzAAoJEEy22O7T6HE4FzIQAKhh+spepRDCWHr5Fp0Ur3pc jo4ewzU7NJcth6lwWElpCt0P3rdscFBZefz0VeZ7alzYFD3lNryO9L6HBu5TvkgK 07KJfi6BJNo+rJfqF9+n+IP6eECKDeKgubZzcTQ8q4cu7BDSu8LiM1lds70HgcSN 1R/ddnwI0Xoo053M7CFVUEoux15wo5/SxP9atZz3ZkTBru2ZoHLHM9ZrQ2g0WOVs ewBCOGANhEudbq3QgYHVhuYDXy5SY4Mew9E3hbXOv0WdOV+AJ96A94oAyPC5aGdZ k/U8PhyNRnFzFsyyWHt/Cuxg1ArrYb0JEOJKTrcOsZVjX3QCTb8fyV9MVU80wym2 PU8if5UGxz1wS+JPLOaaoLXNo3Drjbmk4c1cZRUU2d/Exv9DTiV34DB1fqkq4WOQ XEzVmnSYjjq4Rzfed9shPQpeKWW0CmNn6rKheIYjsqDJvF86VkCOVZ26G2GJf+KI g15UhncidNtmFncP3LzE84+SElE89bSu0pvt0vkXv1SexVVopJI/LuT02uZL/Ok9 Y4+uZURUPhOm8XA93gczSPY3WJgQ6ljRGUZlWFQNMsurBfJQGlDSSVKCxCmD8Mve RM+bljni+sVKY4V6HODFS5rkEcgb5eHoFZEH6UdIyecGBFlhfMsdE53Nt/+Pyupc Zft180C00DMD2NxwNS1k =w6YQ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kraxel/tags/audio-20190924-pull-request' into staging audio: documentation fixes. audio: new backend api (first part of the surround sound patch series). # gpg: Signature made Tue 24 Sep 2019 07:19:31 BST # gpg: using RSA key 4CB6D8EED3E87138 # gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full] # gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full] # gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full] # Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138 * remotes/kraxel/tags/audio-20190924-pull-request: audio: split ctl_* functions into enable_* and volume_* audio: common rate control code for timer based outputs audio: unify input and output mixeng buffer management audio: remove remains of the old backend api wavaudio: port to the new audio backend api spiceaudio: port to the new audio backend api sdlaudio: port to the new audio backend api paaudio: port to the new audio backend api ossaudio: port to the new audio backend api noaudio: port to the new audio backend api dsoundaudio: port to the new audio backend api coreaudio: port to the new audio backend api alsaaudio: port to the new audio backend api audio: api for mixeng code free backends audio: fix ALSA period-length typo in documentation audio: fix buffer-length typo in documentation Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
860d9048c7
@ -2,7 +2,6 @@ common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
|
||||
common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
|
||||
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
|
||||
common-obj-y += wavcapture.o
|
||||
|
||||
|
@ -44,9 +44,6 @@ struct pollhlp {
|
||||
|
||||
typedef struct ALSAVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int wpos;
|
||||
int pending;
|
||||
void *pcm_buf;
|
||||
snd_pcm_t *handle;
|
||||
struct pollhlp pollhlp;
|
||||
Audiodev *dev;
|
||||
@ -55,7 +52,6 @@ typedef struct ALSAVoiceOut {
|
||||
typedef struct ALSAVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
snd_pcm_t *handle;
|
||||
void *pcm_buf;
|
||||
struct pollhlp pollhlp;
|
||||
Audiodev *dev;
|
||||
} ALSAVoiceIn;
|
||||
@ -602,102 +598,64 @@ static int alsa_open(bool in, struct alsa_params_req *req,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
|
||||
{
|
||||
snd_pcm_sframes_t avail;
|
||||
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
if (avail < 0) {
|
||||
if (avail == -EPIPE) {
|
||||
if (!alsa_recover (handle)) {
|
||||
avail = snd_pcm_avail_update (handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (avail < 0) {
|
||||
alsa_logerr (avail,
|
||||
"Could not obtain number of available frames\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return avail;
|
||||
}
|
||||
|
||||
static void alsa_write_pending (ALSAVoiceOut *alsa)
|
||||
{
|
||||
HWVoiceOut *hw = &alsa->hw;
|
||||
|
||||
while (alsa->pending) {
|
||||
int left_till_end_samples = hw->samples - alsa->wpos;
|
||||
int len = MIN (alsa->pending, left_till_end_samples);
|
||||
char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
|
||||
|
||||
while (len) {
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
written = snd_pcm_writei (alsa->handle, src, len);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
trace_alsa_wrote_zero(len);
|
||||
return;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
trace_alsa_xrun_out();
|
||||
continue;
|
||||
|
||||
case -ESTRPIPE:
|
||||
/* stream is suspended and waiting for an
|
||||
application recovery */
|
||||
if (alsa_resume (alsa->handle)) {
|
||||
alsa_logerr (written, "Failed to write %d frames\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
trace_alsa_resume_out();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
return;
|
||||
|
||||
default:
|
||||
alsa_logerr (written, "Failed to write %d frames from %p\n",
|
||||
len, src);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alsa->wpos = (alsa->wpos + written) % hw->samples;
|
||||
alsa->pending -= written;
|
||||
len -= written;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t alsa_run_out(HWVoiceOut *hw, size_t live)
|
||||
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
size_t decr;
|
||||
snd_pcm_sframes_t avail;
|
||||
size_t pos = 0;
|
||||
size_t len_frames = len >> hw->info.shift;
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of available playback frames\n");
|
||||
return 0;
|
||||
while (len_frames) {
|
||||
char *src = advance(buf, pos);
|
||||
snd_pcm_sframes_t written;
|
||||
|
||||
written = snd_pcm_writei(alsa->handle, src, len_frames);
|
||||
|
||||
if (written <= 0) {
|
||||
switch (written) {
|
||||
case 0:
|
||||
trace_alsa_wrote_zero(len_frames);
|
||||
return pos;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover(alsa->handle)) {
|
||||
alsa_logerr(written, "Failed to write %zu frames\n",
|
||||
len_frames);
|
||||
return pos;
|
||||
}
|
||||
trace_alsa_xrun_out();
|
||||
continue;
|
||||
|
||||
case -ESTRPIPE:
|
||||
/*
|
||||
* stream is suspended and waiting for an application
|
||||
* recovery
|
||||
*/
|
||||
if (alsa_resume(alsa->handle)) {
|
||||
alsa_logerr(written, "Failed to write %zu frames\n",
|
||||
len_frames);
|
||||
return pos;
|
||||
}
|
||||
trace_alsa_resume_out();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
return pos;
|
||||
|
||||
default:
|
||||
alsa_logerr(written, "Failed to write %zu frames from %p\n",
|
||||
len, src);
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos += written << hw->info.shift;
|
||||
if (written < len_frames) {
|
||||
break;
|
||||
}
|
||||
len_frames -= written;
|
||||
}
|
||||
|
||||
decr = 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;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void alsa_fini_out (HWVoiceOut *hw)
|
||||
@ -706,9 +664,6 @@ static void alsa_fini_out (HWVoiceOut *hw)
|
||||
|
||||
ldebug ("alsa_fini\n");
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
|
||||
g_free(alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
@ -737,14 +692,6 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog("Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close1 (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->pollhlp.s = hw->s;
|
||||
alsa->handle = handle;
|
||||
alsa->dev = dev;
|
||||
@ -784,34 +731,28 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void alsa_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = apdo->try_poll;
|
||||
if (enable) {
|
||||
bool poll_mode = apdo->try_poll;
|
||||
|
||||
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);
|
||||
ldebug("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_out(hw)) {
|
||||
poll_mode = 0;
|
||||
}
|
||||
|
||||
case VOICE_DISABLE:
|
||||
ldebug ("disabling voice\n");
|
||||
hw->poll_mode = poll_mode;
|
||||
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
|
||||
} else {
|
||||
ldebug("disabling voice\n");
|
||||
if (hw->poll_mode) {
|
||||
hw->poll_mode = 0;
|
||||
alsa_fini_poll (&alsa->pollhlp);
|
||||
alsa_fini_poll(&alsa->pollhlp);
|
||||
}
|
||||
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
|
||||
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
@ -839,14 +780,6 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
hw->samples = obt.samples;
|
||||
|
||||
alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
if (!alsa->pcm_buf) {
|
||||
dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
alsa_anal_close1 (&handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
alsa->pollhlp.s = hw->s;
|
||||
alsa->handle = handle;
|
||||
alsa->dev = dev;
|
||||
@ -858,160 +791,73 @@ static void alsa_fini_in (HWVoiceIn *hw)
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
|
||||
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
|
||||
|
||||
g_free(alsa->pcm_buf);
|
||||
alsa->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static size_t alsa_run_in(HWVoiceIn *hw)
|
||||
static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
size_t live = audio_pcm_hw_get_live_in (hw);
|
||||
size_t dead = hw->samples - live;
|
||||
size_t decr;
|
||||
struct {
|
||||
size_t add;
|
||||
size_t len;
|
||||
} bufs[2] = {
|
||||
{ .add = hw->wpos, .len = 0 },
|
||||
{ .add = 0, .len = 0 }
|
||||
};
|
||||
snd_pcm_sframes_t avail;
|
||||
snd_pcm_uframes_t read_samples = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
avail = alsa_get_avail (alsa->handle);
|
||||
if (avail < 0) {
|
||||
dolog ("Could not get number of captured frames\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!avail) {
|
||||
snd_pcm_state_t state;
|
||||
|
||||
state = snd_pcm_state (alsa->handle);
|
||||
switch (state) {
|
||||
case SND_PCM_STATE_PREPARED:
|
||||
avail = hw->samples;
|
||||
break;
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
/* stream is suspended and waiting for an application recovery */
|
||||
if (alsa_resume (alsa->handle)) {
|
||||
dolog ("Failed to resume suspended input stream\n");
|
||||
return 0;
|
||||
}
|
||||
trace_alsa_resume_in();
|
||||
break;
|
||||
default:
|
||||
trace_alsa_no_frames(state);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
decr = MIN(dead, avail);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hw->wpos + decr > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos);
|
||||
bufs[1].len = (decr - (hw->samples - hw->wpos));
|
||||
}
|
||||
else {
|
||||
bufs[0].len = decr;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
void *src;
|
||||
struct st_sample *dst;
|
||||
while (len) {
|
||||
void *dst = advance(buf, pos);
|
||||
snd_pcm_sframes_t nread;
|
||||
snd_pcm_uframes_t len;
|
||||
|
||||
len = bufs[i].len;
|
||||
nread = snd_pcm_readi(alsa->handle, dst, len >> hw->info.shift);
|
||||
|
||||
src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
|
||||
dst = hw->conv_buf + bufs[i].add;
|
||||
if (nread <= 0) {
|
||||
switch (nread) {
|
||||
case 0:
|
||||
trace_alsa_read_zero(len);
|
||||
return pos;;
|
||||
|
||||
while (len) {
|
||||
nread = snd_pcm_readi (alsa->handle, src, len);
|
||||
|
||||
if (nread <= 0) {
|
||||
switch (nread) {
|
||||
case 0:
|
||||
trace_alsa_read_zero(len);
|
||||
goto exit;
|
||||
|
||||
case -EPIPE:
|
||||
if (alsa_recover (alsa->handle)) {
|
||||
alsa_logerr (nread, "Failed to read %ld frames\n", len);
|
||||
goto exit;
|
||||
}
|
||||
trace_alsa_xrun_in();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
alsa_logerr (
|
||||
nread,
|
||||
"Failed to read %ld frames from %p\n",
|
||||
len,
|
||||
src
|
||||
);
|
||||
goto exit;
|
||||
case -EPIPE:
|
||||
if (alsa_recover(alsa->handle)) {
|
||||
alsa_logerr(nread, "Failed to read %zu frames\n", len);
|
||||
return pos;
|
||||
}
|
||||
trace_alsa_xrun_in();
|
||||
continue;
|
||||
|
||||
case -EAGAIN:
|
||||
return pos;
|
||||
|
||||
default:
|
||||
alsa_logerr(nread, "Failed to read %zu frames to %p\n",
|
||||
len, dst);
|
||||
return pos;;
|
||||
}
|
||||
|
||||
hw->conv (dst, src, nread);
|
||||
|
||||
src = advance (src, nread << hwshift);
|
||||
dst += nread;
|
||||
|
||||
read_samples += nread;
|
||||
len -= nread;
|
||||
}
|
||||
|
||||
pos += nread << hw->info.shift;
|
||||
len -= nread << hw->info.shift;
|
||||
}
|
||||
|
||||
exit:
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
static void alsa_enable_in(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = apdo->try_poll;
|
||||
if (enable) {
|
||||
bool poll_mode = apdo->try_poll;
|
||||
|
||||
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);
|
||||
ldebug("enabling voice\n");
|
||||
if (poll_mode && alsa_poll_in(hw)) {
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
|
||||
} else {
|
||||
ldebug ("disabling voice\n");
|
||||
if (hw->poll_mode) {
|
||||
hw->poll_mode = 0;
|
||||
alsa_fini_poll (&alsa->pollhlp);
|
||||
alsa_fini_poll(&alsa->pollhlp);
|
||||
}
|
||||
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
|
||||
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
|
||||
@ -1065,13 +911,13 @@ static void alsa_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
.init_out = alsa_init_out,
|
||||
.fini_out = alsa_fini_out,
|
||||
.run_out = alsa_run_out,
|
||||
.ctl_out = alsa_ctl_out,
|
||||
.write = alsa_write,
|
||||
.enable_out = alsa_enable_out,
|
||||
|
||||
.init_in = alsa_init_in,
|
||||
.fini_in = alsa_fini_in,
|
||||
.run_in = alsa_run_in,
|
||||
.ctl_in = alsa_ctl_in,
|
||||
.read = alsa_read,
|
||||
.enable_in = alsa_enable_in,
|
||||
};
|
||||
|
||||
static struct audio_driver alsa_audio_driver = {
|
||||
|
383
audio/audio.c
383
audio/audio.c
@ -541,36 +541,33 @@ static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
|
||||
return m;
|
||||
}
|
||||
|
||||
size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
|
||||
static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
|
||||
{
|
||||
size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
|
||||
if (audio_bug(__func__, live > hw->samples)) {
|
||||
dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
|
||||
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
||||
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||
return 0;
|
||||
}
|
||||
return live;
|
||||
}
|
||||
|
||||
size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
|
||||
size_t live, size_t pending)
|
||||
static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
|
||||
{
|
||||
size_t left = hw->samples - pending;
|
||||
size_t len = MIN (left, live);
|
||||
size_t clipped = 0;
|
||||
size_t pos = hw->mix_buf->pos;
|
||||
|
||||
while (len) {
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift);
|
||||
size_t samples_till_end_of_buf = hw->samples - hw->rpos;
|
||||
size_t samples_to_clip = MIN (len, samples_till_end_of_buf);
|
||||
st_sample *src = hw->mix_buf->samples + pos;
|
||||
uint8_t *dst = advance(pcm_buf, clipped << hw->info.shift);
|
||||
size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
|
||||
size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
|
||||
|
||||
hw->clip (dst, src, samples_to_clip);
|
||||
hw->clip(dst, src, samples_to_clip);
|
||||
|
||||
hw->rpos = (hw->rpos + samples_to_clip) % hw->samples;
|
||||
pos = (pos + samples_to_clip) % hw->mix_buf->size;
|
||||
len -= samples_to_clip;
|
||||
clipped += samples_to_clip;
|
||||
}
|
||||
return clipped;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -582,17 +579,17 @@ static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
|
||||
ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
ssize_t rpos;
|
||||
|
||||
if (audio_bug(__func__, live < 0 || live > hw->samples)) {
|
||||
dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
|
||||
if (audio_bug(__func__, live < 0 || live > hw->conv_buf->size)) {
|
||||
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpos = hw->wpos - live;
|
||||
rpos = hw->conv_buf->pos - live;
|
||||
if (rpos >= 0) {
|
||||
return rpos;
|
||||
}
|
||||
else {
|
||||
return hw->samples + rpos;
|
||||
return hw->conv_buf->size + rpos;
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,11 +599,11 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
||||
size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
|
||||
struct st_sample *src, *dst = sw->buf;
|
||||
|
||||
rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
|
||||
rpos = audio_pcm_sw_get_rpos_in(sw) % hw->conv_buf->size;
|
||||
|
||||
live = hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
if (audio_bug(__func__, live > hw->samples)) {
|
||||
dolog("live_in=%zu hw->samples=%zu\n", live, hw->samples);
|
||||
if (audio_bug(__func__, live > hw->conv_buf->size)) {
|
||||
dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -619,11 +616,11 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
||||
swlim = MIN (swlim, samples);
|
||||
|
||||
while (swlim) {
|
||||
src = hw->conv_buf + rpos;
|
||||
if (hw->wpos > rpos) {
|
||||
isamp = hw->wpos - rpos;
|
||||
src = hw->conv_buf->samples + rpos;
|
||||
if (hw->conv_buf->pos > rpos) {
|
||||
isamp = hw->conv_buf->pos - rpos;
|
||||
} else {
|
||||
isamp = hw->samples - rpos;
|
||||
isamp = hw->conv_buf->size - rpos;
|
||||
}
|
||||
|
||||
if (!isamp) {
|
||||
@ -633,13 +630,13 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
|
||||
|
||||
st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
|
||||
swlim -= osamp;
|
||||
rpos = (rpos + isamp) % hw->samples;
|
||||
rpos = (rpos + isamp) % hw->conv_buf->size;
|
||||
dst += osamp;
|
||||
ret += osamp;
|
||||
total += isamp;
|
||||
}
|
||||
|
||||
if (!(hw->ctl_caps & VOICE_VOLUME_CAP)) {
|
||||
if (!hw->pcm_ops->volume_in) {
|
||||
mixeng_volume (sw->buf, ret, &sw->vol);
|
||||
}
|
||||
|
||||
@ -681,8 +678,8 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
|
||||
if (nb_live1) {
|
||||
size_t live = smin;
|
||||
|
||||
if (audio_bug(__func__, live > hw->samples)) {
|
||||
dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
|
||||
if (audio_bug(__func__, live > hw->mix_buf->size)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
|
||||
return 0;
|
||||
}
|
||||
return live;
|
||||
@ -702,11 +699,11 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
hwsamples = sw->hw->samples;
|
||||
hwsamples = sw->hw->mix_buf->size;
|
||||
|
||||
live = sw->total_hw_samples_mixed;
|
||||
if (audio_bug(__func__, live > hwsamples)) {
|
||||
dolog("live=%zu hw->samples=%zu\n", live, hwsamples);
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hwsamples);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -717,7 +714,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
wpos = (sw->hw->rpos + live) % hwsamples;
|
||||
wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
|
||||
samples = size >> sw->info.shift;
|
||||
|
||||
dead = hwsamples - live;
|
||||
@ -726,7 +723,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||
if (swlim) {
|
||||
sw->conv (sw->buf, buf, swlim);
|
||||
|
||||
if (!(sw->hw->ctl_caps & VOICE_VOLUME_CAP)) {
|
||||
if (!sw->hw->pcm_ops->volume_out) {
|
||||
mixeng_volume (sw->buf, swlim, &sw->vol);
|
||||
}
|
||||
}
|
||||
@ -743,7 +740,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
|
||||
st_rate_flow_mix (
|
||||
sw->rate,
|
||||
sw->buf + pos,
|
||||
sw->hw->mix_buf + wpos,
|
||||
sw->hw->mix_buf->samples + wpos,
|
||||
&isamp,
|
||||
&osamp
|
||||
);
|
||||
@ -871,7 +868,7 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
|
||||
|
||||
int AUD_get_buffer_size_out (SWVoiceOut *sw)
|
||||
{
|
||||
return sw->hw->samples << sw->hw->info.shift;
|
||||
return sw->hw->mix_buf->size << sw->hw->info.shift;
|
||||
}
|
||||
|
||||
void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||
@ -893,7 +890,9 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||
if (!hw->enabled) {
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_out(hw, VOICE_ENABLE);
|
||||
if (hw->pcm_ops->enable_out) {
|
||||
hw->pcm_ops->enable_out(hw, true);
|
||||
}
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
}
|
||||
@ -938,7 +937,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
|
||||
if (!hw->enabled) {
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_in(hw, VOICE_ENABLE);
|
||||
if (hw->pcm_ops->enable_in) {
|
||||
hw->pcm_ops->enable_in(hw, true);
|
||||
}
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
}
|
||||
@ -955,7 +956,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
|
||||
|
||||
if (nb_active == 1) {
|
||||
hw->enabled = 0;
|
||||
hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
|
||||
if (hw->pcm_ops->enable_in) {
|
||||
hw->pcm_ops->enable_in(hw, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -972,8 +975,9 @@ static size_t audio_get_avail (SWVoiceIn *sw)
|
||||
}
|
||||
|
||||
live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
|
||||
if (audio_bug(__func__, live > sw->hw->samples)) {
|
||||
dolog("live=%zu sw->hw->samples=%zu\n", live, sw->hw->samples);
|
||||
if (audio_bug(__func__, live > sw->hw->conv_buf->size)) {
|
||||
dolog("live=%zu sw->hw->conv_buf->size=%zu\n", live,
|
||||
sw->hw->conv_buf->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -996,12 +1000,13 @@ static size_t audio_get_free(SWVoiceOut *sw)
|
||||
|
||||
live = sw->total_hw_samples_mixed;
|
||||
|
||||
if (audio_bug(__func__, live > sw->hw->samples)) {
|
||||
dolog("live=%zu sw->hw->samples=%zu\n", live, sw->hw->samples);
|
||||
if (audio_bug(__func__, live > sw->hw->mix_buf->size)) {
|
||||
dolog("live=%zu sw->hw->mix_buf->size=%zu\n", live,
|
||||
sw->hw->mix_buf->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dead = sw->hw->samples - live;
|
||||
dead = sw->hw->mix_buf->size - live;
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
|
||||
@ -1026,12 +1031,12 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
|
||||
|
||||
n = samples;
|
||||
while (n) {
|
||||
size_t till_end_of_hw = hw->samples - rpos2;
|
||||
size_t till_end_of_hw = hw->mix_buf->size - rpos2;
|
||||
size_t to_write = MIN(till_end_of_hw, n);
|
||||
size_t bytes = to_write << hw->info.shift;
|
||||
size_t written;
|
||||
|
||||
sw->buf = hw->mix_buf + rpos2;
|
||||
sw->buf = hw->mix_buf->samples + rpos2;
|
||||
written = audio_pcm_sw_write (sw, NULL, bytes);
|
||||
if (written - bytes) {
|
||||
dolog("Could not mix %zu bytes into a capture "
|
||||
@ -1040,14 +1045,44 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
|
||||
break;
|
||||
}
|
||||
n -= to_write;
|
||||
rpos2 = (rpos2 + to_write) % hw->samples;
|
||||
rpos2 = (rpos2 + to_write) % hw->mix_buf->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n = MIN(samples, hw->samples - rpos);
|
||||
mixeng_clear(hw->mix_buf + rpos, n);
|
||||
mixeng_clear(hw->mix_buf, samples - n);
|
||||
n = MIN(samples, hw->mix_buf->size - rpos);
|
||||
mixeng_clear(hw->mix_buf->samples + rpos, n);
|
||||
mixeng_clear(hw->mix_buf->samples, samples - n);
|
||||
}
|
||||
|
||||
static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
||||
{
|
||||
size_t clipped = 0;
|
||||
|
||||
while (live) {
|
||||
size_t size, decr, proc;
|
||||
void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
|
||||
if (!buf) {
|
||||
/* retrying will likely won't help, drop everything. */
|
||||
hw->mix_buf->pos = (hw->mix_buf->pos + live) % hw->mix_buf->size;
|
||||
return clipped + live;
|
||||
}
|
||||
|
||||
decr = MIN(size >> hw->info.shift, live);
|
||||
audio_pcm_hw_clip_out(hw, buf, decr);
|
||||
proc = hw->pcm_ops->put_buffer_out(hw, buf, decr << hw->info.shift) >>
|
||||
hw->info.shift;
|
||||
|
||||
live -= proc;
|
||||
clipped += proc;
|
||||
hw->mix_buf->pos = (hw->mix_buf->pos + proc) % hw->mix_buf->size;
|
||||
|
||||
if (proc == 0 || proc < decr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
static void audio_run_out (AudioState *s)
|
||||
@ -1064,8 +1099,8 @@ static void audio_run_out (AudioState *s)
|
||||
live = 0;
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, live > hw->samples)) {
|
||||
dolog ("live=%zu hw->samples=%zu\n", live, hw->samples);
|
||||
if (audio_bug(__func__, live > hw->mix_buf->size)) {
|
||||
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1076,7 +1111,9 @@ static void audio_run_out (AudioState *s)
|
||||
#endif
|
||||
hw->enabled = 0;
|
||||
hw->pending_disable = 0;
|
||||
hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
|
||||
if (hw->pcm_ops->enable_out) {
|
||||
hw->pcm_ops->enable_out(hw, false);
|
||||
}
|
||||
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
|
||||
sc->sw.active = 0;
|
||||
audio_recalc_and_notify_capture (sc->cap);
|
||||
@ -1096,13 +1133,13 @@ static void audio_run_out (AudioState *s)
|
||||
continue;
|
||||
}
|
||||
|
||||
prev_rpos = hw->rpos;
|
||||
played = hw->pcm_ops->run_out (hw, live);
|
||||
prev_rpos = hw->mix_buf->pos;
|
||||
played = audio_pcm_hw_run_out(hw, live);
|
||||
replay_audio_out(&played);
|
||||
if (audio_bug(__func__, hw->rpos >= hw->samples)) {
|
||||
dolog("hw->rpos=%zu hw->samples=%zu played=%zu\n",
|
||||
hw->rpos, hw->samples, played);
|
||||
hw->rpos = 0;
|
||||
if (audio_bug(__func__, hw->mix_buf->pos >= hw->mix_buf->size)) {
|
||||
dolog("hw->mix_buf->pos=%zu hw->mix_buf->size=%zu played=%zu\n",
|
||||
hw->mix_buf->pos, hw->mix_buf->size, played);
|
||||
hw->mix_buf->pos = 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
@ -1156,6 +1193,36 @@ static void audio_run_out (AudioState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
|
||||
{
|
||||
size_t conv = 0;
|
||||
STSampleBuffer *conv_buf = hw->conv_buf;
|
||||
|
||||
while (samples) {
|
||||
size_t proc;
|
||||
size_t size = samples << hw->info.shift;
|
||||
void *buf = hw->pcm_ops->get_buffer_in(hw, &size);
|
||||
|
||||
assert((size & hw->info.align) == 0);
|
||||
if (size == 0) {
|
||||
hw->pcm_ops->put_buffer_in(hw, buf, size);
|
||||
break;
|
||||
}
|
||||
|
||||
proc = MIN(size >> hw->info.shift,
|
||||
conv_buf->size - conv_buf->pos);
|
||||
|
||||
hw->conv(conv_buf->samples + conv_buf->pos, buf, proc);
|
||||
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
|
||||
|
||||
samples -= proc;
|
||||
conv += proc;
|
||||
hw->pcm_ops->put_buffer_in(hw, buf, proc << hw->info.shift);
|
||||
}
|
||||
|
||||
return conv;
|
||||
}
|
||||
|
||||
static void audio_run_in (AudioState *s)
|
||||
{
|
||||
HWVoiceIn *hw = NULL;
|
||||
@ -1165,9 +1232,11 @@ static void audio_run_in (AudioState *s)
|
||||
size_t captured = 0, min;
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
captured = hw->pcm_ops->run_in(hw);
|
||||
captured = audio_pcm_hw_run_in(
|
||||
hw, hw->conv_buf->size - audio_pcm_hw_get_live_in(hw));
|
||||
}
|
||||
replay_audio_in(&captured, hw->conv_buf, &hw->wpos, hw->samples);
|
||||
replay_audio_in(&captured, hw->conv_buf->samples, &hw->conv_buf->pos,
|
||||
hw->conv_buf->size);
|
||||
|
||||
min = audio_pcm_hw_find_min_in (hw);
|
||||
hw->total_samples_captured += captured - min;
|
||||
@ -1198,14 +1267,14 @@ static void audio_run_capture (AudioState *s)
|
||||
SWVoiceOut *sw;
|
||||
|
||||
captured = live = audio_pcm_hw_get_live_out (hw, NULL);
|
||||
rpos = hw->rpos;
|
||||
rpos = hw->mix_buf->pos;
|
||||
while (live) {
|
||||
size_t left = hw->samples - rpos;
|
||||
size_t left = hw->mix_buf->size - rpos;
|
||||
size_t to_capture = MIN(live, left);
|
||||
struct st_sample *src;
|
||||
struct capture_callback *cb;
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
src = hw->mix_buf->samples + rpos;
|
||||
hw->clip (cap->buf, src, to_capture);
|
||||
mixeng_clear (src, to_capture);
|
||||
|
||||
@ -1213,10 +1282,10 @@ static void audio_run_capture (AudioState *s)
|
||||
cb->ops.capture (cb->opaque, cap->buf,
|
||||
to_capture << hw->info.shift);
|
||||
}
|
||||
rpos = (rpos + to_capture) % hw->samples;
|
||||
rpos = (rpos + to_capture) % hw->mix_buf->size;
|
||||
live -= to_capture;
|
||||
}
|
||||
hw->rpos = rpos;
|
||||
hw->mix_buf->pos = rpos;
|
||||
|
||||
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
|
||||
if (!sw->active && sw->empty) {
|
||||
@ -1259,12 +1328,137 @@ void audio_run(AudioState *s, const char *msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
||||
{
|
||||
ssize_t start;
|
||||
|
||||
if (unlikely(!hw->buf_emul)) {
|
||||
size_t calc_size = hw->conv_buf->size << hw->info.shift;
|
||||
hw->buf_emul = g_malloc(calc_size);
|
||||
hw->size_emul = calc_size;
|
||||
hw->pos_emul = hw->pending_emul = 0;
|
||||
}
|
||||
|
||||
while (hw->pending_emul < hw->size_emul) {
|
||||
size_t read_len = MIN(hw->size_emul - hw->pos_emul,
|
||||
hw->size_emul - hw->pending_emul);
|
||||
size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,
|
||||
read_len);
|
||||
hw->pending_emul += read;
|
||||
if (read < read_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
*size = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
return hw->buf_emul + start;
|
||||
}
|
||||
|
||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
|
||||
{
|
||||
assert(size <= hw->pending_emul);
|
||||
hw->pending_emul -= size;
|
||||
}
|
||||
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
if (unlikely(!hw->buf_emul)) {
|
||||
size_t calc_size = hw->mix_buf->size << hw->info.shift;
|
||||
|
||||
hw->buf_emul = g_malloc(calc_size);
|
||||
hw->size_emul = calc_size;
|
||||
hw->pos_emul = hw->pending_emul = 0;
|
||||
}
|
||||
|
||||
*size = MIN(hw->size_emul - hw->pending_emul,
|
||||
hw->size_emul - hw->pos_emul);
|
||||
return hw->buf_emul + hw->pos_emul;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
assert(buf == hw->buf_emul + hw->pos_emul &&
|
||||
size + hw->pending_emul <= hw->size_emul);
|
||||
|
||||
hw->pending_emul += size;
|
||||
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
audio_generic_put_buffer_out_nowrite(hw, buf, size);
|
||||
|
||||
while (hw->pending_emul) {
|
||||
size_t write_len, written;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
|
||||
written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= written;
|
||||
|
||||
if (written < write_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fake we have written everything. non-written data remain in pending_emul,
|
||||
* so we do not have to clip them multiple times
|
||||
*/
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
size_t dst_size, copy_size;
|
||||
void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);
|
||||
copy_size = MIN(size, dst_size);
|
||||
|
||||
memcpy(dst, buf, copy_size);
|
||||
return hw->pcm_ops->put_buffer_out(hw, buf, copy_size);
|
||||
}
|
||||
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
|
||||
{
|
||||
size_t dst_size, copy_size;
|
||||
void *dst = hw->pcm_ops->get_buffer_in(hw, &dst_size);
|
||||
copy_size = MIN(size, dst_size);
|
||||
|
||||
memcpy(dst, buf, copy_size);
|
||||
hw->pcm_ops->put_buffer_in(hw, buf, copy_size);
|
||||
return copy_size;
|
||||
}
|
||||
|
||||
|
||||
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
|
||||
bool msg, Audiodev *dev)
|
||||
{
|
||||
s->drv_opaque = drv->init(dev);
|
||||
|
||||
if (s->drv_opaque) {
|
||||
if (!drv->pcm_ops->get_buffer_in) {
|
||||
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
|
||||
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
|
||||
}
|
||||
if (!drv->pcm_ops->get_buffer_out) {
|
||||
drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
|
||||
drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
|
||||
}
|
||||
|
||||
audio_init_nb_voices_out(s, drv);
|
||||
audio_init_nb_voices_in(s, drv);
|
||||
s->drv = drv;
|
||||
@ -1284,15 +1478,18 @@ static void audio_vm_change_state_handler (void *opaque, int running,
|
||||
AudioState *s = opaque;
|
||||
HWVoiceOut *hwo = NULL;
|
||||
HWVoiceIn *hwi = NULL;
|
||||
int op = running ? VOICE_ENABLE : VOICE_DISABLE;
|
||||
|
||||
s->vm_running = running;
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
|
||||
hwo->pcm_ops->ctl_out(hwo, op);
|
||||
if (hwo->pcm_ops->enable_out) {
|
||||
hwo->pcm_ops->enable_out(hwo, running);
|
||||
}
|
||||
}
|
||||
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
|
||||
hwi->pcm_ops->ctl_in(hwi, op);
|
||||
if (hwi->pcm_ops->enable_in) {
|
||||
hwi->pcm_ops->enable_in(hwi, running);
|
||||
}
|
||||
}
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
@ -1312,8 +1509,8 @@ static void free_audio_state(AudioState *s)
|
||||
QLIST_FOREACH_SAFE(hwo, &s->hw_head_out, entries, hwon) {
|
||||
SWVoiceCap *sc;
|
||||
|
||||
if (hwo->enabled) {
|
||||
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
|
||||
if (hwo->enabled && hwo->pcm_ops->enable_out) {
|
||||
hwo->pcm_ops->enable_out(hwo, false);
|
||||
}
|
||||
hwo->pcm_ops->fini_out (hwo);
|
||||
|
||||
@ -1329,8 +1526,8 @@ static void free_audio_state(AudioState *s)
|
||||
}
|
||||
|
||||
QLIST_FOREACH_SAFE(hwi, &s->hw_head_in, entries, hwin) {
|
||||
if (hwi->enabled) {
|
||||
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
|
||||
if (hwi->enabled && hwi->pcm_ops->enable_in) {
|
||||
hwi->pcm_ops->enable_in(hwi, false);
|
||||
}
|
||||
hwi->pcm_ops->fini_in (hwi);
|
||||
QLIST_REMOVE(hwi, entries);
|
||||
@ -1582,11 +1779,11 @@ CaptureVoiceOut *AUD_add_capture(
|
||||
|
||||
/* XXX find a more elegant way */
|
||||
hw->samples = 4096 * 4;
|
||||
hw->mix_buf = g_new0(struct st_sample, hw->samples);
|
||||
audio_pcm_hw_alloc_resources_out(hw);
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
|
||||
cap->buf = g_malloc0_n(hw->samples, 1 << hw->info.shift);
|
||||
cap->buf = g_malloc0_n(hw->mix_buf->size, 1 << hw->info.shift);
|
||||
|
||||
hw->clip = mixeng_clip
|
||||
[hw->info.nchannels == 2]
|
||||
@ -1652,8 +1849,8 @@ void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
|
||||
sw->vol.l = nominal_volume.l * lvol / 255;
|
||||
sw->vol.r = nominal_volume.r * rvol / 255;
|
||||
|
||||
if (hw->pcm_ops->ctl_out) {
|
||||
hw->pcm_ops->ctl_out (hw, VOICE_VOLUME, sw);
|
||||
if (hw->pcm_ops->volume_out) {
|
||||
hw->pcm_ops->volume_out(hw, &sw->vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1667,8 +1864,8 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
|
||||
sw->vol.l = nominal_volume.l * lvol / 255;
|
||||
sw->vol.r = nominal_volume.r * rvol / 255;
|
||||
|
||||
if (hw->pcm_ops->ctl_in) {
|
||||
hw->pcm_ops->ctl_in (hw, VOICE_VOLUME, sw);
|
||||
if (hw->pcm_ops->volume_in) {
|
||||
hw->pcm_ops->volume_in(hw, &sw->vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1865,3 +2062,33 @@ const char *audio_get_id(QEMUSoundCard *card)
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void audio_rate_start(RateCtl *rate)
|
||||
{
|
||||
memset(rate, 0, sizeof(RateCtl));
|
||||
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
|
||||
size_t bytes_avail)
|
||||
{
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
int64_t samples;
|
||||
size_t ret;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ticks = now - rate->start_ticks;
|
||||
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
samples = (bytes - rate->bytes_sent) >> info->shift;
|
||||
if (samples < 0 || samples > 65536) {
|
||||
AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)\n", samples);
|
||||
audio_rate_start(rate);
|
||||
samples = 0;
|
||||
}
|
||||
|
||||
ret = MIN(samples << info->shift, bytes_avail);
|
||||
rate->bytes_sent += ret;
|
||||
return ret;
|
||||
}
|
||||
|
@ -52,6 +52,11 @@ struct audio_pcm_info {
|
||||
typedef struct AudioState AudioState;
|
||||
typedef struct SWVoiceCap SWVoiceCap;
|
||||
|
||||
typedef struct STSampleBuffer {
|
||||
size_t pos, size;
|
||||
st_sample samples[];
|
||||
} STSampleBuffer;
|
||||
|
||||
typedef struct HWVoiceOut {
|
||||
AudioState *s;
|
||||
int enabled;
|
||||
@ -60,16 +65,15 @@ typedef struct HWVoiceOut {
|
||||
struct audio_pcm_info info;
|
||||
|
||||
f_sample *clip;
|
||||
|
||||
size_t rpos;
|
||||
uint64_t ts_helper;
|
||||
|
||||
struct st_sample *mix_buf;
|
||||
STSampleBuffer *mix_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
|
||||
size_t samples;
|
||||
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
||||
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
|
||||
int ctl_caps;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
QLIST_ENTRY (HWVoiceOut) entries;
|
||||
} HWVoiceOut;
|
||||
@ -82,15 +86,15 @@ typedef struct HWVoiceIn {
|
||||
|
||||
t_sample *conv;
|
||||
|
||||
size_t wpos;
|
||||
size_t total_samples_captured;
|
||||
uint64_t ts_helper;
|
||||
|
||||
struct st_sample *conv_buf;
|
||||
STSampleBuffer *conv_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
|
||||
size_t samples;
|
||||
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
|
||||
int ctl_caps;
|
||||
struct audio_pcm_ops *pcm_ops;
|
||||
QLIST_ENTRY (HWVoiceIn) entries;
|
||||
} HWVoiceIn;
|
||||
@ -142,22 +146,46 @@ struct audio_driver {
|
||||
int max_voices_in;
|
||||
int voice_size_out;
|
||||
int voice_size_in;
|
||||
int ctl_caps;
|
||||
QLIST_ENTRY(audio_driver) next;
|
||||
};
|
||||
|
||||
struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
size_t (*run_out)(HWVoiceOut *hw, size_t live);
|
||||
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
|
||||
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
||||
/*
|
||||
* get a buffer that after later can be passed to put_buffer_out; optional
|
||||
* returns the buffer, and writes it's size to size (in bytes)
|
||||
* this is unrelated to the above buffer_size_out function
|
||||
*/
|
||||
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
|
||||
/*
|
||||
* put back the buffer returned by get_buffer_out; optional
|
||||
* buf must be equal the pointer returned by get_buffer_out,
|
||||
* size may be smaller
|
||||
*/
|
||||
size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
|
||||
void (*enable_out)(HWVoiceOut *hw, bool enable);
|
||||
void (*volume_out)(HWVoiceOut *hw, struct mixeng_volume *vol);
|
||||
|
||||
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
size_t (*run_in)(HWVoiceIn *hw);
|
||||
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
|
||||
int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
size_t (*read) (HWVoiceIn *hw, void *buf, size_t size);
|
||||
void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
|
||||
void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
|
||||
void (*enable_in)(HWVoiceIn *hw, bool enable);
|
||||
void (*volume_in)(HWVoiceIn *hw, struct mixeng_volume *vol);
|
||||
};
|
||||
|
||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size);
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
|
||||
|
||||
struct capture_callback {
|
||||
struct audio_capture_ops ops;
|
||||
void *opaque;
|
||||
@ -208,21 +236,19 @@ audio_driver *audio_driver_lookup(const char *name);
|
||||
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
|
||||
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
|
||||
|
||||
size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw);
|
||||
|
||||
size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
|
||||
size_t live, size_t pending);
|
||||
|
||||
int audio_bug (const char *funcname, int cond);
|
||||
void *audio_calloc (const char *funcname, int nmemb, size_t size);
|
||||
|
||||
void audio_run(AudioState *s, const char *msg);
|
||||
|
||||
#define VOICE_ENABLE 1
|
||||
#define VOICE_DISABLE 2
|
||||
#define VOICE_VOLUME 3
|
||||
typedef struct RateCtl {
|
||||
int64_t start_ticks;
|
||||
int64_t bytes_sent;
|
||||
} RateCtl;
|
||||
|
||||
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
|
||||
void audio_rate_start(RateCtl *rate);
|
||||
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
|
||||
size_t bytes_avail);
|
||||
|
||||
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
|
||||
{
|
||||
|
@ -1,173 +0,0 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "audio.h"
|
||||
|
||||
#define AUDIO_CAP "audio-pt"
|
||||
|
||||
#include "audio_int.h"
|
||||
#include "audio_pt_int.h"
|
||||
|
||||
static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start (ap, fmt);
|
||||
AUD_vlog (pt->drv, fmt, ap);
|
||||
va_end (ap);
|
||||
|
||||
AUD_log (NULL, "\n");
|
||||
AUD_log (pt->drv, "Reason: %s\n", strerror (err));
|
||||
}
|
||||
|
||||
int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
|
||||
void *opaque, const char *drv, const char *cap)
|
||||
{
|
||||
int err, err2;
|
||||
const char *efunc;
|
||||
sigset_t set, old_set;
|
||||
|
||||
p->drv = drv;
|
||||
|
||||
err = sigfillset (&set);
|
||||
if (err) {
|
||||
logerr(p, errno, "%s(%s): sigfillset failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = pthread_mutex_init (&p->mutex, NULL);
|
||||
if (err) {
|
||||
efunc = "pthread_mutex_init";
|
||||
goto err0;
|
||||
}
|
||||
|
||||
err = pthread_cond_init (&p->cond, NULL);
|
||||
if (err) {
|
||||
efunc = "pthread_cond_init";
|
||||
goto err1;
|
||||
}
|
||||
|
||||
err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
|
||||
if (err) {
|
||||
efunc = "pthread_sigmask";
|
||||
goto err2;
|
||||
}
|
||||
|
||||
err = pthread_create (&p->thread, NULL, func, opaque);
|
||||
|
||||
err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
|
||||
if (err2) {
|
||||
logerr(p, err2, "%s(%s): pthread_sigmask (restore) failed",
|
||||
cap, __func__);
|
||||
/* We have failed to restore original signal mask, all bets are off,
|
||||
so terminate the process */
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
efunc = "pthread_create";
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
err2 = pthread_cond_destroy (&p->cond);
|
||||
if (err2) {
|
||||
logerr(p, err2, "%s(%s): pthread_cond_destroy failed", cap, __func__);
|
||||
}
|
||||
|
||||
err1:
|
||||
err2 = pthread_mutex_destroy (&p->mutex);
|
||||
if (err2) {
|
||||
logerr(p, err2, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
|
||||
}
|
||||
|
||||
err0:
|
||||
logerr(p, err, "%s(%s): %s failed", cap, __func__, efunc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int audio_pt_fini (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err, ret = 0;
|
||||
|
||||
err = pthread_cond_destroy (&p->cond);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_cond_destroy failed", cap, __func__);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
err = pthread_mutex_destroy (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
|
||||
ret = -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int audio_pt_lock (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_lock (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_lock failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_unlock (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_unlock (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_wait (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_cond_wait (&p->cond, &p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_cond_wait failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pthread_mutex_unlock (&p->mutex);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
err = pthread_cond_signal (&p->cond);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_cond_signal failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
|
||||
{
|
||||
int err;
|
||||
void *ret;
|
||||
|
||||
err = pthread_join (p->thread, &ret);
|
||||
if (err) {
|
||||
logerr(p, err, "%s(%s): pthread_join failed", cap, __func__);
|
||||
return -1;
|
||||
}
|
||||
*arg = ret;
|
||||
return 0;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#ifndef QEMU_AUDIO_PT_INT_H
|
||||
#define QEMU_AUDIO_PT_INT_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
struct audio_pt {
|
||||
const char *drv;
|
||||
pthread_t thread;
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
|
||||
const char *, const char *);
|
||||
int audio_pt_fini (struct audio_pt *, const char *);
|
||||
int audio_pt_lock (struct audio_pt *, const char *);
|
||||
int audio_pt_unlock (struct audio_pt *, const char *);
|
||||
int audio_pt_wait (struct audio_pt *, const char *);
|
||||
int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
|
||||
int audio_pt_join (struct audio_pt *, void **, const char *);
|
||||
|
||||
#endif /* QEMU_AUDIO_PT_INT_H */
|
@ -71,20 +71,20 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
||||
|
||||
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
g_free(hw->buf_emul);
|
||||
g_free (HWBUF);
|
||||
HWBUF = NULL;
|
||||
}
|
||||
|
||||
static bool glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
|
||||
static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
|
||||
{
|
||||
HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
|
||||
if (!HWBUF) {
|
||||
dolog("Could not allocate " NAME " buffer (%zu samples)\n",
|
||||
hw->samples);
|
||||
return false;
|
||||
size_t samples = hw->samples;
|
||||
if (audio_bug(__func__, samples == 0)) {
|
||||
dolog("Attempted to allocate empty buffer\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
|
||||
HWBUF->size = samples;
|
||||
}
|
||||
|
||||
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
|
||||
@ -103,7 +103,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
|
||||
{
|
||||
int samples;
|
||||
|
||||
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
|
||||
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
|
||||
|
||||
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
|
||||
if (!sw->buf) {
|
||||
@ -254,7 +254,6 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
|
||||
hw->s = s;
|
||||
hw->pcm_ops = drv->pcm_ops;
|
||||
hw->ctl_caps = drv->ctl_caps;
|
||||
|
||||
QLIST_INIT (&hw->sw_head);
|
||||
#ifdef DAC
|
||||
@ -279,9 +278,7 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
|
||||
[hw->info.swap_endianness]
|
||||
[audio_bits_to_index (hw->info.bits)];
|
||||
|
||||
if (!glue(audio_pcm_hw_alloc_resources_, TYPE)(hw)) {
|
||||
goto err1;
|
||||
}
|
||||
glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
|
||||
|
||||
QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
|
||||
glue (s->nb_hw_voices_, TYPE) -= 1;
|
||||
|
@ -43,9 +43,6 @@ typedef struct coreaudioVoiceOut {
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
AudioDeviceIOProcID ioprocid;
|
||||
size_t live;
|
||||
size_t decr;
|
||||
size_t rpos;
|
||||
} coreaudioVoiceOut;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
@ -397,31 +394,29 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t coreaudio_run_out(HWVoiceOut *hw, size_t live)
|
||||
{
|
||||
size_t decr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
if (coreaudio_lock (core, "coreaudio_run_out")) {
|
||||
return 0;
|
||||
#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
|
||||
static ret_type glue(coreaudio_, name)args_decl \
|
||||
{ \
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
|
||||
ret_type ret; \
|
||||
\
|
||||
if (coreaudio_lock(core, "coreaudio_" #name)) { \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
ret = glue(audio_generic_, name)args; \
|
||||
\
|
||||
coreaudio_unlock(core, "coreaudio_" #name); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
if (core->decr > live) {
|
||||
ldebug ("core->decr %d live %d core->live %d\n",
|
||||
core->decr,
|
||||
live,
|
||||
core->live);
|
||||
}
|
||||
|
||||
decr = MIN (core->decr, live);
|
||||
core->decr -= decr;
|
||||
|
||||
core->live = live - decr;
|
||||
hw->rpos = core->rpos;
|
||||
|
||||
coreaudio_unlock (core, "coreaudio_run_out");
|
||||
return decr;
|
||||
}
|
||||
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size))
|
||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size),
|
||||
(hw, buf, size))
|
||||
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
|
||||
(hw, buf, size))
|
||||
#undef COREAUDIO_WRAPPER_FUNC
|
||||
|
||||
/* callback to feed audiooutput buffer */
|
||||
static OSStatus audioDeviceIOProc(
|
||||
@ -433,19 +428,11 @@ static OSStatus audioDeviceIOProc(
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* hwptr)
|
||||
{
|
||||
UInt32 frame, frameCount;
|
||||
float *out = outOutputData->mBuffers[0].mData;
|
||||
UInt32 frameCount, pending_frames;
|
||||
void *out = outOutputData->mBuffers[0].mData;
|
||||
HWVoiceOut *hw = hwptr;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
|
||||
int rpos, live;
|
||||
struct st_sample *src;
|
||||
#ifndef FLOAT_MIXENG
|
||||
#ifdef RECIPROCAL
|
||||
const float scale = 1.f / UINT_MAX;
|
||||
#else
|
||||
const float scale = UINT_MAX;
|
||||
#endif
|
||||
#endif
|
||||
size_t len;
|
||||
|
||||
if (coreaudio_lock (core, "audioDeviceIOProc")) {
|
||||
inInputTime = 0;
|
||||
@ -453,42 +440,51 @@ static OSStatus audioDeviceIOProc(
|
||||
}
|
||||
|
||||
frameCount = core->audioDevicePropertyBufferFrameSize;
|
||||
live = core->live;
|
||||
pending_frames = hw->pending_emul >> hw->info.shift;
|
||||
|
||||
/* if there are not enough samples, set signal and return */
|
||||
if (live < frameCount) {
|
||||
if (pending_frames < frameCount) {
|
||||
inInputTime = 0;
|
||||
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpos = core->rpos;
|
||||
src = hw->mix_buf + rpos;
|
||||
len = frameCount << hw->info.shift;
|
||||
while (len) {
|
||||
size_t write_len;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
/* fill buffer */
|
||||
for (frame = 0; frame < frameCount; frame++) {
|
||||
#ifdef FLOAT_MIXENG
|
||||
*out++ = src[frame].l; /* left channel */
|
||||
*out++ = src[frame].r; /* right channel */
|
||||
#else
|
||||
#ifdef RECIPROCAL
|
||||
*out++ = src[frame].l * scale; /* left channel */
|
||||
*out++ = src[frame].r * scale; /* right channel */
|
||||
#else
|
||||
*out++ = src[frame].l / scale; /* left channel */
|
||||
*out++ = src[frame].r / scale; /* right channel */
|
||||
#endif
|
||||
#endif
|
||||
write_len = MIN(MIN(hw->pending_emul, len),
|
||||
hw->size_emul - start);
|
||||
|
||||
memcpy(out, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= write_len;
|
||||
len -= write_len;
|
||||
out += write_len;
|
||||
}
|
||||
|
||||
rpos = (rpos + frameCount) % hw->samples;
|
||||
core->decr += frameCount;
|
||||
core->rpos = rpos;
|
||||
|
||||
coreaudio_unlock (core, "audioDeviceIOProc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UInt32 coreaudio_get_flags(struct audio_pcm_info *info,
|
||||
struct audsettings *as)
|
||||
{
|
||||
UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0;
|
||||
if (as->endianness) { /* 0 = little, 1 = big */
|
||||
flags |= kAudioFormatFlagIsBigEndian;
|
||||
}
|
||||
|
||||
if (flags == 0) { /* must not be 0 */
|
||||
flags = kAudioFormatFlagsAreAllClear;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
void *drv_opaque)
|
||||
{
|
||||
@ -576,6 +572,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
/* set Samplerate */
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
|
||||
core->outputStreamBasicDescription.mFormatFlags =
|
||||
coreaudio_get_flags(&hw->info, as);
|
||||
core->outputStreamBasicDescription.mBytesPerPacket =
|
||||
core->outputStreamBasicDescription.mBytesPerFrame =
|
||||
hw->info.nchannels * hw->info.bits / 8;
|
||||
core->outputStreamBasicDescription.mFramesPerPacket = 1;
|
||||
core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels;
|
||||
core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits;
|
||||
|
||||
status = coreaudio_set_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
@ -642,13 +648,12 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
}
|
||||
}
|
||||
|
||||
static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (enable) {
|
||||
/* start playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
|
||||
@ -656,9 +661,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
coreaudio_logerr (status, "Could not resume playback\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
} else {
|
||||
/* stop playback */
|
||||
if (!audio_is_cleaning_up()) {
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
@ -669,9 +672,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *coreaudio_audio_init(Audiodev *dev)
|
||||
@ -686,8 +687,10 @@ static void coreaudio_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
.init_out = coreaudio_init_out,
|
||||
.fini_out = coreaudio_fini_out,
|
||||
.run_out = coreaudio_run_out,
|
||||
.ctl_out = coreaudio_ctl_out
|
||||
.write = coreaudio_write,
|
||||
.get_buffer_out = coreaudio_get_buffer_out,
|
||||
.put_buffer_out = coreaudio_put_buffer_out_nowrite,
|
||||
.enable_out = coreaudio_enable_out
|
||||
};
|
||||
|
||||
static struct audio_driver coreaudio_audio_driver = {
|
||||
|
@ -29,6 +29,8 @@
|
||||
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
|
||||
#define FIELD dsound_capture_buffer
|
||||
#define FIELD2 dsound_capture
|
||||
#define HWVOICE HWVoiceIn
|
||||
#define DSOUNDVOICE DSoundVoiceIn
|
||||
#else
|
||||
#define NAME "playback buffer"
|
||||
#define NAME2 "DirectSound"
|
||||
@ -37,6 +39,8 @@
|
||||
#define BUFPTR LPDIRECTSOUNDBUFFER
|
||||
#define FIELD dsound_buffer
|
||||
#define FIELD2 dsound
|
||||
#define HWVOICE HWVoiceOut
|
||||
#define DSOUNDVOICE DSoundVoiceOut
|
||||
#endif
|
||||
|
||||
static int glue (dsound_unlock_, TYPE) (
|
||||
@ -72,8 +76,6 @@ static int glue (dsound_lock_, TYPE) (
|
||||
)
|
||||
{
|
||||
HRESULT hr;
|
||||
LPVOID p1 = NULL, p2 = NULL;
|
||||
DWORD blen1 = 0, blen2 = 0;
|
||||
DWORD flag;
|
||||
|
||||
#ifdef DSBTYPE_IN
|
||||
@ -81,7 +83,7 @@ static int glue (dsound_lock_, TYPE) (
|
||||
#else
|
||||
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
|
||||
#endif
|
||||
hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
|
||||
hr = glue(IFACE, _Lock)(buf, pos, len, p1p, blen1p, p2p, blen2p, flag);
|
||||
|
||||
if (FAILED (hr)) {
|
||||
#ifndef DSBTYPE_IN
|
||||
@ -96,34 +98,34 @@ static int glue (dsound_lock_, TYPE) (
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
|
||||
dolog ("DirectSound returned misaligned buffer %ld %ld\n",
|
||||
blen1, blen2);
|
||||
glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
|
||||
if ((p1p && *p1p && (*blen1p & info->align)) ||
|
||||
(p2p && *p2p && (*blen2p & info->align))) {
|
||||
dolog("DirectSound returned misaligned buffer %ld %ld\n",
|
||||
*blen1p, *blen2p);
|
||||
glue(dsound_unlock_, TYPE)(buf, *p1p, p2p ? *p2p : NULL, *blen1p,
|
||||
blen2p ? *blen2p : 0);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!p1 && blen1) {
|
||||
dolog ("warning: !p1 && blen1=%ld\n", blen1);
|
||||
blen1 = 0;
|
||||
if (p1p && !*p1p && *blen1p) {
|
||||
dolog("warning: !p1 && blen1=%ld\n", *blen1p);
|
||||
*blen1p = 0;
|
||||
}
|
||||
|
||||
if (!p2 && blen2) {
|
||||
dolog ("warning: !p2 && blen2=%ld\n", blen2);
|
||||
blen2 = 0;
|
||||
if (p2p && !*p2p && *blen2p) {
|
||||
dolog("warning: !p2 && blen2=%ld\n", *blen2p);
|
||||
*blen2p = 0;
|
||||
}
|
||||
|
||||
*p1p = p1;
|
||||
*p2p = p2;
|
||||
*blen1p = blen1;
|
||||
*blen2p = blen2;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*p1p = NULL - 1;
|
||||
*p2p = NULL - 1;
|
||||
*blen1p = -1;
|
||||
*blen2p = -1;
|
||||
if (p2p) {
|
||||
*p2p = NULL - 1;
|
||||
*blen2p = -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -242,7 +244,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
goto fail0;
|
||||
}
|
||||
|
||||
ds->first_time = 1;
|
||||
obt_as.endianness = 0;
|
||||
audio_pcm_init_info (&hw->info, &obt_as);
|
||||
|
||||
@ -252,15 +253,13 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
bc.dwBufferBytes, hw->info.align + 1
|
||||
);
|
||||
}
|
||||
hw->size_emul = bc.dwBufferBytes;
|
||||
hw->samples = bc.dwBufferBytes >> hw->info.shift;
|
||||
ds->s = s;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("caps %ld, desc %ld\n",
|
||||
bc.dwBufferBytes, bd.dwBufferBytes);
|
||||
|
||||
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
|
||||
hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@ -276,3 +275,5 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
#undef BUFPTR
|
||||
#undef FIELD
|
||||
#undef FIELD2
|
||||
#undef HWVOICE
|
||||
#undef DSOUNDVOICE
|
||||
|
@ -53,19 +53,11 @@ typedef struct {
|
||||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
LPDIRECTSOUNDBUFFER dsound_buffer;
|
||||
DWORD old_pos;
|
||||
int first_time;
|
||||
dsound *s;
|
||||
#ifdef DEBUG_DSOUND
|
||||
DWORD old_ppos;
|
||||
DWORD played;
|
||||
DWORD mixed;
|
||||
#endif
|
||||
} DSoundVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceIn hw;
|
||||
int first_time;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
|
||||
dsound *s;
|
||||
} DSoundVoiceIn;
|
||||
@ -243,11 +235,6 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
|
||||
dsound_log_hresult (hr);
|
||||
}
|
||||
|
||||
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
|
||||
{
|
||||
return muldiv64(usecs, info->bytes_per_second, 1000000);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
static void print_wave_format (WAVEFORMATEX *wfx)
|
||||
{
|
||||
@ -312,33 +299,6 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
||||
{
|
||||
int src_len1 = dst_len;
|
||||
int src_len2 = 0;
|
||||
int pos = hw->rpos + dst_len;
|
||||
struct st_sample *src1 = hw->mix_buf + hw->rpos;
|
||||
struct st_sample *src2 = NULL;
|
||||
|
||||
if (pos > hw->samples) {
|
||||
src_len1 = hw->samples - hw->rpos;
|
||||
src2 = hw->mix_buf;
|
||||
src_len2 = dst_len - src_len1;
|
||||
pos = src_len2;
|
||||
}
|
||||
|
||||
if (src_len1) {
|
||||
hw->clip (dst, src1, src_len1);
|
||||
}
|
||||
|
||||
if (src_len2) {
|
||||
dst = advance (dst, src_len1 << hw->info.shift);
|
||||
hw->clip (dst, src2, src_len2);
|
||||
}
|
||||
|
||||
hw->rpos = pos % hw->samples;
|
||||
}
|
||||
|
||||
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
|
||||
dsound *s)
|
||||
{
|
||||
@ -350,7 +310,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
|
||||
dsb,
|
||||
&hw->info,
|
||||
0,
|
||||
hw->samples << hw->info.shift,
|
||||
hw->size_emul,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2,
|
||||
1,
|
||||
@ -401,7 +361,7 @@ static int dsound_open (dsound *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD status;
|
||||
@ -411,18 +371,17 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
|
||||
if (!dsb) {
|
||||
dolog ("Attempt to control voice without a buffer\n");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (enable) {
|
||||
if (dsound_get_status_out (dsb, &status, s)) {
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status & DSBSTATUS_PLAYING) {
|
||||
dolog ("warning: Voice is already playing\n");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dsound_clear_sample (hw, dsb, s);
|
||||
@ -430,166 +389,74 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not start playing buffer\n");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
} else {
|
||||
if (dsound_get_status_out (dsb, &status, s)) {
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status & DSBSTATUS_PLAYING) {
|
||||
hr = IDirectSoundBuffer_Stop (dsb);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not stop playing buffer\n");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Voice is not playing\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t dsound_run_out(HWVoiceOut *hw, size_t live)
|
||||
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||
size_t len;
|
||||
int hwshift;
|
||||
DWORD blen1, blen2;
|
||||
DWORD len1, len2;
|
||||
DWORD decr;
|
||||
DWORD wpos, ppos, old_pos;
|
||||
LPVOID p1, p2;
|
||||
size_t bufsize;
|
||||
dsound *s = ds->s;
|
||||
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
|
||||
HRESULT hr;
|
||||
DWORD ppos, act_size;
|
||||
size_t req_size;
|
||||
int err;
|
||||
void *ret;
|
||||
|
||||
if (!dsb) {
|
||||
dolog ("Attempt to run empty with playback buffer\n");
|
||||
return 0;
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, NULL);
|
||||
if (FAILED(hr)) {
|
||||
dsound_logerr(hr, "Could not get playback buffer position\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hwshift = hw->info.shift;
|
||||
bufsize = hw->samples << hwshift;
|
||||
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
|
||||
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
|
||||
|
||||
hr = IDirectSoundBuffer_GetCurrentPosition (
|
||||
dsb,
|
||||
&ppos,
|
||||
ds->first_time ? &wpos : NULL
|
||||
);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not get playback buffer position\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = live << hwshift;
|
||||
|
||||
if (ds->first_time) {
|
||||
if (dso->latency) {
|
||||
DWORD cur_blat;
|
||||
|
||||
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
||||
ds->first_time = 0;
|
||||
old_pos = wpos;
|
||||
old_pos +=
|
||||
usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
|
||||
old_pos %= bufsize;
|
||||
old_pos &= ~hw->info.align;
|
||||
}
|
||||
else {
|
||||
old_pos = wpos;
|
||||
}
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->played = 0;
|
||||
ds->mixed = 0;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
if (ds->old_pos == ppos) {
|
||||
#ifdef DEBUG_DSOUND
|
||||
dolog ("old_pos == ppos\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
|
||||
#endif
|
||||
old_pos = ds->old_pos;
|
||||
}
|
||||
|
||||
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
||||
len = ppos - old_pos;
|
||||
}
|
||||
else {
|
||||
if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
|
||||
len = bufsize - old_pos + ppos;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_bug(__func__, len > bufsize)) {
|
||||
dolog("len=%zu bufsize=%zu old_pos=%ld ppos=%ld\n",
|
||||
len, bufsize, old_pos, ppos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
len &= ~hw->info.align;
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->old_ppos = ppos;
|
||||
#endif
|
||||
err = dsound_lock_out (
|
||||
dsb,
|
||||
&hw->info,
|
||||
old_pos,
|
||||
len,
|
||||
&p1, &p2,
|
||||
&blen1, &blen2,
|
||||
0,
|
||||
s
|
||||
);
|
||||
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
||||
&act_size, NULL, false, ds->s);
|
||||
if (err) {
|
||||
return 0;
|
||||
dolog("Failed to lock buffer\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hwshift;
|
||||
len2 = blen2 >> hwshift;
|
||||
decr = len1 + len2;
|
||||
|
||||
if (p1 && len1) {
|
||||
dsound_write_sample (hw, p1, len1);
|
||||
}
|
||||
|
||||
if (p2 && len2) {
|
||||
dsound_write_sample (hw, p2, len2);
|
||||
}
|
||||
|
||||
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
|
||||
ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
|
||||
|
||||
#ifdef DEBUG_DSOUND
|
||||
ds->mixed += decr << hwshift;
|
||||
|
||||
dolog ("played %lu mixed %lu diff %ld sec %f\n",
|
||||
ds->played,
|
||||
ds->mixed,
|
||||
ds->mixed - ds->played,
|
||||
abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
|
||||
#endif
|
||||
return decr;
|
||||
*size = act_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
|
||||
int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
|
||||
|
||||
if (err) {
|
||||
dolog("Failed to unlock buffer!!\n");
|
||||
return 0;
|
||||
}
|
||||
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void dsound_enable_in(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD status;
|
||||
@ -598,18 +465,17 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
|
||||
if (!dscb) {
|
||||
dolog ("Attempt to control capture voice without a buffer\n");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (enable) {
|
||||
if (dsound_get_status_in (dscb, &status)) {
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status & DSCBSTATUS_CAPTURING) {
|
||||
dolog ("warning: Voice is already capturing\n");
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* clear ?? */
|
||||
@ -617,120 +483,69 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not start capturing\n");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
} else {
|
||||
if (dsound_get_status_in (dscb, &status)) {
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status & DSCBSTATUS_CAPTURING) {
|
||||
hr = IDirectSoundCaptureBuffer_Stop (dscb);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not stop capturing\n");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dolog ("warning: Voice is not capturing\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t dsound_run_in(HWVoiceIn *hw)
|
||||
static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
|
||||
size_t live, len, dead;
|
||||
DWORD blen1, blen2;
|
||||
DWORD len1, len2;
|
||||
DWORD decr;
|
||||
DWORD cpos, rpos;
|
||||
LPVOID p1, p2;
|
||||
int hwshift;
|
||||
dsound *s = ds->s;
|
||||
HRESULT hr;
|
||||
DWORD cpos, act_size;
|
||||
size_t req_size;
|
||||
int err;
|
||||
void *ret;
|
||||
|
||||
if (!dscb) {
|
||||
dolog ("Attempt to run without capture buffer\n");
|
||||
return 0;
|
||||
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, &cpos, NULL);
|
||||
if (FAILED(hr)) {
|
||||
dsound_logerr(hr, "Could not get capture buffer position\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hwshift = hw->info.shift;
|
||||
req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
|
||||
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
|
||||
|
||||
live = audio_pcm_hw_get_live_in (hw);
|
||||
dead = hw->samples - live;
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
|
||||
dscb,
|
||||
&cpos,
|
||||
ds->first_time ? &rpos : NULL
|
||||
);
|
||||
if (FAILED (hr)) {
|
||||
dsound_logerr (hr, "Could not get capture buffer position\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ds->first_time) {
|
||||
ds->first_time = 0;
|
||||
if (rpos & hw->info.align) {
|
||||
ldebug ("warning: Misaligned capture read position %ld(%d)\n",
|
||||
rpos, hw->info.align);
|
||||
}
|
||||
hw->wpos = rpos >> hwshift;
|
||||
}
|
||||
|
||||
if (cpos & hw->info.align) {
|
||||
ldebug ("warning: Misaligned capture position %ld(%d)\n",
|
||||
cpos, hw->info.align);
|
||||
}
|
||||
cpos >>= hwshift;
|
||||
|
||||
len = audio_ring_dist (cpos, hw->wpos, hw->samples);
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
len = MIN (len, dead);
|
||||
|
||||
err = dsound_lock_in (
|
||||
dscb,
|
||||
&hw->info,
|
||||
hw->wpos << hwshift,
|
||||
len << hwshift,
|
||||
&p1,
|
||||
&p2,
|
||||
&blen1,
|
||||
&blen2,
|
||||
0,
|
||||
s
|
||||
);
|
||||
err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
|
||||
&act_size, NULL, false, ds->s);
|
||||
if (err) {
|
||||
return 0;
|
||||
dolog("Failed to lock buffer\n");
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len1 = blen1 >> hwshift;
|
||||
len2 = blen2 >> hwshift;
|
||||
decr = len1 + len2;
|
||||
*size = act_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (p1 && len1) {
|
||||
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
|
||||
static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
|
||||
{
|
||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
|
||||
int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
|
||||
|
||||
if (err) {
|
||||
dolog("Failed to unlock buffer!!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (p2 && len2) {
|
||||
hw->conv (hw->conv_buf, p2, len2);
|
||||
}
|
||||
|
||||
dsound_unlock_in (dscb, p1, p2, blen1, blen2);
|
||||
hw->wpos = (hw->wpos + decr) % hw->samples;
|
||||
return decr;
|
||||
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
|
||||
}
|
||||
|
||||
static void dsound_audio_fini (void *opaque)
|
||||
@ -846,13 +661,17 @@ static void *dsound_audio_init(Audiodev *dev)
|
||||
static struct audio_pcm_ops dsound_pcm_ops = {
|
||||
.init_out = dsound_init_out,
|
||||
.fini_out = dsound_fini_out,
|
||||
.run_out = dsound_run_out,
|
||||
.ctl_out = dsound_ctl_out,
|
||||
.write = audio_generic_write,
|
||||
.get_buffer_out = dsound_get_buffer_out,
|
||||
.put_buffer_out = dsound_put_buffer_out,
|
||||
.enable_out = dsound_enable_out,
|
||||
|
||||
.init_in = dsound_init_in,
|
||||
.fini_in = dsound_fini_in,
|
||||
.run_in = dsound_run_in,
|
||||
.ctl_in = dsound_ctl_in
|
||||
.read = audio_generic_read,
|
||||
.get_buffer_in = dsound_get_buffer_in,
|
||||
.put_buffer_in = dsound_put_buffer_in,
|
||||
.enable_in = dsound_enable_in,
|
||||
};
|
||||
|
||||
static struct audio_driver dsound_audio_driver = {
|
||||
|
@ -33,38 +33,27 @@
|
||||
|
||||
typedef struct NoVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
int64_t old_ticks;
|
||||
RateCtl rate;
|
||||
} NoVoiceOut;
|
||||
|
||||
typedef struct NoVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
int64_t old_ticks;
|
||||
RateCtl rate;
|
||||
} NoVoiceIn;
|
||||
|
||||
static size_t no_run_out(HWVoiceOut *hw, size_t live)
|
||||
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
size_t decr, samples;
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ticks = now - no->old_ticks;
|
||||
bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
bytes = MIN(bytes, SIZE_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
|
||||
no->old_ticks = now;
|
||||
decr = MIN (live, samples);
|
||||
hw->rpos = (hw->rpos + decr) % hw->samples;
|
||||
return decr;
|
||||
return audio_rate_get_bytes(&hw->info, &no->rate, len);
|
||||
}
|
||||
|
||||
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
audio_rate_start(&no->rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -73,17 +62,22 @@ static void no_fini_out (HWVoiceOut *hw)
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void no_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||
|
||||
if (enable) {
|
||||
audio_rate_start(&no->rate);
|
||||
}
|
||||
}
|
||||
|
||||
static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
{
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
hw->samples = 1024;
|
||||
audio_rate_start(&no->rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -92,32 +86,22 @@ static void no_fini_in (HWVoiceIn *hw)
|
||||
(void) hw;
|
||||
}
|
||||
|
||||
static size_t no_run_in(HWVoiceIn *hw)
|
||||
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
|
||||
{
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
size_t live = audio_pcm_hw_get_live_in(hw);
|
||||
size_t dead = hw->samples - live;
|
||||
size_t samples = 0;
|
||||
int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
|
||||
|
||||
if (dead) {
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t ticks = now - no->old_ticks;
|
||||
int64_t bytes =
|
||||
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
|
||||
no->old_ticks = now;
|
||||
bytes = MIN (bytes, SIZE_MAX);
|
||||
samples = bytes >> hw->info.shift;
|
||||
samples = MIN (samples, dead);
|
||||
}
|
||||
return samples;
|
||||
audio_pcm_info_clear_buf(&hw->info, buf, bytes >> hw->info.shift);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
static void no_enable_in(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||
|
||||
if (enable) {
|
||||
audio_rate_start(&no->rate);
|
||||
}
|
||||
}
|
||||
|
||||
static void *no_audio_init(Audiodev *dev)
|
||||
@ -133,13 +117,13 @@ static void no_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops no_pcm_ops = {
|
||||
.init_out = no_init_out,
|
||||
.fini_out = no_fini_out,
|
||||
.run_out = no_run_out,
|
||||
.ctl_out = no_ctl_out,
|
||||
.write = no_write,
|
||||
.enable_out = no_enable_out,
|
||||
|
||||
.init_in = no_init_in,
|
||||
.fini_in = no_fini_in,
|
||||
.run_in = no_run_in,
|
||||
.ctl_in = no_ctl_in
|
||||
.read = no_read,
|
||||
.enable_in = no_enable_in
|
||||
};
|
||||
|
||||
static struct audio_driver no_audio_driver = {
|
||||
|
358
audio/ossaudio.c
358
audio/ossaudio.c
@ -40,19 +40,15 @@
|
||||
|
||||
typedef struct OSSVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int wpos;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
int mmapped;
|
||||
int pending;
|
||||
Audiodev *dev;
|
||||
} OSSVoiceOut;
|
||||
|
||||
typedef struct OSSVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
void *pcm_buf;
|
||||
int fd;
|
||||
int nfrags;
|
||||
int fragsize;
|
||||
@ -371,98 +367,87 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void oss_write_pending (OSSVoiceOut *oss)
|
||||
static size_t oss_get_available_bytes(OSSVoiceOut *oss)
|
||||
{
|
||||
HWVoiceOut *hw = &oss->hw;
|
||||
int err;
|
||||
struct count_info cntinfo;
|
||||
assert(oss->mmapped);
|
||||
|
||||
if (oss->mmapped) {
|
||||
return;
|
||||
err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (oss->pending) {
|
||||
int samples_written;
|
||||
ssize_t bytes_written;
|
||||
int samples_till_end = hw->samples - oss->wpos;
|
||||
int samples_to_write = MIN (oss->pending, samples_till_end);
|
||||
int bytes_to_write = samples_to_write << hw->info.shift;
|
||||
void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
|
||||
return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
|
||||
}
|
||||
|
||||
bytes_written = write (oss->fd, pcm, bytes_to_write);
|
||||
if (bytes_written < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
oss_logerr (errno, "failed to write %d bytes\n",
|
||||
bytes_to_write);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytes_written & hw->info.align) {
|
||||
dolog ("misaligned write asked for %d, but got %zd\n",
|
||||
bytes_to_write, bytes_written);
|
||||
return;
|
||||
}
|
||||
|
||||
samples_written = bytes_written >> hw->info.shift;
|
||||
oss->pending -= samples_written;
|
||||
oss->wpos = (oss->wpos + samples_written) % hw->samples;
|
||||
if (bytes_written - bytes_to_write) {
|
||||
break;
|
||||
}
|
||||
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
if (oss->mmapped) {
|
||||
*size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
|
||||
return hw->buf_emul + hw->pos_emul;
|
||||
} else {
|
||||
return audio_generic_get_buffer_out(hw, size);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t oss_run_out(HWVoiceOut *hw, size_t live)
|
||||
static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
int err;
|
||||
size_t decr;
|
||||
struct audio_buf_info abinfo;
|
||||
struct count_info cntinfo;
|
||||
size_t bufsize;
|
||||
if (oss->mmapped) {
|
||||
assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul);
|
||||
|
||||
bufsize = hw->samples << hw->info.shift;
|
||||
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
|
||||
return size;
|
||||
} else {
|
||||
return audio_generic_put_buffer_out(hw, buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
size_t pos;
|
||||
|
||||
if (oss->mmapped) {
|
||||
int bytes, pos;
|
||||
size_t total_len;
|
||||
len = MIN(len, oss_get_available_bytes(oss));
|
||||
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
total_len = len;
|
||||
while (len) {
|
||||
size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul);
|
||||
memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
|
||||
|
||||
pos = hw->rpos << hw->info.shift;
|
||||
bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
|
||||
decr = MIN (bytes >> hw->info.shift, live);
|
||||
}
|
||||
else {
|
||||
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
|
||||
if (err < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (abinfo.bytes > bufsize) {
|
||||
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
|
||||
abinfo.bytes = bufsize;
|
||||
}
|
||||
|
||||
if (abinfo.bytes < 0) {
|
||||
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = MIN (abinfo.bytes >> hw->info.shift, live);
|
||||
if (!decr) {
|
||||
return 0;
|
||||
hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul;
|
||||
buf += to_copy;
|
||||
len -= to_copy;
|
||||
}
|
||||
return total_len;
|
||||
}
|
||||
|
||||
decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
|
||||
oss->pending += decr;
|
||||
oss_write_pending (oss);
|
||||
pos = 0;
|
||||
while (len) {
|
||||
ssize_t bytes_written;
|
||||
void *pcm = advance(buf, pos);
|
||||
|
||||
return decr;
|
||||
bytes_written = write(oss->fd, pcm, len);
|
||||
if (bytes_written < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
oss_logerr(errno, "failed to write %zu bytes\n",
|
||||
len);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
pos += bytes_written;
|
||||
if (bytes_written < len) {
|
||||
break;
|
||||
}
|
||||
len -= bytes_written;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void oss_fini_out (HWVoiceOut *hw)
|
||||
@ -473,18 +458,13 @@ static void oss_fini_out (HWVoiceOut *hw)
|
||||
ldebug ("oss_fini\n");
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
if (oss->pcm_buf) {
|
||||
if (oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
if (err) {
|
||||
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
}
|
||||
if (oss->mmapped && hw->buf_emul) {
|
||||
err = munmap(hw->buf_emul, hw->size_emul);
|
||||
if (err) {
|
||||
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
|
||||
hw->buf_emul, hw->size_emul);
|
||||
}
|
||||
else {
|
||||
g_free (oss->pcm_buf);
|
||||
}
|
||||
oss->pcm_buf = NULL;
|
||||
hw->buf_emul = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -535,19 +515,20 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
oss->mmapped = 0;
|
||||
if (oopts->has_try_mmap && oopts->try_mmap) {
|
||||
oss->pcm_buf = mmap (
|
||||
hw->size_emul = hw->samples << hw->info.shift;
|
||||
hw->buf_emul = mmap(
|
||||
NULL,
|
||||
hw->samples << hw->info.shift,
|
||||
hw->size_emul,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0
|
||||
);
|
||||
if (oss->pcm_buf == MAP_FAILED) {
|
||||
if (hw->buf_emul == MAP_FAILED) {
|
||||
oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
|
||||
hw->samples << hw->info.shift);
|
||||
}
|
||||
else {
|
||||
hw->size_emul);
|
||||
hw->buf_emul = NULL;
|
||||
} else {
|
||||
int err;
|
||||
int trig = 0;
|
||||
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
@ -567,88 +548,65 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
err = munmap(hw->buf_emul, hw->size_emul);
|
||||
if (err) {
|
||||
oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
|
||||
oss->pcm_buf, hw->samples << hw->info.shift);
|
||||
hw->buf_emul, hw->size_emul);
|
||||
}
|
||||
hw->buf_emul = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
oss->pcm_buf = audio_calloc(__func__,
|
||||
hw->samples,
|
||||
1 << hw->info.shift);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog (
|
||||
"Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
|
||||
hw->samples,
|
||||
1 << hw->info.shift
|
||||
);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->dev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void oss_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
int trig;
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = opdo->try_poll;
|
||||
if (enable) {
|
||||
bool poll_mode = opdo->try_poll;
|
||||
|
||||
ldebug ("enabling voice\n");
|
||||
if (poll_mode) {
|
||||
oss_poll_out (hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (
|
||||
errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
ldebug("enabling voice\n");
|
||||
if (poll_mode) {
|
||||
oss_poll_out(hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
break;
|
||||
hw->poll_mode = poll_mode;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
if (!oss->mmapped) {
|
||||
return;
|
||||
}
|
||||
|
||||
audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->mix_buf->size);
|
||||
trig = PCM_ENABLE_OUTPUT;
|
||||
if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr(errno,
|
||||
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (hw->poll_mode) {
|
||||
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
|
||||
hw->poll_mode = 0;
|
||||
}
|
||||
|
||||
if (!oss->mmapped) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ldebug ("disabling voice\n");
|
||||
trig = 0;
|
||||
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
|
||||
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
@ -692,13 +650,6 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
}
|
||||
|
||||
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||
oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
if (!oss->pcm_buf) {
|
||||
dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
|
||||
hw->samples, 1 << hw->info.shift);
|
||||
oss_anal_close (&fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oss->fd = fd;
|
||||
oss->dev = dev;
|
||||
@ -710,106 +661,57 @@ static void oss_fini_in (HWVoiceIn *hw)
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
|
||||
oss_anal_close (&oss->fd);
|
||||
|
||||
g_free(oss->pcm_buf);
|
||||
oss->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static size_t oss_run_in(HWVoiceIn *hw)
|
||||
static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
int hwshift = hw->info.shift;
|
||||
int i;
|
||||
size_t live = audio_pcm_hw_get_live_in (hw);
|
||||
size_t dead = hw->samples - live;
|
||||
size_t read_samples = 0;
|
||||
struct {
|
||||
size_t add;
|
||||
size_t len;
|
||||
} bufs[2] = {
|
||||
{ .add = hw->wpos, .len = 0 },
|
||||
{ .add = 0, .len = 0 }
|
||||
};
|
||||
size_t pos = 0;
|
||||
|
||||
if (!dead) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (hw->wpos + dead > hw->samples) {
|
||||
bufs[0].len = (hw->samples - hw->wpos) << hwshift;
|
||||
bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
|
||||
}
|
||||
else {
|
||||
bufs[0].len = dead << hwshift;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
while (len) {
|
||||
ssize_t nread;
|
||||
|
||||
if (bufs[i].len) {
|
||||
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
|
||||
nread = read (oss->fd, p, bufs[i].len);
|
||||
void *dst = advance(buf, pos);
|
||||
nread = read(oss->fd, dst, len);
|
||||
|
||||
if (nread > 0) {
|
||||
if (nread & hw->info.align) {
|
||||
dolog("warning: Misaligned read %zd (requested %zu), "
|
||||
"alignment %d\n", nread, bufs[i].add << hwshift,
|
||||
hw->info.align + 1);
|
||||
}
|
||||
read_samples += nread >> hwshift;
|
||||
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
|
||||
}
|
||||
|
||||
if (bufs[i].len - nread) {
|
||||
if (nread == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
break;
|
||||
default:
|
||||
oss_logerr(
|
||||
errno,
|
||||
"Failed to read %zu bytes of audio (to %p)\n",
|
||||
bufs[i].len, p
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nread == -1) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
break;
|
||||
default:
|
||||
oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n",
|
||||
len, dst);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pos += nread;
|
||||
len -= nread;
|
||||
}
|
||||
|
||||
hw->wpos = (hw->wpos + read_samples) % hw->samples;
|
||||
return read_samples;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
static void oss_enable_in(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
{
|
||||
bool poll_mode = opdo->try_poll;
|
||||
if (enable) {
|
||||
bool poll_mode = opdo->try_poll;
|
||||
|
||||
if (poll_mode) {
|
||||
oss_poll_in (hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
hw->poll_mode = poll_mode;
|
||||
if (poll_mode) {
|
||||
oss_poll_in(hw);
|
||||
poll_mode = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
hw->poll_mode = poll_mode;
|
||||
} else {
|
||||
if (hw->poll_mode) {
|
||||
hw->poll_mode = 0;
|
||||
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
|
||||
@ -845,13 +747,15 @@ static void oss_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops oss_pcm_ops = {
|
||||
.init_out = oss_init_out,
|
||||
.fini_out = oss_fini_out,
|
||||
.run_out = oss_run_out,
|
||||
.ctl_out = oss_ctl_out,
|
||||
.write = oss_write,
|
||||
.get_buffer_out = oss_get_buffer_out,
|
||||
.put_buffer_out = oss_put_buffer_out,
|
||||
.enable_out = oss_enable_out,
|
||||
|
||||
.init_in = oss_init_in,
|
||||
.fini_in = oss_fini_in,
|
||||
.run_in = oss_run_in,
|
||||
.ctl_in = oss_ctl_in
|
||||
.read = oss_read,
|
||||
.enable_in = oss_enable_in
|
||||
};
|
||||
|
||||
static struct audio_driver oss_audio_driver = {
|
||||
|
503
audio/paaudio.c
503
audio/paaudio.c
@ -9,7 +9,6 @@
|
||||
|
||||
#define AUDIO_CAP "pulseaudio"
|
||||
#include "audio_int.h"
|
||||
#include "audio_pt_int.h"
|
||||
|
||||
typedef struct PAConnection {
|
||||
char *server;
|
||||
@ -30,28 +29,16 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
HWVoiceOut hw;
|
||||
size_t done;
|
||||
size_t live;
|
||||
size_t decr;
|
||||
size_t rpos;
|
||||
pa_stream *stream;
|
||||
void *pcm_buf;
|
||||
struct audio_pt pt;
|
||||
paaudio *g;
|
||||
size_t samples;
|
||||
} PAVoiceOut;
|
||||
|
||||
typedef struct {
|
||||
HWVoiceIn hw;
|
||||
size_t done;
|
||||
size_t dead;
|
||||
size_t incr;
|
||||
size_t wpos;
|
||||
pa_stream *stream;
|
||||
void *pcm_buf;
|
||||
struct audio_pt pt;
|
||||
const void *read_data;
|
||||
size_t read_index, read_length;
|
||||
size_t read_length;
|
||||
paaudio *g;
|
||||
size_t samples;
|
||||
} PAVoiceIn;
|
||||
@ -89,298 +76,96 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
|
||||
#define CHECK_SUCCESS_GOTO(c, expression, label, msg) \
|
||||
do { \
|
||||
if (!(expression)) { \
|
||||
if (rerror) { \
|
||||
*(rerror) = pa_context_errno ((c)->context); \
|
||||
} \
|
||||
qpa_logerr(pa_context_errno((c)->context), msg); \
|
||||
goto label; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
|
||||
#define CHECK_DEAD_GOTO(c, stream, label, msg) \
|
||||
do { \
|
||||
if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
|
||||
!(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
|
||||
if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
|
||||
((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
|
||||
if (rerror) { \
|
||||
*(rerror) = pa_context_errno ((c)->context); \
|
||||
} \
|
||||
qpa_logerr(pa_context_errno((c)->context), msg); \
|
||||
} else { \
|
||||
if (rerror) { \
|
||||
*(rerror) = PA_ERR_BADSTATE; \
|
||||
} \
|
||||
qpa_logerr(PA_ERR_BADSTATE, msg); \
|
||||
} \
|
||||
goto label; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
|
||||
static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
|
||||
{
|
||||
PAVoiceIn *p = (PAVoiceIn *) hw;
|
||||
PAConnection *c = p->g->conn;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
|
||||
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
|
||||
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
|
||||
"pa_threaded_mainloop_lock failed\n");
|
||||
|
||||
while (length > 0) {
|
||||
size_t l;
|
||||
if (!p->read_length) {
|
||||
r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
|
||||
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
|
||||
"pa_stream_peek failed\n");
|
||||
}
|
||||
|
||||
while (!p->read_data) {
|
||||
int r;
|
||||
l = MIN(p->read_length, length);
|
||||
memcpy(data, p->read_data, l);
|
||||
|
||||
r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
|
||||
CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
|
||||
p->read_data += l;
|
||||
p->read_length -= l;
|
||||
|
||||
if (!p->read_data) {
|
||||
pa_threaded_mainloop_wait(c->mainloop);
|
||||
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
|
||||
} else {
|
||||
p->read_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
l = p->read_length < length ? p->read_length : length;
|
||||
memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
|
||||
|
||||
data = (uint8_t *) data + l;
|
||||
length -= l;
|
||||
|
||||
p->read_index += l;
|
||||
p->read_length -= l;
|
||||
|
||||
if (!p->read_length) {
|
||||
int r;
|
||||
|
||||
r = pa_stream_drop (p->stream);
|
||||
p->read_data = NULL;
|
||||
p->read_length = 0;
|
||||
p->read_index = 0;
|
||||
|
||||
CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
|
||||
}
|
||||
if (!p->read_length) {
|
||||
r = pa_stream_drop(p->stream);
|
||||
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
|
||||
"pa_stream_drop failed\n");
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
return 0;
|
||||
return l;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
|
||||
static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
|
||||
{
|
||||
PAVoiceOut *p = (PAVoiceOut *) hw;
|
||||
PAConnection *c = p->g->conn;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
|
||||
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
|
||||
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
|
||||
"pa_threaded_mainloop_lock failed\n");
|
||||
|
||||
while (length > 0) {
|
||||
size_t l;
|
||||
int r;
|
||||
l = pa_stream_writable_size(p->stream);
|
||||
|
||||
while (!(l = pa_stream_writable_size (p->stream))) {
|
||||
pa_threaded_mainloop_wait(c->mainloop);
|
||||
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
|
||||
}
|
||||
CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
|
||||
"pa_stream_writable_size failed\n");
|
||||
|
||||
CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail);
|
||||
|
||||
if (l > length) {
|
||||
l = length;
|
||||
}
|
||||
|
||||
r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail);
|
||||
|
||||
data = (const uint8_t *) data + l;
|
||||
length -= l;
|
||||
if (l > length) {
|
||||
l = length;
|
||||
}
|
||||
|
||||
r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
|
||||
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
return 0;
|
||||
return l;
|
||||
|
||||
unlock_and_fail:
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *qpa_thread_out (void *arg)
|
||||
{
|
||||
PAVoiceOut *pa = arg;
|
||||
HWVoiceOut *hw = &pa->hw;
|
||||
|
||||
if (audio_pt_lock(&pa->pt, __func__)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
size_t decr, to_mix, rpos;
|
||||
|
||||
for (;;) {
|
||||
if (pa->done) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (pa->live > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (audio_pt_wait(&pa->pt, __func__)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
decr = to_mix = MIN(pa->live, pa->samples >> 5);
|
||||
rpos = pa->rpos;
|
||||
|
||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (to_mix) {
|
||||
int error;
|
||||
size_t chunk = MIN (to_mix, hw->samples - rpos);
|
||||
struct st_sample *src = hw->mix_buf + rpos;
|
||||
|
||||
hw->clip (pa->pcm_buf, src, chunk);
|
||||
|
||||
if (qpa_simple_write (pa, pa->pcm_buf,
|
||||
chunk << hw->info.shift, &error) < 0) {
|
||||
qpa_logerr (error, "pa_simple_write failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rpos = (rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
}
|
||||
|
||||
if (audio_pt_lock(&pa->pt, __func__)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa->rpos = rpos;
|
||||
pa->live -= decr;
|
||||
pa->decr += decr;
|
||||
}
|
||||
|
||||
exit:
|
||||
audio_pt_unlock(&pa->pt, __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t qpa_run_out(HWVoiceOut *hw, size_t live)
|
||||
{
|
||||
size_t decr;
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
|
||||
if (audio_pt_lock(&pa->pt, __func__)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
decr = MIN (live, pa->decr);
|
||||
pa->decr -= decr;
|
||||
pa->live = live - decr;
|
||||
hw->rpos = pa->rpos;
|
||||
if (pa->live > 0) {
|
||||
audio_pt_unlock_and_signal(&pa->pt, __func__);
|
||||
}
|
||||
else {
|
||||
audio_pt_unlock(&pa->pt, __func__);
|
||||
}
|
||||
return decr;
|
||||
}
|
||||
|
||||
/* capture */
|
||||
static void *qpa_thread_in (void *arg)
|
||||
{
|
||||
PAVoiceIn *pa = arg;
|
||||
HWVoiceIn *hw = &pa->hw;
|
||||
|
||||
if (audio_pt_lock(&pa->pt, __func__)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
size_t incr, to_grab, wpos;
|
||||
|
||||
for (;;) {
|
||||
if (pa->done) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (pa->dead > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (audio_pt_wait(&pa->pt, __func__)) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
incr = to_grab = MIN(pa->dead, pa->samples >> 5);
|
||||
wpos = pa->wpos;
|
||||
|
||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (to_grab) {
|
||||
int error;
|
||||
size_t chunk = MIN (to_grab, hw->samples - wpos);
|
||||
void *buf = advance (pa->pcm_buf, wpos);
|
||||
|
||||
if (qpa_simple_read (pa, buf,
|
||||
chunk << hw->info.shift, &error) < 0) {
|
||||
qpa_logerr (error, "pa_simple_read failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hw->conv (hw->conv_buf + wpos, buf, chunk);
|
||||
wpos = (wpos + chunk) % hw->samples;
|
||||
to_grab -= chunk;
|
||||
}
|
||||
|
||||
if (audio_pt_lock(&pa->pt, __func__)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa->wpos = wpos;
|
||||
pa->dead -= incr;
|
||||
pa->incr += incr;
|
||||
}
|
||||
|
||||
exit:
|
||||
audio_pt_unlock(&pa->pt, __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t qpa_run_in(HWVoiceIn *hw)
|
||||
{
|
||||
size_t live, incr, dead;
|
||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
|
||||
if (audio_pt_lock(&pa->pt, __func__)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
live = audio_pcm_hw_get_live_in (hw);
|
||||
dead = hw->samples - live;
|
||||
incr = MIN (dead, pa->incr);
|
||||
pa->incr -= incr;
|
||||
pa->dead = dead - incr;
|
||||
hw->wpos = pa->wpos;
|
||||
if (pa->dead > 0) {
|
||||
audio_pt_unlock_and_signal(&pa->pt, __func__);
|
||||
}
|
||||
else {
|
||||
audio_pt_unlock(&pa->pt, __func__);
|
||||
}
|
||||
return incr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
|
||||
@ -468,13 +253,6 @@ static void stream_state_cb (pa_stream *s, void * userdata)
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
|
||||
{
|
||||
PAConnection *c = userdata;
|
||||
|
||||
pa_threaded_mainloop_signal(c->mainloop, 0);
|
||||
}
|
||||
|
||||
static pa_stream *qpa_simple_new (
|
||||
PAConnection *c,
|
||||
const char *name,
|
||||
@ -497,8 +275,6 @@ static pa_stream *qpa_simple_new (
|
||||
}
|
||||
|
||||
pa_stream_set_state_callback(stream, stream_state_cb, c);
|
||||
pa_stream_set_read_callback(stream, stream_request_cb, c);
|
||||
pa_stream_set_write_callback(stream, stream_request_cb, c);
|
||||
|
||||
flags =
|
||||
PA_STREAM_INTERPOLATE_TIMING
|
||||
@ -579,28 +355,9 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
hw->samples = pa->samples = audio_buffer_samples(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||
&obt_as, ppdo->buffer_length);
|
||||
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
pa->rpos = hw->rpos;
|
||||
if (!pa->pcm_buf) {
|
||||
dolog("Could not allocate buffer (%zu bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if (audio_pt_init(&pa->pt, qpa_thread_out, hw, AUDIO_CAP, __func__)) {
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
g_free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
fail2:
|
||||
if (pa->stream) {
|
||||
pa_stream_unref (pa->stream);
|
||||
pa->stream = NULL;
|
||||
}
|
||||
fail1:
|
||||
return -1;
|
||||
}
|
||||
@ -647,28 +404,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
hw->samples = pa->samples = audio_buffer_samples(
|
||||
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
|
||||
&obt_as, ppdo->buffer_length);
|
||||
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
pa->wpos = hw->wpos;
|
||||
if (!pa->pcm_buf) {
|
||||
dolog("Could not allocate buffer (%zu bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if (audio_pt_init(&pa->pt, qpa_thread_in, hw, AUDIO_CAP, __func__)) {
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
g_free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
fail2:
|
||||
if (pa->stream) {
|
||||
pa_stream_unref (pa->stream);
|
||||
pa->stream = NULL;
|
||||
}
|
||||
fail1:
|
||||
return -1;
|
||||
}
|
||||
@ -696,45 +434,25 @@ static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
|
||||
|
||||
static void qpa_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
void *ret;
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
|
||||
audio_pt_lock(&pa->pt, __func__);
|
||||
pa->done = 1;
|
||||
audio_pt_unlock_and_signal(&pa->pt, __func__);
|
||||
audio_pt_join(&pa->pt, &ret, __func__);
|
||||
|
||||
if (pa->stream) {
|
||||
qpa_simple_disconnect(pa->g->conn, pa->stream);
|
||||
pa->stream = NULL;
|
||||
}
|
||||
|
||||
audio_pt_fini(&pa->pt, __func__);
|
||||
g_free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static void qpa_fini_in (HWVoiceIn *hw)
|
||||
{
|
||||
void *ret;
|
||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
|
||||
audio_pt_lock(&pa->pt, __func__);
|
||||
pa->done = 1;
|
||||
audio_pt_unlock_and_signal(&pa->pt, __func__);
|
||||
audio_pt_join(&pa->pt, &ret, __func__);
|
||||
|
||||
if (pa->stream) {
|
||||
qpa_simple_disconnect(pa->g->conn, pa->stream);
|
||||
pa->stream = NULL;
|
||||
}
|
||||
|
||||
audio_pt_fini(&pa->pt, __func__);
|
||||
g_free (pa->pcm_buf);
|
||||
pa->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void qpa_volume_out(HWVoiceOut *hw, struct mixeng_volume *vol)
|
||||
{
|
||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||
pa_operation *op;
|
||||
@ -745,49 +463,36 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
pa_cvolume_init (&v); /* function is present in 0.9.13+ */
|
||||
#endif
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
SWVoiceOut *sw;
|
||||
va_list ap;
|
||||
v.channels = 2;
|
||||
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->l) / UINT32_MAX;
|
||||
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->r) / UINT32_MAX;
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceOut *);
|
||||
va_end (ap);
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
|
||||
v.channels = 2;
|
||||
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
|
||||
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
|
||||
op = pa_context_set_sink_input_volume(c->context,
|
||||
pa_stream_get_index (pa->stream),
|
||||
&v, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_sink_input_volume() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
|
||||
op = pa_context_set_sink_input_mute(c->context,
|
||||
pa_stream_get_index (pa->stream),
|
||||
sw->vol.mute, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_sink_input_mute() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
}
|
||||
op = pa_context_set_sink_input_volume(c->context,
|
||||
pa_stream_get_index(pa->stream),
|
||||
&v, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_sink_input_volume() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
return 0;
|
||||
|
||||
op = pa_context_set_sink_input_mute(c->context,
|
||||
pa_stream_get_index(pa->stream),
|
||||
vol->mute, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_sink_input_mute() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
}
|
||||
|
||||
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
static void qpa_volume_in(HWVoiceIn *hw, struct mixeng_volume *vol)
|
||||
{
|
||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||
pa_operation *op;
|
||||
@ -798,46 +503,33 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
pa_cvolume_init (&v);
|
||||
#endif
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
SWVoiceIn *sw;
|
||||
va_list ap;
|
||||
v.channels = 2;
|
||||
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->l) / UINT32_MAX;
|
||||
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->r) / UINT32_MAX;
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceIn *);
|
||||
va_end (ap);
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
|
||||
v.channels = 2;
|
||||
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
|
||||
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
|
||||
|
||||
pa_threaded_mainloop_lock(c->mainloop);
|
||||
|
||||
op = pa_context_set_source_output_volume(c->context,
|
||||
pa_stream_get_index(pa->stream),
|
||||
&v, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_source_output_volume() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
|
||||
op = pa_context_set_source_output_mute(c->context,
|
||||
pa_stream_get_index (pa->stream),
|
||||
sw->vol.mute, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_source_output_mute() failed\n");
|
||||
} else {
|
||||
pa_operation_unref (op);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
}
|
||||
op = pa_context_set_source_output_volume(c->context,
|
||||
pa_stream_get_index(pa->stream),
|
||||
&v, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_source_output_volume() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
return 0;
|
||||
|
||||
op = pa_context_set_source_output_mute(c->context,
|
||||
pa_stream_get_index(pa->stream),
|
||||
vol->mute, NULL, NULL);
|
||||
if (!op) {
|
||||
qpa_logerr(pa_context_errno(c->context),
|
||||
"set_source_output_mute() failed\n");
|
||||
} else {
|
||||
pa_operation_unref(op);
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_unlock(c->mainloop);
|
||||
}
|
||||
|
||||
static int qpa_validate_per_direction_opts(Audiodev *dev,
|
||||
@ -1005,13 +697,13 @@ static void qpa_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops qpa_pcm_ops = {
|
||||
.init_out = qpa_init_out,
|
||||
.fini_out = qpa_fini_out,
|
||||
.run_out = qpa_run_out,
|
||||
.ctl_out = qpa_ctl_out,
|
||||
.write = qpa_write,
|
||||
.volume_out = qpa_volume_out,
|
||||
|
||||
.init_in = qpa_init_in,
|
||||
.fini_in = qpa_fini_in,
|
||||
.run_in = qpa_run_in,
|
||||
.ctl_in = qpa_ctl_in
|
||||
.read = qpa_read,
|
||||
.volume_in = qpa_volume_in
|
||||
};
|
||||
|
||||
static struct audio_driver pa_audio_driver = {
|
||||
@ -1025,7 +717,6 @@ static struct audio_driver pa_audio_driver = {
|
||||
.max_voices_in = INT_MAX,
|
||||
.voice_size_out = sizeof (PAVoiceOut),
|
||||
.voice_size_in = sizeof (PAVoiceIn),
|
||||
.ctl_caps = VOICE_VOLUME_CAP
|
||||
};
|
||||
|
||||
static void register_audio_pa(void)
|
||||
|
102
audio/sdlaudio.c
102
audio/sdlaudio.c
@ -41,8 +41,6 @@
|
||||
|
||||
typedef struct SDLVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
size_t live;
|
||||
size_t decr;
|
||||
} SDLVoiceOut;
|
||||
|
||||
static struct SDLAudioState {
|
||||
@ -184,62 +182,59 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
SDLVoiceOut *sdl = opaque;
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
HWVoiceOut *hw = &sdl->hw;
|
||||
size_t samples = len >> hw->info.shift;
|
||||
size_t to_mix, decr;
|
||||
|
||||
if (s->exit || !sdl->live) {
|
||||
if (s->exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */
|
||||
|
||||
to_mix = MIN(samples, sdl->live);
|
||||
decr = to_mix;
|
||||
while (to_mix) {
|
||||
size_t chunk = MIN(to_mix, hw->samples - hw->rpos);
|
||||
struct st_sample *src = hw->mix_buf + hw->rpos;
|
||||
while (hw->pending_emul && len) {
|
||||
size_t write_len;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
/* dolog ("in callback to_mix %zu, chunk %zu\n", to_mix, chunk); */
|
||||
hw->clip(buf, src, chunk);
|
||||
hw->rpos = (hw->rpos + chunk) % hw->samples;
|
||||
to_mix -= chunk;
|
||||
buf += chunk << hw->info.shift;
|
||||
write_len = MIN(MIN(hw->pending_emul, len),
|
||||
hw->size_emul - start);
|
||||
|
||||
memcpy(buf, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= write_len;
|
||||
len -= write_len;
|
||||
buf += write_len;
|
||||
}
|
||||
samples -= decr;
|
||||
sdl->live -= decr;
|
||||
sdl->decr += decr;
|
||||
|
||||
/* dolog ("done len=%zu\n", len); */
|
||||
|
||||
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
|
||||
if (samples) {
|
||||
memset(buf, 0, samples << hw->info.shift);
|
||||
/* clear remaining buffer that we couldn't fill with data */
|
||||
if (len) {
|
||||
memset(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t sdl_run_out(HWVoiceOut *hw, size_t live)
|
||||
{
|
||||
size_t decr;
|
||||
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
|
||||
|
||||
SDL_LockAudio();
|
||||
|
||||
if (sdl->decr > live) {
|
||||
ldebug ("sdl->decr %d live %d sdl->live %d\n",
|
||||
sdl->decr,
|
||||
live,
|
||||
sdl->live);
|
||||
#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \
|
||||
static ret_type glue(sdl_, name)args_decl \
|
||||
{ \
|
||||
ret_type ret; \
|
||||
\
|
||||
SDL_LockAudio(); \
|
||||
\
|
||||
ret = glue(audio_generic_, name)args; \
|
||||
\
|
||||
SDL_UnlockAudio(); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
decr = MIN (sdl->decr, live);
|
||||
sdl->decr -= decr;
|
||||
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size), *size = 0, sdl_unlock)
|
||||
SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
|
||||
/*nothing*/, sdl_unlock_and_post)
|
||||
SDL_WRAPPER_FUNC(write, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
|
||||
/*nothing*/, sdl_unlock_and_post)
|
||||
|
||||
sdl->live = live;
|
||||
|
||||
SDL_UnlockAudio();
|
||||
|
||||
return decr;
|
||||
}
|
||||
#undef SDL_WRAPPER_FUNC
|
||||
|
||||
static void sdl_fini_out (HWVoiceOut *hw)
|
||||
{
|
||||
@ -290,20 +285,9 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void sdl_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
(void) hw;
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
SDL_PauseAudio (0);
|
||||
break;
|
||||
|
||||
case VOICE_DISABLE:
|
||||
SDL_PauseAudio (1);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
SDL_PauseAudio(!enable);
|
||||
}
|
||||
|
||||
static void *sdl_audio_init(Audiodev *dev)
|
||||
@ -336,8 +320,10 @@ static void sdl_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
.init_out = sdl_init_out,
|
||||
.fini_out = sdl_fini_out,
|
||||
.run_out = sdl_run_out,
|
||||
.ctl_out = sdl_ctl_out,
|
||||
.write = sdl_write,
|
||||
.get_buffer_out = sdl_get_buffer_out,
|
||||
.put_buffer_out = sdl_put_buffer_out_nowrite,
|
||||
.enable_out = sdl_enable_out,
|
||||
};
|
||||
|
||||
static struct audio_driver sdl_audio_driver = {
|
||||
|
@ -40,27 +40,21 @@
|
||||
#define LINE_IN_SAMPLES (256 * 4)
|
||||
#endif
|
||||
|
||||
typedef struct SpiceRateCtl {
|
||||
int64_t start_ticks;
|
||||
int64_t bytes_sent;
|
||||
} SpiceRateCtl;
|
||||
|
||||
typedef struct SpiceVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
SpicePlaybackInstance sin;
|
||||
SpiceRateCtl rate;
|
||||
RateCtl rate;
|
||||
int active;
|
||||
uint32_t *frame;
|
||||
uint32_t *fpos;
|
||||
uint32_t fpos;
|
||||
uint32_t fsize;
|
||||
} SpiceVoiceOut;
|
||||
|
||||
typedef struct SpiceVoiceIn {
|
||||
HWVoiceIn hw;
|
||||
SpiceRecordInstance sin;
|
||||
SpiceRateCtl rate;
|
||||
RateCtl rate;
|
||||
int active;
|
||||
uint32_t samples[LINE_IN_SAMPLES];
|
||||
} SpiceVoiceIn;
|
||||
|
||||
static const SpicePlaybackInterface playback_sif = {
|
||||
@ -90,32 +84,6 @@ static void spice_audio_fini (void *opaque)
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void rate_start (SpiceRateCtl *rate)
|
||||
{
|
||||
memset (rate, 0, sizeof (*rate));
|
||||
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
|
||||
{
|
||||
int64_t now;
|
||||
int64_t ticks;
|
||||
int64_t bytes;
|
||||
int64_t samples;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
ticks = now - rate->start_ticks;
|
||||
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
samples = (bytes - rate->bytes_sent) >> info->shift;
|
||||
if (samples < 0 || samples > 65536) {
|
||||
error_report("Resetting rate control (%" PRId64 " samples)", samples);
|
||||
rate_start(rate);
|
||||
samples = 0;
|
||||
}
|
||||
rate->bytes_sent += samples << info->shift;
|
||||
return samples;
|
||||
}
|
||||
|
||||
/* playback */
|
||||
|
||||
static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
|
||||
@ -152,94 +120,77 @@ static void line_out_fini (HWVoiceOut *hw)
|
||||
spice_server_remove_interface (&out->sin.base);
|
||||
}
|
||||
|
||||
static size_t line_out_run (HWVoiceOut *hw, size_t live)
|
||||
static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
size_t rpos, decr;
|
||||
size_t samples;
|
||||
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
|
||||
|
||||
if (!live) {
|
||||
return 0;
|
||||
if (!out->frame) {
|
||||
spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
|
||||
out->fpos = 0;
|
||||
}
|
||||
|
||||
decr = rate_get_samples (&hw->info, &out->rate);
|
||||
decr = MIN (live, decr);
|
||||
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int len = MIN (samples, left_till_end_samples);
|
||||
|
||||
if (!out->frame) {
|
||||
spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
|
||||
out->fpos = out->frame;
|
||||
}
|
||||
if (out->frame) {
|
||||
len = MIN (len, out->fsize);
|
||||
hw->clip (out->fpos, hw->mix_buf + rpos, len);
|
||||
out->fsize -= len;
|
||||
out->fpos += len;
|
||||
if (out->fsize == 0) {
|
||||
spice_server_playback_put_samples (&out->sin, out->frame);
|
||||
out->frame = out->fpos = NULL;
|
||||
}
|
||||
}
|
||||
rpos = (rpos + len) % hw->samples;
|
||||
samples -= len;
|
||||
if (out->frame) {
|
||||
*size = audio_rate_get_bytes(
|
||||
&hw->info, &out->rate, (out->fsize - out->fpos) << hw->info.shift);
|
||||
} else {
|
||||
audio_rate_start(&out->rate);
|
||||
}
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
return out->frame + out->fpos;
|
||||
}
|
||||
|
||||
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
|
||||
static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
|
||||
|
||||
assert(buf == out->frame + out->fpos && out->fpos <= out->fsize);
|
||||
out->fpos += size >> 2;
|
||||
|
||||
if (out->fpos == out->fsize) { /* buffer full */
|
||||
spice_server_playback_put_samples(&out->sin, out->frame);
|
||||
out->frame = NULL;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void line_out_enable(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (enable) {
|
||||
if (out->active) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
out->active = 1;
|
||||
rate_start (&out->rate);
|
||||
audio_rate_start(&out->rate);
|
||||
spice_server_playback_start (&out->sin);
|
||||
break;
|
||||
case VOICE_DISABLE:
|
||||
} else {
|
||||
if (!out->active) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
out->active = 0;
|
||||
if (out->frame) {
|
||||
memset (out->fpos, 0, out->fsize << 2);
|
||||
memset(out->frame + out->fpos, 0, (out->fsize - out->fpos) << 2);
|
||||
spice_server_playback_put_samples (&out->sin, out->frame);
|
||||
out->frame = out->fpos = NULL;
|
||||
out->frame = NULL;
|
||||
}
|
||||
spice_server_playback_stop (&out->sin);
|
||||
break;
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
|
||||
SWVoiceOut *sw;
|
||||
va_list ap;
|
||||
uint16_t vol[2];
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceOut *);
|
||||
va_end (ap);
|
||||
|
||||
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
|
||||
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
|
||||
spice_server_playback_set_volume (&out->sin, 2, vol);
|
||||
spice_server_playback_set_mute (&out->sin, sw->vol.mute);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
|
||||
static void line_out_volume(HWVoiceOut *hw, struct mixeng_volume *vol)
|
||||
{
|
||||
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
|
||||
uint16_t svol[2];
|
||||
|
||||
svol[0] = vol->l / ((1ULL << 16) + 1);
|
||||
svol[1] = vol->r / ((1ULL << 16) + 1);
|
||||
spice_server_playback_set_volume(&out->sin, 2, svol);
|
||||
spice_server_playback_set_mute(&out->sin, vol->mute);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* record */
|
||||
|
||||
static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||
@ -275,104 +226,73 @@ static void line_in_fini (HWVoiceIn *hw)
|
||||
spice_server_remove_interface (&in->sin.base);
|
||||
}
|
||||
|
||||
static size_t line_in_run(HWVoiceIn *hw)
|
||||
static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
size_t num_samples;
|
||||
int ready;
|
||||
size_t len[2];
|
||||
uint64_t delta_samp;
|
||||
const uint32_t *samples;
|
||||
uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2;
|
||||
size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
|
||||
|
||||
if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
delta_samp = rate_get_samples (&hw->info, &in->rate);
|
||||
num_samples = MIN (num_samples, delta_samp);
|
||||
|
||||
ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
|
||||
samples = in->samples;
|
||||
/* XXX: do we need this? */
|
||||
if (ready == 0) {
|
||||
static const uint32_t silence[LINE_IN_SAMPLES];
|
||||
samples = silence;
|
||||
ready = LINE_IN_SAMPLES;
|
||||
memset(buf, 0, to_read << 2);
|
||||
ready = to_read;
|
||||
}
|
||||
|
||||
num_samples = MIN (ready, num_samples);
|
||||
|
||||
if (hw->wpos + num_samples > hw->samples) {
|
||||
len[0] = hw->samples - hw->wpos;
|
||||
len[1] = num_samples - len[0];
|
||||
} else {
|
||||
len[0] = num_samples;
|
||||
len[1] = 0;
|
||||
}
|
||||
|
||||
hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
|
||||
|
||||
if (len[1]) {
|
||||
hw->conv (hw->conv_buf, samples + len[0], len[1]);
|
||||
}
|
||||
|
||||
hw->wpos = (hw->wpos + num_samples) % hw->samples;
|
||||
|
||||
return num_samples;
|
||||
return ready << 2;
|
||||
}
|
||||
|
||||
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
|
||||
static void line_in_enable(HWVoiceIn *hw, bool enable)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_ENABLE:
|
||||
if (enable) {
|
||||
if (in->active) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
in->active = 1;
|
||||
rate_start (&in->rate);
|
||||
audio_rate_start(&in->rate);
|
||||
spice_server_record_start (&in->sin);
|
||||
break;
|
||||
case VOICE_DISABLE:
|
||||
} else {
|
||||
if (!in->active) {
|
||||
break;
|
||||
return;
|
||||
}
|
||||
in->active = 0;
|
||||
spice_server_record_stop (&in->sin);
|
||||
break;
|
||||
case VOICE_VOLUME:
|
||||
{
|
||||
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
|
||||
SWVoiceIn *sw;
|
||||
va_list ap;
|
||||
uint16_t vol[2];
|
||||
|
||||
va_start (ap, cmd);
|
||||
sw = va_arg (ap, SWVoiceIn *);
|
||||
va_end (ap);
|
||||
|
||||
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
|
||||
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
|
||||
spice_server_record_set_volume (&in->sin, 2, vol);
|
||||
spice_server_record_set_mute (&in->sin, sw->vol.mute);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
|
||||
static void line_in_volume(HWVoiceIn *hw, struct mixeng_volume *vol)
|
||||
{
|
||||
SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
|
||||
uint16_t svol[2];
|
||||
|
||||
svol[0] = vol->l / ((1ULL << 16) + 1);
|
||||
svol[1] = vol->r / ((1ULL << 16) + 1);
|
||||
spice_server_record_set_volume(&in->sin, 2, svol);
|
||||
spice_server_record_set_mute(&in->sin, vol->mute);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct audio_pcm_ops audio_callbacks = {
|
||||
.init_out = line_out_init,
|
||||
.fini_out = line_out_fini,
|
||||
.run_out = line_out_run,
|
||||
.ctl_out = line_out_ctl,
|
||||
.write = audio_generic_write,
|
||||
.get_buffer_out = line_out_get_buffer,
|
||||
.put_buffer_out = line_out_put_buffer,
|
||||
.enable_out = line_out_enable,
|
||||
#if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \
|
||||
(SPICE_INTERFACE_PLAYBACK_MINOR >= 2)
|
||||
.volume_out = line_out_volume,
|
||||
#endif
|
||||
|
||||
.init_in = line_in_init,
|
||||
.fini_in = line_in_fini,
|
||||
.run_in = line_in_run,
|
||||
.ctl_in = line_in_ctl,
|
||||
.read = line_in_read,
|
||||
.enable_in = line_in_enable,
|
||||
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
|
||||
.volume_in = line_in_volume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct audio_driver spice_audio_driver = {
|
||||
@ -385,9 +305,6 @@ static struct audio_driver spice_audio_driver = {
|
||||
.max_voices_in = 1,
|
||||
.voice_size_out = sizeof (SpiceVoiceOut),
|
||||
.voice_size_in = sizeof (SpiceVoiceIn),
|
||||
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
|
||||
.ctl_caps = VOICE_VOLUME_CAP
|
||||
#endif
|
||||
};
|
||||
|
||||
void qemu_spice_audio_init (void)
|
||||
|
@ -35,53 +35,23 @@
|
||||
typedef struct WAVVoiceOut {
|
||||
HWVoiceOut hw;
|
||||
FILE *f;
|
||||
int64_t old_ticks;
|
||||
void *pcm_buf;
|
||||
RateCtl rate;
|
||||
int total_samples;
|
||||
} WAVVoiceOut;
|
||||
|
||||
static size_t wav_run_out(HWVoiceOut *hw, size_t live)
|
||||
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
|
||||
{
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
size_t rpos, decr, samples;
|
||||
uint8_t *dst;
|
||||
struct st_sample *src;
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t ticks = now - wav->old_ticks;
|
||||
int64_t bytes =
|
||||
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
|
||||
int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
|
||||
assert(bytes >> hw->info.shift << hw->info.shift == bytes);
|
||||
|
||||
if (bytes > INT_MAX) {
|
||||
samples = INT_MAX >> hw->info.shift;
|
||||
}
|
||||
else {
|
||||
samples = bytes >> hw->info.shift;
|
||||
if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
|
||||
dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n",
|
||||
bytes, strerror(errno));
|
||||
}
|
||||
|
||||
wav->old_ticks = now;
|
||||
decr = MIN (live, samples);
|
||||
samples = decr;
|
||||
rpos = hw->rpos;
|
||||
while (samples) {
|
||||
int left_till_end_samples = hw->samples - rpos;
|
||||
int convert_samples = MIN (samples, left_till_end_samples);
|
||||
|
||||
src = hw->mix_buf + rpos;
|
||||
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
|
||||
|
||||
hw->clip (dst, src, convert_samples);
|
||||
if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
|
||||
dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
|
||||
convert_samples << hw->info.shift, strerror (errno));
|
||||
}
|
||||
|
||||
rpos = (rpos + convert_samples) % hw->samples;
|
||||
samples -= convert_samples;
|
||||
wav->total_samples += convert_samples;
|
||||
}
|
||||
|
||||
hw->rpos = rpos;
|
||||
return decr;
|
||||
wav->total_samples += bytes >> hw->info.shift;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* VICE code: Store number as little endian. */
|
||||
@ -137,13 +107,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
audio_pcm_init_info (&hw->info, &wav_as);
|
||||
|
||||
hw->samples = 1024;
|
||||
wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||
if (!wav->pcm_buf) {
|
||||
dolog("Could not allocate buffer (%zu bytes)\n",
|
||||
hw->samples << hw->info.shift);
|
||||
return -1;
|
||||
}
|
||||
|
||||
le_store (hdr + 22, hw->info.nchannels, 2);
|
||||
le_store (hdr + 24, hw->info.freq, 4);
|
||||
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
|
||||
@ -153,8 +116,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
if (!wav->f) {
|
||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||
wav_path, strerror(errno));
|
||||
g_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -163,6 +124,8 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
audio_rate_start(&wav->rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -208,16 +171,15 @@ static void wav_fini_out (HWVoiceOut *hw)
|
||||
wav->f, strerror (errno));
|
||||
}
|
||||
wav->f = NULL;
|
||||
|
||||
g_free (wav->pcm_buf);
|
||||
wav->pcm_buf = NULL;
|
||||
}
|
||||
|
||||
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
static void wav_enable_out(HWVoiceOut *hw, bool enable)
|
||||
{
|
||||
(void) hw;
|
||||
(void) cmd;
|
||||
return 0;
|
||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
||||
|
||||
if (enable) {
|
||||
audio_rate_start(&wav->rate);
|
||||
}
|
||||
}
|
||||
|
||||
static void *wav_audio_init(Audiodev *dev)
|
||||
@ -234,8 +196,8 @@ static void wav_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops wav_pcm_ops = {
|
||||
.init_out = wav_init_out,
|
||||
.fini_out = wav_fini_out,
|
||||
.run_out = wav_run_out,
|
||||
.ctl_out = wav_ctl_out,
|
||||
.write = wav_write_out,
|
||||
.enable_out = wav_enable_out,
|
||||
};
|
||||
|
||||
static struct audio_driver wav_audio_driver = {
|
||||
|
5
configure
vendored
5
configure
vendored
@ -297,7 +297,6 @@ host_cc="cc"
|
||||
libs_cpu=""
|
||||
libs_softmmu=""
|
||||
libs_tools=""
|
||||
audio_pt_int=""
|
||||
audio_win_int=""
|
||||
libs_qga=""
|
||||
debug_info="yes"
|
||||
@ -3388,7 +3387,6 @@ for drv in $audio_drv_list; do
|
||||
pa | try-pa)
|
||||
if $pkg_config libpulse --exists; then
|
||||
pulse_libs=$($pkg_config libpulse --libs)
|
||||
audio_pt_int="yes"
|
||||
if test "$drv" = "try-pa"; then
|
||||
audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-pa/pa/')
|
||||
fi
|
||||
@ -6611,9 +6609,6 @@ echo "PULSE_LIBS=$pulse_libs" >> $config_host_mak
|
||||
echo "COREAUDIO_LIBS=$coreaudio_libs" >> $config_host_mak
|
||||
echo "DSOUND_LIBS=$dsound_libs" >> $config_host_mak
|
||||
echo "OSS_LIBS=$oss_libs" >> $config_host_mak
|
||||
if test "$audio_pt_int" = "yes" ; then
|
||||
echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$audio_win_int" = "yes" ; then
|
||||
echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -439,13 +439,13 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
|
||||
" in|out.format= sample format to use with fixed settings\n"
|
||||
" valid values: s8, s16, s32, u8, u16, u32\n"
|
||||
" in|out.voices= number of voices to use\n"
|
||||
" in|out.buffer-len= length of buffer in microseconds\n"
|
||||
" in|out.buffer-length= length of buffer in microseconds\n"
|
||||
"-audiodev none,id=id,[,prop[=value][,...]]\n"
|
||||
" dummy driver that discards all output\n"
|
||||
#ifdef CONFIG_AUDIO_ALSA
|
||||
"-audiodev alsa,id=id[,prop[=value][,...]]\n"
|
||||
" in|out.dev= name of the audio device to use\n"
|
||||
" in|out.period-len= length of period in microseconds\n"
|
||||
" in|out.period-length= length of period in microseconds\n"
|
||||
" in|out.try-poll= attempt to use poll mode\n"
|
||||
" threshold= threshold (in microseconds) when playback starts\n"
|
||||
#endif
|
||||
@ -524,7 +524,7 @@ Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8},
|
||||
@item in|out.voices=@var{voices}
|
||||
Specify the number of @var{voices} to use. Default is 1.
|
||||
|
||||
@item in|out.buffer=@var{usecs}
|
||||
@item in|out.buffer-length=@var{usecs}
|
||||
Sets the size of the buffer in microseconds.
|
||||
|
||||
@end table
|
||||
@ -545,7 +545,7 @@ ALSA specific options are:
|
||||
Specify the ALSA @var{device} to use for input and/or output. Default
|
||||
is @code{default}.
|
||||
|
||||
@item in|out.period-len=@var{usecs}
|
||||
@item in|out.period-length=@var{usecs}
|
||||
Sets the period length in microseconds.
|
||||
|
||||
@item in|out.try-poll=on|off
|
||||
|
Loading…
Reference in New Issue
Block a user