feat:add virtio-sound interface

Signed-off-by: Aneesh-Hegde <aneeshhegde7110@gmail.com>
This commit is contained in:
Aneesh-Hegde committed 2026-07-04 03:17:24 +05:30
1 parent 67b84c58e3
commit a9b8e59abe
12 files changed
+93 -22

No files matched your search

+5
View File
@@ -116,6 +116,9 @@ func RegisterCreate(cmd *cobra.Command, commentPrefix string) {
_ = cmd.RegisterFlagCompletionFunc("port-forward", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return []string{"8080:80", "3000:3000", "8080:80,static=true"}, cobra.ShellCompDirectiveNoFileComp
})
flags.String("audio-device", "", commentPrefix+"Audio device backend (e.g., 'pa', 'coreaudio', 'none')")
flags.String("audio-interface", "", commentPrefix+"Audio virtual hardware interface ('hda' or 'virtio')")
}
func defaultExprFunc(expr string) func(v *flag.Flag) ([]string, error) {
@@ -373,6 +376,8 @@ func YQExpressions(flags *flag.FlagSet, newInstance bool, params map[string]stri
false,
false,
},
{"audio-device", d(".audio.device = %q"), false, true},
{"audio-interface", d(".audio.interface = %q"), false, true},
{"ssh-port", d(".ssh.localPort = %s"), false, false},
{"arch", d(".arch = %q"), true, false},
{
+1 -1
View File
@@ -32,7 +32,7 @@ func NewDriverClient(socketPath string, logger *logrus.Logger) (*DriverClient, e
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
//nolint:staticcheck // grpc.Dial is used for compatibility reasons
//nolint:staticcheck,nolintlint // grpc.Dial is used for compatibility reasons
conn, err := grpc.Dial("unix://"+socketPath, opts...)
if err != nil {
logger.Errorf("failed to dial gRPC driver client connection: %v", err)
+33 -12
View File
@@ -375,6 +375,7 @@ func adjustMemBytesDarwinARM64HVF(memBytes int64, accel string) int64 {
return memBytes
}
macOSProductVersion, err := osutil.ProductVersion()
//nolint:staticcheck,nolintlint // SA4023: always true on Linux, but valid on macOS
if err != nil {
logrus.Warn(err)
return memBytes
@@ -818,19 +819,39 @@ func Cmdline(ctx context.Context, cfg Config) (exe string, args []string, err er
input := "mouse"
// Sound
if *y.Audio.Device != "" {
id := "default"
// audio device
audiodev := *y.Audio.Device
if audiodev == "default" {
audiodev = audioDevice()
if y.Audio.Device != nil && y.Audio.Interface != nil {
audioDev := *y.Audio.Device
audioInterface := *y.Audio.Interface
if audioDev == "" || audioDev == "none" {
args = append(args, "-audiodev", "none,id=snd0")
} else {
if audioDev == "default" {
audioDev = audioDevice()
}
args = append(args, "-audiodev", fmt.Sprintf("%s,id=snd0", audioDev))
if audioInterface == "" {
audioInterface = "hda"
}
switch audioInterface {
case "virtio":
minVirtioVersion := semver.New("8.2.0")
if version.LessThan(*minVirtioVersion) {
logrus.Warnf("virtio-sound requires QEMU 8.2.0 or newer. Falling back to intel-hda.")
// We append the HDA hardware directly here instead of mutating the variable
args = append(args, "-device", "ich9-intel-hda", "-device", "hda-output,audiodev=snd0")
} else {
args = append(args, "-device", "virtio-sound-pci,audiodev=snd0")
}
case "hda":
args = append(args,
"-device", "ich9-intel-hda",
"-device", "hda-output,audiodev=snd0",
)
default:
return "", nil, fmt.Errorf("unknown audio.interface %q", audioInterface)
}
}
audiodev += fmt.Sprintf(",id=%s", id)
args = append(args, "-audiodev", audiodev)
// audio controller
args = append(args, "-device", "ich9-intel-hda")
// audio codec
args = append(args, "-device", fmt.Sprintf("hda-output,audiodev=%s", id))
}
// Graphics
if *y.Video.Display != "" {
+18 -3
View File
@@ -765,15 +765,30 @@ func attachFolderMounts(inst *limatype.Instance, vmConfig *vz.VirtualMachineConf
}
func attachAudio(inst *limatype.Instance, config *vz.VirtualMachineConfiguration) error {
switch *inst.Config.Audio.Device {
y := inst.Config
if y.Audio.Device == nil || y.Audio.Interface == nil {
return nil
}
audioDev := *y.Audio.Device
audioInterface := *y.Audio.Interface
// If the user explicitly asks for HDA, we gracefully ignore it.
if audioInterface == "hda" {
logrus.Warn("VZ driver natively uses virtio-sound. Ignoring 'hda' interface request.")
}
switch audioDev {
case "vz", "default":
outputStream, err := vz.NewVirtioSoundDeviceHostOutputStreamConfiguration()
if err != nil {
return err
return fmt.Errorf("failed to create host audio output stream: %w", err)
}
soundDeviceConfiguration, err := vz.NewVirtioSoundDeviceConfiguration()
if err != nil {
return err
return fmt.Errorf("failed to create virtio sound device configuration: %w", err)
}
soundDeviceConfiguration.SetStreams(outputStream)
config.SetAudioDevicesVirtualMachineConfiguration([]vz.AudioDeviceConfiguration{
+3 -1
View File
@@ -233,7 +233,9 @@ func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) {
// Probably never supportable for WSL2
logrus.Warn(".ssh.overVsock is not supported for WSL2 driver")
}
if l.Instance.Config.Audio.Interface != nil {
logrus.Warn("`audio.interface` is ignored when using the WSL2 driver")
}
logrus.Infof("Starting WSL VM")
status, err := getWslStatus(ctx, l.Instance.Name)
if err != nil {
+1 -1
View File
@@ -44,7 +44,7 @@ func (c *secureTC) OverrideServerName(serverNameOverride string) error {
// SA1019: c.info.ServerName is deprecated:
// Users should use grpc.WithAuthority to override the authority on a channel instead of configuring the credentials.
//nolint:staticcheck // c.info.ServerName is used for compatibility reason
//nolint:staticcheck,nolintlint // c.info.ServerName is used for compatibility reason
c.info.ServerName = serverNameOverride
return nil
}
+2
View File
@@ -220,6 +220,8 @@ type Firmware struct {
type Audio struct {
// Device is a QEMU audiodev string
Device *string `yaml:"device,omitempty" json:"device,omitempty" jsonschema:"nullable"`
// Interface is the virtual hardware presentation
Interface *string `yaml:"interface,omitempty" json:"interface,omitempty" jsonschema:"nullable"`
}
type VNCOptions struct {
+10
View File
@@ -296,6 +296,16 @@ func FillDefault(ctx context.Context, y, d, o *limatype.LimaYAML, filePath strin
y.Audio.Device = ptr.Of("")
}
if y.Audio.Interface == nil {
y.Audio.Interface = d.Audio.Interface
}
if o.Audio.Interface != nil {
y.Audio.Interface = o.Audio.Interface
}
if y.Audio.Interface == nil {
y.Audio.Interface = ptr.Of("")
}
if y.Video.Display == nil {
y.Video.Display = d.Video.Display
}
+6 -3
View File
@@ -99,7 +99,8 @@ func TestFillDefault(t *testing.T) {
LegacyBIOS: ptr.Of(false),
},
Audio: limatype.Audio{
Device: ptr.Of(""),
Device: ptr.Of(""),
Interface: ptr.Of(""),
},
Video: limatype.Video{
Display: ptr.Of("none"),
@@ -344,7 +345,8 @@ func TestFillDefault(t *testing.T) {
// Remove driver-specific firmware images from defaults
},
Audio: limatype.Audio{
Device: ptr.Of("coreaudio"),
Device: ptr.Of("coreaudio"),
Interface: ptr.Of("virtio"),
},
Video: limatype.Video{
Display: ptr.Of("cocoa"),
@@ -556,7 +558,8 @@ func TestFillDefault(t *testing.T) {
LegacyBIOS: ptr.Of(true),
},
Audio: limatype.Audio{
Device: ptr.Of("coreaudio"),
Device: ptr.Of("coreaudio"),
Interface: ptr.Of("hda"),
},
Video: limatype.Video{
Display: ptr.Of("cocoa"),
+9
View File
@@ -432,6 +432,12 @@ func Validate(y *limatype.LimaYAML, warn bool) error {
}
}
if y.Audio.Interface != nil {
if *y.Audio.Interface != "" && *y.Audio.Interface != "hda" && *y.Audio.Interface != "virtio" {
return fmt.Errorf("invalid audio.interface %q, must be \"hda\" or \"virtio\"", *y.Audio.Interface)
}
}
return errs
}
@@ -623,6 +629,9 @@ func warnExperimental(y *limatype.LimaYAML) {
}
if y.Audio.Device != nil && *y.Audio.Device != "" {
logrus.Warn("`audio.device` is experimental")
if y.Audio.Interface != nil && *y.Audio.Interface != "" {
logrus.Warn("`audio.interface` is experimental")
}
}
if y.MountInotify != nil && *y.MountInotify {
logrus.Warn("`mountInotify` is experimental")
+4
View File
@@ -448,6 +448,10 @@ audio:
# that is still visible in the guest but not connected to the host.
# 🟢 Builtin default: ""
device: null
# The virtual hardware presentation for QEMU driver. Options are "hda" (legacy) or "virtio" (paravirtualized, requires QEMU >= 8.2).
# VZ driver always uses virtio-sound regardless of this setting.
# 🟢 Builtin default: "" (resolves to "hda" when audio.device is set)
interface: null
video:
# QEMU display, e.g., "none", "cocoa", "sdl", "gtk", "vnc", "default".
@@ -10,7 +10,7 @@ The following features are experimental and subject to change:
- `vmType: wsl2` and relevant configurations (`mountType: wsl2`)
- `arch`: `riscv64`, `armv7l`, `s390x`, and `ppc64le`
- `video.display: vnc` and relevant configuration (`video.vnc.display`)
- `audio.device`
- `audio.device` and `audio.interface`
- `mountInotify: true`
- `tpm: true`
- `External drivers`: building and using drivers as separate executables (see [Virtual Machine Drivers](../dev/drivers))