392 changes: 192 additions & 200 deletions audio/audio.c

Large diffs are not rendered by default.

20 changes: 7 additions & 13 deletions audio/audio_int.h
Expand Up @@ -58,7 +58,7 @@ typedef struct SWVoiceCap SWVoiceCap;

typedef struct STSampleBuffer {
size_t pos, size;
st_sample samples[];
st_sample *buffer;
} STSampleBuffer;

typedef struct HWVoiceOut {
Expand All @@ -71,7 +71,7 @@ typedef struct HWVoiceOut {
f_sample *clip;
uint64_t ts_helper;

STSampleBuffer *mix_buf;
STSampleBuffer mix_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;

Expand All @@ -93,7 +93,7 @@ typedef struct HWVoiceIn {
size_t total_samples_captured;
uint64_t ts_helper;

STSampleBuffer *conv_buf;
STSampleBuffer conv_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;

Expand All @@ -108,8 +108,7 @@ struct SWVoiceOut {
AudioState *s;
struct audio_pcm_info info;
t_sample *conv;
int64_t ratio;
struct st_sample *buf;
STSampleBuffer resample_buf;
void *rate;
size_t total_hw_samples_mixed;
int active;
Expand All @@ -126,10 +125,9 @@ struct SWVoiceIn {
AudioState *s;
int active;
struct audio_pcm_info info;
int64_t ratio;
void *rate;
size_t total_hw_samples_acquired;
struct st_sample *buf;
STSampleBuffer resample_buf;
f_sample *clip;
HWVoiceIn *hw;
char *name;
Expand All @@ -151,8 +149,8 @@ struct audio_driver {
int can_be_default;
int max_voices_out;
int max_voices_in;
int voice_size_out;
int voice_size_in;
size_t voice_size_out;
size_t voice_size_in;
QLIST_ENTRY(audio_driver) next;
};

Expand Down Expand Up @@ -251,7 +249,6 @@ 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);

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

Expand Down Expand Up @@ -294,9 +291,6 @@ static inline size_t audio_ring_posb(size_t pos, size_t dist, size_t len)
#define ldebug(fmt, ...) (void)0
#endif

#define AUDIO_STRINGIFY_(n) #n
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)

typedef struct AudiodevListEntry {
Audiodev *dev;
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
Expand Down
105 changes: 53 additions & 52 deletions audio/audio_template.h
Expand Up @@ -40,7 +40,7 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
struct audio_driver *drv)
{
int max_voices = glue (drv->max_voices_, TYPE);
int voice_size = glue (drv->voice_size_, TYPE);
size_t voice_size = glue(drv->voice_size_, TYPE);

if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
if (!max_voices) {
Expand All @@ -63,16 +63,17 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
}

if (audio_bug(__func__, voice_size && !max_voices)) {
dolog ("drv=`%s' voice_size=%d max_voices=0\n",
drv->name, voice_size);
dolog("drv=`%s' voice_size=%zu max_voices=0\n",
drv->name, voice_size);
}
}

static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
{
g_free(hw->buf_emul);
g_free (HWBUF);
HWBUF = NULL;
g_free(HWBUF.buffer);
HWBUF.buffer = NULL;
HWBUF.size = 0;
}

static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
Expand All @@ -83,56 +84,67 @@ static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
dolog("Attempted to allocate empty buffer\n");
}

HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
HWBUF->size = samples;
HWBUF.buffer = g_new0(st_sample, samples);
HWBUF.size = samples;
HWBUF.pos = 0;
} else {
HWBUF = NULL;
HWBUF.buffer = NULL;
HWBUF.size = 0;
}
}

static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
{
g_free (sw->buf);
g_free(sw->resample_buf.buffer);
sw->resample_buf.buffer = NULL;
sw->resample_buf.size = 0;

if (sw->rate) {
st_rate_stop (sw->rate);
}

sw->buf = NULL;
sw->rate = NULL;
}

static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
{
int samples;
HW *hw = sw->hw;
uint64_t samples;

if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) {
return 0;
}

#ifdef DAC
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
#else
samples = (int64_t)sw->HWBUF->size * sw->ratio >> 32;
#endif
samples = muldiv64(HWBUF.size, sw->info.freq, hw->info.freq);
if (samples == 0) {
uint64_t f_fe_min;
uint64_t f_be = (uint32_t)hw->info.freq;

sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) {
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
SW_NAME (sw), samples);
/* f_fe_min = ceil(1 [frames] * f_be [Hz] / size_be [frames]) */
f_fe_min = (f_be + HWBUF.size - 1) / HWBUF.size;
qemu_log_mask(LOG_UNIMP,
AUDIO_CAP ": The guest selected a " NAME " sample rate"
" of %d Hz for %s. Only sample rates >= %" PRIu64 " Hz"
" are supported.\n",
sw->info.freq, sw->name, f_fe_min);
return -1;
}

/*
* Allocate one additional audio frame that is needed for upsampling
* if the resample buffer size is small. For large buffer sizes take
* care of overflows and truncation.
*/
samples = samples < SIZE_MAX ? samples + 1 : SIZE_MAX;
sw->resample_buf.buffer = g_new0(st_sample, samples);
sw->resample_buf.size = samples;
sw->resample_buf.pos = 0;

#ifdef DAC
sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
sw->rate = st_rate_start(sw->info.freq, hw->info.freq);
#else
sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
sw->rate = st_rate_start(hw->info.freq, sw->info.freq);
#endif
if (!sw->rate) {
g_free (sw->buf);
sw->buf = NULL;
return -1;
}

return 0;
}

Expand All @@ -149,11 +161,8 @@ static int glue (audio_pcm_sw_init_, TYPE) (
sw->hw = hw;
sw->active = 0;
#ifdef DAC
sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
sw->total_hw_samples_mixed = 0;
sw->empty = 1;
#else
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
#endif

if (sw->info.is_float) {
Expand Down Expand Up @@ -264,13 +273,11 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
return NULL;
}

hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE));
if (!hw) {
dolog ("Can not allocate voice `%s' size %d\n",
drv->name, glue (drv->voice_size_, TYPE));
return NULL;
}

/*
* Since glue(s->nb_hw_voices_, TYPE) is != 0, glue(drv->voice_size_, TYPE)
* is guaranteed to be != 0. See the audio_init_nb_voices_* functions.
*/
hw = g_malloc0(glue(drv->voice_size_, TYPE));
hw->s = s;
hw->pcm_ops = drv->pcm_ops;

Expand Down Expand Up @@ -418,33 +425,28 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
hw_as = *as;
}

sw = audio_calloc(__func__, 1, sizeof(*sw));
if (!sw) {
dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
sw_name ? sw_name : "unknown", sizeof (*sw));
goto err1;
}
sw = g_new0(SW, 1);
sw->s = s;

hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
if (!hw) {
goto err2;
dolog("Could not create a backend for voice `%s'\n", sw_name);
goto err1;
}

glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);

if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
goto err3;
goto err2;
}

return sw;

err3:
err2:
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
glue (audio_pcm_hw_gc_, TYPE) (&hw);
err2:
g_free (sw);
err1:
g_free(sw);
return NULL;
}

Expand Down Expand Up @@ -515,8 +517,8 @@ SW *glue (AUD_open_, TYPE) (
HW *hw = sw->hw;

if (!hw) {
dolog ("Internal logic error voice `%s' has no hardware store\n",
SW_NAME (sw));
dolog("Internal logic error: voice `%s' has no backend\n",
SW_NAME(sw));
goto fail;
}

Expand All @@ -527,7 +529,6 @@ SW *glue (AUD_open_, TYPE) (
} else {
sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
if (!sw) {
dolog ("Failed to create voice `%s'\n", name);
return NULL;
}
}
Expand Down
87 changes: 81 additions & 6 deletions audio/mixeng.c
Expand Up @@ -414,12 +414,7 @@ struct rate {
*/
void *st_rate_start (int inrate, int outrate)
{
struct rate *rate = audio_calloc(__func__, 1, sizeof(*rate));

if (!rate) {
dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
return NULL;
}
struct rate *rate = g_new0(struct rate, 1);

rate->opos = 0;

Expand All @@ -445,6 +440,86 @@ void st_rate_stop (void *opaque)
g_free (opaque);
}

/**
* st_rate_frames_out() - returns the number of frames the resampling code
* generates from frames_in frames
*
* @opaque: pointer to struct rate
* @frames_in: number of frames
*
* When upsampling, there may be more than one correct result. In this case,
* the function returns the maximum number of output frames the resampling
* code can generate.
*/
uint32_t st_rate_frames_out(void *opaque, uint32_t frames_in)
{
struct rate *rate = opaque;
uint64_t opos_end, opos_delta;
uint32_t ipos_end;
uint32_t frames_out;

if (rate->opos_inc == 1ULL << 32) {
return frames_in;
}

/* no output frame without at least one input frame */
if (!frames_in) {
return 0;
}

/* last frame read was at rate->ipos - 1 */
ipos_end = rate->ipos - 1 + frames_in;
opos_end = (uint64_t)ipos_end << 32;

/* last frame written was at rate->opos - rate->opos_inc */
if (opos_end + rate->opos_inc <= rate->opos) {
return 0;
}
opos_delta = opos_end - rate->opos + rate->opos_inc;
frames_out = opos_delta / rate->opos_inc;

return opos_delta % rate->opos_inc ? frames_out : frames_out - 1;
}

/**
* st_rate_frames_in() - returns the number of frames needed to
* get frames_out frames after resampling
*
* @opaque: pointer to struct rate
* @frames_out: number of frames
*
* When downsampling, there may be more than one correct result. In this
* case, the function returns the maximum number of input frames needed.
*/
uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out)
{
struct rate *rate = opaque;
uint64_t opos_start, opos_end;
uint32_t ipos_start, ipos_end;

if (rate->opos_inc == 1ULL << 32) {
return frames_out;
}

if (frames_out) {
opos_start = rate->opos;
ipos_start = rate->ipos;
} else {
uint64_t offset;

/* add offset = ceil(opos_inc) to opos and ipos to avoid an underflow */
offset = (rate->opos_inc + (1ULL << 32) - 1) & ~((1ULL << 32) - 1);
opos_start = rate->opos + offset;
ipos_start = rate->ipos + (offset >> 32);
}
/* last frame written was at opos_start - rate->opos_inc */
opos_end = opos_start - rate->opos_inc + rate->opos_inc * frames_out;
ipos_end = (opos_end >> 32) + 1;

/* last frame read was at ipos_start - 1 */
return ipos_end + 1 > ipos_start ? ipos_end + 1 - ipos_start : 0;
}

void mixeng_clear (struct st_sample *buf, int len)
{
memset (buf, 0, len * sizeof (struct st_sample));
Expand Down
2 changes: 2 additions & 0 deletions audio/mixeng.h
Expand Up @@ -52,6 +52,8 @@ void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
size_t *isamp, size_t *osamp);
void st_rate_stop (void *opaque);
uint32_t st_rate_frames_out(void *opaque, uint32_t frames_in);
uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out);
void mixeng_clear (struct st_sample *buf, int len);
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);

Expand Down
21 changes: 13 additions & 8 deletions audio/rate_template.h
Expand Up @@ -40,8 +40,6 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
int64_t t;
#endif

ilast = rate->ilast;

istart = ibuf;
iend = ibuf + *isamp;

Expand All @@ -59,15 +57,17 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
return;
}

while (obuf < oend) {
/* without input samples, there's nothing to do */
if (ibuf >= iend) {
*osamp = 0;
return;
}

/* Safety catch to make sure we have input samples. */
if (ibuf >= iend) {
break;
}
ilast = rate->ilast;

/* read as many input samples so that ipos > opos */
while (true) {

/* read as many input samples so that ipos > opos */
while (rate->ipos <= (rate->opos >> 32)) {
ilast = *ibuf++;
rate->ipos++;
Expand All @@ -78,6 +78,11 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
}
}

/* make sure that the next output sample can be written */
if (obuf >= oend) {
break;
}

icur = *ibuf;

/* wrap ipos and opos around long before they overflow */
Expand Down