diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index cecb27e744..87f9c8d04c 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -6851,6 +6851,69 @@ to the guest sound device.
``id``
Integer id of the audio device. Must be greater than 0.
+All the backends support child element for configuring input and
+output properties
+
+::
+
+ ...
+
+
+
+
+
+
+
+
+
+
+ ...
+
+The input and output elements support the same set of attributes and
+elements
+
+* ``mixingEngine``
+
+ Control whether the host mixing engine is used to convert between
+ different audio formats and sampling rates. When the mixing engine
+ is disabled it is possible to make use of improved audio formats
+ such as 5.1/7.1. If not specified, a hypervisor default applies.
+
+* ``fixedSettings``
+
+ Control whether the mixing engine can dynamically choose settings
+ to mimimize format conversion. This is only valid when the
+ mixing engine is explicitly enabled.
+
+* ``voices``
+
+ The number of voices voices to use, usually defaults to 1
+
+* ``bufferLength``
+
+ The length of the audio buffer in microseconds. Default is
+ backend specific.
+
+The `` `` and ```` elements may also permit backend
+specific options.
+
+When fixed settings are enabled, the ```` child element
+is permitted with the following attributes.
+
+* ``frequency``
+
+ The frequency in HZ, usually defaulting to 44100
+
+* ``channels``
+
+ The number of channels, usually defaulting to 2. The permitted
+ max number of channels is hypervisor specific.
+
+* ``format``
+
+ The audio format, one of ``s8``, ``u8``, ``s16``, ``u16``,
+ ``s32``, ``u32``, ``f32``. The defalt is hypervisor specific.
+
None audio backend
^^^^^^^^^^^^^^^^^^
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 4729590871..8dbfdbb8e2 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -4528,12 +4528,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ s8
+ u8
+ s16
+ u16
+ s32
+ u32
+ f32
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4546,21 +4651,69 @@
none
+
+
+
+
+
+
+
+
+
+
+
+
alsa
+
+
+
+
+
+
+
+
+
+
+
+
coreaudio
+
+
+
+
+
+
+
+
+
+
+
+
jack
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4585,6 +4738,18 @@
pulseaudio
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4600,16 +4765,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
spice
+
+
+
+
+
+
+
+
+
+
+
+
file
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index 6788a0a4c3..c4c64788b9 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -514,6 +514,13 @@ bhyveBuildSoundArgStr(const virDomainDef *def G_GNUC_UNUSED,
if (audio) {
switch ((virDomainAudioType) audio->type) {
case VIR_DOMAIN_AUDIO_TYPE_OSS:
+ if (virDomainAudioIOCommonIsSet(&audio->input) ||
+ virDomainAudioIOCommonIsSet(&audio->output)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("cannot set common audio backend settings"));
+ return -1;
+ }
+
if (audio->backend.oss.input.dev)
virBufferAsprintf(¶ms, ",play=%s",
audio->backend.oss.input.dev);
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 67b905fe98..a07c7eccf3 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -756,6 +756,18 @@ VIR_ENUM_IMPL(virDomainAudioSDLDriver,
"pulseaudio",
);
+VIR_ENUM_IMPL(virDomainAudioFormat,
+ VIR_DOMAIN_AUDIO_FORMAT_LAST,
+ "",
+ "u8",
+ "s8",
+ "u16",
+ "s16",
+ "u32",
+ "s32",
+ "f32",
+);
+
VIR_ENUM_IMPL(virDomainKeyWrapCipherName,
VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST,
"aes",
@@ -13941,6 +13953,99 @@ virDomainSoundDefFind(const virDomainDef *def,
}
+static int
+virDomainAudioCommonParse(virDomainAudioIOCommonPtr def,
+ xmlNodePtr node,
+ xmlXPathContextPtr ctxt)
+{
+ g_autofree char *mixingEngine = virXMLPropString(node, "mixingEngine");
+ g_autofree char *fixedSettings = virXMLPropString(node, "fixedSettings");
+ g_autofree char *voices = virXMLPropString(node, "voices");
+ g_autofree char *bufferLength = virXMLPropString(node, "bufferLength");
+ xmlNodePtr settings;
+ VIR_XPATH_NODE_AUTORESTORE(ctxt);
+
+ ctxt->node = node;
+ settings = virXPathNode("./settings", ctxt);
+
+ if (mixingEngine &&
+ ((def->mixingEngine =
+ virTristateBoolTypeFromString(mixingEngine)) <= 0)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unknown 'mixingEngine' value '%s'"), mixingEngine);
+ return -1;
+ }
+
+ if (fixedSettings &&
+ ((def->fixedSettings =
+ virTristateBoolTypeFromString(fixedSettings)) <= 0)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unknown 'fixedSettings' value '%s'"), fixedSettings);
+ return -1;
+ }
+
+ if (def->fixedSettings == VIR_TRISTATE_BOOL_YES &&
+ def->mixingEngine != VIR_TRISTATE_BOOL_YES) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("fixed audio settings requires mixing engine"));
+ return -1;
+ }
+
+ if (voices &&
+ (virStrToLong_ui(voices, NULL, 10, &def->voices) < 0 ||
+ !def->voices)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("cannot parse 'voices' value '%s'"), voices);
+ return -1;
+ }
+
+ if (bufferLength &&
+ (virStrToLong_ui(bufferLength, NULL, 10, &def->bufferLength) < 0 ||
+ !def->bufferLength)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("cannot parse 'bufferLength' value '%s'"), bufferLength);
+ return -1;
+ }
+
+ if (settings) {
+ g_autofree char *frequency = virXMLPropString(settings, "frequency");
+ g_autofree char *channels = virXMLPropString(settings, "channels");
+ g_autofree char *format = virXMLPropString(settings, "format");
+
+ if (def->fixedSettings != VIR_TRISTATE_BOOL_YES) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("audio settings specified without fixed settings flag"));
+ return -1;
+ }
+
+ if (frequency &&
+ (virStrToLong_ui(frequency, NULL, 10, &def->frequency) < 0 ||
+ !def->frequency)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("cannot parse 'frequency' value '%s'"), frequency);
+ return -1;
+ }
+
+ if (channels &&
+ (virStrToLong_ui(channels, NULL, 10, &def->channels) < 0 ||
+ !def->channels)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("cannot parse 'channels' value '%s'"), channels);
+ return -1;
+ }
+
+ if (format &&
+ (def->format = virDomainAudioFormatTypeFromString(format)) <= 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("cannot parse 'format' value '%s'"), format);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
static int
virDomainAudioOSSParse(virDomainAudioIOOSSPtr def,
xmlNodePtr node)
@@ -13953,8 +14058,8 @@ virDomainAudioOSSParse(virDomainAudioIOOSSPtr def,
static virDomainAudioDefPtr
virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED,
- xmlNodePtr node G_GNUC_UNUSED,
- xmlXPathContextPtr ctxt G_GNUC_UNUSED)
+ xmlNodePtr node,
+ xmlXPathContextPtr ctxt)
{
virDomainAudioDefPtr def;
VIR_XPATH_NODE_AUTORESTORE(ctxt)
@@ -13994,6 +14099,11 @@ virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED,
inputNode = virXPathNode("./input", ctxt);
outputNode = virXPathNode("./output", ctxt);
+ if (inputNode && virDomainAudioCommonParse(&def->input, inputNode, ctxt) < 0)
+ goto error;
+ if (outputNode && virDomainAudioCommonParse(&def->output, outputNode, ctxt) < 0)
+ goto error;
+
switch ((virDomainAudioType) def->type) {
case VIR_DOMAIN_AUDIO_TYPE_NONE:
break;
@@ -14030,6 +14140,8 @@ virDomainAudioDefParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED,
}
case VIR_DOMAIN_AUDIO_TYPE_SPICE:
+ break;
+
case VIR_DOMAIN_AUDIO_TYPE_FILE:
break;
@@ -26470,18 +26582,59 @@ virDomainSoundDefFormat(virBufferPtr buf,
static void
-virDomainAudioCommonFormat(virBufferPtr childBuf,
+virDomainAudioCommonFormat(virDomainAudioIOCommonPtr def,
+ virBufferPtr childBuf,
virBufferPtr backendAttrBuf,
const char *direction)
{
- if (virBufferUse(backendAttrBuf)) {
+ g_auto(virBuffer) settingsBuf = VIR_BUFFER_INITIALIZER;
+
+ if (def->fixedSettings == VIR_TRISTATE_BOOL_YES) {
+ if (def->frequency)
+ virBufferAsprintf(&settingsBuf, " frequency='%u'",
+ def->frequency);
+ if (def->channels)
+ virBufferAsprintf(&settingsBuf, " channels='%u'",
+ def->channels);
+ if (def->format)
+ virBufferAsprintf(&settingsBuf, " format='%s'",
+ virDomainAudioFormatTypeToString(def->format));
+ }
+
+ if (def->mixingEngine || def->fixedSettings ||
+ def->voices || def->bufferLength ||
+ virBufferUse(backendAttrBuf)) {
virBufferAsprintf(childBuf, "<%s", direction);
- virBufferAdd(childBuf, virBufferCurrentContent(backendAttrBuf), -1);
- virBufferAddLit(childBuf, "/>\n");
+ if (def->mixingEngine)
+ virBufferAsprintf(childBuf, " mixingEngine='%s'",
+ virTristateBoolTypeToString(def->mixingEngine));
+ if (def->fixedSettings)
+ virBufferAsprintf(childBuf, " fixedSettings='%s'",
+ virTristateBoolTypeToString(def->fixedSettings));
+ if (def->voices)
+ virBufferAsprintf(childBuf, " voices='%u'",
+ def->voices);
+ if (def->bufferLength)
+ virBufferAsprintf(childBuf, " bufferLength='%u'",
+ def->bufferLength);
+ if (virBufferUse(backendAttrBuf))
+ virBufferAdd(childBuf, virBufferCurrentContent(backendAttrBuf), -1);
+ if (def->fixedSettings == VIR_TRISTATE_BOOL_YES) {
+ virBufferAddLit(childBuf, ">\n");
+ virBufferAdjustIndent(childBuf, 2);
+ virBufferAddLit(childBuf, " \n");
+ virBufferAdjustIndent(childBuf, -2);
+ virBufferAsprintf(childBuf, "%s>\n", direction);
+ } else {
+ virBufferAddLit(childBuf, "/>\n");
+ }
}
}
-
static void
virDomainAudioOSSFormat(virDomainAudioIOOSSPtr def,
virBufferPtr buf)
@@ -26547,8 +26700,8 @@ virDomainAudioDefFormat(virBufferPtr buf,
return -1;
}
- virDomainAudioCommonFormat(&childBuf, &inputBuf, "input");
- virDomainAudioCommonFormat(&childBuf, &outputBuf, "output");
+ virDomainAudioCommonFormat(&def->input, &childBuf, &inputBuf, "input");
+ virDomainAudioCommonFormat(&def->output, &childBuf, &outputBuf, "output");
if (virBufferUse(&childBuf)) {
virBufferAddLit(buf, ">\n");
@@ -30548,6 +30701,17 @@ virDomainSoundModelSupportsCodecs(virDomainSoundDefPtr def)
def->model == VIR_DOMAIN_SOUND_MODEL_ICH9;
}
+bool
+virDomainAudioIOCommonIsSet(virDomainAudioIOCommonPtr common)
+{
+ return common->mixingEngine ||
+ common->fixedSettings ||
+ common->frequency ||
+ common->channels ||
+ common->voices ||
+ common->format ||
+ common->bufferLength;
+}
char *
virDomainObjGetMetadata(virDomainObjPtr vm,
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 62c78ba988..93bab14cb1 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1469,6 +1469,32 @@ typedef enum {
VIR_DOMAIN_AUDIO_SDL_DRIVER_LAST
} virDomainAudioSDLDriver;
+typedef enum {
+ VIR_DOMAIN_AUDIO_FORMAT_DEFAULT,
+ VIR_DOMAIN_AUDIO_FORMAT_U8,
+ VIR_DOMAIN_AUDIO_FORMAT_S8,
+ VIR_DOMAIN_AUDIO_FORMAT_U16,
+ VIR_DOMAIN_AUDIO_FORMAT_S16,
+ VIR_DOMAIN_AUDIO_FORMAT_U32,
+ VIR_DOMAIN_AUDIO_FORMAT_S32,
+ VIR_DOMAIN_AUDIO_FORMAT_F32,
+
+ VIR_DOMAIN_AUDIO_FORMAT_LAST
+} virDomainAudioFormat;
+
+typedef struct _virDomainAudioIOCommon virDomainAudioIOCommon;
+typedef virDomainAudioIOCommon *virDomainAudioIOCommonPtr;
+struct _virDomainAudioIOCommon {
+ virTristateBool mixingEngine;
+ virTristateBool fixedSettings;
+ unsigned int frequency;
+ unsigned int channels;
+ unsigned int voices;
+ int format; /* virDomainAudioFormat */
+ unsigned int bufferLength; /* milliseconds */
+};
+
+
typedef struct _virDomainAudioIOOSS virDomainAudioIOOSS;
typedef virDomainAudioIOOSS *virDomainAudioIOOSSPtr;
struct _virDomainAudioIOOSS {
@@ -1480,6 +1506,8 @@ struct _virDomainAudioDef {
unsigned int id;
+ virDomainAudioIOCommon input;
+ virDomainAudioIOCommon output;
union {
struct {
virDomainAudioIOOSS input;
@@ -3717,6 +3745,7 @@ VIR_ENUM_DECL(virDomainChrTcpProtocol);
VIR_ENUM_DECL(virDomainChrSpicevmc);
VIR_ENUM_DECL(virDomainSoundCodec);
VIR_ENUM_DECL(virDomainSoundModel);
+VIR_ENUM_DECL(virDomainAudioFormat);
VIR_ENUM_DECL(virDomainAudioType);
VIR_ENUM_DECL(virDomainAudioSDLDriver);
VIR_ENUM_DECL(virDomainKeyWrapCipherName);
@@ -3817,6 +3846,8 @@ virDomainDefFindAudioByID(const virDomainDef *def,
int id);
bool
virDomainSoundModelSupportsCodecs(virDomainSoundDefPtr def);
+bool
+virDomainAudioIOCommonIsSet(virDomainAudioIOCommonPtr common);
const char *virDomainChrSourceDefGetPath(virDomainChrSourceDefPtr chr);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1d837dd34a..c4052aa41b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -226,6 +226,9 @@ virDiskNameParse;
virDiskNameToBusDeviceIndex;
virDiskNameToIndex;
virDomainActualNetDefFree;
+virDomainAudioFormatTypeFromString;
+virDomainAudioFormatTypeToString;
+virDomainAudioIOCommonIsSet;
virDomainAudioSDLDriverTypeFromString;
virDomainAudioSDLDriverTypeToString;
virDomainAudioTypeTypeFromString;