blob: 25ea72fd41cd6d83f949c15c3d30cf7f5a338515 [file] [log] [blame]
/*
* QEMU Audio subsystem header
*
* Copyright (c) 2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef DAC
#define TYPE out
#define HW glue (HWVoice, Out)
#define SW glue (SWVoice, Out)
#else
#define TYPE in
#define HW glue (HWVoice, In)
#define SW glue (SWVoice, In)
#endif
static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
{
glue (audio_pcm_sw_free_resources_, TYPE) (sw);
if (sw->name) {
qemu_free (sw->name);
sw->name = NULL;
}
}
static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
{
LIST_INSERT_HEAD (&hw->sw_head, sw, entries);
}
static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
{
LIST_REMOVE (sw, entries);
}
static void glue (audio_pcm_hw_fini_, TYPE) (HW *hw)
{
if (hw->active) {
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
glue (hw->pcm_ops->fini_, TYPE) (hw);
memset (hw, 0, glue (audio_state.drv->voice_size_, TYPE));
}
}
static void glue (audio_pcm_hw_gc_, TYPE) (HW *hw)
{
if (!hw->sw_head.lh_first) {
glue (audio_pcm_hw_fini_, TYPE) (hw);
}
}
static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
{
return hw ? hw->entries.le_next : glue (hw_head_, TYPE).lh_first;
}
static HW *glue (audio_pcm_hw_find_any_active_, TYPE) (HW *hw)
{
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (hw->active) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_find_any_active_enabled_, TYPE) (HW *hw)
{
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (hw->active && hw->enabled) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (HW *hw)
{
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (!hw->active) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
HW *hw,
int freq,
int nchannels,
audfmt_e fmt
)
{
while ((hw = glue (audio_pcm_hw_find_any_active_, TYPE) (hw))) {
if (audio_pcm_info_eq (&hw->info, freq, nchannels, fmt)) {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_add_new_, TYPE) (
int freq,
int nchannels,
audfmt_e fmt
)
{
HW *hw;
hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (NULL);
if (hw) {
hw->pcm_ops = audio_state.drv->pcm_ops;
if (!hw->pcm_ops) {
return NULL;
}
if (glue (audio_pcm_hw_init_, TYPE) (hw, freq, nchannels, fmt)) {
glue (audio_pcm_hw_gc_, TYPE) (hw);
return NULL;
}
else {
return hw;
}
}
return NULL;
}
static HW *glue (audio_pcm_hw_add_, TYPE) (
int freq,
int nchannels,
audfmt_e fmt
)
{
HW *hw;
if (glue (audio_state.greedy_, TYPE)) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt);
if (hw) {
return hw;
}
}
hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, freq, nchannels, fmt);
if (hw) {
return hw;
}
hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt);
if (hw) {
return hw;
}
return glue (audio_pcm_hw_find_any_active_, TYPE) (NULL);
}
static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
const char *name,
int freq,
int nchannels,
audfmt_e fmt
)
{
SW *sw;
HW *hw;
int hw_freq = freq;
int hw_nchannels = nchannels;
int hw_fmt = fmt;
if (glue (audio_state.fixed_settings_, TYPE)) {
hw_freq = glue (audio_state.fixed_freq_, TYPE);
hw_nchannels = glue (audio_state.fixed_channels_, TYPE);
hw_fmt = glue (audio_state.fixed_fmt_, TYPE);
}
sw = qemu_mallocz (sizeof (*sw));
if (!sw) {
goto err1;
}
hw = glue (audio_pcm_hw_add_, TYPE) (hw_freq, hw_nchannels, hw_fmt);
if (!hw) {
goto err2;
}
glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, freq, nchannels, fmt)) {
goto err3;
}
return sw;
err3:
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
glue (audio_pcm_hw_gc_, TYPE) (hw);
err2:
qemu_free (sw);
err1:
return NULL;
}
void glue (AUD_close_, TYPE) (SW *sw)
{
if (sw) {
glue (audio_pcm_sw_fini_, TYPE) (sw);
glue (audio_pcm_hw_del_sw_, TYPE) (sw);
glue (audio_pcm_hw_gc_, TYPE) (sw->hw);
qemu_free (sw);
}
}
SW *glue (AUD_open_, TYPE) (
SW *sw,
const char *name,
void *callback_opaque ,
audio_callback_fn_t callback_fn,
int freq,
int nchannels,
audfmt_e fmt
)
{
#ifdef DAC
int live = 0;
SW *old_sw = NULL;
#endif
if (!callback_fn) {
dolog ("No callback specifed for voice `%s'\n", name);
goto fail;
}
if (nchannels != 1 && nchannels != 2) {
dolog ("Bogus channel count %d for voice `%s'\n", nchannels, name);
goto fail;
}
if (!audio_state.drv) {
dolog ("No audio driver defined\n");
goto fail;
}
if (sw && audio_pcm_info_eq (&sw->info, freq, nchannels, fmt)) {
return sw;
}
#ifdef DAC
if (audio_state.plive && sw && (!sw->active && !sw->empty)) {
live = sw->total_hw_samples_mixed;
#ifdef DEBUG_PLIVE
dolog ("Replacing voice %s with %d live samples\n", sw->name, live);
dolog ("Old %s freq %d, bits %d, channels %d\n",
sw->name, sw->info.freq, sw->info.bits, sw->info.nchannels);
dolog ("New %s freq %d, bits %d, channels %d\n",
name, freq, (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
nchannels);
#endif
if (live) {
old_sw = sw;
old_sw->callback.fn = NULL;
sw = NULL;
}
}
#endif
if (!glue (audio_state.fixed_settings_, TYPE) && sw) {
glue (AUD_close_, TYPE) (sw);
sw = NULL;
}
if (sw) {
HW *hw = sw->hw;
if (!hw) {
dolog ("Internal logic error voice %s has no hardware store\n",
name);
goto fail;
}
if (glue (audio_pcm_sw_init_, TYPE) (
sw,
hw,
name,
freq,
nchannels,
fmt
)) {
goto fail;
}
}
else {
sw = glue (audio_pcm_create_voice_pair_, TYPE) (
name,
freq,
nchannels,
fmt);
if (!sw) {
dolog ("Failed to create voice %s\n", name);
goto fail;
}
}
if (sw) {
sw->vol = nominal_volume;
sw->callback.fn = callback_fn;
sw->callback.opaque = callback_opaque;
#ifdef DAC
if (live) {
int mixed =
(live << old_sw->info.shift)
* old_sw->info.bytes_per_second
/ sw->info.bytes_per_second;
#ifdef DEBUG_PLIVE
dolog ("Silence will be mixed %d\n", mixed);
#endif
sw->total_hw_samples_mixed += mixed;
}
#endif
#ifdef DEBUG_AUDIO
dolog ("%s\n", name);
audio_pcm_print_info ("hw", &sw->hw->info);
audio_pcm_print_info ("sw", &sw->info);
#endif
}
return sw;
fail:
glue (AUD_close_, TYPE) (sw);
return NULL;
}
int glue (AUD_is_active_, TYPE) (SW *sw)
{
return sw ? sw->active : 0;
}
void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
{
if (!sw) {
return;
}
ts->old_ts = sw->hw->ts_helper;
}
uint64_t glue (AUD_time_stamp_get_elapsed_usec_, TYPE) (
SW *sw,
QEMUAudioTimeStamp *ts
)
{
uint64_t delta, cur_ts, old_ts;
if (!sw) {
return 0;
}
cur_ts = sw->hw->ts_helper;
old_ts = ts->old_ts;
/* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
if (cur_ts >= old_ts) {
delta = cur_ts - old_ts;
}
else {
delta = UINT64_MAX - old_ts + cur_ts;
}
if (!delta) {
return 0;
}
return (delta * sw->hw->info.freq) / 1000000;
}
#undef TYPE
#undef HW
#undef SW