Index: src/sys/miscfs/specfs/spec_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/specfs/spec_vnops.c,v
retrieving revision 1.162
diff -u -r1.162 spec_vnops.c
--- src/sys/miscfs/specfs/spec_vnops.c	4 Apr 2016 08:03:53 -0000	1.162
+++ src/sys/miscfs/specfs/spec_vnops.c	15 Apr 2016 13:01:13 -0000
@@ -1228,7 +1228,7 @@
 		sd->sd_bdevvp = NULL;
 	mutex_exit(&device_lock);
 
-	if (count != 0)
+	if (count != 0 && (vp->v_type != VCHR || cdev_type(dev) != D_MCLOSE))
 		return 0;
 
 	/*
Index: src/sys/sys/conf.h
===================================================================
RCS file: /cvsroot/src/sys/sys/conf.h,v
retrieving revision 1.146
diff -u -r1.146 conf.h
--- src/sys/sys/conf.h	17 Jan 2016 23:16:46 -0000	1.146
+++ src/sys/sys/conf.h	15 Apr 2016 13:01:45 -0000
@@ -60,6 +60,7 @@
 #define	D_TAPE		0x0001
 #define	D_DISK		0x0002
 #define	D_TTY		0x0003
+#define	D_MCLOSE	0x0004
 #define	D_TYPEMASK	0x00ff
 #define	D_MPSAFE	0x0100
 #define	D_NEGOFFSAFE	0x0200
Index: src/sys/dev/audiobell.c
===================================================================
RCS file: /cvsroot/src/sys/dev/audiobell.c,v
retrieving revision 1.8
diff -u -r1.8 audiobell.c
--- src/sys/dev/audiobell.c	12 May 2009 10:22:31 -0000	1.8
+++ src/sys/dev/audiobell.c	25 May 2016 12:14:24 -0000
@@ -1,5 +1,6 @@
 /*	$NetBSD: audiobell.c,v 1.8 2009/05/12 10:22:31 cegger Exp $	*/
 
+
 /*
  * Copyright (c) 1999 Richard Earnshaw
  * Copyright (c) 2004 Ben Harris
@@ -37,6 +38,7 @@
 #include <sys/conf.h>
 #include <sys/device.h>
 #include <sys/fcntl.h>
+#include <sys/ioctl.h>
 #include <sys/malloc.h>
 #include <sys/null.h>
 #include <sys/systm.h>
@@ -46,6 +48,7 @@
 #include <dev/audiobellvar.h>
 
 extern dev_type_open(audioopen);
+extern dev_type_ioctl(audioioctl);
 extern dev_type_write(audiowrite);
 extern dev_type_close(audioclose);
 
@@ -138,8 +141,10 @@
 {
 	device_t audio = arg;
 	uint8_t *buf;
+	struct audio_info ai;
 	struct uio auio;
 	struct iovec aiov;
+	int size, len, offset;
 
 	/* The audio system isn't built for polling. */
 	if (poll) return;
@@ -148,21 +153,32 @@
 	if (audioopen(AUDIO_DEVICE | device_unit(audio), FWRITE, 0, NULL) != 0)
 		return;
 
-	buf = malloc(period * 8, M_TEMP, M_WAITOK);
-	if (buf == NULL) goto out;
-	if (audiobell_synthesize(buf, pitch, period, volume) != 0) goto out;
-
-	aiov.iov_base = (void *)buf;
-	aiov.iov_len = period * 8;
-	auio.uio_iov = &aiov;
-	auio.uio_iovcnt = 1;
-	auio.uio_offset = 0;
-	auio.uio_resid = period * 8;
-	auio.uio_rw = UIO_WRITE;
-	UIO_SETUP_SYSSPACE(&auio);
+	if (audioioctl((dev_t)(AUDIO_DEVICE | device_unit(audio)),
+	    AUDIO_GETINFO, &ai, 0, NULL) != 0)
+		return;
 
-	audiowrite(AUDIO_DEVICE | device_unit(audio), &auio, 0);
+	buf = malloc(ai.blocksize, M_TEMP, M_WAITOK);
+	if (buf == NULL) goto out;
 
+	len = period * 8;
+	offset = 0;
+	while (len > 0) {
+		size = min(len, ai.blocksize);
+		if (audiobell_synthesize(buf, pitch, size / 8, volume) != 0)
+			goto out;
+		aiov.iov_base = (void *)buf;
+		aiov.iov_len = size;
+		auio.uio_iov = &aiov;
+		auio.uio_iovcnt = 1;
+		auio.uio_offset = 0;
+		auio.uio_resid = size;
+		auio.uio_rw = UIO_WRITE;
+		UIO_SETUP_SYSSPACE(&auio);
+
+		audiowrite(AUDIO_DEVICE | device_unit(audio), &auio, 0);
+		len -= size;
+		offset += size;
+	}
 out:
 	if (buf != NULL) free(buf, M_TEMP);
 	audioclose(AUDIO_DEVICE | device_unit(audio), FWRITE, 0, NULL);
Index: src/sys/dev/files.audio
===================================================================
RCS file: /cvsroot/src/sys/dev/files.audio,v
retrieving revision 1.3
diff -u -r1.3 files.audio
--- src/sys/dev/files.audio	18 Nov 2014 01:53:17 -0000	1.3
+++ src/sys/dev/files.audio	25 May 2016 12:14:53 -0000
@@ -13,10 +13,12 @@
 
 # audio and midi devices, attaches to audio hardware driver
 #
-device	audio: audiodev
+device	audio {}: audiodev
 attach	audio at audiobus
 device	midi: audio
 attach	midi at midibus
+device	spkr: audiobell
+attach  spkr at audio
 
 # console bell via audio device
 #
@@ -31,3 +33,4 @@
 file	dev/midictl.c			midisyn
 file	dev/midisyn.c			midisyn
 file	dev/mulaw.c			mulaw			needs-flag
+file	dev/isa/spkr.c			spkr
Index: src/sys/conf/majors
===================================================================
RCS file: /cvsroot/src/sys/conf/majors,v
retrieving revision 1.73
diff -u -r1.73 majors
--- src/sys/conf/majors	13 May 2016 07:41:47 -0000	1.73
+++ src/sys/conf/majors	25 May 2016 12:15:21 -0000
@@ -9,6 +9,7 @@
 #
 # Majors 160-511 are used for the MI drivers.
 
+device-major spkr      char 156		   spkr
 device-major crypto    char 160		   crypto	single
 device-major pf        char 161		   pf		single
 #obsolete    vinum     char 162		   vinum
Index: src/sys/arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.433
diff -u -r1.433 GENERIC
--- src/sys/arch/amd64/conf/GENERIC	14 May 2016 17:11:30 -0000	1.433
+++ src/sys/arch/amd64/conf/GENERIC	25 May 2016 12:16:40 -0000
@@ -1099,6 +1103,12 @@
 
 # Audio support
 audio*	at audiobus?
+options VAUDIO
+#vaudio* at audio?
+
+# The spkr driver provides a simple tone interface to the built in speaker.
+options VAUDIOSPEAKER
+spkr0	at audio0		# PC speaker
 
 # MPU 401 UARTs
 #mpu*	at isa? port 0x330 irq 9	# MPU401 or compatible card
@@ -1110,10 +1120,6 @@
 midi*	at midibus?
 midi*	at pcppi?		# MIDI interface to the PC speaker
 
-# The spkr driver provides a simple tone interface to the built in speaker.
-#spkr0	at pcppi?		# PC speaker
-
-
 # FM-Radio devices
 # PCI radio devices
 #gtp*	at pci? dev ? function ? # Guillemot Maxi Radio FM 2000 Radio Card
Index: src/sys/dev/isa/spkr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/isa/spkr.c,v
retrieving revision 1.36
diff -u -r1.36 spkr.c
--- src/sys/dev/isa/spkr.c	17 May 2015 05:20:37 -0000	1.36
+++ src/sys/dev/isa/spkr.c	21 Apr 2016 12:20:39 -0000
@@ -56,34 +56,51 @@
 #include <sys/proc.h>
 #include <sys/ioctl.h>
 #include <sys/conf.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/kthread.h>
+
+struct vbell_args {
+	device_t *cookie;
+	u_int pitch;
+	u_int period;
+	u_int volume;
+	bool dying;
+};
 
-#include <sys/bus.h>
+void bell_thread(void *);
 
-#include <dev/isa/pcppivar.h>
+#include <dev/audiobellvar.h>
 
+#include <dev/spkrvar.h>
 #include <dev/isa/spkrio.h>
 
 int spkrprobe(device_t, cfdata_t, void *);
 void spkrattach(device_t, device_t, void *);
 int spkrdetach(device_t, int);
+device_t speakerattach_mi(device_t);
 
 #include "ioconf.h"
 
-MODULE(MODULE_CLASS_DRIVER, spkr, NULL /* "pcppi" */);
+MODULE(MODULE_CLASS_DRIVER, spkr, NULL /* "audio" */);
 
 #ifdef _MODULE
 #include "ioconf.c"
 #endif
 
 
-CFATTACH_DECL_NEW(spkr, 0,
-    spkrprobe, spkrattach, spkrdetach, NULL);
+CFATTACH_DECL3_NEW(spkr, 0,
+    spkrprobe, spkrattach, spkrdetach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN);
 
 dev_type_open(spkropen);
 dev_type_close(spkrclose);
 dev_type_write(spkrwrite);
 dev_type_ioctl(spkrioctl);
 
+struct spkr_attach_args {
+	device_t dev;
+};
+
 const struct cdevsw spkr_cdevsw = {
 	.d_open = spkropen,
 	.d_close = spkrclose,
@@ -99,7 +116,12 @@
 	.d_flag = D_OTHER
 };
 
-static pcppi_tag_t ppicookie;
+device_t vacookie;
+device_t *vaudiocookie;
+struct vbell_args sc_bell_args;
+lwp_t		*sc_bellthread;
+kmutex_t	sc_bellock;
+kcondvar_t	sc_bellcv;
 
 #define SPKRPRI (PZERO - 1)
 
@@ -113,7 +135,7 @@
 tone(u_int xhz, u_int ticks)
 /* emit tone of frequency hz for given number of ticks */
 {
-	pcppi_bell(ppicookie, xhz, ticks, PCPPI_BELL_SLEEP);
+	audiobell(vaudiocookie, xhz, ticks * (1000 / hz), 80, 0);
 }
 
 static void
@@ -129,7 +151,7 @@
     printf("rest: %d\n", ticks);
 #endif /* SPKRDEBUG */
     if (ticks > 0)
-	    tsleep(rest, SPKRPRI | PCATCH, "rest", ticks);
+	audiobell(vaudiocookie, 0, ticks * (1000 / hz), 80, 0);
 }
 
 /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
@@ -424,14 +446,30 @@
 	return (!spkr_attached);
 }
 
+device_t
+speakerattach_mi(device_t dev)
+{
+	struct spkr_attach_args sa;
+	sa.dev = dev;
+	return config_found(dev, &sa, NULL);
+}
+
 void
 spkrattach(device_t parent, device_t self, void *aux)
 {
+	struct spkr_attach_args *sa;
+	sa = aux;
+
 	printf("\n");
-	ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
+	vacookie = sa->dev;
+	vaudiocookie = &vacookie;
 	spkr_attached = 1;
 	if (!pmf_device_register(self, NULL, NULL))
 		aprint_error_dev(self, "couldn't establish power handler\n"); 
+	mutex_init(&sc_bellock, MUTEX_DEFAULT, IPL_SCHED);
+	cv_init(&sc_bellcv, "bellcv");
+	kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, bell_thread, &sc_bell_args,
+	    &sc_bellthread, "vbell");
 }
 
 int
@@ -439,8 +477,19 @@
 {
 
 	pmf_device_deregister(self);
+
+	mutex_enter(&sc_bellock);
+	sc_bell_args.dying = true;
+
+	cv_broadcast(&sc_bellcv);
+	mutex_exit(&sc_bellock);
+
+	kthread_join(sc_bellthread);
+	cv_destroy(&sc_bellcv);
+	mutex_destroy(&sc_bellock);
+
 	spkr_attached = 0;
-	ppicookie = NULL;
+	vaudiocookie = NULL;
 
 	return 0;
 }
@@ -546,6 +595,44 @@
     return(0);
 }
 
+void
+bell_thread(void *arg)
+{
+	struct vbell_args *vb = arg;
+	u_int bpitch;
+	u_int bperiod;
+	u_int bvolume;
+	
+	for (;;) {
+		mutex_enter(&sc_bellock);
+		cv_wait_sig(&sc_bellcv, &sc_bellock);
+		
+		if (vb->dying == true) {
+			mutex_exit(&sc_bellock);
+			kthread_exit(0);
+		}
+		
+		bpitch = vb->pitch;
+		bperiod = vb->period;
+		bvolume = vb->volume;
+		mutex_exit(&sc_bellock);
+		audiobell(vaudiocookie, bpitch, bperiod, bvolume, 0);
+	}
+}
+
+void
+speaker_play(u_int pitch, u_int period, u_int volume)
+{
+	mutex_enter(&sc_bellock);
+	sc_bell_args.dying = false;
+	sc_bell_args.pitch = pitch;
+	sc_bell_args.period = period;
+	sc_bell_args.volume = volume;
+
+	cv_broadcast(&sc_bellcv);
+	mutex_exit(&sc_bellock);
+}
+
 static int
 spkr_modcmd(modcmd_t cmd, void *arg)
 {
Index: src/sys/dev/wscons/wskbd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/wscons/wskbd.c,v
retrieving revision 1.136
diff -u -r1.136 wskbd.c
--- src/sys/dev/wscons/wskbd.c	24 Aug 2015 22:50:33 -0000	1.136
+++ src/sys/dev/wscons/wskbd.c	21 Apr 2016 12:21:30 -0000
@@ -156,6 +156,10 @@
 
 #include <dev/wscons/wsmuxvar.h>
 
+#ifdef VAUDIO
+#include <dev/spkrvar.h>
+#endif
+
 struct wskbd_internal {
 	const struct wskbd_mapdata *t_keymap;
 
@@ -1089,16 +1093,27 @@
 	case WSKBDIO_BELL:
 		if ((flag & FWRITE) == 0)
 			return (EACCES);
+#ifndef VAUDIO
 		return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
 		    WSKBDIO_COMPLEXBELL, (void *)&sc->sc_bell_data, flag, l));
+#else
+		wskbd_cnbell(0, sc->sc_bell_data.pitch, sc->sc_bell_data.period,
+		    sc->sc_bell_data.volume);
+		return 0;
+#endif
 
 	case WSKBDIO_COMPLEXBELL:
 		if ((flag & FWRITE) == 0)
 			return (EACCES);
 		ubdp = (struct wskbd_bell_data *)data;
 		SETBELL(ubdp, ubdp, &sc->sc_bell_data);
+#ifndef VAUDIO
 		return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
 		    WSKBDIO_COMPLEXBELL, (void *)ubdp, flag, l));
+#else
+		wskbd_cnbell(0, ubdp->pitch, ubdp->period, ubdp->volume);
+		return 0;
+#endif
 
 	case WSKBDIO_SETBELL:
 		if ((flag & FWRITE) == 0)
@@ -1466,10 +1481,14 @@
 	if (!wskbd_console_initted)
 		return;
 
+#ifndef VAUDIO
 	if (wskbd_console_data.t_consops->bell != NULL)
 		(*wskbd_console_data.t_consops->bell)
 		    (wskbd_console_data.t_consaccesscookie, pitch, period,
 			volume);
+#else
+	speaker_play(pitch, period, volume);
+#endif
 }
 
Index: src/sys/arch/amd64/conf/majors.amd64
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/majors.amd64,v
retrieving revision 1.25
diff -u -r1.25 majors.amd64
--- src/sys/arch/amd64/conf/majors.amd64	23 Apr 2015 23:22:51 -0000	1.25
+++ src/sys/arch/amd64/conf/majors.amd64	21 Apr 2016 12:24:11 -0000
@@ -29,8 +29,6 @@
 device-major	bpf		char 23			bpfilter
 device-major	md		char 24  block 17	md
 
-device-major	spkr		char 27			spkr
-
 device-major	cy		char 38			cy
 device-major	mcd		char 39  block 7	mcd
 device-major	tun		char 40			tun
Index: src/sys/dev/isa/files.isa
===================================================================
RCS file: /cvsroot/src/sys/dev/isa/files.isa,v
retrieving revision 1.163
diff -u -r1.163 files.isa
--- src/sys/dev/isa/files.isa	10 Jun 2013 07:14:02 -0000	1.163
+++ src/sys/dev/isa/files.isa	25 May 2016 12:26:30 -0000
@@ -432,9 +432,6 @@
 device	pcppi {}
 attach	pcppi at isa
 file	dev/isa/pcppi.c			pcppi			needs-flag
-device	spkr
-attach	spkr at pcppi
-file	dev/isa/spkr.c			spkr			needs-flag
 attach	midi at pcppi with midi_pcppi: midisyn
 file	dev/isa/midi_pcppi.c		midi_pcppi
 
Index: audiovar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/audiovar.h,v
retrieving revision 1.46
diff -u -r1.46 audiovar.h
--- src/sys/dev/audiovar.h	23 Nov 2011 23:07:31 -0000	1.46
+++ src/sys/dev/audiovar.h	3 Jun 2016 22:46:23 -0000
@@ -67,6 +67,7 @@
 #define _SYS_DEV_AUDIOVAR_H_
 
 #include <sys/condvar.h>
+#include <sys/proc.h>
 
 #include <dev/audio_if.h>
 
@@ -101,6 +102,35 @@
 	bool mmapped;		/* device is mmap()-ed */
 };
 
+#ifndef VAUDIOCHANS
+#define VAUDIOCHANS 4096
+#endif
+
+struct virtual_channel {
+	u_char			sc_open;	/* multiple use device */
+	u_char			sc_mode;	/* bitmask for RECORD/PLAY */
+
+	bool			sc_blkset;	/* Blocksize has been set */
+
+	uint8_t			*sc_sil_start;	/* start of silence in buffer */
+	int			sc_sil_count;	/* # of silence bytes */
+	bool			sc_pbus;	/* output DMA in progress */
+	audio_params_t		sc_pparams;	/* play encoding parameters */
+	audio_stream_t		*sc_pustream;	/* the first buffer */
+	int			sc_npfilters;	/* number of filters */
+	audio_stream_t		sc_pstreams[AUDIO_MAX_FILTERS];
+	stream_filter_t		*sc_pfilters[AUDIO_MAX_FILTERS];
+	struct audio_ringbuffer	sc_mpr;	/* Play ring to mix*/
+	u_long			sc_wstamp;	/* # of bytes read with read(2) */
+	u_long			sc_playdrop;
+
+	int			sc_full_duplex;	/* device in full duplex mode */
+
+	struct audio_info 	sc_lastinfo;
+	bool			sc_lastinfovalid;
+	uint8_t			sc_swvol;
+};
+
 #define AUDIO_N_PORTS 4
 
 struct au_mixer_ports {
@@ -126,16 +156,17 @@
 	void		*hw_hdl;	/* Hardware driver handle */
 	const struct audio_hw_if *hw_if; /* Hardware interface */
 	device_t	sc_dev;		/* Hardware device struct */
-	u_char		sc_open;	/* single use device */
+	pid_t		sc_audiopid[VAUDIOCHANS]; /* audio caller */
 #define AUOPEN_READ	0x01
 #define AUOPEN_WRITE	0x02
-	u_char		sc_mode;	/* bitmask for RECORD/PLAY */
 
+	struct audio_encoding_set *sc_encodings;
 	struct	selinfo sc_wsel; /* write selector */
 	struct	selinfo sc_rsel; /* read selector */
 	pid_t		sc_async_audio;	/* process who wants audio SIGIO */
 	void		*sc_sih_rd;
 	void		*sc_sih_wr;
+	struct virtual_channel	*sc_vchan[VAUDIOCHANS];
 	struct	mixer_asyncs {
 		struct mixer_asyncs *next;
 		pid_t	pid;
@@ -147,16 +178,12 @@
 	kcondvar_t	sc_rchan;
 	kcondvar_t	sc_wchan;
 	kcondvar_t	sc_lchan;
-	int		sc_dvlock;
+	bool		sc_trigger_started;
+	bool		sc_writeme;
+	int		sc_opens;
 	bool		sc_dying;
 
-	bool		sc_blkset;	/* Blocksize has been set */
-
-	uint8_t		*sc_sil_start;	/* start of silence in buffer */
-	int		sc_sil_count;	/* # of silence bytes */
-
 	bool		sc_rbus;	/* input DMA in progress */
-	bool		sc_pbus;	/* output DMA in progress */
 
 	/**
 	 *  userland
@@ -168,15 +195,19 @@
 	 *  sc_pstreams[n-1]	<list_t::filters[1].param>
 	 *      |  sc_pfilters[n-1]
 	 *    sc_pr		<list_t::filters[0].param>
+	 * (vchans mixed into sc_pr)
+	 *
+	 * play_thread
+	 *    sc_pr
 	 *      |
+	 *  vchan[0]->sc_pustream
+	 *	|
+	 *  vchan[0]->sc_mpr
+	 *	|
 	 *  hardware
 	 */
-	audio_params_t		sc_pparams;	/* play encoding parameters */
-	audio_stream_t		*sc_pustream;	/* the first buffer */
-	int			sc_npfilters;	/* number of filters */
-	audio_stream_t		sc_pstreams[AUDIO_MAX_FILTERS];
-	stream_filter_t		*sc_pfilters[AUDIO_MAX_FILTERS];
-	struct audio_ringbuffer	sc_pr;		/* Play ring */
+
+	struct audio_ringbuffer	sc_pr;	/* Play ring to mix into */
 
 	/**
 	 *  hardware
@@ -200,11 +231,6 @@
 	audio_params_t		sc_rparams;	/* record encoding parameters */
 
 	int		sc_eof;		/* EOF, i.e. zero sized write, counter */
-	u_long		sc_wstamp;	/* # of bytes read with read(2) */
-	u_long		sc_playdrop;
-
-	int		sc_full_duplex;	/* device in full duplex mode */
-
 	struct	au_mixer_ports sc_inports, sc_outports;
 	int		sc_monitor_port;
 
@@ -220,11 +246,16 @@
 #endif
 
 	u_int	sc_lastgain;
-	struct audio_info sc_lastinfo;
-	bool	sc_lastinfovalid;
 
 	mixer_ctrl_t	*sc_mixer_state;
 	int		sc_nmixer_states;
+	int		sc_static_nmixer_states;
+
+	bool		schedule_wih;
+	bool		schedule_rih;
+
+	lwp_t		*sc_playthread;
+	kcondvar_t	sc_condvar;
 };
 
 #endif /* _SYS_DEV_AUDIOVAR_H_ */
Index: audio.c
===================================================================
RCS file: /cvsroot/src/sys/dev/audio.c,v
retrieving revision 1.267
diff -u -r1.267 audio.c
--- src/sys/dev/audio.c	23 Apr 2016 10:15:31 -0000	1.267
+++ src/sys/dev/audio.c	3 Jun 2016 22:46:47 -0000
@@ -1,6 +1,32 @@
 /*	$NetBSD: audio.c,v 1.267 2016/04/23 10:15:31 skrll Exp $	*/
 
 /*-
+ * Copyright (c) 2016 Nathanial Sloss <nathanialsloss@yahoo.com.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
@@ -109,16 +135,6 @@
  *   sc_intr_lock.  This is to ensure that groups of hardware operations are
  *   made atomically.  SLEEPS CANNOT OCCUR WITH THIS LOCK HELD.
  *
- * - sc_dvlock, private to this module.  This is a custom reader/writer lock
- *   built on sc_lock and a condition variable.  Some operations release
- *   sc_lock in order to allocate memory, to wait for in-flight I/O to
- *   complete, to copy to/from user context, etc.  sc_dvlock serializes
- *   changes to filters and audio device settings while a read/write to the
- *   hardware is in progress.  A write lock is taken only under exceptional
- *   circumstances, for example when opening /dev/audio or changing audio
- *   parameters.  Long term sleeps and copy to/from user space may be done
- *   with this lock held.
- *
  * List of hardware interface methods, and which locks are held when each
  * is called by this module:
  *
@@ -160,6 +176,7 @@
 #include "audio.h"
 #if NAUDIO > 0
 
+#include <sys/types.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/fcntl.h>
@@ -177,10 +194,17 @@
 #include <sys/audioio.h>
 #include <sys/device.h>
 #include <sys/intr.h>
+#include <sys/kthread.h>
 #include <sys/cpu.h>
 
 #include <dev/audio_if.h>
 #include <dev/audiovar.h>
+#include <dev/auconv.h>
+#include <dev/auvolconv.h>
+
+#ifdef VAUDIOSPEAKER
+#include <dev/spkrvar.h>
+#endif
 
 #include <machine/endian.h>
 
@@ -198,6 +222,12 @@
 #define SPECIFIED(x)	(x != ~0)
 #define SPECIFIED_CH(x)	(x != (u_char)~0)
 
+#define VAUDIO_NFORMATS	1
+static const struct audio_format vaudio_formats[VAUDIO_NFORMATS] = {
+	{ NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
+	  2, AUFMT_STEREO, 1, { 44100 } },
+};
+
 /* #define AUDIO_PM_IDLE */
 #ifdef AUDIO_PM_IDLE
 int	audio_idle_timeout = 30;
@@ -205,11 +235,11 @@
 
 int	audio_blk_ms = AUDIO_BLK_MS;
 
-int	audiosetinfo(struct audio_softc *, struct audio_info *);
-int	audiogetinfo(struct audio_softc *, struct audio_info *, int);
+int	audiosetinfo(struct audio_softc *, struct audio_info *, bool, int);
+int	audiogetinfo(struct audio_softc *, struct audio_info *, int, int);
 
 int	audio_open(dev_t, struct audio_softc *, int, int, struct lwp *);
-int	audio_close(struct audio_softc *, int, int, struct lwp *);
+int	audio_close(struct audio_softc *, int, int, struct lwp *, int);
 int	audio_read(struct audio_softc *, struct uio *, int);
 int	audio_write(struct audio_softc *, struct uio *, int);
 int	audio_ioctl(struct audio_softc *, u_long, void *, int, struct lwp *);
@@ -223,33 +253,37 @@
 static	void mixer_remove(struct audio_softc *);
 static	void mixer_signal(struct audio_softc *);
 
-void	audio_init_record(struct audio_softc *);
-void	audio_init_play(struct audio_softc *);
+void	audio_init_record(struct audio_softc *, int);
+void	audio_init_play(struct audio_softc *, int);
 int	audiostartr(struct audio_softc *);
-int	audiostartp(struct audio_softc *);
+int	audiostartp(struct audio_softc *, int);
 void	audio_rint(void *);
 void	audio_pint(void *);
+void	audio_mix(void *);
+void	audio_play_thread(void *);
+void	mix_func(struct audio_softc *, struct audio_ringbuffer *, int);
+void	mix_write(void *);
 int	audio_check_params(struct audio_params *);
 
-void	audio_calc_blksize(struct audio_softc *, int);
+void	audio_calc_blksize(struct audio_softc *, int, int);
 void	audio_fill_silence(struct audio_params *, uint8_t *, int);
 int	audio_silence_copyout(struct audio_softc *, int, struct uio *);
 
 void	audio_init_ringbuffer(struct audio_softc *,
 			      struct audio_ringbuffer *, int);
-int	audio_initbufs(struct audio_softc *);
-void	audio_calcwater(struct audio_softc *);
-int	audio_drain(struct audio_softc *);
-void	audio_clear(struct audio_softc *);
-void	audio_clear_intr_unlocked(struct audio_softc *sc);
+int	audio_initbufs(struct audio_softc *, int);
+void	audio_calcwater(struct audio_softc *, int);
+int	audio_drain(struct audio_softc *, int);
+void	audio_clear(struct audio_softc *, int);
+void	audio_clear_intr_unlocked(struct audio_softc *sc, int);
 static inline void audio_pint_silence
-	(struct audio_softc *, struct audio_ringbuffer *, uint8_t *, int);
+	(struct audio_softc *, struct audio_ringbuffer *, uint8_t *, int, int);
 
 int	audio_alloc_ring
 	(struct audio_softc *, struct audio_ringbuffer *, int, size_t);
 void	audio_free_ring(struct audio_softc *, struct audio_ringbuffer *);
 static int audio_setup_pfilters(struct audio_softc *, const audio_params_t *,
-				stream_filter_list_t *);
+				stream_filter_list_t *, int);
 static int audio_setup_rfilters(struct audio_softc *, const audio_params_t *,
 				stream_filter_list_t *);
 static void audio_stream_dtor(audio_stream_t *);
@@ -263,7 +297,7 @@
 static void stream_filter_list_set
 	(stream_filter_list_t *, int, stream_filter_factory_t,
 	 const audio_params_t *);
-int	audio_set_defaults(struct audio_softc *, u_int);
+int	audio_set_defaults(struct audio_softc *, u_int, int);
 
 int	audioprobe(device_t, cfdata_t, void *);
 void	audioattach(device_t, device_t, void *);
@@ -295,6 +329,8 @@
 static void	audio_exit(struct audio_softc *);
 static int	audio_waitio(struct audio_softc *, kcondvar_t *);
 
+#define AUDIO_OUTPUT_CLASS 0
+
 struct portname {
 	const char *name;
 	int mask;
@@ -320,6 +356,20 @@
 int	au_set_port(struct audio_softc *, struct au_mixer_ports *,
 			u_int);
 int	au_get_port(struct audio_softc *, struct au_mixer_ports *);
+static int
+	audio_get_port(struct audio_softc *, mixer_ctrl_t *);
+static int
+	audio_set_port(struct audio_softc *, mixer_ctrl_t *);
+static int
+	audio_query_devinfo(struct audio_softc *, mixer_devinfo_t *);
+static int
+audio_set_params(struct audio_softc *, int, int, audio_params_t *,
+    audio_params_t *, stream_filter_list_t *, stream_filter_list_t *, int);
+static int
+audio_query_encoding(struct audio_softc *, struct audio_encoding *);
+static int
+audio_set_vchan_defaults(struct audio_softc *, u_int,
+    const struct audio_format *, int);
 int	au_get_lr_value(struct audio_softc *, mixer_ctrl_t *,
 			int *, int *);
 int	au_set_lr_value(struct audio_softc *, mixer_ctrl_t *,
@@ -360,7 +410,16 @@
 	.d_mmap = audiommap,
 	.d_kqfilter = audiokqfilter,
 	.d_discard = nodiscard,
-	.d_flag = D_OTHER | D_MPSAFE
+	.d_flag = D_MCLOSE | D_MPSAFE
+};
+
+/* The default vchan mode: 48 kHz stereo signed linear */
+struct audio_params vchan_default = {
+	.sample_rate = 44100,
+	.encoding = AUDIO_ENCODING_SLINEAR_LE,
+	.precision = 16,
+	.validbits = 16,
+	.channels = 2,
 };
 
 /* The default audio mode: 8 kHz mono mu-law */
@@ -394,6 +453,7 @@
 {
 	struct audio_softc *sc;
 	struct audio_attach_args *sa;
+	struct virtual_channel *vc;
 	const struct audio_hw_if *hwp;
 	void *hdlp;
 	int error;
@@ -407,10 +467,30 @@
 	sa = aux;
 	hwp = sa->hwif;
 	hdlp = sa->hdl;
+	sc->sc_opens = 0;
+
+	sc->sc_trigger_started = false;
+	sc->sc_vchan[0] = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP);
+	vc = sc->sc_vchan[0];
+	memset(sc->sc_audiopid, -1, sizeof(sc->sc_audiopid));
+	vc->sc_open = 0;
+	vc->sc_mode = 0;
+	vc->sc_npfilters = 0;
+	memset(vc->sc_pfilters, 0,
+	    sizeof(vc->sc_pfilters));
+	vc->sc_lastinfovalid = false;
+	vc->sc_swvol = 255;
+
+	if (auconv_create_encodings(vaudio_formats, VAUDIO_NFORMATS,
+	    &sc->sc_encodings) != 0) {
+		aprint_error_dev(self, "couldn't create encodings\n");
+		return;
+	}
 
 	cv_init(&sc->sc_rchan, "audiord");
 	cv_init(&sc->sc_wchan, "audiowr");
 	cv_init(&sc->sc_lchan, "audiolk");
+	cv_init(&sc->sc_condvar,"play");
 
 	if (hwp == 0 || hwp->get_locks == 0) {
 		printf(": missing method\n");
@@ -440,7 +520,6 @@
 	sc->hw_if = hwp;
 	sc->hw_hdl = hdlp;
 	sc->sc_dev = parent;
-	sc->sc_lastinfovalid = false;
 
 	mutex_enter(sc->sc_lock);
 	props = audio_get_props(sc);
@@ -466,11 +545,12 @@
 	mutex_enter(sc->sc_lock);
 	can_playback = audio_can_playback(sc);
 	can_capture = audio_can_capture(sc);
- 	mutex_exit(sc->sc_lock);
 
 	if (can_playback) {
 		error = audio_alloc_ring(sc, &sc->sc_pr,
-		    AUMODE_PLAY, AU_RING_SIZE);
+	    	    AUMODE_PLAY, AU_RING_SIZE);
+		error = audio_alloc_ring(sc, &sc->sc_vchan[0]->sc_mpr,
+	    	    AUMODE_PLAY, AU_RING_SIZE);
 		if (error) {
 			sc->hw_if = NULL;
 			aprint_error("audio: could not allocate play buffer\n");
@@ -483,6 +563,8 @@
 		if (error) {
 			if (sc->sc_pr.s.start != 0)
 				audio_free_ring(sc, &sc->sc_pr);
+			if (sc->sc_vchan[0]->sc_mpr.s.start != 0)
+				audio_free_ring(sc, &sc->sc_vchan[0]->sc_mpr);
 			sc->hw_if = NULL;
 			aprint_error("audio: could not allocate record buffer\n");
 			return;
@@ -491,8 +573,8 @@
 
 	sc->sc_lastgain = 128;
 
-	mutex_enter(sc->sc_lock);
-	error = audio_set_defaults(sc, 0);
+	audio_set_vchan_defaults(sc, AUMODE_PLAY | AUMODE_PLAY_ALL | AUMODE_RECORD,
+	    &vaudio_formats[0], 0);
 	mutex_exit(sc->sc_lock);
 	if (error != 0) {
 		aprint_error("audioattach: audio_set_defaults() failed\n");
@@ -500,6 +582,7 @@
 		return;
 	}
 
+	sc->sc_pr.blksize = sc->sc_vchan[0]->sc_mpr.blksize;
 	sc->sc_sih_rd = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE,
 	    audio_softintr_rd, sc);
 	sc->sc_sih_wr = softint_establish(SOFTINT_SERIAL | SOFTINT_MPSAFE,
@@ -530,7 +613,7 @@
 	 */
 	mutex_enter(sc->sc_lock);
 	for(mi.index = 0; ; mi.index++) {
-		if (hwp->query_devinfo(hdlp, &mi) != 0)
+		if (audio_query_devinfo(sc, &mi) != 0)
 			break;
 		 /*
 		  * The type of AUDIO_MIXER_CLASS merely introduces a class.
@@ -551,8 +634,9 @@
 
 	/* Allocate save area.  Ensure non-zero allocation. */
 	sc->sc_nmixer_states = mi.index;
-	sc->sc_mixer_state = kmem_alloc(sizeof(mixer_ctrl_t) *
-	    sc->sc_nmixer_states + 1, KM_SLEEP);
+	sc->sc_static_nmixer_states = mi.index;
+	sc->sc_mixer_state = kmem_zalloc(sizeof(mixer_ctrl_t) *
+	    (sc->sc_nmixer_states + VAUDIOCHANS + 1), KM_SLEEP);
 
 	/*
 	 * This is where we assign each control in the "audio" model, to the
@@ -563,7 +647,7 @@
 	record_source_found = 0;
 	mutex_enter(sc->sc_lock);
 	for(mi.index = 0; ; mi.index++) {
-		if (hwp->query_devinfo(hdlp, &mi) != 0)
+		if (audio_query_devinfo(sc, &mi) != 0)
 			break;
 		KASSERT(mi.index < sc->sc_nmixer_states);
 		if (mi.type == AUDIO_MIXER_CLASS)
@@ -670,6 +754,11 @@
 #ifdef AUDIO_PM_IDLE
 	callout_schedule(&sc->sc_idle_counter, audio_idle_timeout * hz);
 #endif
+	kthread_create(PRI_BIO, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL,
+	    audio_play_thread, sc, &sc->sc_playthread, "audiomix");
+#ifdef VAUDIOSPEAKER
+	speakerattach_mi(self);
+#endif
 }
 
 int
@@ -692,16 +781,23 @@
 audiodetach(device_t self, int flags)
 {
 	struct audio_softc *sc;
-	int maj, mn, i;
+	int maj, mn, i, n, rc;
 
 	sc = device_private(self);
 	DPRINTF(("audio_detach: sc=%p flags=%d\n", sc, flags));
 
 	/* Start draining existing accessors of the device. */
+	if ((rc = config_detach_children(self, flags)) != 0)
+		return rc;
 	mutex_enter(sc->sc_lock);
 	sc->sc_dying = true;
 	cv_broadcast(&sc->sc_wchan);
 	cv_broadcast(&sc->sc_rchan);
+	cv_broadcast(&sc->sc_condvar);
+	mutex_exit(sc->sc_lock);
+	kthread_join(sc->sc_playthread);
+	mutex_enter(sc->sc_lock);
+	cv_destroy(&sc->sc_condvar);
 	mutex_exit(sc->sc_lock);
 
 	/* locate the major number */
@@ -737,6 +833,11 @@
 	pmf_device_deregister(self);
 
 	/* free resources */
+	for (n = 0; n < VAUDIOCHANS; n++) {
+		if (n != 0 && sc->sc_audiopid[n] == -1)
+			continue;
+		audio_free_ring(sc, &sc->sc_vchan[n]->sc_mpr);
+	}
 	audio_free_ring(sc, &sc->sc_pr);
 	audio_free_ring(sc, &sc->sc_rr);
 	for (i = 0; i < sc->sc_nrfilters; i++) {
@@ -745,12 +846,18 @@
 		audio_stream_dtor(&sc->sc_rstreams[i]);
 	}
 	sc->sc_nrfilters = 0;
-	for (i = 0; i < sc->sc_npfilters; i++) {
-		sc->sc_pfilters[i]->dtor(sc->sc_pfilters[i]);
-		sc->sc_pfilters[i] = NULL;
-		audio_stream_dtor(&sc->sc_pstreams[i]);
+	for (n = 0; n < VAUDIOCHANS; n++) {
+		if (n != 0 && sc->sc_audiopid[n] == -1)
+			continue;
+		for (i = 0; i < sc->sc_vchan[n]->sc_npfilters; i++) {
+			sc->sc_vchan[n]->sc_pfilters[i]->dtor(sc->sc_vchan[n]->sc_pfilters[i]);
+			sc->sc_vchan[n]->sc_pfilters[i] = NULL;
+			audio_stream_dtor(&sc->sc_vchan[n]->sc_pstreams[i]);
+		}
+		sc->sc_vchan[n]->sc_npfilters = 0;
 	}
-	sc->sc_npfilters = 0;
+
+	auconv_delete_encodings(sc->sc_encodings);
 
 	if (sc->sc_sih_rd) {
 		softint_disestablish(sc->sc_sih_rd);
@@ -761,6 +868,8 @@
 		sc->sc_sih_wr = NULL;
 	}
 
+	kmem_free(sc->sc_vchan[0], sizeof(struct virtual_channel));
+
 #ifdef AUDIO_PM_IDLE
 	callout_destroy(&sc->sc_idle_counter);
 #endif
@@ -780,7 +889,7 @@
 	mixer_devinfo_t mi;
 
 	for(mi.index = 0;
-	    sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0;
+	    audio_query_devinfo(sc, &mi) == 0;
 	    mi.index++)
 		if (mi.mixer_class == class && strcmp(mi.label.name, name) == 0)
 			return mi.index;
@@ -857,16 +966,28 @@
 void
 audio_printsc(struct audio_softc *sc)
 {
+	int n;
+
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+
+	if (n == VAUDIOCHANS)
+		return;
+
 	printf("hwhandle %p hw_if %p ", sc->hw_hdl, sc->hw_if);
-	printf("open 0x%x mode 0x%x\n", sc->sc_open, sc->sc_mode);
+	printf("open 0x%x mode 0x%x\n", sc->sc_vchan[n]->sc_open,
+	    sc->sc_vchan[n]->sc_mode);
 	printf("rchan 0x%x wchan 0x%x ", cv_has_waiters(&sc->sc_rchan),
 	    cv_has_waiters(&sc->sc_wchan));
 	printf("rring used 0x%x pring used=%d\n",
 	       audio_stream_get_used(&sc->sc_rr.s),
-	       audio_stream_get_used(&sc->sc_pr.s));
-	printf("rbus 0x%x pbus 0x%x ", sc->sc_rbus, sc->sc_pbus);
-	printf("blksize %d", sc->sc_pr.blksize);
-	printf("hiwat %d lowat %d\n", sc->sc_pr.usedhigh, sc->sc_pr.usedlow);
+	       audio_stream_get_used(&sc->sc_vhan[n].sc_mpr.s));
+	printf("rbus 0x%x pbus 0x%x ", sc->sc_rbus, sc->sc_vchan[n]->sc_pbus);
+	printf("blksize %d", sc->sc_vchan[n]->sc_mpr.blksize);
+	printf("hiwat %d lowat %d\n", sc->sc_vchan[n]->sc_mpr.usedhigh,
+	    sc->sc_vchan[n]->sc_mpr.usedlow);
 }
 
 void
@@ -893,17 +1014,16 @@
 		bufsize = AUMINBUF;
 	ROUNDSIZE(bufsize);
 	if (hw->round_buffersize) {
-		mutex_enter(sc->sc_lock);
 		bufsize = hw->round_buffersize(hdl, direction, bufsize);
- 		mutex_exit(sc->sc_lock);
 	}
-	if (hw->allocm)
+	if (hw->allocm && (r == &sc->sc_vchan[0]->sc_mpr || r == &sc->sc_rr))
 		r->s.start = hw->allocm(hdl, direction, bufsize);
 	else
-		r->s.start = kmem_alloc(bufsize, KM_SLEEP);
+		r->s.start = kmem_zalloc(bufsize, KM_SLEEP);
 	if (r->s.start == 0)
 		return ENOMEM;
 	r->s.bufsize = bufsize;
+
 	return 0;
 }
 
@@ -913,7 +1033,7 @@
 	if (r->s.start == 0)
 		return;
 
-	if (sc->hw_if->freem)
+	if (sc->hw_if->freem && (r == &sc->sc_vchan[0]->sc_mpr || r == &sc->sc_rr))
 		sc->hw_if->freem(sc->hw_hdl, r->s.start, r->s.bufsize);
 	else
 		kmem_free(r->s.start, r->s.bufsize);
@@ -922,7 +1042,7 @@
 
 static int
 audio_setup_pfilters(struct audio_softc *sc, const audio_params_t *pp,
-		     stream_filter_list_t *pfilters)
+		     stream_filter_list_t *pfilters, int m)
 {
 	stream_filter_t *pf[AUDIO_MAX_FILTERS], *of[AUDIO_MAX_FILTERS];
 	audio_stream_t ps[AUDIO_MAX_FILTERS], os[AUDIO_MAX_FILTERS];
@@ -964,22 +1084,22 @@
 
 	/* Swap in new filters. */
 	mutex_enter(sc->sc_intr_lock);
-	memcpy(of, sc->sc_pfilters, sizeof(of));
-	memcpy(os, sc->sc_pstreams, sizeof(os));
-	onfilters = sc->sc_npfilters;
-	memcpy(sc->sc_pfilters, pf, sizeof(pf));
-	memcpy(sc->sc_pstreams, ps, sizeof(ps));
-	sc->sc_npfilters = pfilters->req_size;
+	memcpy(of, sc->sc_vchan[m]->sc_pfilters, sizeof(of));
+	memcpy(os, sc->sc_vchan[m]->sc_pstreams, sizeof(os));
+	onfilters = sc->sc_vchan[m]->sc_npfilters;
+	memcpy(sc->sc_vchan[m]->sc_pfilters, pf, sizeof(pf));
+	memcpy(sc->sc_vchan[m]->sc_pstreams, ps, sizeof(ps));
+	sc->sc_vchan[m]->sc_npfilters = pfilters->req_size;
 	for (i = 0; i < pfilters->req_size; i++) {
-		pf[i]->set_inputbuffer(pf[i], &sc->sc_pstreams[i]);
+		pf[i]->set_inputbuffer(pf[i], &sc->sc_vchan[m]->sc_pstreams[i]);
 	}
 	/* hardware format and the buffer near to userland */
 	if (pfilters->req_size <= 0) {
-		sc->sc_pr.s.param = *pp;
-		sc->sc_pustream = &sc->sc_pr.s;
+		sc->sc_vchan[m]->sc_mpr.s.param = *pp;
+		sc->sc_vchan[m]->sc_pustream = &sc->sc_vchan[m]->sc_mpr.s;
 	} else {
-		sc->sc_pr.s.param = pfilters->filters[0].param;
-		sc->sc_pustream = &sc->sc_pstreams[0];
+		sc->sc_vchan[m]->sc_mpr.s.param = pfilters->filters[0].param;
+		sc->sc_vchan[m]->sc_pustream = &sc->sc_vchan[m]->sc_pstreams[0];
 	}
 	mutex_exit(sc->sc_intr_lock);
 
@@ -993,13 +1113,13 @@
 
 #ifdef AUDIO_DEBUG
 	printf("%s: HW-buffer=%p pustream=%p\n",
-	       __func__, &sc->sc_pr.s, sc->sc_pustream);
+	       __func__, &sc->sc_vchan[m]->sc_mpr.s, sc->sc_vchan[m]->sc_pustream);
 	for (i = 0; i < pfilters->req_size; i++) {
 		char num[100];
 		snprintf(num, 100, "[%d]", i);
-		audio_print_params(num, &sc->sc_pstreams[i].param);
+		audio_print_params(num, &sc->sc_vchan[m]->sc_pstreams[i].param);
 	}
-	audio_print_params("[HW]", &sc->sc_pr.s.param);
+	audio_print_params("[HW]", &sc->sc_vchan[m]->sc_mpr.s.param);
 #endif /* AUDIO_DEBUG */
 
 	return 0;
@@ -1111,7 +1231,7 @@
 
 	size = min(size, AU_RING_SIZE);
 	stream->bufsize = size;
-	stream->start = kmem_alloc(size, KM_SLEEP);
+	stream->start = kmem_zalloc(size, KM_SLEEP);
 	if (stream->start == NULL)
 		return ENOMEM;
 	frame_size = (param->precision + 7) / 8 * param->channels;
@@ -1182,6 +1302,7 @@
 static int
 audio_enter(dev_t dev, krw_t rw, struct audio_softc **scp)
 {
+
 	struct audio_softc *sc;
 
 	/* First, find the device and take sc_lock. */
@@ -1194,24 +1315,6 @@
 		return EIO;
 	}
 
-	/* Acquire device access lock. */
-	switch (rw) {
-	case RW_WRITER:
-		while (__predict_false(sc->sc_dvlock != 0)) {
-			cv_wait(&sc->sc_lchan, sc->sc_lock);
-		}
-		sc->sc_dvlock = -1;
-		break;
-	case RW_READER:
-		while (__predict_false(sc->sc_dvlock < 0)) {
-			cv_wait(&sc->sc_lchan, sc->sc_lock);
-		}
-		sc->sc_dvlock++;
-		break;
-	default:
-		panic("audio_enter");
-	}
-
 	*scp = sc;
 	return 0;
 }
@@ -1222,16 +1325,6 @@
 static void
 audio_exit(struct audio_softc *sc)
 {
-
-	KASSERT(mutex_owned(sc->sc_lock));
-	KASSERT(sc->sc_dvlock != 0);
-
-	/* Release device level lock. */
-	if (__predict_false(sc->sc_dvlock < 0)) {
-		sc->sc_dvlock = 0;
-	} else {
-		sc->sc_dvlock--;
-	}
 	cv_broadcast(&sc->sc_lchan);
 	mutex_exit(sc->sc_lock);
 }
@@ -1243,37 +1336,13 @@
 audio_waitio(struct audio_softc *sc, kcondvar_t *chan)
 {
 	int error;
-	krw_t rw;
 
 	KASSERT(mutex_owned(sc->sc_lock));
-
-	/* Release device level lock while sleeping. */
-	if (__predict_false(sc->sc_dvlock < 0)) {
-		sc->sc_dvlock = 0;
-		rw = RW_WRITER;
-	} else {
-		KASSERT(sc->sc_dvlock > 0);
-		sc->sc_dvlock--;
-		rw = RW_READER;
-	}
 	cv_broadcast(&sc->sc_lchan);
 
 	/* Wait for pending I/O to complete. */
 	error = cv_wait_sig(chan, sc->sc_lock);
 
-	/* Re-acquire device level lock. */
-	if (__predict_false(rw == RW_WRITER)) {
-		while (__predict_false(sc->sc_dvlock != 0)) {
-			cv_wait(&sc->sc_lchan, sc->sc_lock);
-		}
-		sc->sc_dvlock = -1;
-	} else {
-		while (__predict_false(sc->sc_dvlock < 0)) {
-			cv_wait(&sc->sc_lchan, sc->sc_lock);
-		}
-		sc->sc_dvlock++;
-	}
-
 	return error;
 }
 
@@ -1310,7 +1379,7 @@
 audioclose(dev_t dev, int flags, int ifmt, struct lwp *l)
 {
 	struct audio_softc *sc;
-	int error;
+	int error, n;
 
 	if ((error = audio_enter(dev, RW_WRITER, &sc)) != 0)
 		return error;
@@ -1318,7 +1387,15 @@
 	switch (AUDIODEV(dev)) {
 	case SOUND_DEVICE:
 	case AUDIO_DEVICE:
-		error = audio_close(sc, flags, ifmt, l);
+		for (n = 1; n < VAUDIOCHANS; n++) {
+			if (sc->sc_audiopid[n] == curproc->p_pid)
+				break;
+		}
+		if (n == VAUDIOCHANS) {
+			error = EIO;
+			break;	
+		}
+		error = audio_close(sc, flags, ifmt, l, n);
 		break;
 	case MIXER_DEVICE:
 		error = mixer_close(sc, flags, ifmt, l);
@@ -1565,19 +1642,23 @@
 	rp->copying = false;
 	rp->needfill = false;
 	rp->mmapped = false;
+	memset(rp->s.start, 0, blksize * 2);
 }
 
 int
-audio_initbufs(struct audio_softc *sc)
+audio_initbufs(struct audio_softc *sc, int n)
 {
 	const struct audio_hw_if *hw;
+	struct virtual_channel *vc;
 	int error;
 
-	DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_mode));
+	DPRINTF(("audio_initbufs: mode=0x%x\n", sc->sc_vchan[n]->sc_mode));
+	vc = sc->sc_vchan[0];
 	hw = sc->hw_if;
 	if (audio_can_capture(sc)) {
 		audio_init_ringbuffer(sc, &sc->sc_rr, AUMODE_RECORD);
-		if (hw->init_input && (sc->sc_mode & AUMODE_RECORD)) {
+		if (sc->sc_opens == 1 && hw->init_input &&
+		    (sc->sc_vchan[n]->sc_mode & AUMODE_RECORD)) {
 			error = hw->init_input(sc->hw_hdl, sc->sc_rr.s.start,
 				       sc->sc_rr.s.end - sc->sc_rr.s.start);
 			if (error)
@@ -1586,11 +1667,12 @@
 	}
 
 	if (audio_can_playback(sc)) {
-		audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY);
-		sc->sc_sil_count = 0;
-		if (hw->init_output && (sc->sc_mode & AUMODE_PLAY)) {
-			error = hw->init_output(sc->hw_hdl, sc->sc_pr.s.start,
-					sc->sc_pr.s.end - sc->sc_pr.s.start);
+		audio_init_ringbuffer(sc, &sc->sc_vchan[n]->sc_mpr, AUMODE_PLAY);
+		sc->sc_vchan[n]->sc_sil_count = 0;
+		if (sc->sc_opens == 1 && hw->init_output &&
+		    (sc->sc_vchan[n]->sc_mode & AUMODE_PLAY)) {
+			error = hw->init_output(sc->hw_hdl, vc->sc_mpr.s.start,
+					vc->sc_mpr.s.end - vc->sc_mpr.s.start);
 			if (error)
 				return error;
 		}
@@ -1601,12 +1683,12 @@
 	if (audio_can_playback(sc)) {
 		sc->sc_pnintr = 0;
 		sc->sc_pblktime = (u_long)(
-		    (double)sc->sc_pr.blksize * 100000 /
-		    (double)(sc->sc_pparams.precision / NBBY *
-			     sc->sc_pparams.channels *
-			     sc->sc_pparams.sample_rate)) * 10;
+		    (double)sc->sc_vchan[n]->sc_mpr.blksize * 100000 /
+		    (double)(sc->sc_vchan[n]->sc_pparams.precision / NBBY *
+			     sc->sc_vhan[n].sc_pparams.channels *
+			     sc->sc_vhan[n].sc_pparams.sample_rate)) * 10;
 		DPRINTF(("audio: play blktime = %lu for %d\n",
-			 sc->sc_pblktime, sc->sc_pr.blksize));
+			 sc->sc_pblktime, sc->sc_vhan[n].sc_mpr.blksize));
 	}
 	if (audio_can_capture(sc)) {
 		sc->sc_rnintr = 0;
@@ -1625,17 +1707,17 @@
 }
 
 void
-audio_calcwater(struct audio_softc *sc)
+audio_calcwater(struct audio_softc *sc, int n)
 {
+	struct virtual_channel *vc = sc->sc_vchan[n];
 
 	/* set high at 100% */
 	if (audio_can_playback(sc)) {
-		sc->sc_pr.usedhigh =
-		    sc->sc_pustream->end - sc->sc_pustream->start;
+		vc->sc_mpr.usedhigh = vc->sc_pustream->end - vc->sc_pustream->start;
 		/* set low at 75% of usedhigh */
-		sc->sc_pr.usedlow = sc->sc_pr.usedhigh * 3 / 4;
-		if (sc->sc_pr.usedlow == sc->sc_pr.usedhigh)
-			sc->sc_pr.usedlow -= sc->sc_pr.blksize;
+		vc->sc_mpr.usedlow = vc->sc_mpr.usedhigh * 3 / 4;
+		if (vc->sc_mpr.usedlow == vc->sc_mpr.usedhigh)
+			vc->sc_mpr.usedlow -= vc->sc_mpr.blksize;
 	}
 
 	if (audio_can_capture(sc)) {
@@ -1644,7 +1726,7 @@
 		    sc->sc_rr.blksize;
 		sc->sc_rr.usedlow = 0;
 		DPRINTF(("%s: plow=%d phigh=%d rlow=%d rhigh=%d\n", __func__,
-			 sc->sc_pr.usedlow, sc->sc_pr.usedhigh,
+			 vc->sc_mpr.usedlow, vc->sc_mpr.usedhigh,
 			 sc->sc_rr.usedlow, sc->sc_rr.usedhigh));
 	}
 }
@@ -1653,51 +1735,106 @@
 audio_open(dev_t dev, struct audio_softc *sc, int flags, int ifmt,
     struct lwp *l)
 {
-	int error;
+	int error, i, n;
 	u_int mode;
 	const struct audio_hw_if *hw;
+	struct virtual_channel *vc;
+	mixer_ctrl_t *mc;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
+	if (sc->sc_opens >= VAUDIOCHANS)
+		return ENXIO;
+
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n < VAUDIOCHANS)
+		return ENXIO;
+
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == -1)
+			break;
+	}
+	if (n == VAUDIOCHANS)
+		return ENXIO;
+
 	hw = sc->hw_if;
 	if (hw == NULL)
 		return ENXIO;
 
+	sc->sc_vchan[n] = kmem_zalloc(sizeof(struct virtual_channel), KM_SLEEP);
+	if (sc->sc_vchan[n] == NULL)
+		return ENOMEM;
+	vc = sc->sc_vchan[n];
+
+	vc->sc_open = 0;
+	vc->sc_mode = 0;
+	vc->sc_sil_count = 0;
+	vc->sc_npfilters = 0;
+	memset(vc->sc_pfilters, 0,
+	    sizeof(vc->sc_pfilters));
+	vc->sc_pbus = false;
+	vc->sc_blkset = false;
+	vc->sc_lastinfovalid = false;
+	vc->sc_swvol = 255;
+
 	DPRINTF(("audio_open: flags=0x%x sc=%p hdl=%p\n",
 		 flags, sc, sc->hw_hdl));
 
-	if (((flags & FREAD) && (sc->sc_open & AUOPEN_READ)) ||
-	    ((flags & FWRITE) && (sc->sc_open & AUOPEN_WRITE)))
+	if (((flags & FREAD) && (vc->sc_open & AUOPEN_READ)) ||
+	    ((flags & FWRITE) && (vc->sc_open & AUOPEN_WRITE))) {
+		kmem_free(vc, sizeof(struct virtual_channel));
 		return EBUSY;
+	}
 
-	if (hw->open != NULL) {
-		mutex_enter(sc->sc_intr_lock);
-		error = hw->open(sc->hw_hdl, flags);
-		mutex_exit(sc->sc_intr_lock);
-		if (error)
-			return error;
+	sc->sc_opens++;
+	sc->sc_audiopid[n] = curproc->p_pid;
+	error = audio_alloc_ring(sc, &vc->sc_mpr,
+	    	    AUMODE_PLAY, AU_RING_SIZE);
+	if (error) {
+		sc->sc_opens--;
+		sc->sc_audiopid[n] = -1;
+		kmem_free(vc, sizeof(struct virtual_channel));
+		return error;
 	}
 
-	sc->sc_async_audio = 0;
-	sc->sc_sil_count = 0;
-	sc->sc_rbus = false;
-	sc->sc_pbus = false;
-	sc->sc_eof = 0;
-	sc->sc_playdrop = 0;
+	if (sc->sc_opens == 1) {
+		if (hw->open != NULL) {
+			mutex_enter(sc->sc_intr_lock);
+			error = hw->open(sc->hw_hdl, flags);
+			mutex_exit(sc->sc_intr_lock);
+			if (error) {
+				sc->sc_opens--;
+				sc->sc_audiopid[n] = -1;
+				kmem_free(vc,
+				    sizeof(struct virtual_channel));
+				return error;
+			}
+		}
+		audio_init_ringbuffer(sc, &sc->sc_pr, AUMODE_PLAY);
+		audio_initbufs(sc, 0);
+		sc->schedule_wih = false;
+		sc->schedule_rih = false;
+		sc->sc_eof = 0;
+		sc->sc_rbus = false;
+		sc->sc_async_audio = 0;
+	}
 
 	mutex_enter(sc->sc_intr_lock);
-	sc->sc_full_duplex = 
+	vc->sc_full_duplex = 
 		(flags & (FWRITE|FREAD)) == (FWRITE|FREAD) &&
 		(audio_get_props(sc) & AUDIO_PROP_FULLDUPLEX);
 	mutex_exit(sc->sc_intr_lock);
 
 	mode = 0;
 	if (flags & FREAD) {
-		sc->sc_open |= AUOPEN_READ;
+		vc->sc_open |= AUOPEN_READ;
 		mode |= AUMODE_RECORD;
 	}
 	if (flags & FWRITE) {
-		sc->sc_open |= AUOPEN_WRITE;
+		vc->sc_open |= AUOPEN_WRITE;
 		mode |= AUMODE_PLAY | AUMODE_PLAY_ALL;
 	}
 
@@ -1706,14 +1843,13 @@
 	 * The /dev/audio is always (re)set to 8-bit MU-Law mono
 	 * For the other devices, you get what they were last set to.
 	 */
-	if (ISDEVAUDIO(dev)) {
-		error = audio_set_defaults(sc, mode);
-	} else {
+	error = audio_set_defaults(sc, mode, n);
+	if (ISDEVSOUND(dev)) {
 		struct audio_info ai;
 
 		AUDIO_INITINFO(&ai);
 		ai.mode = mode;
-		error = audiosetinfo(sc, &ai);
+		error = audiosetinfo(sc, &ai, true, n);
 	}
 	if (error)
 		goto bad;
@@ -1724,27 +1860,44 @@
 	 * default values by the hardware driver, so that it may give
 	 * us these values.
 	 */
-	if (sc->sc_rparams.precision == 0 || sc->sc_pparams.precision == 0) {
+	if (sc->sc_rparams.precision == 0 || vc->sc_pparams.precision == 0) {
 		printf("audio_open: 0 precision\n");
-		return EINVAL;
+		goto bad;
 	}
 #endif
 
-	/* audio_close() decreases sc_pr.usedlow, recalculate here */
-	audio_calcwater(sc);
+	/* audio_close() decreases sc_mpr[n].usedlow, recalculate here */
+	audio_calcwater(sc, n);
+
+	DPRINTF(("audio_open: done sc_mode = 0x%x\n", sc->sc_mode[n]));
+
+	mutex_enter(sc->sc_intr_lock);
+	sc->sc_nmixer_states++;
+	mc = &sc->sc_mixer_state[sc->sc_nmixer_states];
+	mc->dev = sc->sc_nmixer_states;
+	mc->type = AUDIO_MIXER_VALUE;
+	mc->un.value.num_channels = 1;
+	(void)audio_get_port(sc, mc);
 
-	DPRINTF(("audio_open: done sc_mode = 0x%x\n", sc->sc_mode));
+	mutex_exit(sc->sc_intr_lock);
 
 	return 0;
 
 bad:
-	mutex_enter(sc->sc_intr_lock);
-	if (hw->close != NULL)
+	for (i = 0; i < vc->sc_npfilters; i++) {
+		vc->sc_pfilters[i]->dtor(vc->sc_pfilters[i]);
+		vc->sc_pfilters[i] = NULL;
+		audio_stream_dtor(&vc->sc_pstreams[i]);
+	}
+	vc->sc_npfilters = 0;
+	if (hw->close != NULL && sc->sc_opens == 0)
 		hw->close(sc->hw_hdl);
-	sc->sc_open = 0;
-	sc->sc_mode = 0;
-	mutex_exit(sc->sc_intr_lock);
-	sc->sc_full_duplex = 0;
+	sc->sc_opens--;
+	sc->sc_audiopid[n] = -1;
+	mutex_exit(sc->sc_lock);
+	audio_free_ring(sc, &vc->sc_mpr);
+	mutex_enter(sc->sc_lock);
+	kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel));
 	return error;
 }
 
@@ -1752,14 +1905,19 @@
  * Must be called from task context.
  */
 void
-audio_init_record(struct audio_softc *sc)
+audio_init_record(struct audio_softc *sc, int n)
 {
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
+	struct virtual_channel *vc = sc->sc_vchan[n];
+ 
+	if (sc->sc_opens != 0)
+		return;
+
 	mutex_enter(sc->sc_intr_lock);
 	if (sc->hw_if->speaker_ctl &&
-	    (!sc->sc_full_duplex || (sc->sc_mode & AUMODE_PLAY) == 0))
+	    (!vc->sc_full_duplex || (vc->sc_mode & AUMODE_PLAY) == 0))
 		sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_OFF);
 	mutex_exit(sc->sc_intr_lock);
 }
@@ -1768,20 +1926,23 @@
  * Must be called from task context.
  */
 void
-audio_init_play(struct audio_softc *sc)
+audio_init_play(struct audio_softc *sc, int n)
 {
 
 	KASSERT(mutex_owned(sc->sc_lock));
+	
+	if (sc->sc_opens != 0)
+		return;
 
 	mutex_enter(sc->sc_intr_lock);
-	sc->sc_wstamp = sc->sc_pr.stamp;
+	sc->sc_vchan[n]->sc_wstamp = sc->sc_vchan[n]->sc_mpr.stamp;
 	if (sc->hw_if->speaker_ctl)
 		sc->hw_if->speaker_ctl(sc->hw_hdl, SPKR_ON);
 	mutex_exit(sc->sc_intr_lock);
 }
 
 int
-audio_drain(struct audio_softc *sc)
+audio_drain(struct audio_softc *sc, int n)
 {
 	struct audio_ringbuffer *cb;
 	int error, drops;
@@ -1789,19 +1950,19 @@
 
 	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(mutex_owned(sc->sc_intr_lock));
-
-	DPRINTF(("audio_drain: enter busy=%d\n", sc->sc_pbus));
-	cb = &sc->sc_pr;
+	
+	DPRINTF(("audio_drain: enter busy=%d\n", sc->sc_pbus[n]));
+	cb = &sc->sc_vchan[n]->sc_mpr;
 	if (cb->mmapped)
 		return 0;
 
-	used = audio_stream_get_used(&sc->sc_pr.s);
-	for (i = 0; i < sc->sc_npfilters; i++)
-		used += audio_stream_get_used(&sc->sc_pstreams[i]);
+	used = audio_stream_get_used(&cb->s);
+	for (i = 0; i < sc->sc_vchan[n]->sc_npfilters; i++)
+		used += audio_stream_get_used(&sc->sc_vchan[n]->sc_pstreams[i]);
 	if (used <= 0)
 		return 0;
 
-	if (!sc->sc_pbus) {
+	if (n != 0 && !sc->sc_vchan[n]->sc_pbus) {
 		/* We've never started playing, probably because the
 		 * block was too short.  Pad it and start now.
 		 */
@@ -1811,7 +1972,7 @@
 		cc = cb->blksize - (inp - cb->s.start) % cb->blksize;
 		audio_fill_silence(&cb->s.param, inp, cc);
 		cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc);
-		error = audiostartp(sc);
+		error = audiostartp(sc, n);
 		if (error)
 			return error;
 	}
@@ -1823,15 +1984,15 @@
 	 */
 #ifdef DIAGNOSTIC
 	if (cb->copying) {
-		printf("audio_drain: copying in progress!?!\n");
+		DPRINTF(("audio_drain: copying in progress!?!\n"));
 		cb->copying = false;
 	}
 #endif
 	drops = cb->drops;
 	error = 0;
 	while (cb->drops == drops && !error) {
-		DPRINTF(("audio_drain: used=%d, drops=%ld\n",
-			 audio_stream_get_used(&sc->sc_pr.s), cb->drops));
+		DPRINTF(("audio_drain: n=%d, used=%d, drops=%ld\n", n,
+			 audio_stream_get_used(&sc->sc_vchan[n]->sc_mpr.s), cb->drops));
 		mutex_exit(sc->sc_intr_lock);
 		error = audio_waitio(sc, &sc->sc_wchan);
 		mutex_enter(sc->sc_intr_lock);
@@ -1847,47 +2008,77 @@
 /* ARGSUSED */
 int
 audio_close(struct audio_softc *sc, int flags, int ifmt,
-    struct lwp *l)
+    struct lwp *l, int n)
 {
+	struct virtual_channel *vc;
 	const struct audio_hw_if *hw;
+	int o;
 
 	KASSERT(mutex_owned(sc->sc_lock));
+	
+	if (sc->sc_opens == 0 || n == 0)
+		return ENXIO;
+
+	vc = sc->sc_vchan[n];
 
-	DPRINTF(("audio_close: sc=%p\n", sc));
 	hw = sc->hw_if;
 	mutex_enter(sc->sc_intr_lock);
+	DPRINTF(("audio_close: sc=%p\n", sc));
 	/* Stop recording. */
-	if ((flags & FREAD) && sc->sc_rbus) {
+	if (sc->sc_opens == 1 && (flags & FREAD) && sc->sc_rbus) {
 		/*
 		 * XXX Some drivers (e.g. SB) use the same routine
 		 * to halt input and output so don't halt input if
 		 * in full duplex mode.  These drivers should be fixed.
 		 */
-		if (!sc->sc_full_duplex || hw->halt_input != hw->halt_output)
+		if (!vc->sc_full_duplex || hw->halt_input != hw->halt_output)
 			hw->halt_input(sc->hw_hdl);
 		sc->sc_rbus = false;
 	}
 	/*
 	 * Block until output drains, but allow ^C interrupt.
 	 */
-	sc->sc_pr.usedlow = sc->sc_pr.blksize;	/* avoid excessive wakeups */
+	vc->sc_mpr.usedlow = vc->sc_mpr.blksize;  /* avoid excessive wakeups */
 	/*
 	 * If there is pending output, let it drain (unless
 	 * the output is paused).
 	 */
-	if ((flags & FWRITE) && sc->sc_pbus) {
-		if (!sc->sc_pr.pause && !audio_drain(sc) && hw->drain)
+	if ((flags & FWRITE) && vc->sc_pbus) {
+		if (!vc->sc_mpr.pause)
+			audio_drain(sc, n);
+		vc->sc_pbus = false;
+	}
+	if (sc->sc_opens == 1) {
+		audio_drain(sc, 0);
+		if (hw->drain)
 			(void)hw->drain(sc->hw_hdl);
 		hw->halt_output(sc->hw_hdl);
-		sc->sc_pbus = false;
+		sc->sc_trigger_started = false;
 	}
-	if (hw->close != NULL)
+	if (sc->sc_opens == 1 && hw->close != NULL)
 		hw->close(sc->hw_hdl);
-	sc->sc_open = 0;
-	sc->sc_mode = 0;
-	sc->sc_full_duplex = 0;
+	if (sc->sc_opens == 1)
+		sc->sc_async_audio = 0;
+
+	vc->sc_open = 0;
+	vc->sc_mode = 0;
+	vc->sc_full_duplex = 0;
+	
+	for (o = 0; o < vc->sc_npfilters; o++) {
+		vc->sc_pfilters[o]->dtor(vc->sc_pfilters[o]);
+		vc->sc_pfilters[o] = NULL;
+		audio_stream_dtor(&vc->sc_pstreams[o]);
+	}
+	vc->sc_npfilters = 0;
+
+	sc->sc_opens--;
+	sc->sc_nmixer_states--;
+	sc->sc_audiopid[n] = -1;
 	mutex_exit(sc->sc_intr_lock);
-	sc->sc_async_audio = 0;
+	mutex_exit(sc->sc_lock);
+	audio_free_ring(sc, &vc->sc_mpr);
+	mutex_enter(sc->sc_lock);
+	kmem_free(sc->sc_vchan[n], sizeof(struct virtual_channel));
 
 	return 0;
 }
@@ -1896,18 +2087,27 @@
 audio_read(struct audio_softc *sc, struct uio *uio, int ioflag)
 {
 	struct audio_ringbuffer *cb;
+	struct virtual_channel *vc;
 	const uint8_t *outp;
 	uint8_t *inp;
-	int error, used, cc, n;
+	int error, used, cc, n, m;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
+	for (m = 1; m < VAUDIOCHANS; m++) {
+		if (sc->sc_audiopid[m] == curproc->p_pid)
+			break;
+	}
+	if (m == VAUDIOCHANS)
+		return ENXIO;
+	vc = sc->sc_vchan[m];
+
 	cb = &sc->sc_rr;
 	if (cb->mmapped)
 		return EINVAL;
 
 	DPRINTFN(1,("audio_read: cc=%zu mode=%d\n",
-		    uio->uio_resid, sc->sc_mode));
+		    uio->uio_resid, vc->sc_mode));
 
 #ifdef AUDIO_PM_IDLE
 	if (device_is_active(&sc->dev) || sc->sc_idle)
@@ -1919,18 +2119,18 @@
 	 * If hardware is half-duplex and currently playing, return
 	 * silence blocks based on the number of blocks we have output.
 	 */
-	if (!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY)) {
+	if (!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY)) {
 		while (uio->uio_resid > 0 && !error) {
 			for(;;) {
 				/*
 				 * No need to lock, as any wakeup will be
 				 * held for us while holding sc_lock.
 				 */
-				cc = sc->sc_pr.stamp - sc->sc_wstamp;
+				cc = vc->sc_mpr.stamp - vc->sc_wstamp;
 				if (cc > 0)
 					break;
 				DPRINTF(("audio_read: stamp=%lu, wstamp=%lu\n",
-					 sc->sc_pr.stamp, sc->sc_wstamp));
+					 vc->sc_mpr.stamp, vc->sc_wstamp));
 				if (ioflag & IO_NDELAY)
 					return EWOULDBLOCK;
 				error = audio_waitio(sc, &sc->sc_rchan);
@@ -1945,7 +2145,7 @@
 			DPRINTFN(1,("audio_read: reading in write mode, "
 				    "cc=%d\n", cc));
 			error = audio_silence_copyout(sc, cc, uio);
-			sc->sc_wstamp += cc;
+			vc->sc_wstamp += cc;
 		}
 		return error;
 	}
@@ -1998,45 +2198,47 @@
 }
 
 void
-audio_clear(struct audio_softc *sc)
+audio_clear(struct audio_softc *sc, int n)
 {
 
+	struct virtual_channel *vc = sc->sc_vchan[n];
+ 
 	KASSERT(mutex_owned(sc->sc_intr_lock));
 
 	if (sc->sc_rbus) {
 		cv_broadcast(&sc->sc_rchan);
-		sc->hw_if->halt_input(sc->hw_hdl);
+		if (sc->sc_opens == 1)
+			sc->hw_if->halt_input(sc->hw_hdl);
 		sc->sc_rbus = false;
 		sc->sc_rr.pause = false;
 	}
-	if (sc->sc_pbus) {
+	if (vc->sc_pbus) {
 		cv_broadcast(&sc->sc_wchan);
-		sc->hw_if->halt_output(sc->hw_hdl);
-		sc->sc_pbus = false;
-		sc->sc_pr.pause = false;
+		vc->sc_pbus = false;
+		vc->sc_mpr.pause = false;
 	}
 }
 
 void
-audio_clear_intr_unlocked(struct audio_softc *sc)
+audio_clear_intr_unlocked(struct audio_softc *sc, int n)
 {
 
 	mutex_enter(sc->sc_intr_lock);
-	audio_clear(sc);
+	audio_clear(sc, n);
 	mutex_exit(sc->sc_intr_lock);
 }
 
 void
-audio_calc_blksize(struct audio_softc *sc, int mode)
+audio_calc_blksize(struct audio_softc *sc, int mode, int n)
 {
 	const audio_params_t *parm;
 	struct audio_ringbuffer *rb;
 
-	if (sc->sc_blkset)
+	if (sc->sc_vchan[n]->sc_blkset)
 		return;
 
 	if (mode == AUMODE_PLAY) {
-		rb = &sc->sc_pr;
+		rb = &sc->sc_vchan[n]->sc_mpr ;
 		parm = &rb->s.param;
 	} else {
 		rb = &sc->sc_rr;
@@ -2210,18 +2412,28 @@
 	uio_fetcher_t ufetcher;
 	audio_stream_t stream;
 	struct audio_ringbuffer *cb;
+	struct virtual_channel *vc;
 	stream_fetcher_t *fetcher;
 	stream_filter_t *filter;
 	uint8_t *inp, *einp;
-	int saveerror, error, n, cc, used;
+	int saveerror, error, n, m, cc, used;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n == VAUDIOCHANS)
+		return EINVAL;
+
+	vc = sc->sc_vchan[n];
+	cb = &vc->sc_mpr;
+
 	DPRINTFN(2,("audio_write: sc=%p count=%zu used=%d(hi=%d)\n",
-		    sc, uio->uio_resid, audio_stream_get_used(sc->sc_pustream),
-		    sc->sc_pr.usedhigh));
-	cb = &sc->sc_pr;
-	if (cb->mmapped)
+		    sc, uio->uio_resid, audio_stream_get_used(vc->sc_pustream),
+		    vc->sc_mpr.usedhigh));
+	if (vc->sc_mpr.mmapped)
 		return EINVAL;
 
 	if (uio->uio_resid == 0) {
@@ -2237,20 +2449,20 @@
 	/*
 	 * If half-duplex and currently recording, throw away data.
 	 */
-	if (!sc->sc_full_duplex &&
-	    (sc->sc_mode & AUMODE_RECORD)) {
+	if (!vc->sc_full_duplex &&
+	    (vc->sc_mode & AUMODE_RECORD)) {
 		uio->uio_offset += uio->uio_resid;
 		uio->uio_resid = 0;
 		DPRINTF(("audio_write: half-dpx read busy\n"));
 		return 0;
 	}
 
-	if (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) {
-		n = min(sc->sc_playdrop, uio->uio_resid);
-		DPRINTF(("audio_write: playdrop %d\n", n));
-		uio->uio_offset += n;
-		uio->uio_resid -= n;
-		sc->sc_playdrop -= n;
+	if (!(vc->sc_mode & AUMODE_PLAY_ALL) && vc->sc_playdrop > 0) {
+		m = min(vc->sc_playdrop, uio->uio_resid);
+		DPRINTF(("audio_write: playdrop %d\n", m));
+		uio->uio_offset += m;
+		uio->uio_resid -= m;
+		vc->sc_playdrop -= m;
 		if (uio->uio_resid == 0)
 			return 0;
 	}
@@ -2258,9 +2470,9 @@
 	/**
 	 * setup filter pipeline
 	 */
-	uio_fetcher_ctor(&ufetcher, uio, cb->usedhigh);
-	if (sc->sc_npfilters > 0) {
-		fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base;
+	uio_fetcher_ctor(&ufetcher, uio, vc->sc_mpr.usedhigh);
+	if (vc->sc_npfilters > 0) {
+		fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
 	} else {
 		fetcher = &ufetcher.base;
 	}
@@ -2269,7 +2481,7 @@
 	mutex_enter(sc->sc_intr_lock);
 	while (uio->uio_resid > 0 && !error) {
 		/* wait if the first buffer is occupied */
-		while ((used = audio_stream_get_used(sc->sc_pustream))
+		while ((used = audio_stream_get_used(vc->sc_pustream))
 		    >= cb->usedhigh) {
 			DPRINTFN(2, ("audio_write: sleep used=%d lowat=%d "
 				     "hiwat=%d\n", used,
@@ -2291,17 +2503,17 @@
 
 		/* Write to the sc_pustream as much as possible. */
 		mutex_exit(sc->sc_intr_lock);
-		if (sc->sc_npfilters > 0) {
-			filter = sc->sc_pfilters[0];
+		if (vc->sc_npfilters > 0) {
+			filter = vc->sc_pfilters[0];
 			filter->set_fetcher(filter, &ufetcher.base);
-			fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base;
+			fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
 			cc = cb->blksize * 2;
 			error = fetcher->fetch_to(sc, fetcher, &stream, cc);
 			if (error != 0) {
 				fetcher = &ufetcher.base;
-				cc = sc->sc_pustream->end - sc->sc_pustream->start;
+				cc = vc->sc_pustream->end - vc->sc_pustream->start;
 				error = fetcher->fetch_to(sc, fetcher,
-				    sc->sc_pustream, cc);
+				    vc->sc_pustream, cc);
 			}
 		} else {
 			fetcher = &ufetcher.base;
@@ -2309,9 +2521,9 @@
 			error = fetcher->fetch_to(sc, fetcher, &stream, cc);
 		}
 		mutex_enter(sc->sc_intr_lock);
-		if (sc->sc_npfilters > 0) {
+		if (vc->sc_npfilters > 0) {
 			cb->fstamp += ufetcher.last_used
-			    - audio_stream_get_used(sc->sc_pustream);
+			    - audio_stream_get_used(vc->sc_pustream);
 		}
 		cb->s.used += stream.used - used;
 		cb->s.inp = stream.inp;
@@ -2321,7 +2533,7 @@
 		 * This is a very suboptimal way of keeping track of
 		 * silence in the buffer, but it is simple.
 		 */
-		sc->sc_sil_count = 0;
+		vc->sc_sil_count = 0;
 
 		/*
 		 * If the interrupt routine wants the last block filled AND
@@ -2338,9 +2550,9 @@
 			cc = 0;
 		cb->needfill = false;
 		cb->copying = false;
-		if (!sc->sc_pbus && !cb->pause) {
+		if (!vc->sc_pbus && !cb->pause) {
 			saveerror = error;
-			error = audiostartp(sc);
+			error = audiostartp(sc, n);
 			if (saveerror != 0) {
 				/* Report the first error that occurred. */
 				error = saveerror;
@@ -2361,12 +2573,21 @@
 	    struct lwp *l)
 {
 	const struct audio_hw_if *hw;
+	struct virtual_channel *vc;
 	struct audio_offset *ao;
 	u_long stamp;
-	int error, offs, fd;
+	int error, offs, fd, n;
 	bool rbus, pbus;
 
 	KASSERT(mutex_owned(sc->sc_lock));
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n >= VAUDIOCHANS)
+		n = 1;
+
+	vc = sc->sc_vchan[n];
 
 	DPRINTF(("audio_ioctl(%lu,'%c',%lu)\n",
 		 IOCPARM_LEN(cmd), (char)IOCGROUP(cmd), cmd&0xff));
@@ -2396,18 +2617,18 @@
 	case AUDIO_FLUSH:
 		DPRINTF(("AUDIO_FLUSH\n"));
 		rbus = sc->sc_rbus;
-		pbus = sc->sc_pbus;
+		pbus = vc->sc_pbus;
 		mutex_enter(sc->sc_intr_lock);
-		audio_clear(sc);
-		error = audio_initbufs(sc);
+		audio_clear(sc, n);
+		error = audio_initbufs(sc, n);
 		if (error) {
 			mutex_exit(sc->sc_intr_lock);
 			return error;
 		}
-		if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_pbus && pbus)
-			error = audiostartp(sc);
+		if ((vc->sc_mode & AUMODE_PLAY) && !vc->sc_pbus && pbus)
+			error = audiostartp(sc, n);
 		if (!error &&
-		    (sc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus && rbus)
+		    (vc->sc_mode & AUMODE_RECORD) && !sc->sc_rbus && rbus)
 			error = audiostartr(sc);
 		mutex_exit(sc->sc_intr_lock);
 		break;
@@ -2421,7 +2642,7 @@
 		break;
 
 	case AUDIO_PERROR:
-		*(int *)addr = sc->sc_pr.drops;
+		*(int *)addr = vc->sc_mpr.drops;
 		break;
 
 	/*
@@ -2447,17 +2668,17 @@
 		ao = (struct audio_offset *)addr;
 		mutex_enter(sc->sc_intr_lock);
 		/* figure out where next DMA will start */
-		stamp = sc->sc_pustream == &sc->sc_pr.s
-			? sc->sc_pr.stamp : sc->sc_pr.fstamp;
-		offs = sc->sc_pustream->outp - sc->sc_pustream->start
-			+ sc->sc_pr.blksize;
+		stamp = vc->sc_pustream == &vc->sc_mpr.s
+			? vc->sc_mpr.stamp : vc->sc_mpr.fstamp;
+		offs = vc->sc_pustream->outp - vc->sc_pustream->start
+			+ vc->sc_mpr.blksize;
 		mutex_exit(sc->sc_intr_lock);
 		ao->samples = stamp;
 		ao->deltablks =
-		  (stamp / sc->sc_pr.blksize) -
-		  (sc->sc_pr.stamp_last / sc->sc_pr.blksize);
-		sc->sc_pr.stamp_last = stamp;
-		if (sc->sc_pustream->start + offs >= sc->sc_pustream->end)
+		  (stamp / vc->sc_mpr.blksize) -
+		  (vc->sc_mpr.stamp_last / vc->sc_mpr.blksize);
+		vc->sc_mpr.stamp_last = stamp;
+		if (vc->sc_pustream->start + offs >= vc->sc_pustream->end)
 			offs = 0;
 		ao->offset = offs;
 		break;
@@ -2467,29 +2688,29 @@
 	 * sample of what we write next?
 	 */
 	case AUDIO_WSEEK:
-		*(u_long *)addr = audio_stream_get_used(sc->sc_pustream);
+		*(u_long *)addr = audio_stream_get_used(vc->sc_pustream);
 		break;
 
 	case AUDIO_SETINFO:
-		DPRINTF(("AUDIO_SETINFO mode=0x%x\n", sc->sc_mode));
-		error = audiosetinfo(sc, (struct audio_info *)addr);
+		DPRINTF(("AUDIO_SETINFO mode=0x%x\n", vc->sc_mode));
+		error = audiosetinfo(sc, (struct audio_info *)addr, false, n);
 		break;
 
 	case AUDIO_GETINFO:
 		DPRINTF(("AUDIO_GETINFO\n"));
-		error = audiogetinfo(sc, (struct audio_info *)addr, 0);
+		error = audiogetinfo(sc, (struct audio_info *)addr, 0, n);
 		break;
 
 	case AUDIO_GETBUFINFO:
 		DPRINTF(("AUDIO_GETBUFINFO\n"));
-		error = audiogetinfo(sc, (struct audio_info *)addr, 1);
+		error = audiogetinfo(sc, (struct audio_info *)addr, 1, n);
 		break;
 
 	case AUDIO_DRAIN:
 		DPRINTF(("AUDIO_DRAIN\n"));
 		mutex_enter(sc->sc_intr_lock);
-		error = audio_drain(sc);
-		if (!error && hw->drain)
+		error = audio_drain(sc, n);
+		if (!error && sc->sc_opens == 1 && hw->drain)
 		    error = hw->drain(sc->hw_hdl);
 		mutex_exit(sc->sc_intr_lock);
 		break;
@@ -2501,13 +2722,13 @@
 
 	case AUDIO_GETENC:
 		DPRINTF(("AUDIO_GETENC\n"));
-		error = hw->query_encoding(sc->hw_hdl,
+		error = audio_query_encoding(sc,
 		    (struct audio_encoding *)addr);
 		break;
 
 	case AUDIO_GETFD:
 		DPRINTF(("AUDIO_GETFD\n"));
-		*(int *)addr = sc->sc_full_duplex;
+		*(int *)addr = vc->sc_full_duplex;
 		break;
 
 	case AUDIO_SETFD:
@@ -2519,7 +2740,7 @@
 			else
 				error = 0;
 			if (!error)
-				sc->sc_full_duplex = fd;
+				vc->sc_full_duplex = fd;
 		} else {
 			if (fd)
 				error = ENOTTY;
@@ -2550,12 +2771,20 @@
 int
 audio_poll(struct audio_softc *sc, int events, struct lwp *l)
 {
+	struct virtual_channel *vc;
 	int revents;
-	int used;
+	int used, n;
 
 	KASSERT(mutex_owned(sc->sc_lock));
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n == VAUDIOCHANS)
+		return ENXIO;
+	vc = sc->sc_vchan[n];
 
-	DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, sc->sc_mode));
+	DPRINTF(("audio_poll: events=0x%x mode=%d\n", events, vc->sc_mode));
 
 	revents = 0;
 	mutex_enter(sc->sc_intr_lock);
@@ -2566,23 +2795,23 @@
 		 * silence at the play rate; poll for silence being
 		 * available.  Otherwise, poll for recorded sound.
 		 */
-		if ((!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY)) ?
-		    sc->sc_pr.stamp > sc->sc_wstamp :
+		if ((!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY)) ?
+		    vc->sc_mpr.stamp > vc->sc_wstamp :
 		    used > sc->sc_rr.usedlow)
 			revents |= events & (POLLIN | POLLRDNORM);
 	}
 
 	if (events & (POLLOUT | POLLWRNORM)) {
-		used = audio_stream_get_used(sc->sc_pustream);
+		used = audio_stream_get_used(vc->sc_pustream);
 		/*
 		 * If half duplex and recording, audio_write() will throw
 		 * away play data, which means we are always ready to write.
 		 * Otherwise, poll for play buffer being below its low water
 		 * mark.
 		 */
-		if ((!sc->sc_full_duplex && (sc->sc_mode & AUMODE_RECORD)) ||
-		    (!(sc->sc_mode & AUMODE_PLAY_ALL) && sc->sc_playdrop > 0) ||
-		    (used <= sc->sc_pr.usedlow))
+		if ((!vc->sc_full_duplex && (vc->sc_mode & AUMODE_RECORD)) ||
+		    (!(vc->sc_mode & AUMODE_PLAY_ALL) && vc->sc_playdrop > 0) ||
+		    (used <= vc->sc_mpr.usedlow))
 			revents |= events & (POLLOUT | POLLWRNORM);
 	}
 	mutex_exit(sc->sc_intr_lock);
@@ -2613,11 +2842,21 @@
 filt_audioread(struct knote *kn, long hint)
 {
 	struct audio_softc *sc;
+	struct virtual_channel *vc;
+	int n;
 
 	sc = kn->kn_hook;
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n == VAUDIOCHANS)
+		return ENXIO;
+
+	vc = sc->sc_vchan[n];
 	mutex_enter(sc->sc_intr_lock);
-	if (!sc->sc_full_duplex && (sc->sc_mode & AUMODE_PLAY))
-		kn->kn_data = sc->sc_pr.stamp - sc->sc_wstamp;
+	if (!vc->sc_full_duplex && (vc->sc_mode & AUMODE_PLAY))
+		kn->kn_data = vc->sc_mpr.stamp - vc->sc_wstamp;
 	else
 		kn->kn_data = audio_stream_get_used(sc->sc_rustream)
 			- sc->sc_rr.usedlow;
@@ -2645,10 +2884,18 @@
 {
 	struct audio_softc *sc;
 	audio_stream_t *stream;
+	int n;
 
 	sc = kn->kn_hook;
 	mutex_enter(sc->sc_intr_lock);
-	stream = sc->sc_pustream;
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n == VAUDIOCHANS)
+		return ENXIO;
+
+	stream = sc->sc_vchan[n]->sc_pustream;
 	kn->kn_data = (stream->end - stream->start)
 		- audio_stream_get_used(stream);
 	mutex_exit(sc->sc_intr_lock);
@@ -2694,9 +2941,16 @@
 	const struct audio_hw_if *hw;
 	struct audio_ringbuffer *cb;
 	paddr_t rv;
+	int n;
 
 	KASSERT(mutex_owned(sc->sc_lock));
-	KASSERT(sc->sc_dvlock > 0);
+
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n == VAUDIOCHANS)
+		return -1;
 
 	DPRINTF(("audio_mmap: off=%lld, prot=%d\n", (long long)off, prot));
 	hw = sc->hw_if;
@@ -2717,26 +2971,26 @@
  */
 	if (prot == (VM_PROT_READ|VM_PROT_WRITE) ||
 	    prot == VM_PROT_WRITE)
-		cb = &sc->sc_pr;
+		cb = &sc->sc_vchan[n]->sc_mpr;
 	else if (prot == VM_PROT_READ)
 		cb = &sc->sc_rr;
 	else
 		return -1;
 #else
-	cb = &sc->sc_pr;
+	cb = &sc->sc_vchan[n]->sc_mpr;
 #endif
 
 	if ((u_int)off >= cb->s.bufsize)
 		return -1;
 	if (!cb->mmapped) {
 		cb->mmapped = true;
-		if (cb == &sc->sc_pr) {
+		if (cb != &sc->sc_rr) {
 			audio_fill_silence(&cb->s.param, cb->s.start,
 					   cb->s.bufsize);
 			mutex_enter(sc->sc_intr_lock);
-			sc->sc_pustream = &cb->s;
-			if (!sc->sc_pbus && !sc->sc_pr.pause)
-				(void)audiostartp(sc);
+			sc->sc_vchan[n]->sc_pustream = &cb->s;
+			if (!sc->sc_vchan[n]->sc_pbus && !sc->sc_vchan[n]->sc_mpr.pause)
+				(void)audiostartp(sc, n);
 			mutex_exit(sc->sc_intr_lock);
 		} else {
 			mutex_enter(sc->sc_intr_lock);
@@ -2785,45 +3039,38 @@
 }
 
 int
-audiostartp(struct audio_softc *sc)
+audiostartp(struct audio_softc *sc, int n)
 {
-	int error;
-	int used;
+	struct virtual_channel *vc = sc->sc_vchan[n];
+	int used, error;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 	KASSERT(mutex_owned(sc->sc_intr_lock));
 
-	used = audio_stream_get_used(&sc->sc_pr.s);
+	error = 0;
+	used = audio_stream_get_used(&vc->sc_mpr.s);
 	DPRINTF(("audiostartp: start=%p used=%d(hi=%d blk=%d) mmapped=%d\n",
-		 sc->sc_pr.s.start, used, sc->sc_pr.usedhigh,
-		 sc->sc_pr.blksize, sc->sc_pr.mmapped));
+		 vc->sc_mpr.s.start, used, vc->sc_mpr.usedhigh,
+		 vc->sc_mpr.blksize, vc->sc_mpr.mmapped));
 
 	if (!audio_can_playback(sc))
 		return EINVAL;
 
-	if (!sc->sc_pr.mmapped && used < sc->sc_pr.blksize) {
+	if (!vc->sc_mpr.mmapped && used < vc->sc_mpr.blksize) {
 		cv_broadcast(&sc->sc_wchan);
 		DPRINTF(("%s: wakeup and return\n", __func__));
 		return 0;
 	}
-
-	if (sc->hw_if->trigger_output) {
-		DPRINTF(("%s: call trigger_output\n", __func__));
-		error = sc->hw_if->trigger_output(sc->hw_hdl, sc->sc_pr.s.start,
-		    sc->sc_pr.s.end, sc->sc_pr.blksize,
-		    audio_pint, (void *)sc, &sc->sc_pr.s.param);
-	} else {
-		DPRINTF(("%s: call start_output\n", __func__));
-		error = sc->hw_if->start_output(sc->hw_hdl,
-		    __UNCONST(sc->sc_pr.s.outp), sc->sc_pr.blksize,
-		    audio_pint, (void *)sc);
-	}
-	if (error) {
-		DPRINTF(("audiostartp failed: %d\n", error));
-		return error;
+	
+	vc->sc_pbus = true;
+	mix_func(sc, &vc->sc_mpr, n);
+	if (sc->sc_trigger_started == false) {
+		mix_write(sc);
+		cv_broadcast(&sc->sc_condvar);
+		
 	}
-	sc->sc_pbus = true;
-	return 0;
+
+	return error;
 }
 
 /*
@@ -2843,15 +3090,16 @@
  */
 static inline void
 audio_pint_silence(struct audio_softc *sc, struct audio_ringbuffer *cb,
-		   uint8_t *inp, int cc)
+		   uint8_t *inp, int cc, int n)
 {
+	struct virtual_channel *vc = sc->sc_vchan[n];
 	uint8_t *s, *e, *p, *q;
 
-	KASSERT(mutex_owned(sc->sc_intr_lock));
+	KASSERT(mutex_owned(sc->sc_lock));
 
-	if (sc->sc_sil_count > 0) {
-		s = sc->sc_sil_start; /* start of silence */
-		e = s + sc->sc_sil_count; /* end of sil., may be beyond end */
+	if (vc->sc_sil_count > 0) {
+		s = vc->sc_sil_start; /* start of silence */
+		e = s + vc->sc_sil_count; /* end of sil., may be beyond end */
 		p = inp;	/* adjusted pointer to area to fill */
 		if (p < s)
 			p += cb->s.end - cb->s.start;
@@ -2860,10 +3108,10 @@
 		if (!(s <= p && p <  e &&
 		      s <= q && q <= e)) {
 			if (s <= p)
-				sc->sc_sil_count = max(sc->sc_sil_count, q-s);
+				vc->sc_sil_count = max(vc->sc_sil_count, q-s);
 			DPRINTFN(5,("audio_pint_silence: fill cc=%d inp=%p, "
 				    "count=%d size=%d\n",
-				    cc, inp, sc->sc_sil_count,
+				    cc, inp, vc->sc_sil_count,
 				    (int)(cb->s.end - cb->s.start)));
 			audio_fill_silence(&cb->s.param, inp, cc);
 		} else {
@@ -2872,8 +3120,8 @@
 
 		}
 	} else {
-		sc->sc_sil_start = inp;
-		sc->sc_sil_count = cc;
+		vc->sc_sil_start = inp;
+		vc->sc_sil_count = cc;
 		DPRINTFN(5, ("audio_pint_silence: start fill %p %d\n",
 			     inp, cc));
 		audio_fill_silence(&cb->s.param, inp, cc);
@@ -2929,136 +3177,180 @@
 void
 audio_pint(void *v)
 {
+	struct audio_softc *sc;
+	struct virtual_channel *vc;
+
+	sc = v;
+	vc = sc->sc_vchan[0];
+
+	if (sc->sc_dying == true)
+		return;
+
+	vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s,
+	    vc->sc_mpr.s.outp, vc->sc_mpr.blksize);
+	mix_write(sc);
+
+	cv_broadcast(&sc->sc_condvar);
+}
+
+void
+audio_mix(void *v)
+{
 	stream_fetcher_t null_fetcher;
 	struct audio_softc *sc;
-	const struct audio_hw_if *hw;
+	struct virtual_channel *vc;
 	struct audio_ringbuffer *cb;
 	stream_fetcher_t *fetcher;
 	uint8_t *inp;
 	int cc, used;
 	int blksize;
-	int error;
-
+	int n, i;
+	
 	sc = v;
 
-	KASSERT(mutex_owned(sc->sc_intr_lock));
-
-	if (!sc->sc_open)
-		return;		/* ignore interrupt if not open */
+	DPRINTF(("PINT\n"));
+	sc->schedule_rih = false;
+	sc->schedule_wih = false;
+	sc->sc_writeme = false;
 
-	hw = sc->hw_if;
-	cb = &sc->sc_pr;
-	blksize = cb->blksize;
-	cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize);
-	cb->stamp += blksize;
-	if (cb->mmapped) {
-		DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n",
-			     cb->s.outp, blksize, cb->s.inp));
-		if (hw->trigger_output == NULL)
-			(void)hw->start_output(sc->hw_hdl, __UNCONST(cb->s.outp),
-			    blksize, audio_pint, (void *)sc);
+	if (sc->sc_dying == true)
 		return;
-	}
+
+	i = sc->sc_opens;
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (!sc->sc_opens || i <= 0)
+			break;		/* ignore interrupt if not open */
+
+		if (sc->sc_audiopid[n] == -1)
+			continue;
+		i--;
+		vc = sc->sc_vchan[n];
+		if (!vc->sc_open)
+			continue;
+		if (!vc->sc_pbus)
+			continue;
+
+		sc->sc_writeme = true;
+
+		cb = &vc->sc_mpr;
+		blksize = cb->blksize;
+
+		cb->s.outp = audio_stream_add_outp(&cb->s, cb->s.outp, blksize);
+		cb->stamp += blksize;
+		if (cb->mmapped) {
+			DPRINTFN(5, ("audio_pint: mmapped outp=%p cc=%d inp=%p\n",
+				     cb->s.outp, blksize, cb->s.inp));
+			mix_func(sc, cb, n);
+			continue;
+		}
 
 #ifdef AUDIO_INTR_TIME
-	{
-		struct timeval tv;
-		u_long t;
-		microtime(&tv);
-		t = tv.tv_usec + 1000000 * tv.tv_sec;
-		if (sc->sc_pnintr) {
-			long lastdelta, totdelta;
-			lastdelta = t - sc->sc_plastintr - sc->sc_pblktime;
-			if (lastdelta > sc->sc_pblktime / 3) {
-				printf("audio: play interrupt(%d) off "
+		{
+			struct timeval tv;
+			u_long t;
+			microtime(&tv);
+			t = tv.tv_usec + 1000000 * tv.tv_sec;
+			if (sc->sc_pnintr) {
+				long lastdelta, totdelta;
+				lastdelta = t - sc->sc_plastintr - sc->sc_pblktime;
+				if (lastdelta > sc->sc_pblktime / 3) {
+					printf("audio: play interrupt(%d) off "
 				       "relative by %ld us (%lu)\n",
-				       sc->sc_pnintr, lastdelta,
-				       sc->sc_pblktime);
-			}
-			totdelta = t - sc->sc_pfirstintr -
-				sc->sc_pblktime * sc->sc_pnintr;
-			if (totdelta > sc->sc_pblktime) {
-				printf("audio: play interrupt(%d) off "
-				       "absolute by %ld us (%lu) (LOST)\n",
-				       sc->sc_pnintr, totdelta,
-				       sc->sc_pblktime);
-				sc->sc_pnintr++; /* avoid repeated messages */
-			}
-		} else
-			sc->sc_pfirstintr = t;
-		sc->sc_plastintr = t;
-		sc->sc_pnintr++;
-	}
+					       sc->sc_pnintr, lastdelta,
+					       sc->sc_pblktime);
+				}
+				totdelta = t - sc->sc_pfirstintr -
+					sc->sc_pblktime * sc->sc_pnintr;
+				if (totdelta > sc->sc_pblktime) {
+					printf("audio: play interrupt(%d) off "
+					       "absolute by %ld us (%lu) (LOST)\n",
+					       sc->sc_pnintr, totdelta,
+					       sc->sc_pblktime);
+					sc->sc_pnintr++; /* avoid repeated messages */
+				}
+			} else
+				sc->sc_pfirstintr = t;
+			sc->sc_plastintr = t;
+			sc->sc_pnintr++;
+		}
 #endif
 
-	used = audio_stream_get_used(&cb->s);
-	/*
-	 * "used <= cb->usedlow" should be "used < blksize" ideally.
-	 * Some HW drivers such as uaudio(4) does not call audio_pint()
-	 * at accurate timing.  If used < blksize, uaudio(4) already
-	 * request transfer of garbage data.
-	 */
-	if (used <= cb->usedlow && !cb->copying && sc->sc_npfilters > 0) {
-		/* we might have data in filter pipeline */
-		null_fetcher.fetch_to = null_fetcher_fetch_to;
-		fetcher = &sc->sc_pfilters[sc->sc_npfilters - 1]->base;
-		sc->sc_pfilters[0]->set_fetcher(sc->sc_pfilters[0],
-						&null_fetcher);
-		used = audio_stream_get_used(sc->sc_pustream);
-		cc = cb->s.end - cb->s.start;
-		if (blksize * 2 < cc)
-			cc = blksize * 2;
-		fetcher->fetch_to(sc, fetcher, &cb->s, cc);
-		cb->fstamp += used - audio_stream_get_used(sc->sc_pustream);
 		used = audio_stream_get_used(&cb->s);
-	}
-	if (used < blksize) {
-		/* we don't have a full block to use */
-		if (cb->copying) {
-			/* writer is in progress, don't disturb */
-			cb->needfill = true;
-			DPRINTFN(1, ("audio_pint: copying in progress\n"));
-		} else {
-			inp = cb->s.inp;
-			cc = blksize - (inp - cb->s.start) % blksize;
-			if (cb->pause)
-				cb->pdrops += cc;
-			else {
-				cb->drops += cc;
-				sc->sc_playdrop += cc;
-			}
-			audio_pint_silence(sc, cb, inp, cc);
-			cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc);
-
-			/* Clear next block so we keep ahead of the DMA. */
+		/*
+		 * "used <= cb->usedlow" should be "used < blksize" ideally.
+		 * Some HW drivers such as uaudio(4) does not call audio_pint()
+		 * at accurate timing.  If used < blksize, uaudio(4) already
+		 * request transfer of garbage data.
+		 */
+		if (used <= cb->usedlow && !cb->copying && vc->sc_npfilters > 0) {
+			/* we might have data in filter pipeline */
+			null_fetcher.fetch_to = null_fetcher_fetch_to;
+			fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
+			vc->sc_pfilters[0]->set_fetcher(vc->sc_pfilters[0],
+							&null_fetcher);
+			used = audio_stream_get_used(vc->sc_pustream);
+			cc = cb->s.end - cb->s.start;
+			if (blksize * 2 < cc)
+				cc = blksize * 2;
+			fetcher->fetch_to(sc, fetcher, &cb->s, cc);
+			cb->fstamp += used - audio_stream_get_used(vc->sc_pustream);
 			used = audio_stream_get_used(&cb->s);
-			if (used + blksize < cb->s.end - cb->s.start)
-				audio_pint_silence(sc, cb, cb->s.inp, blksize);
 		}
-	}
+		if (used < blksize) {
+			/* we don't have a full block to use */
+			if (cb->copying) {
+				/* writer is in progress, don't disturb */
+				cb->needfill = true;
+				DPRINTFN(1, ("audio_pint: copying in progress\n"));
+			} else {
+				inp = cb->s.inp;
+				cc = blksize - (inp - cb->s.start) % blksize;
+				if (cb->pause)
+					cb->pdrops += cc;
+				else {
+					cb->drops += cc;
+					vc->sc_playdrop += cc;
+				}
 
-	DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->s.outp, blksize));
-	if (hw->trigger_output == NULL) {
-		error = hw->start_output(sc->hw_hdl, __UNCONST(cb->s.outp),
-		    blksize, audio_pint, (void *)sc);
-		if (error) {
-			/* XXX does this really help? */
-			DPRINTF(("audio_pint restart failed: %d\n", error));
-			audio_clear(sc);
+				audio_pint_silence(sc, cb, inp, cc, n);
+				cb->s.inp = audio_stream_add_inp(&cb->s, inp, cc);
+
+				/* Clear next block so we keep ahead of the DMA. */
+				used = audio_stream_get_used(&cb->s);
+				if (used + blksize < cb->s.end - cb->s.start)
+					audio_pint_silence(sc, cb, cb->s.inp, blksize, n);
+			}
 		}
-	}
 
-	DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n",
-		     sc->sc_mode, cb->pause,
-		     audio_stream_get_used(sc->sc_pustream), cb->usedlow));
-	if ((sc->sc_mode & AUMODE_PLAY) && !cb->pause) {
-		if (audio_stream_get_used(sc->sc_pustream) <= cb->usedlow)
-			softint_schedule(sc->sc_sih_wr);
+		DPRINTFN(5, ("audio_pint: outp=%p cc=%d\n", cb->s.outp, blksize));
+		mix_func(sc, cb, n);
+
+		DPRINTFN(2, ("audio_pint: mode=%d pause=%d used=%d lowat=%d\n",
+			     sc->sc_mode[n], cb->pause,
+			     audio_stream_get_used(vc->sc_pustream), cb->usedlow));
+
+		if ((vc->sc_mode & AUMODE_PLAY) && !cb->pause) {
+			if (audio_stream_get_used(&cb->s) <= cb->usedlow)
+				sc->schedule_wih = true;
+		}
+		/* Possible to return one or more "phantom blocks" now. */
+		if (!vc->sc_full_duplex && vc->sc_mode & AUMODE_RECORD)
+				sc->schedule_rih = true;
 	}
 
-	/* Possible to return one or more "phantom blocks" now. */
-	if (!sc->sc_full_duplex)
+	kpreempt_disable();
+	if (sc->schedule_wih == true)
+		softint_schedule(sc->sc_sih_wr);
+
+	if (sc->schedule_rih == true)
 		softint_schedule(sc->sc_sih_rd);
+	kpreempt_enable();
+
+	cc = sc->sc_vchan[0]->sc_mpr.blksize;
+	if (sc->sc_writeme == false) {
+		sc->sc_vchan[0]->sc_mpr.drops += cc;
+		cv_broadcast(&sc->sc_wchan);
+	}
 }
 
 /*
@@ -3083,8 +3375,7 @@
 	cb = &sc->sc_rr;
 
 	KASSERT(mutex_owned(sc->sc_intr_lock));
-
-	if (!sc->sc_open)
+	if (!sc->sc_opens)
 		return;		/* ignore interrupt if not open */
 
 	hw = sc->hw_if;
@@ -3162,7 +3453,7 @@
 		if (error) {
 			/* XXX does this really help? */
 			DPRINTF(("audio_rint: restart failed: %d\n", error));
-			audio_clear(sc);
+			audio_clear(sc, 0);
 		}
 	}
 
@@ -3242,17 +3533,66 @@
 	return 0;
 }
 
+static int
+audio_set_vchan_defaults(struct audio_softc *sc, u_int mode,
+     const struct audio_format *format, int n)
+{
+	struct virtual_channel *vc = sc->sc_vchan[n];
+	struct audio_info ai;
+	int i, error;
+
+	KASSERT(mutex_owned(sc->sc_lock));
+
+	/* default parameters */
+	sc->sc_rparams = vchan_default;
+	vc->sc_pparams = vchan_default;
+	vc->sc_blkset = false;
+
+	AUDIO_INITINFO(&ai);
+	ai.record.sample_rate = vc->sc_pparams.sample_rate;
+	ai.record.encoding    = vc->sc_pparams.encoding;
+	ai.record.channels    = vc->sc_pparams.channels;
+	ai.record.precision   = vc->sc_pparams.precision;
+	ai.record.pause	      = false;
+	ai.play.sample_rate   = format->frequency[0];
+	ai.play.encoding      = format->encoding;
+	ai.play.channels      = format->channels;
+	ai.play.precision     = format->precision;
+	ai.play.pause         = false;
+	ai.mode		      = mode;
+
+	error = audiosetinfo(sc, &ai, true, n);
+	if (vc->sc_npfilters > 0) {
+		ai.play.sample_rate = vc->sc_pstreams[vc->sc_npfilters -1].param.sample_rate;
+		ai.record.sample_rate =
+		    vc->sc_pstreams[vc->sc_npfilters -1].param.sample_rate;
+		vc->sc_pparams.sample_rate = ai.play.sample_rate;
+		sc->sc_rparams.sample_rate = ai.record.sample_rate;
+
+		for (i = 0; i < vc->sc_npfilters; i++) {
+			vc->sc_pfilters[i]->dtor(vc->sc_pfilters[i]);
+			vc->sc_pfilters[i] = NULL;
+			audio_stream_dtor(&vc->sc_pstreams[i]);
+		}
+		vc->sc_npfilters = 0;
+
+		return audiosetinfo(sc, &ai, true, n);
+	} else
+		return error;
+}
+
 int
-audio_set_defaults(struct audio_softc *sc, u_int mode)
+audio_set_defaults(struct audio_softc *sc, u_int mode, int n)
 {
+	struct virtual_channel *vc = sc->sc_vchan[n];
 	struct audio_info ai;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
 	/* default parameters */
 	sc->sc_rparams = audio_default;
-	sc->sc_pparams = audio_default;
-	sc->sc_blkset = false;
+	vc->sc_pparams = audio_default;
+	vc->sc_blkset = false;
 
 	AUDIO_INITINFO(&ai);
 	ai.record.sample_rate = sc->sc_rparams.sample_rate;
@@ -3260,14 +3600,14 @@
 	ai.record.channels    = sc->sc_rparams.channels;
 	ai.record.precision   = sc->sc_rparams.precision;
 	ai.record.pause	      = false;
-	ai.play.sample_rate   = sc->sc_pparams.sample_rate;
-	ai.play.encoding      = sc->sc_pparams.encoding;
-	ai.play.channels      = sc->sc_pparams.channels;
-	ai.play.precision     = sc->sc_pparams.precision;
+	ai.play.sample_rate   = vc->sc_pparams.sample_rate;
+	ai.play.encoding      = vc->sc_pparams.encoding;
+	ai.play.channels      = vc->sc_pparams.channels;
+	ai.play.precision     = vc->sc_pparams.precision;
 	ai.play.pause         = false;
 	ai.mode		      = mode;
 
-	return audiosetinfo(sc, &ai);
+	return audiosetinfo(sc, &ai, true, n);
 }
 
 int
@@ -3280,11 +3620,11 @@
 	ct->un.value.num_channels = 2;
 	ct->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
 	ct->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
-	if (sc->hw_if->set_port(sc->hw_hdl, ct) == 0)
+	if (audio_set_port(sc, ct) == 0)
 		return 0;
 	ct->un.value.num_channels = 1;
 	ct->un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
-	return sc->hw_if->set_port(sc->hw_hdl, ct);
+	return audio_set_port(sc, ct);
 }
 
 int
@@ -3322,7 +3662,7 @@
 		ct.dev = ports->index;
 		if (ports->isenum) {
 			ct.type = AUDIO_MIXER_ENUM;
-			error = sc->hw_if->get_port(sc->hw_hdl, &ct);
+			error = audio_get_port(sc, &ct);
 			if (error)
 				return error;
 			if (ports->isdual) {
@@ -3344,7 +3684,7 @@
 			}
 		} else {
 			ct.type = AUDIO_MIXER_SET;
-			error = sc->hw_if->get_port(sc->hw_hdl, &ct);
+			error = audio_get_port(sc, &ct);
 			if (error)
 				return error;
 			mask = ct.un.mask;
@@ -3374,12 +3714,12 @@
 	KASSERT(mutex_owned(sc->sc_lock));
 
 	ct->un.value.num_channels = 2;
-	if (sc->hw_if->get_port(sc->hw_hdl, ct) == 0) {
+	if (audio_get_port(sc, ct) == 0) {
 		*l = ct->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
 		*r = ct->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
 	} else {
 		ct->un.value.num_channels = 1;
-		error = sc->hw_if->get_port(sc->hw_hdl, ct);
+		error = audio_get_port(sc, ct);
 		if (error)
 			return error;
 		*r = *l = ct->un.value.level[AUDIO_MIXER_LEVEL_MONO];
@@ -3411,7 +3751,7 @@
 		ct.dev = ports->index;
 		if (ports->isenum) {
 			ct.type = AUDIO_MIXER_ENUM;
-			if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+			if (audio_get_port(sc, &ct))
 				goto bad;
 			ct.type = AUDIO_MIXER_VALUE;
 			if (ports->isdual) {
@@ -3434,7 +3774,7 @@
 			}
 		} else {
 			ct.type = AUDIO_MIXER_SET;
-			if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+			if (audio_get_port(sc, &ct))
 				goto bad;
 			ct.type = AUDIO_MIXER_VALUE;
 			lgain = rgain = n = 0;
@@ -3511,7 +3851,7 @@
 				} else {
 					ct.un.ord = ports->misel[i];
 				}
-				error = sc->hw_if->set_port(sc->hw_hdl, &ct);
+				error = audio_set_port(sc, &ct);
 				break;
 			}
 	} else {
@@ -3523,7 +3863,7 @@
 		if (port != 0 && ct.un.mask == 0)
 			error = EINVAL;
 		else
-			error = sc->hw_if->set_port(sc->hw_hdl, &ct);
+			error = audio_set_port(sc, &ct);
 	}
 	if (!error)
 		mixer_signal(sc);
@@ -3542,7 +3882,7 @@
 		return 0;
 	ct.dev = ports->index;
 	ct.type = ports->isenum ? AUDIO_MIXER_ENUM : AUDIO_MIXER_SET;
-	if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+	if (audio_get_port(sc, &ct))
 		return 0;
 	aumask = 0;
 	if (ports->isenum) {
@@ -3565,12 +3905,13 @@
 }
 
 int
-audiosetinfo(struct audio_softc *sc, struct audio_info *ai)
+audiosetinfo(struct audio_softc *sc, struct audio_info *ai, bool reset, int n)
 {
 	stream_filter_list_t pfilters, rfilters;
 	audio_params_t pp, rp;
 	struct audio_prinfo *r, *p;
 	const struct audio_hw_if *hw;
+	struct virtual_channel *vc;
 	audio_stream_t *oldpus, *oldrus;
 	int setmode;
 	int error;
@@ -3584,6 +3925,7 @@
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
+	vc = sc->sc_vchan[n];
 	hw = sc->hw_if;
 	if (hw == NULL)		/* HW has not attached */
 		return ENXIO;
@@ -3592,13 +3934,13 @@
 	r = &ai->record;
 	p = &ai->play;
 	rbus = sc->sc_rbus;
-	pbus = sc->sc_pbus;
+	pbus = vc->sc_pbus;
 	error = 0;
 	cleared = false;
 	modechange = false;
 	pausechange = false;
 
-	pp = sc->sc_pparams;	/* Temporary encoding storage in */
+	pp = vc->sc_pparams;	/* Temporary encoding storage in */
 	rp = sc->sc_rparams;	/* case setting the modes fails. */
 	nr = np = 0;
 
@@ -3655,13 +3997,13 @@
 	if (np > 0 && (error = audio_check_params(&pp)))
 		return error;
 
-	oldpblksize = sc->sc_pr.blksize;
+	oldpblksize = vc->sc_mpr.blksize;
 	oldrblksize = sc->sc_rr.blksize;
 
 	setmode = 0;
 	if (nr > 0) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc);
+			audio_clear_intr_unlocked(sc, n);
 			cleared = true;
 		}
 		modechange = true;
@@ -3669,7 +4011,7 @@
 	}
 	if (np > 0) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc);
+			audio_clear_intr_unlocked(sc, n);
 			cleared = true;
 		}
 		modechange = true;
@@ -3678,19 +4020,19 @@
 
 	if (SPECIFIED(ai->mode)) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc);
+			audio_clear_intr_unlocked(sc, n);
 			cleared = true;
 		}
 		modechange = true;
-		sc->sc_mode = ai->mode;
-		if (sc->sc_mode & AUMODE_PLAY_ALL)
-			sc->sc_mode |= AUMODE_PLAY;
-		if ((sc->sc_mode & AUMODE_PLAY) && !sc->sc_full_duplex)
+		vc->sc_mode = ai->mode;
+		if (vc->sc_mode & AUMODE_PLAY_ALL)
+			vc->sc_mode |= AUMODE_PLAY;
+		if ((vc->sc_mode & AUMODE_PLAY) && !vc->sc_full_duplex)
 			/* Play takes precedence */
-			sc->sc_mode &= ~AUMODE_RECORD;
+			vc->sc_mode &= ~AUMODE_RECORD;
 	}
 
-	oldpus = sc->sc_pustream;
+	oldpus = vc->sc_pustream;
 	oldrus = sc->sc_rustream;
 	if (modechange) {
 		int indep;
@@ -3712,12 +4054,12 @@
 		rfilters.set = stream_filter_list_set;
 		/* Some device drivers change channels/sample_rate and change
 		 * no channels/sample_rate. */
-		error = hw->set_params(sc->hw_hdl, setmode,
-		    sc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp,
-		    &pfilters, &rfilters);
+		error = audio_set_params(sc, setmode,
+		    vc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD), &pp, &rp,
+		    &pfilters, &rfilters, n);
 		if (error) {
-			DPRINTF(("%s: hw->set_params() failed with %d\n",
-				 __func__, error));
+			DPRINTF(("%s: audio_set_params() failed with %d\n",
+			    __func__, error));
 			goto cleanup;
 		}
 
@@ -3733,7 +4075,7 @@
 			}
 		}
 
-		if (sc->sc_pr.mmapped && pfilters.req_size > 0) {
+		if (vc->sc_mpr.mmapped && pfilters.req_size > 0) {
 			DPRINTF(("%s: mmapped, and filters are requested.\n",
 				 __func__));
 			error = EINVAL;
@@ -3742,7 +4084,7 @@
 
 		/* construct new filter chain */
 		if (setmode & AUMODE_PLAY) {
-			error = audio_setup_pfilters(sc, &pp, &pfilters);
+			error = audio_setup_pfilters(sc, &pp, &pfilters, n);
 			if (error)
 				goto cleanup;
 		}
@@ -3754,25 +4096,29 @@
 		DPRINTF(("%s: filter setup is completed.\n", __func__));
 
 		/* userland formats */
-		sc->sc_pparams = pp;
+		vc->sc_pparams = pp;
 		sc->sc_rparams = rp;
 	}
 
 	/* Play params can affect the record params, so recalculate blksize. */
 	if (nr > 0 || np > 0) {
-		audio_calc_blksize(sc, AUMODE_RECORD);
-		audio_calc_blksize(sc, AUMODE_PLAY);
+		audio_calc_blksize(sc, AUMODE_RECORD, n);
+		audio_calc_blksize(sc, AUMODE_PLAY, n);
 	}
 #ifdef AUDIO_DEBUG
-	if (audiodebug > 1 && nr > 0)
-	    audio_print_params("audiosetinfo() After setting record params:", &sc->sc_rparams);
-	if (audiodebug > 1 && np > 0)
-	    audio_print_params("audiosetinfo() After setting play params:", &sc->sc_pparams);
+	if (audiodebug > 1 && nr > 0) {
+		audio_print_params("audiosetinfo() After setting record params:",
+		    &sc->sc_rparams);
+	}
+	if (audiodebug > 1 && np > 0) {
+		audio_print_params("audiosetinfo() After setting play params:",
+		    &vc->sc_pparams);
+	}
 #endif
 
 	if (SPECIFIED(p->port)) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc);
+			audio_clear_intr_unlocked(sc, n);
 			cleared = true;
 		}
 		error = au_set_port(sc, &sc->sc_outports, p->port);
@@ -3781,7 +4127,7 @@
 	}
 	if (SPECIFIED(r->port)) {
 		if (!cleared) {
-			audio_clear_intr_unlocked(sc);
+			audio_clear_intr_unlocked(sc, n);
 			cleared = true;
 		}
 		error = au_set_port(sc, &sc->sc_inports, r->port);
@@ -3821,13 +4167,13 @@
 		ct.type = AUDIO_MIXER_VALUE;
 		ct.un.value.num_channels = 1;
 		ct.un.value.level[AUDIO_MIXER_LEVEL_MONO] = ai->monitor_gain;
-		error = sc->hw_if->set_port(sc->hw_hdl, &ct);
+		error = audio_set_port(sc, &ct);
 		if (error)
 			goto cleanup;
 	}
 
 	if (SPECIFIED_CH(p->pause)) {
-		sc->sc_pr.pause = p->pause;
+		vc->sc_mpr.pause = p->pause;
 		pbus = !p->pause;
 		pausechange = true;
 	}
@@ -3843,34 +4189,34 @@
 		/* Block size specified explicitly. */
 		if (ai->blocksize == 0) {
 			if (!cleared) {
-				audio_clear_intr_unlocked(sc);
+				audio_clear_intr_unlocked(sc, n);
 				cleared = true;
 			}
-			sc->sc_blkset = false;
-			audio_calc_blksize(sc, AUMODE_RECORD);
-			audio_calc_blksize(sc, AUMODE_PLAY);
+			vc->sc_blkset = false;
+			audio_calc_blksize(sc, AUMODE_RECORD, n);
+			audio_calc_blksize(sc, AUMODE_PLAY, n);
 		} else {
-			sc->sc_blkset = true;
+			vc->sc_blkset = true;
 			/* check whether new blocksize changes actually */
 			if (hw->round_blocksize == NULL) {
 				if (!cleared) {
-					audio_clear_intr_unlocked(sc);
+					audio_clear_intr_unlocked(sc, n);
 					cleared = true;
 				}
-				sc->sc_pr.blksize = ai->blocksize;
+				vc->sc_mpr.blksize = ai->blocksize;
 				sc->sc_rr.blksize = ai->blocksize;
 			} else {
 				pblksize = hw->round_blocksize(sc->hw_hdl,
-				    ai->blocksize, AUMODE_PLAY, &sc->sc_pr.s.param);
+				    ai->blocksize, AUMODE_PLAY, &vc->sc_mpr.s.param);
 				rblksize = hw->round_blocksize(sc->hw_hdl,
 				    ai->blocksize, AUMODE_RECORD, &sc->sc_rr.s.param);
-				if (pblksize != sc->sc_pr.blksize ||
+				if (pblksize != vc->sc_mpr.blksize ||
 				    rblksize != sc->sc_rr.blksize) {
 					if (!cleared) {
-						audio_clear_intr_unlocked(sc);
+						audio_clear_intr_unlocked(sc, n);
 						cleared = true;
 					}
-					sc->sc_pr.blksize = ai->blocksize;
+					vc->sc_mpr.blksize = ai->blocksize;
 					sc->sc_rr.blksize = ai->blocksize;
 				}
 			}
@@ -3878,38 +4224,38 @@
 	}
 
 	if (SPECIFIED(ai->mode)) {
-		if (sc->sc_mode & AUMODE_PLAY)
-			audio_init_play(sc);
-		if (sc->sc_mode & AUMODE_RECORD)
-			audio_init_record(sc);
+		if (vc->sc_mode & AUMODE_PLAY)
+			audio_init_play(sc, n);
+		if (vc->sc_mode & AUMODE_RECORD)
+			audio_init_record(sc, n);
 	}
 
-	if (hw->commit_settings) {
+	if (hw->commit_settings && sc->sc_opens == 0) {
 		error = hw->commit_settings(sc->hw_hdl);
 		if (error)
 			goto cleanup;
 	}
 
-	sc->sc_lastinfo = *ai;
-	sc->sc_lastinfovalid = true;
+	vc->sc_lastinfo = *ai;
+	vc->sc_lastinfovalid = true;
 
 cleanup:
-	if (cleared || pausechange) {
+	if (cleared || pausechange|| reset) {
 		int init_error;
 
 		mutex_enter(sc->sc_intr_lock);
-		init_error = audio_initbufs(sc);
+		init_error = audio_initbufs(sc, n);
 		if (init_error) goto err;
-		if (sc->sc_pr.blksize != oldpblksize ||
+		if (vc->sc_mpr.blksize != oldpblksize ||
 		    sc->sc_rr.blksize != oldrblksize ||
-		    sc->sc_pustream != oldpus ||
+		    vc->sc_pustream != oldpus ||
 		    sc->sc_rustream != oldrus)
-			audio_calcwater(sc);
-		if ((sc->sc_mode & AUMODE_PLAY) &&
-		    pbus && !sc->sc_pbus)
-			init_error = audiostartp(sc);
+			audio_calcwater(sc, n);
+		if ((vc->sc_mode & AUMODE_PLAY) &&
+		    pbus && !vc->sc_pbus)
+			init_error = audiostartp(sc, n);
 		if (!init_error &&
-		    (sc->sc_mode & AUMODE_RECORD) &&
+		    (vc->sc_mode & AUMODE_RECORD) &&
 		    rbus && !sc->sc_rbus)
 			init_error = audiostartr(sc);
 	err:
@@ -3921,48 +4267,51 @@
 	/* Change water marks after initializing the buffers. */
 	if (SPECIFIED(ai->hiwat)) {
 		blks = ai->hiwat;
-		if (blks > sc->sc_pr.maxblks)
-			blks = sc->sc_pr.maxblks;
+		if (blks > vc->sc_mpr.maxblks)
+			blks = vc->sc_mpr.maxblks;
 		if (blks < 2)
 			blks = 2;
-		sc->sc_pr.usedhigh = blks * sc->sc_pr.blksize;
+		vc->sc_mpr.usedhigh = blks * vc->sc_mpr.blksize;
 	}
 	if (SPECIFIED(ai->lowat)) {
 		blks = ai->lowat;
-		if (blks > sc->sc_pr.maxblks - 1)
-			blks = sc->sc_pr.maxblks - 1;
-		sc->sc_pr.usedlow = blks * sc->sc_pr.blksize;
+		if (blks > vc->sc_mpr.maxblks - 1)
+			blks = vc->sc_mpr.maxblks - 1;
+		vc->sc_mpr.usedlow = blks * vc->sc_mpr.blksize;
 	}
 	if (SPECIFIED(ai->hiwat) || SPECIFIED(ai->lowat)) {
-		if (sc->sc_pr.usedlow > sc->sc_pr.usedhigh - sc->sc_pr.blksize)
-			sc->sc_pr.usedlow =
-				sc->sc_pr.usedhigh - sc->sc_pr.blksize;
+		if (vc->sc_mpr.usedlow > vc->sc_mpr.usedhigh - vc->sc_mpr.blksize)
+			vc->sc_mpr.usedlow =
+				vc->sc_mpr.usedhigh - vc->sc_mpr.blksize;
 	}
 
 	return error;
 }
 
 int
-audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int buf_only_mode)
+audiogetinfo(struct audio_softc *sc, struct audio_info *ai, int buf_only_mode,
+    int n)
 {
 	struct audio_prinfo *r, *p;
 	const struct audio_hw_if *hw;
+	struct virtual_channel *vc;
 
 	KASSERT(mutex_owned(sc->sc_lock));
 
 	r = &ai->record;
 	p = &ai->play;
+	vc = sc->sc_vchan[n];
 	hw = sc->hw_if;
 	if (hw == NULL)		/* HW has not attached */
 		return ENXIO;
 
-	p->sample_rate = sc->sc_pparams.sample_rate;
+	p->sample_rate = vc->sc_pparams.sample_rate;
 	r->sample_rate = sc->sc_rparams.sample_rate;
-	p->channels = sc->sc_pparams.channels;
+	p->channels = vc->sc_pparams.channels;
 	r->channels = sc->sc_rparams.channels;
-	p->precision = sc->sc_pparams.precision;
+	p->precision = vc->sc_pparams.precision;
 	r->precision = sc->sc_rparams.precision;
-	p->encoding = sc->sc_pparams.encoding;
+	p->encoding = vc->sc_pparams.encoding;
 	r->encoding = sc->sc_rparams.encoding;
 
 	if (buf_only_mode) {
@@ -3984,7 +4333,7 @@
 		r->avail_ports = sc->sc_inports.allports;
 		p->avail_ports = sc->sc_outports.allports;
 
-		au_get_gain(sc, &sc->sc_inports,  &r->gain, &r->balance);
+		au_get_gain(sc, &sc->sc_inports, &r->gain, &r->balance);
 		au_get_gain(sc, &sc->sc_outports, &p->gain, &p->balance);
 	}
 
@@ -3994,7 +4343,7 @@
 		ct.dev = sc->sc_monitor_port;
 		ct.type = AUDIO_MIXER_VALUE;
 		ct.un.value.num_channels = 1;
-		if (sc->hw_if->get_port(sc->hw_hdl, &ct))
+		if (audio_get_port(sc, &ct))
 			ai->monitor_gain = 0;
 		else
 			ai->monitor_gain =
@@ -4002,45 +4351,45 @@
 	} else
 		ai->monitor_gain = 0;
 
-	p->seek = audio_stream_get_used(sc->sc_pustream);
+	p->seek = audio_stream_get_used(vc->sc_pustream);
 	r->seek = audio_stream_get_used(sc->sc_rustream);
 
 	/*
 	 * XXX samples should be a value for userland data.
 	 * But drops is a value for HW data.
 	 */
-	p->samples = (sc->sc_pustream == &sc->sc_pr.s
-		      ? sc->sc_pr.stamp : sc->sc_pr.fstamp) - sc->sc_pr.drops;
+	p->samples = (vc->sc_pustream == &vc->sc_mpr.s
+		      ? vc->sc_mpr.stamp : vc->sc_mpr.fstamp) - vc->sc_mpr.drops;
 	r->samples = (sc->sc_rustream == &sc->sc_rr.s
 		      ? sc->sc_rr.stamp : sc->sc_rr.fstamp) - sc->sc_rr.drops;
 
 	p->eof = sc->sc_eof;
 	r->eof = 0;
 
-	p->pause = sc->sc_pr.pause;
+	p->pause = vc->sc_mpr.pause;
 	r->pause = sc->sc_rr.pause;
 
-	p->error = sc->sc_pr.drops != 0;
+	p->error = vc->sc_mpr.drops != 0;
 	r->error = sc->sc_rr.drops != 0;
 
 	p->waiting = r->waiting = 0;		/* open never hangs */
 
-	p->open = (sc->sc_open & AUOPEN_WRITE) != 0;
-	r->open = (sc->sc_open & AUOPEN_READ) != 0;
+	p->open = (vc->sc_open & AUOPEN_WRITE) != 0;
+	r->open = (vc->sc_open & AUOPEN_READ) != 0;
 
-	p->active = sc->sc_pbus;
+	p->active = vc->sc_pbus;
 	r->active = sc->sc_rbus;
 
-	p->buffer_size = sc->sc_pustream ? sc->sc_pustream->bufsize : 0;
+	p->buffer_size = vc->sc_pustream ? vc->sc_pustream->bufsize : 0;
 	r->buffer_size = sc->sc_rustream ? sc->sc_rustream->bufsize : 0;
 
-	ai->blocksize = sc->sc_pr.blksize;
-	if (sc->sc_pr.blksize > 0) {
-		ai->hiwat = sc->sc_pr.usedhigh / sc->sc_pr.blksize;
-		ai->lowat = sc->sc_pr.usedlow / sc->sc_pr.blksize;
+	ai->blocksize = vc->sc_mpr.blksize;
+	if (vc->sc_mpr.blksize > 0) {
+		ai->hiwat = vc->sc_mpr.usedhigh / vc->sc_mpr.blksize;
+		ai->lowat = vc->sc_mpr.usedlow / vc->sc_mpr.blksize;
 	} else
 		ai->hiwat = ai->lowat = 0;
-	ai->mode = sc->sc_mode;
+	ai->mode = vc->sc_mode;
 
 	return 0;
 }
@@ -4162,7 +4511,7 @@
 	case AUDIO_MIXER_DEVINFO:
 		DPRINTF(("AUDIO_MIXER_DEVINFO\n"));
 		((mixer_devinfo_t *)addr)->un.v.delta = 0; /* default */
-		error = hw->query_devinfo(sc->hw_hdl, (mixer_devinfo_t *)addr);
+		error = audio_query_devinfo(sc, (mixer_devinfo_t *)addr);
 		break;
 
 	case AUDIO_MIXER_READ:
@@ -4170,7 +4519,7 @@
 		mc = (mixer_ctrl_t *)addr;
 
 		if (device_is_active(sc->sc_dev))
-			error = hw->get_port(sc->hw_hdl, mc);
+			error = audio_get_port(sc, mc);
 		else if (mc->dev >= sc->sc_nmixer_states)
 			error = ENXIO;
 		else {
@@ -4183,7 +4532,7 @@
 
 	case AUDIO_MIXER_WRITE:
 		DPRINTF(("AUDIO_MIXER_WRITE\n"));
-		error = hw->set_port(sc->hw_hdl, (mixer_ctrl_t *)addr);
+		error = audio_set_port(sc, (mixer_ctrl_t *)addr);
 		if (!error && hw->commit_settings)
 			error = hw->commit_settings(sc->hw_hdl);
 		if (!error)
@@ -4263,7 +4612,7 @@
 	KASSERT(mutex_owned(sc->sc_lock));
 
 	for (mi.index = 0;; mi.index++) {
-		if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) != 0)
+		if (audio_query_devinfo(sc, &mi) != 0)
 			break;
 		KASSERT(mi.index < sc->sc_nmixer_states);
 		if (mi.type == AUDIO_MIXER_CLASS)
@@ -4272,7 +4621,7 @@
 		mc->dev = mi.index;
 		mc->type = mi.type;
 		mc->un.value.num_channels = mi.un.v.num_channels;
-		(void)sc->hw_if->get_port(sc->hw_hdl, mc);
+		(void)audio_get_port(sc, mc);
 	}
 
 	return;
@@ -4287,12 +4636,12 @@
 	KASSERT(mutex_owned(sc->sc_lock));
 
 	for (mi.index = 0; ; mi.index++) {
-		if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) != 0)
+		if (audio_query_devinfo(sc, &mi) != 0)
 			break;
 		if (mi.type == AUDIO_MIXER_CLASS)
 			continue;
 		mc = &sc->sc_mixer_state[mi.index];
-		(void)sc->hw_if->set_port(sc->hw_hdl, mc);
+		(void)audio_set_port(sc, mc);
 	}
 	if (sc->hw_if->commit_settings)
 		sc->hw_if->commit_settings(sc->hw_hdl);
@@ -4347,11 +4696,19 @@
 {
 	struct audio_softc *sc = device_private(dv);
 	const struct audio_hw_if *hwp = sc->hw_if;
+	int n;
 
 	mutex_enter(sc->sc_lock);
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	if (n == VAUDIOCHANS)
+		return false;
+
 	audio_mixer_capture(sc);
 	mutex_enter(sc->sc_intr_lock);
-	if (sc->sc_pbus == true)
+	if (sc->sc_vchan[n]->sc_pbus == true)
 		hwp->halt_output(sc->hw_hdl);
 	if (sc->sc_rbus == true)
 		hwp->halt_input(sc->hw_hdl);
@@ -4368,14 +4725,25 @@
 audio_resume(device_t dv, const pmf_qual_t *qual)
 {
 	struct audio_softc *sc = device_private(dv);
+	struct virtual_channel *vc;
+	int n;
+	
+	for (n = 1; n < VAUDIOCHANS; n++) {
+		if (sc->sc_audiopid[n] == curproc->p_pid)
+			break;
+	}
+	
+	if (n == VAUDIOCHANS)
+		return false;
+	vc = sc->sc_vchan[n];
 
 	mutex_enter(sc->sc_lock);
-	if (sc->sc_lastinfovalid)
-		audiosetinfo(sc, &sc->sc_lastinfo);
+	if (vc->sc_lastinfovalid)
+		audiosetinfo(sc, &vc->sc_lastinfo, true, n);
 	audio_mixer_restore(sc);
 	mutex_enter(sc->sc_intr_lock);
-	if ((sc->sc_pbus == true) && !sc->sc_pr.pause)
-		audiostartp(sc);
+	if ((vc->sc_pbus == true) && !vc->sc_mpr.pause)
+		audiostartp(sc, n);
 	if ((sc->sc_rbus == true) && !sc->sc_rr.pause)
 		audiostartr(sc);
 	mutex_exit(sc->sc_intr_lock);
@@ -4397,7 +4765,7 @@
 	if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) {
 		mi.index = sc->sc_outports.master;
 		mi.un.v.delta = 0;
-		if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0) {
+		if (audio_query_devinfo(sc, &mi) == 0) {
 			au_get_gain(sc, &sc->sc_outports, &gain, &balance);
 			newgain = gain - mi.un.v.delta;
 			if (newgain < AUDIO_MIN_GAIN)
@@ -4420,7 +4788,7 @@
 	if (sc->sc_outports.index == -1 && sc->sc_outports.master != -1) {
 		mi.index = sc->sc_outports.master;
 		mi.un.v.delta = 0;
-		if (sc->hw_if->query_devinfo(sc->hw_hdl, &mi) == 0) {
+		if (audio_query_devinfo(sc, &mi) == 0) {
 			au_get_gain(sc, &sc->sc_outports, &gain, &balance);
 			newgain = gain + mi.un.v.delta;
 			if (newgain > AUDIO_MAX_GAIN)
@@ -4482,4 +4850,219 @@
 	return audio_get_props(sc) & AUDIO_PROP_CAPTURE ? true : false;
 }
 
+void
+mix_write(void *arg)
+{
+	struct audio_softc *sc = arg;
+	struct virtual_channel *vc;
+	stream_filter_t *filter;
+	stream_fetcher_t *fetcher;
+	stream_fetcher_t null_fetcher;
+	int cc, cc1, blksize, error;
+	uint8_t *inp;
+
+	vc = sc->sc_vchan[0];
+	blksize = vc->sc_mpr.blksize;
+	cc = blksize;
+
+	if (sc->sc_trigger_started == false)
+		cc *= 2;
+
+	cc1 = cc;
+	if (vc->sc_pustream->inp + cc > vc->sc_pustream->end)
+		cc1 = vc->sc_pustream->end - vc->sc_pustream->inp;
+	memcpy(vc->sc_pustream->inp, sc->sc_pr.s.start, cc1);
+	if (cc1 < cc)
+		memcpy(vc->sc_pustream->start, sc->sc_pr.s.start + cc1, cc - cc1);
+	memset(sc->sc_pr.s.start, 0, cc);
+
+	inp = vc->sc_pustream->inp;
+	vc->sc_pustream->inp = audio_stream_add_inp(vc->sc_pustream, inp, cc);
+
+	if (vc->sc_npfilters > 0) {
+		null_fetcher.fetch_to = null_fetcher_fetch_to;
+		filter = vc->sc_pfilters[0];
+		filter->set_fetcher(filter, &null_fetcher);
+		fetcher = &vc->sc_pfilters[vc->sc_npfilters - 1]->base;
+		fetcher->fetch_to(sc, fetcher, &vc->sc_mpr.s, cc);
+ 	}
+
+	if (sc->hw_if->trigger_output && sc->sc_trigger_started == false) {
+		DPRINTF(("%s: call trigger_output\n", __func__));
+		error = sc->hw_if->trigger_output(sc->hw_hdl, vc->sc_mpr.s.start,
+		    vc->sc_mpr.s.end, blksize,
+		    audio_pint, (void *)sc, &vc->sc_mpr.s.param);
+	} else if (sc->hw_if->start_output) {
+		DPRINTF(("%s: call start_output\n", __func__));
+		error = sc->hw_if->start_output(sc->hw_hdl,
+		    __UNCONST(vc->sc_mpr.s.outp), blksize,
+		    audio_pint, (void *)sc);
+		if (error) {
+			/* XXX does this really help? */
+			DPRINTF(("audio_mix restart failed: %d\n", error));
+			audio_clear(sc, 0);
+		}
+	}
+	if (sc->sc_trigger_started == false) {
+		vc->sc_mpr.s.outp = audio_stream_add_outp(&vc->sc_mpr.s, vc->sc_mpr.s.outp,
+		    blksize);
+	}
+	sc->sc_trigger_started = true;
+}
+
+void
+mix_func(struct audio_softc *sc, struct audio_ringbuffer *cb, int n)
+{
+	int blksize, cc, cc1, cc2, m, resid;
+	int16_t *orig, *tomix;
+
+	blksize = cb->blksize;
+	resid = blksize;
+	if (sc->sc_trigger_started == false)
+		resid *= 2;
+
+	tomix = __UNCONST(cb->s.outp);
+	orig = (int16_t *)(sc->sc_pr.s.start);
+
+	while (resid > 0) {
+		cc = resid;
+		cc1 = sc->sc_pr.s.end - (uint8_t *)orig;
+		cc2 = cb->s.end - (uint8_t *)tomix;
+		if (cc > cc1)
+			cc = cc1;
+		if (cc > cc2)
+			cc = cc2;
+
+		for (m = 0; m < cc / 2; m++) {
+			orig[m] += (int16_t)((int32_t)(tomix[m] *
+			    ((sc->sc_vchan[n]->sc_swvol + 1) * 16)) /
+			    (sc->sc_opens * VAUDIOCHANS));
+		}
+
+		if (&orig[m] >= (int16_t *)sc->sc_pr.s.end)
+			orig = (int16_t *)sc->sc_pr.s.start;
+		if (&tomix[m] >= (int16_t *)cb->s.end)
+			tomix = (int16_t *)cb->s.start;
+
+		resid -= cc;
+	}
+}
+
+static int
+audio_set_port(struct audio_softc *sc, mixer_ctrl_t *mc)
+{
+	int n;
+
+	KASSERT(mutex_owned(sc->sc_lock));
+
+	n = (mc->dev - sc->sc_static_nmixer_states) + 1;
+
+	if (mc->dev >= sc->sc_static_nmixer_states && mc->dev < sc->sc_nmixer_states) {
+		sc->sc_vchan[n]->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
+		return 0;
+	}
+
+	return sc->hw_if->set_port(sc->hw_hdl, mc);
+}
+
+static int
+audio_get_port(struct audio_softc *sc, mixer_ctrl_t *mc)
+{
+	int n;
+
+	KASSERT(mutex_owned(sc->sc_lock));
+
+	n = (mc->dev - sc->sc_static_nmixer_states) + 1;
+
+	if (mc->dev >= sc->sc_static_nmixer_states && mc->dev < sc->sc_nmixer_states) {
+		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_vchan[n]->sc_swvol;
+		return 0;
+	}
+
+	return sc->hw_if->get_port(sc->hw_hdl, mc);
+}
+
+static int
+audio_query_devinfo(struct audio_softc *sc, mixer_devinfo_t *di)
+{
+	char mixLabel[255];
+
+	KASSERT(mutex_owned(sc->sc_lock));
+
+	if (di->index >= sc->sc_static_nmixer_states && di->index <
+	    sc->sc_nmixer_states) {
+		di->mixer_class = AUDIO_OUTPUT_CLASS;
+		snprintf(mixLabel, sizeof(mixLabel), AudioNdac"%d", di->index 
+		     - sc->sc_static_nmixer_states);
+		strcpy(di->label.name, mixLabel);
+		di->type = AUDIO_MIXER_VALUE;
+		di->next = di->prev = AUDIO_MIXER_LAST;
+		di->un.v.num_channels = 1;
+		strcpy(di->un.v.units.name, AudioNvolume);
+		return 0;
+	}
+
+	return sc->hw_if->query_devinfo(sc->hw_hdl, di);
+}
+
+static int
+audio_set_params(struct audio_softc *sc, int setmode, int usemode,
+    audio_params_t *play, audio_params_t *rec,
+    stream_filter_list_t *pfil, stream_filter_list_t *rfil, int n)
+{
+
+	struct audio_format norm_format[VAUDIO_NFORMATS] = {
+	    { NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
+	      2, AUFMT_STEREO, 1, { 44100 } },
+	};
+	KASSERT(mutex_owned(sc->sc_lock));
+
+	norm_format[0].frequency[0] = sc->sc_vchan[0]->sc_pparams.sample_rate;
+
+	if (n == 0 && sc->hw_if->set_params != NULL) {
+		return sc->hw_if->set_params(sc->hw_hdl, setmode, usemode,
+		    play, rec, pfil, rfil);
+	}
+
+	if (auconv_set_converter(norm_format, VAUDIO_NFORMATS, AUMODE_PLAY,
+	    play, true, pfil) < 0)
+		return EINVAL;
+	if (auconv_set_converter(norm_format, VAUDIO_NFORMATS, AUMODE_RECORD,
+	    rec, true, rfil) < 0)
+		return EINVAL;
+
+	if (pfil->req_size > 0)
+		play = &pfil->filters[0].param;
+
+	return 0;
+}
+
+static int
+audio_query_encoding(struct audio_softc *sc, struct audio_encoding *ae)
+{
+	KASSERT(mutex_owned(sc->sc_lock));
+
+	return auconv_query_encoding(sc->sc_encodings, ae);
+}
+
+void
+audio_play_thread(void *v)
+{
+	struct audio_softc *sc;
+	
+	sc = (struct audio_softc *)v;
+
+	mutex_enter(sc->sc_lock);
+	for (;;) {
+		cv_wait_sig(&sc->sc_condvar, sc->sc_lock);
+		if (sc->sc_dying) {
+			mutex_exit(sc->sc_lock);
+			kthread_exit(0);
+		}
+
+		audio_mix(sc);
+	}
+
+}
+
 #endif /* NAUDIO > 0 */
Index: src/sys/arch/arm/broadcom/bcm2835_vcaudio.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm/broadcom/bcm2835_vcaudio.c,v
retrieving revision 1.10
diff -u -r1.10 bcm2835_vcaudio.c
--- src/sys/arch/arm/broadcom/bcm2835_vcaudio.c	28 Jul 2015 21:24:43 -0000	1.10
+++ src/sys/arch/arm/broadcom/bcm2835_vcaudio.c	4 Jun 2016 15:42:49 -0000
@@ -436,7 +436,7 @@
 				sched = true;
 			}
 
-			if (sched) {
+			if (sched && sc->sc_pint) {
 				intr(intrarg);
 				sc->sc_abytes += sc->sc_pblksize;
 				cv_signal(&sc->sc_datacv);
