summaryrefslogtreecommitdiff
path: root/kernel/framework/vmix_core
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/framework/vmix_core')
-rw-r--r--kernel/framework/vmix_core/.config1
-rw-r--r--kernel/framework/vmix_core/db_scale.h45
-rw-r--r--kernel/framework/vmix_core/outexport.inc83
-rw-r--r--kernel/framework/vmix_core/outexport_int.inc78
-rw-r--r--kernel/framework/vmix_core/playmix.inc115
-rw-r--r--kernel/framework/vmix_core/playmix_int.inc102
-rw-r--r--kernel/framework/vmix_core/playmix_src.inc152
-rw-r--r--kernel/framework/vmix_core/rec_export.inc76
-rw-r--r--kernel/framework/vmix_core/rec_export_int.inc72
-rw-r--r--kernel/framework/vmix_core/vmix.h242
-rw-r--r--kernel/framework/vmix_core/vmix_core.c2352
-rw-r--r--kernel/framework/vmix_core/vmix_import.inc77
-rw-r--r--kernel/framework/vmix_core/vmix_import_int.inc56
-rw-r--r--kernel/framework/vmix_core/vmix_input.c505
-rw-r--r--kernel/framework/vmix_core/vmix_output.c687
15 files changed, 4643 insertions, 0 deletions
diff --git a/kernel/framework/vmix_core/.config b/kernel/framework/vmix_core/.config
new file mode 100644
index 0000000..f6f2c2e
--- /dev/null
+++ b/kernel/framework/vmix_core/.config
@@ -0,0 +1 @@
+configcheck=VMIX
diff --git a/kernel/framework/vmix_core/db_scale.h b/kernel/framework/vmix_core/db_scale.h
new file mode 100644
index 0000000..3b23edd
--- /dev/null
+++ b/kernel/framework/vmix_core/db_scale.h
@@ -0,0 +1,45 @@
+/*
+ * Purpose: dB to linear conversion tables for vmix
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * Attenuation table for dB->linear conversion. Indexed in steps of 0.5 dB.
+ * Table size is 25 dB (first entry is handled as mute).
+ */
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+const float vmix_db_table[DB_SIZE + 1] = {
+ 0.0 /* MUTE */ , 0.0035481, 0.0039811, 0.0044668, 0.0050119,
+ 0.0056234, 0.0063096, 0.0070795, 0.0079433, 0.0089125,
+ 0.01, 0.01122, 0.012589, 0.014125, 0.015849,
+ 0.017783, 0.019953, 0.022387, 0.025119, 0.028184,
+ 0.031623, 0.035481, 0.039811, 0.044668, 0.050119,
+ 0.056234, 0.063096, 0.070795, 0.079433, 0.089125,
+ 0.1, 0.1122, 0.12589, 0.14125, 0.15849,
+ 0.17783, 0.19953, 0.22387, 0.25119, 0.28184,
+ 0.31623, 0.35481, 0.39811, 0.44668, 0.50119,
+ 0.56234, 0.63096, 0.70795, 0.79433, 0.89125,
+ 1.0 /* Full level */
+};
+#else
+/* #define VMIX_VOL_SCALE moved to vmix.h */
+const int vmix_db_table[DB_SIZE + 1] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3,
+ 4, 4, 5, 5, 6, 7, 8, 9, 10, 11,
+ 12, 14, 16, 18, 20, 22, 25, 28, 32, 36,
+ 40, 45, 50, 57, 64, 71, 80, 90, 101, 114,
+ 128
+};
+#endif
diff --git a/kernel/framework/vmix_core/outexport.inc b/kernel/framework/vmix_core/outexport.inc
new file mode 100644
index 0000000..6a28b3c
--- /dev/null
+++ b/kernel/framework/vmix_core/outexport.inc
@@ -0,0 +1,83 @@
+#ifdef CONFIG_OSS_VMIX_FLOAT
+/*
+ * Purpose: Local output buffer to device export routine for vmix (FP version)
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+int i, ch, och;
+double vol;
+
+vol = vmix_db_table[eng->outvol / 5];
+
+for (ch = 0; ch < channels; ch++)
+ {
+ double vu;
+ float *chbuf;
+
+ och = eng->channel_order[ch];
+ op = (SAMPLE_TYPE *) outbuf;
+ op += och;
+
+ chbuf = chbufs[ch];
+
+ vu = eng->vu[och % 2];
+ vu = vu / 255.0;
+
+ for (i = 0; i < samples; i++)
+ {
+ double tmp;
+
+#if 0 && defined(SINE_DEBUG)
+ tmp = sine_table[sine_phase[ch]];
+ sine_phase[ch] = (sine_phase[ch] + 1 + ch / 2) % SINE_SIZE;
+#else
+ tmp = *chbuf++;
+#endif
+
+ tmp *= vol;
+
+/*
+ * Check for clipping. Decrease volume if necessary.
+ */
+ if (tmp < -1.0)
+ {
+ vol /= -tmp;
+ eng->outvol--;
+ tmp = -1.0;
+ }
+ else if (tmp > 1.0)
+ {
+ vol /= tmp;
+ eng->outvol--;
+ tmp = 1.0;
+ }
+
+ *op = VMIX_BYTESWAP ((SAMPLE_TYPE) (tmp * SAMPLE_RANGE));
+ op += channels;
+
+ /* VU meter */
+ if (tmp < 0.0)
+ tmp = -tmp;
+ if (tmp > vu)
+ vu = tmp;
+ }
+
+ if (och < 2)
+ {
+ vu = vu * 255.0;
+ eng->vu[och] = (int)vu;
+ }
+ }
+#else
+#include "outexport_int.inc"
+#endif
diff --git a/kernel/framework/vmix_core/outexport_int.inc b/kernel/framework/vmix_core/outexport_int.inc
new file mode 100644
index 0000000..074705f
--- /dev/null
+++ b/kernel/framework/vmix_core/outexport_int.inc
@@ -0,0 +1,78 @@
+/*
+ * Purpose: Local output buffer to device export routine for vmix (int)
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+int i, ch, och;
+int vol;
+
+#define RANGE_MAX ((1<<24)-1)
+#define RANGE_MIN -(1<<24)
+
+vol = vmix_db_table[eng->outvol / 5];
+
+for (ch = 0; ch < channels; ch++)
+ {
+ int vu;
+ int *chbuf;
+
+ och = eng->channel_order[ch];
+ op = (SAMPLE_TYPE *) outbuf;
+ op += och;
+
+ chbuf = chbufs[ch];
+
+ vu = eng->vu[och % 2];
+ vu = vu * 65536;
+
+ for (i = 0; i < samples; i++)
+ {
+ int tmp;
+
+ tmp = *chbuf++;
+
+ tmp = (tmp * vol) / VMIX_VOL_SCALE;
+
+/*
+ * Check for clipping. Decrease volume if necessary.
+ */
+ if (tmp<RANGE_MIN)
+ {
+ tmp=RANGE_MIN;
+ eng->outvol -= 1;
+ vol /= 2;
+ }
+ else
+ if (tmp>RANGE_MAX)
+ {
+ tmp=RANGE_MAX;
+ eng->outvol -= 1;
+ vol /= 2;
+ }
+
+ *op = VMIX_BYTESWAP(INT_EXPORT(tmp));
+ op += channels;
+
+ /* VU meter */
+ if (tmp < 0)
+ tmp = -tmp;
+ if (tmp > vu)
+ vu = tmp;
+ }
+
+ if (och < 2)
+ {
+ vu = vu / 65536;
+ eng->vu[och] = vu;
+ }
+ }
diff --git a/kernel/framework/vmix_core/playmix.inc b/kernel/framework/vmix_core/playmix.inc
new file mode 100644
index 0000000..50d07f5
--- /dev/null
+++ b/kernel/framework/vmix_core/playmix.inc
@@ -0,0 +1,115 @@
+#ifdef CONFIG_OSS_VMIX_FLOAT
+/*
+ * Purpose: Application to local playback buffer import routine for vmix (FP)
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * Mixing function for virtual devices
+ */
+
+vmix_mixer_t *mixer = portc->mixer;
+vmix_engine_t *eng = &mixer->play_engine;
+
+dmap_t *dmap = audio_engines[portc->audio_dev]->dmap_out;
+
+int frame_size;
+
+int inptr, inmax;
+int used_channels;
+int i, ch, nch;
+double vol;
+
+/*
+ * Initial setup
+ */
+
+frame_size = sizeof (*inp);
+
+inmax = dmap->bytes_in_use / frame_size;
+inptr = portc->play_dma_pointer / frame_size;
+
+inp = (BUFFER_TYPE) dmap->dmabuf;
+
+used_channels = portc->channels;
+if (used_channels > eng->channels)
+ used_channels = eng->channels;
+
+/* ignored_channels = portc->channels - used_channels; */
+
+/*
+ * Handle mono playback by playing the mono stream twice (for left and right ch)
+ */
+nch=used_channels;
+if (nch<2)nch=2;
+
+/*
+ * Do the mixing
+ */
+for (ch = 0; ch < nch; ch++)
+ {
+ int ip = inptr + (ch%used_channels);
+ double vu = portc->vu[ch % 2];
+ float *chbuf = eng->chbufs[ch+portc->play_choffs];
+
+ i = portc->volume[ch%2];
+ vol = vmix_db_table[i / 5];
+
+ vu = vu / 255.0;
+
+ for (i = 0; i < nsamples; i++)
+ {
+ double tmp;
+
+#if 0 && defined(SINE_DEBUG)
+ if (ch > 1)
+ tmp = 0.0;
+ else
+ tmp = sine_table[sine_phase[ch]];
+ sine_phase[ch] = (sine_phase[ch] + 1) % SINE_SIZE;
+#else
+ /*
+ * Convert the sample to right endianess.
+ */
+ tmp = VMIX_BYTESWAP (inp[ip]);
+ tmp = tmp * range;
+#endif
+ tmp = tmp * vol;
+
+ *chbuf++ += tmp;
+ ip = (ip + portc->channels) % inmax;
+
+ /* VU meter */
+ if (tmp < 0.0)
+ tmp = -tmp;
+ if (tmp > vu)
+ vu = tmp;
+ }
+
+ if (ch < 2)
+ { /* Save left/right VU meters */
+ vu = vu * 255.0;
+ portc->vu[ch] = (int)vu;
+ }
+ }
+
+/*
+ * Finally save the state variables
+ */
+
+portc->play_dma_pointer =
+ (portc->play_dma_pointer +
+ nsamples * frame_size * portc->channels) % dmap->bytes_in_use;
+#else
+#include "playmix_int.inc"
+#endif
diff --git a/kernel/framework/vmix_core/playmix_int.inc b/kernel/framework/vmix_core/playmix_int.inc
new file mode 100644
index 0000000..18e4815
--- /dev/null
+++ b/kernel/framework/vmix_core/playmix_int.inc
@@ -0,0 +1,102 @@
+/*
+ * Purpose: Application to local playback buffer import routine for vmix (int)
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * Mixing function for virtual devices
+ */
+
+vmix_mixer_t *mixer = portc->mixer;
+vmix_engine_t *eng = &mixer->play_engine;
+
+dmap_t *dmap = audio_engines[portc->audio_dev]->dmap_out;
+
+int frame_size;
+
+int inptr, inmax;
+int used_channels;
+int i, ch, nch;
+int vol;
+
+/*
+ * Initial setup
+ */
+
+frame_size = sizeof (*inp);
+
+inmax = dmap->bytes_in_use / frame_size;
+inptr = portc->play_dma_pointer / frame_size;
+
+inp = (BUFFER_TYPE) dmap->dmabuf;
+
+used_channels = portc->channels;
+if (used_channels > eng->channels)
+ used_channels = eng->channels;
+
+/* ignored_channels = portc->channels - used_channels; */
+
+/*
+ * Handle mono playback by playing the mono stream twice (for left and right ch)
+ */
+nch=used_channels;
+if (nch<2)nch=2;
+
+/*
+ * Do the mixing
+ */
+for (ch = 0; ch < nch; ch++)
+ {
+ int ip = inptr + (ch%used_channels);
+ int vu = portc->vu[ch % 2];
+ int *chbuf = eng->chbufs[ch+portc->play_choffs];
+
+ i = portc->volume[ch%2];
+ vol = vmix_db_table[i / 5];
+
+ vu = vu * 65536;
+
+ for (i = 0; i < nsamples; i++)
+ {
+ int tmp;
+
+ /*
+ * Convert the sample to right endianess.
+ */
+ tmp = INT_OUTMIX(VMIX_BYTESWAP (inp[ip]));
+ tmp = (tmp * vol) / VMIX_VOL_SCALE;
+
+ *chbuf++ += tmp;
+ ip = (ip + portc->channels) % inmax;
+
+ /* VU meter */
+ if (tmp < 0)
+ tmp = -tmp;
+ if (tmp > vu)
+ vu = tmp;
+ }
+
+ if (ch < 2)
+ { /* Save left/right VU meters */
+ vu = vu / 65536;
+ portc->vu[ch] = vu;
+ }
+ }
+
+/*
+ * Finally save the state variables
+ */
+
+portc->play_dma_pointer =
+ (portc->play_dma_pointer +
+ nsamples * frame_size * portc->channels) % dmap->bytes_in_use;
diff --git a/kernel/framework/vmix_core/playmix_src.inc b/kernel/framework/vmix_core/playmix_src.inc
new file mode 100644
index 0000000..1877c34
--- /dev/null
+++ b/kernel/framework/vmix_core/playmix_src.inc
@@ -0,0 +1,152 @@
+/*
+ * Purpose: Application to local playback buffer resampling routine for vmix
+ *
+ * This file is almost the same than playmix.inc but uses simple
+ * linear interpolation algorithm for sample rate conversion.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * Mixing function for virtual devices
+ */
+
+vmix_mixer_t *mixer = portc->mixer;
+vmix_engine_t *eng = &mixer->play_engine;
+
+dmap_t *dmap = audio_engines[portc->audio_dev]->dmap_out;
+
+int frame_size;
+
+int inptr, inmax;
+int used_channels;
+int i, ch, nch;
+double vol, step;
+double tmp;
+
+/*
+ * Initial setup
+ */
+step = (double) portc->rate / (double) mixer->play_engine.rate;
+
+#if 1
+/*
+ * TODO:
+ * For some reason "trivial" conversion rations such as 4:1, 2:1, 1:2 and 1:4
+ * don't work properly. As a workaround we add a tiny error to break the
+ * evil ratio. Need to find out why the algorithm doesn't work with such ratios.
+ */
+ if (step==4.0)step=4.0001;
+ if (step==2.0)step=2.0001;
+ if (step==0.5)step=0.5001;
+ if (step==0.25)step=0.25001;
+#endif
+
+frame_size = sizeof (*inp);
+
+inmax = dmap->bytes_in_use / frame_size / portc->channels;
+inptr = (int)(portc->play_dma_pointer_src / frame_size / portc->channels);
+
+used_channels = portc->channels;
+if (used_channels > eng->channels)
+ used_channels = eng->channels;
+
+/* ignored_channels = portc->channels - used_channels; */
+
+/*
+ * Handle mono playback by playing the mono stream twice (for left and right ch)
+ */
+nch=used_channels;
+if (nch<2)nch=2;
+
+/*
+ * Do the mixing
+ */
+for (ch = 0; ch < nch; ch++)
+ {
+ double ip = inptr + (ch%used_channels);
+ double vu = portc->vu[ch % 2];
+ float *chbuf = eng->chbufs[ch+portc->play_choffs];
+
+ inp = (BUFFER_TYPE) dmap->dmabuf;
+ inp += ch;
+
+ i = portc->volume[ch%2];
+ vol = vmix_db_table[i / 5];
+
+ vu = vu / 255.0;
+
+ for (i = 0; i < nsamples; i++)
+ {
+ double frag, tmp_next;
+ int ip_next;
+
+#if 0 && defined(SINE_DEBUG)
+ if (ch > 1)
+ tmp = 0.0;
+ else
+ tmp = sine_table[sine_phase[ch]];
+ sine_phase[ch] = (sine_phase[ch] + 1) % SINE_SIZE;
+#else
+ /*
+ * Convert the sample to right endianess.
+ */
+ tmp = VMIX_BYTESWAP (inp[((int) ip) * portc->channels]);
+
+ /* perform linear interpolation */
+ ip_next = (((int) ip) + 1) % inmax;
+ tmp_next = VMIX_BYTESWAP (inp[ip_next * portc->channels]);
+ frag = ip - (int) ip; /* Pointer fraction */
+ tmp += (tmp_next - tmp) * frag;
+ tmp /= 2.0;
+
+ tmp = tmp * range;
+#endif
+ tmp = tmp * vol;
+
+ *chbuf++ += tmp;
+ ip = ip + step;
+ if (ip > inmax)
+ ip -= inmax;
+
+ /* VU meter */
+ if (tmp < 0.0)
+ tmp = -tmp;
+ if (tmp > vu)
+ vu = tmp;
+ }
+
+ if (ch < 2)
+ { /* Save left/right VU meters */
+ vu = vu * 255.0;
+ portc->vu[ch] = (int)vu;
+ }
+ }
+
+/*
+ * Finally save the state variables
+ */
+tmp = (nsamples * frame_size * portc->channels);
+
+tmp *= step;
+
+portc->play_dma_pointer_src = portc->play_dma_pointer_src + tmp;
+
+if (portc->play_dma_pointer_src > dmap->bytes_in_use)
+ portc->play_dma_pointer_src -= dmap->bytes_in_use;
+
+portc->play_dma_pointer = (int)portc->play_dma_pointer_src;
+
+frame_size *= portc->channels;
+
+portc->play_dma_pointer = (portc->play_dma_pointer / frame_size) * frame_size;
+
diff --git a/kernel/framework/vmix_core/rec_export.inc b/kernel/framework/vmix_core/rec_export.inc
new file mode 100644
index 0000000..a8ac880
--- /dev/null
+++ b/kernel/framework/vmix_core/rec_export.inc
@@ -0,0 +1,76 @@
+#ifdef CONFIG_OSS_VMIX_FLOAT
+/*
+ * Purpose: Local input buffer to application export routine for vmix (FP version)
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+vmix_mixer_t *mixer = portc->mixer;
+vmix_engine_t *eng = &mixer->record_engine;
+
+dmap_t *dmap = audio_engines[portc->audio_dev]->dmap_in;
+
+int frame_size;
+
+int outptr, outmax;
+int used_channels;
+int i, ch;
+
+/*
+ * Initial setup
+ */
+
+frame_size = sizeof (*outp);
+
+outmax = dmap->bytes_in_use / frame_size;
+outptr = portc->rec_dma_pointer / frame_size;
+
+outp = (BUFFER_TYPE) dmap->dmabuf;
+
+used_channels = portc->channels;
+if (used_channels > eng->channels)
+ used_channels = eng->channels;
+
+/* ignored_channels = portc->channels - used_channels; */
+
+/*
+ * Do the mixing
+ */
+for (ch = 0; ch < used_channels; ch++)
+ {
+ int op = outptr + ch;
+ float *chbuf = eng->chbufs[ch+portc->rec_choffs];
+
+ for (i = 0; i < nsamples; i++)
+ {
+ float tmp;
+
+ tmp = *chbuf++;
+ /*
+ * Convert the sample to right endianess.
+ */
+ outp[op] = VMIX_BYTESWAP ((int)(tmp * range));
+
+ op = (op + portc->channels) % outmax;
+ }
+ }
+
+/*
+ * Finally save the state variables
+ */
+
+portc->rec_dma_pointer =
+ (portc->rec_dma_pointer +
+ nsamples * frame_size * portc->channels) % dmap->bytes_in_use;
+#else
+#include "rec_export_int.inc"
+#endif
diff --git a/kernel/framework/vmix_core/rec_export_int.inc b/kernel/framework/vmix_core/rec_export_int.inc
new file mode 100644
index 0000000..3a9953d
--- /dev/null
+++ b/kernel/framework/vmix_core/rec_export_int.inc
@@ -0,0 +1,72 @@
+/*
+ * Purpose: Local input buffer to application export routine for vmix (int)
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+vmix_mixer_t *mixer = portc->mixer;
+vmix_engine_t *eng = &mixer->record_engine;
+
+dmap_t *dmap = audio_engines[portc->audio_dev]->dmap_in;
+
+int frame_size;
+
+int outptr, outmax;
+int used_channels;
+int i, ch;
+
+/*
+ * Initial setup
+ */
+
+frame_size = sizeof (*outp);
+
+outmax = dmap->bytes_in_use / frame_size;
+outptr = portc->rec_dma_pointer / frame_size;
+
+outp = (BUFFER_TYPE) dmap->dmabuf;
+
+used_channels = portc->channels;
+if (used_channels > eng->channels)
+ used_channels = eng->channels;
+
+/* ignored_channels = portc->channels - used_channels; */
+
+/*
+ * Do the mixing
+ */
+for (ch = 0; ch < used_channels; ch++)
+ {
+ int op = outptr + ch;
+ int *chbuf = eng->chbufs[ch+portc->rec_choffs];
+
+ for (i = 0; i < nsamples; i++)
+ {
+ int tmp;
+
+ tmp = *chbuf++;
+ /*
+ * Convert the sample to right endianess.
+ */
+ outp[op] = VMIX_BYTESWAP (INT_EXPORT(tmp));
+
+ op = (op + portc->channels) % outmax;
+ }
+ }
+
+/*
+ * Finally save the state variables
+ */
+
+portc->rec_dma_pointer =
+ (portc->rec_dma_pointer +
+ nsamples * frame_size * portc->channels) % dmap->bytes_in_use;
diff --git a/kernel/framework/vmix_core/vmix.h b/kernel/framework/vmix_core/vmix.h
new file mode 100644
index 0000000..a01c722
--- /dev/null
+++ b/kernel/framework/vmix_core/vmix.h
@@ -0,0 +1,242 @@
+/*
+ * Purpose: Definitions for the vmix driver
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#define SUPPORTED_FORMATS (AFMT_S16_NE | AFMT_S16_OE | AFMT_S32_NE | AFMT_S32_OE)
+
+/*
+ * Maximum number of clients per "real" device is defined by MAX_CLIENTS. Limit of 4 would be good for 95% of systems.
+ * For each client there will be a mixer volume control and peak meter in the mixer interface. Raising the
+ * client limit will make the mixer interface larger and larger. Something like 8 is probably the practical limit
+ * for number of clients.
+ *
+ * Mixing more than 16 streams together doesn't make much sense since the result is likely to be
+ * just noise. Mixing a stream will cause some overhead so it's not a good idea to let large number of iddle
+ * applications running muted or playing silence.
+ */
+
+#define MAX_CLIENTS 9
+
+#define MAX_LOOPDEVS 2 /* Maximum number of vmix loopback devices */
+
+/*
+ * 8 play channels and 2 rec channels might be OK for most devices. However envy24 requires 10 play and 12 rec
+ * channels for the "raw devices". Some professional (ADAT) cards like Digi96 requires 8+8 channels.
+ */
+#define MAX_PLAY_CHANNELS 12
+/* MAX_REC_CHANNELS must be less or equal than MAX_PLAY_CHANNELS */
+#define MAX_REC_CHANNELS 12
+
+#define CHBUF_SAMPLES 2048 /* Max samples (frames) per fragment */
+
+typedef struct _vmix_mixer_t vmix_mixer_t;
+typedef struct _vmix_portc_t vmix_portc_t;
+typedef struct _vmix_engine_t vmix_engine_t;
+typedef unsigned char vmix_channel_map_t[MAX_PLAY_CHANNELS];
+
+struct _vmix_portc_t /* Audio device specific data */
+{
+ int num;
+ vmix_mixer_t *mixer;
+ vmix_portc_t *next; /* Linked list for all portc structures */
+ int dev_type;
+#define DT_IN 1
+#define DT_OUT 2
+#define DT_LOOP 3
+
+ int disabled_modes;
+
+ int fmt, bits;
+ int channels;
+ int rate;
+
+ int audio_dev;
+ int open_mode;
+ int trigger_bits;
+
+ int open_pending; /* Set to 1 by vmix_create_client() and cleared by vmix_open() */
+
+ int play_dma_pointer;
+ int play_choffs; /* Index of the first channel on multich play engines */
+ int rec_choffs; /* Index of the first channel on multich rec engines */
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double play_dma_pointer_src;
+#endif
+ int rec_dma_pointer;
+ int volume[2]; /* Left and right ch volumes */
+ int vu[2];
+
+ void (*play_mixing_func) (vmix_portc_t * portc, int nsamples);
+ void (*rec_mixing_func) (vmix_portc_t * portc, int nsamples);
+ int do_src;
+ vmix_channel_map_t channel_order;
+};
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ typedef float vmix_sample_t;
+#else
+ typedef int vmix_sample_t;
+#endif
+
+typedef void (*converter_t) (vmix_engine_t * engine, void *outbuf,
+ vmix_sample_t * chbufs[], int channels,
+ int samples);
+
+struct _vmix_engine_t
+{
+ int rate, channels, fmt, bits;
+ int max_playahead;
+ int fragsize;
+ int samples_per_frag;
+
+ converter_t converter;
+ vmix_sample_t *chbufs[MAX_PLAY_CHANNELS];
+ unsigned int limiter_statevar;
+
+/*
+ * Mixer volumes, etc.
+ */
+ int outvol;
+ int vu[2];
+ int num_active_outputs;
+ vmix_channel_map_t channel_order;
+};
+
+struct _vmix_mixer_t /* Instance specific data */
+{
+ vmix_mixer_t *next; /* Pointer to the next vmix instance */
+ int instance_num;
+ int disabled;
+ oss_device_t *osdev;
+ oss_device_t *master_osdev;
+ oss_mutex_t mutex;
+ unsigned int attach_flags;
+
+ int installed_ok;
+
+ int open_devices, open_inputs;
+ struct fileinfo master_finfo, input_finfo;
+ int masterdev_opened;
+
+ int vmix_flags; /* Copy of adev[master]->vmix_flags */
+
+/*
+ * Config options for this instance
+ */
+ int masterdev;
+ int inputdev;
+ int rate;
+
+ int src_quality; /* Control panel setting */
+ int multich_enable; /* Enable multi channel mode */
+ int max_channels;
+
+ vmix_engine_t play_engine, record_engine;
+
+ vmix_portc_t *client_portc[MAX_CLIENTS];
+ vmix_portc_t *loop_portc[MAX_LOOPDEVS];
+ int num_clientdevs, num_loopdevs;
+
+/*
+ * Mixer interface
+ *
+ * Mixer device numbers for the master audio devices
+ */
+ int output_mixer_dev;
+ int input_mixer_dev;
+ int first_input_mixext;
+ int first_output_mixext;
+ int client_mixer_group; /* Create the client controls under this mixer group */
+};
+
+extern void vmix_setup_play_engine (vmix_mixer_t * mixer, adev_t * adev,
+ dmap_t * dmap);
+extern void vmix_setup_record_engine (vmix_mixer_t * mixer, adev_t * adev,
+ dmap_t * dmap);
+extern void finalize_record_engine (vmix_mixer_t * mixer, int fmt,
+ adev_t * adev, dmap_p dmap);
+
+extern void vmix_outmix_16ne (vmix_portc_t * portc, int nsamples);
+extern void vmix_outmix_16oe (vmix_portc_t * portc, int nsamples);
+extern void vmix_outmix_32ne (vmix_portc_t * portc, int nsamples);
+extern void vmix_outmix_32oe (vmix_portc_t * portc, int nsamples);
+#ifdef CONFIG_OSS_VMIX_FLOAT
+extern void vmix_outmix_float (vmix_portc_t * portc, int nsamples);
+#endif
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+/*
+ * For the time being these routines will only work in floating point.
+ */
+extern void vmix_outmix_16ne_src (vmix_portc_t * portc, int nsamples);
+extern void vmix_outmix_16oe_src (vmix_portc_t * portc, int nsamples);
+extern void vmix_outmix_32ne_src (vmix_portc_t * portc, int nsamples);
+extern void vmix_outmix_32oe_src (vmix_portc_t * portc, int nsamples);
+extern void vmix_outmix_float_src (vmix_portc_t * portc, int nsamples);
+#endif
+
+extern void vmix_rec_export_16ne (vmix_portc_t * portc, int nsamples);
+extern void vmix_rec_export_16oe (vmix_portc_t * portc, int nsamples);
+extern void vmix_rec_export_32ne (vmix_portc_t * portc, int nsamples);
+extern void vmix_rec_export_32oe (vmix_portc_t * portc, int nsamples);
+#ifdef CONFIG_OSS_VMIX_FLOAT
+extern void vmix_rec_export_float (vmix_portc_t * portc, int nsamples);
+#endif
+
+#define DB_SIZE 50
+#define VMIX_VOL_SCALE 127
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ extern const float vmix_db_table[DB_SIZE + 1];
+#else
+ extern const int vmix_db_table[DB_SIZE + 1];
+#endif
+
+#ifdef VMIX_MAIN
+#include "db_scale.h"
+#endif
+
+#ifdef SWAP_SUPPORT
+/*
+ * Endianess swapping functions
+ */
+static __inline__ short
+bswap16 (short x)
+{
+ short y = 0;
+ unsigned char *a = ((unsigned char *) &x) + 1;
+ unsigned char *b = (unsigned char *) &y;
+
+ *b++ = *a--;
+ *b++ = *a--;
+
+ return y;
+}
+
+static __inline__ int
+bswap32 (int x)
+{
+
+ int y = 0;
+ unsigned char *a = ((unsigned char *) &x) + 3;
+ unsigned char *b = (unsigned char *) &y;
+
+ *b++ = *a--;
+ *b++ = *a--;
+ *b++ = *a--;
+ *b++ = *a--;
+
+ return y;
+}
+#endif
diff --git a/kernel/framework/vmix_core/vmix_core.c b/kernel/framework/vmix_core/vmix_core.c
new file mode 100644
index 0000000..d74499f
--- /dev/null
+++ b/kernel/framework/vmix_core/vmix_core.c
@@ -0,0 +1,2352 @@
+/*
+ * Purpose: Viartual audio mixing framework
+ *
+ * This subsystem makes it possible to share one physical audio between
+ * multiple applications at the same time.
+ *
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#define VMIX_MAIN
+#include <oss_config.h>
+#include "vmix.h"
+
+extern int vmix_disabled; /* Configuration option (osscore.conf) */
+extern int vmix_loopdevs; /* Configuration option (osscore.conf) */
+extern int flat_device_model;
+extern int vmix_no_autoattach;
+static vmix_mixer_t *mixer_list = NULL; /* List of all currently installed mixer instances */
+static int num_instances = 0;
+
+static const unsigned char peak_cnv[256] = {
+ 0, 18, 29, 36, 42, 47, 51, 54, 57, 60, 62, 65, 67, 69, 71, 72,
+ 74, 75, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 89, 90,
+ 91, 92, 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 100, 101,
+ 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108,
+ 108,
+ 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 113, 113, 113, 114, 114,
+ 114,
+ 115, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119,
+ 119,
+ 119, 119, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123,
+ 123,
+ 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126,
+ 126,
+ 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129,
+ 130,
+ 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 132, 132, 132, 132,
+ 132,
+ 132, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 135,
+ 135,
+ 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137,
+ 137,
+ 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139,
+ 139,
+ 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141,
+ 141,
+ 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143,
+ 143,
+ 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144,
+};
+
+/*
+ * Mixer/control panel interface
+ */
+static int
+vmix_outvol (int dev, int ctrl, unsigned int cmd, int value)
+{
+ vmix_mixer_t *mixer = mixer_devs[dev]->vmix_devc;
+ int vol;
+
+ if (mixer == NULL)
+ return OSS_ENXIO;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ switch (ctrl)
+ {
+ case 0: /* Main output volume */
+ return mixer->play_engine.outvol | (mixer->play_engine.
+ outvol << 16);
+ break;
+
+ case 1: /* Peak meter */
+ vol =
+ peak_cnv[mixer->play_engine.
+ vu[0]] | (peak_cnv[mixer->play_engine.vu[1]] << 8);
+ mixer->play_engine.vu[0] = 0;
+ mixer->play_engine.vu[1] = 0;
+ return vol;
+ break;
+
+ case 509: /* Curren sample rate */
+ return mixer->rate;
+ break;
+
+ case 510: /* Enable/disable */
+ return !mixer->disabled;
+ break;
+
+ case 511: /* Multi channel enable */
+ return mixer->multich_enable;
+ break;
+
+ case 512: /* grc3<->interpolation selector */
+ return mixer->src_quality;
+ break;
+
+ default:
+ return OSS_EINVAL;
+ }
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ vol = value & 0xffff; /* Left/mono channel volume */
+ if (vol > DB_SIZE * 5)
+ vol = DB_SIZE * 5;
+
+ switch (ctrl)
+ {
+ case 0:
+ mixer->play_engine.outvol = vol;
+
+ mixer_devs[dev]->modify_counter++;
+ return vol | (vol << 16);
+ break;
+
+ case 510: /* Enable/disable */
+ mixer->disabled = !value;
+ return !!value;
+ break;
+
+ case 511: /* Multich enable */
+ mixer->multich_enable = !!value;
+ mixer_devs[dev]->modify_counter++;
+ return mixer->multich_enable;
+ break;
+
+ case 512: /* grc3<->interpolation selector */
+ mixer->src_quality = value & 0xff;
+ mixer_devs[dev]->modify_counter++;
+ return mixer->src_quality;
+ break;
+
+ default:
+ return OSS_EINVAL;
+ }
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+vmix_invol (int dev, int ctrl, unsigned int cmd, int value)
+{
+ vmix_mixer_t *mixer = mixer_devs[dev]->vmix_devc;
+ int vol;
+
+ if (mixer == NULL)
+ return OSS_ENXIO;
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ switch (ctrl)
+ {
+ case 0: /* Main input volume */
+ return mixer->record_engine.outvol | (mixer->record_engine.
+ outvol << 16);
+ break;
+
+ case 1: /* recording peak meter */
+ vol =
+ peak_cnv[mixer->record_engine.
+ vu[0]] | (peak_cnv[mixer->record_engine.vu[1]] << 8);
+ mixer->record_engine.vu[0] = 0;
+ mixer->record_engine.vu[1] = 0;
+ return vol & 0x7fffffff;
+ break;
+
+ default:
+ return OSS_EINVAL;
+ }
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ vol = value & 0xffff; /* Left/mono channel volume */
+ if (vol > DB_SIZE * 5)
+ vol = DB_SIZE * 5;
+
+ switch (ctrl)
+ {
+ case 0:
+ mixer->record_engine.outvol = vol;
+ mixer_devs[mixer->input_mixer_dev]->modify_counter++;
+ return vol | (vol << 16);
+ break;
+
+ default:
+ return OSS_EINVAL;
+ }
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+vmix_outportc_vol (int dev, int ctrl, unsigned int cmd, int value)
+{
+ vmix_mixer_t *mixer = mixer_devs[dev]->vmix_devc;
+ vmix_portc_t *portc;
+ int vol, rvol;
+
+ if (mixer == NULL)
+ return OSS_ENXIO;
+
+ if (ctrl < 0)
+ return OSS_ENXIO;
+
+ if (ctrl >= mixer->num_clientdevs) /* Client engine not created yet */
+ return (DB_SIZE * 5) | ((DB_SIZE * 5) << 16); /* Force to maximum level */
+
+ portc = mixer->client_portc[ctrl];
+
+ if (cmd == SNDCTL_MIX_READ)
+ {
+ return portc->volume[0] | (portc->volume[1] << 16);
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ vol = (value) & 0xffff; /* Left/mono channel volume */
+ rvol = (value >> 16) & 0xffff; /* Right channel volume */
+ if (vol > DB_SIZE * 5)
+ vol = DB_SIZE * 5;
+ if (rvol > DB_SIZE * 5)
+ rvol = DB_SIZE * 5;
+ portc->volume[0] = vol;
+ portc->volume[1] = rvol;
+ return vol | (rvol << 16);
+ }
+
+ return OSS_EINVAL;
+}
+
+/*ARGSUSED*/
+static int
+vmix_outportc_vu (int dev, int ctrl, unsigned int cmd, int value)
+{
+ vmix_mixer_t *mixer = mixer_devs[dev]->vmix_devc;
+ vmix_portc_t *portc;
+ int val;
+
+ if (mixer == NULL)
+ return OSS_ENXIO;
+
+ if (cmd != SNDCTL_MIX_READ)
+ return OSS_EINVAL;
+
+ if (ctrl < 0 || ctrl >= mixer->num_clientdevs)
+ return OSS_ENXIO;
+
+ portc = mixer->client_portc[ctrl];
+
+ val = peak_cnv[portc->vu[0]] | (peak_cnv[portc->vu[1]] << 8);
+ portc->vu[0] = portc->vu[1] = 0;
+
+ return val;
+}
+
+static void
+create_client_controls (void *vmix_mixer, int client_num)
+{
+ /*
+ * Create the pcmN sliders
+ */
+ int group, err;
+ vmix_mixer_t *mixer = vmix_mixer;
+ int mixer_dev;
+ char name[32];
+
+ mixer_dev = mixer->output_mixer_dev;
+ group = mixer->client_mixer_group;
+
+ if (mixer_dev < 0 || mixer_dev >= num_mixers)
+ return;
+
+ sprintf (name, "@pcm%d", mixer->client_portc[client_num]->audio_dev);
+ if ((err =
+ mixer_ext_create_control (mixer_dev, group, client_num, vmix_outportc_vol,
+ MIXT_STEREOSLIDER16, name, DB_SIZE * 5,
+ MIXF_READABLE | MIXF_WRITEABLE |
+ MIXF_CENTIBEL | MIXF_PCMVOL)) < 0)
+ return;
+
+ if ((err =
+ mixer_ext_create_control (mixer_dev, group, client_num, vmix_outportc_vu,
+ MIXT_STEREOPEAK, "", 144,
+ MIXF_READABLE | MIXF_DECIBEL)) < 0)
+ return;
+}
+
+static int
+create_output_controls (int mixer_dev)
+{
+ int ctl;
+ oss_mixext *ext;
+ vmix_mixer_t *mixer = mixer_devs[mixer_dev]->vmix_devc;
+ char tmp[32];
+
+ /*
+ * Misc vmix related mixer settings.
+ */
+ sprintf (tmp, "vmix%d-enable", mixer->instance_num);
+ if ((ctl = mixer_ext_create_control (mixer_dev, 0, 510, vmix_outvol,
+ MIXT_ONOFF,
+ tmp, 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) <
+ 0)
+ return ctl;
+
+ sprintf (tmp, "vmix%d-rate", mixer->instance_num);
+ if ((ctl = mixer_ext_create_control (mixer_dev, 0, 509, vmix_outvol,
+ MIXT_VALUE,
+ tmp, 500000,
+ MIXF_READABLE | MIXF_HZ)) <
+ 0)
+ return ctl;
+ mixer_ext_set_description(mixer_dev, ctl, "Sample rate currently used by virtual mixer on this device.\n"
+ "Use vmixctl(1) command to change the rate.");
+
+ if (mixer->max_channels>2)
+ {
+ sprintf (tmp, "vmix%d-channels", mixer->instance_num);
+ if ((ctl = mixer_ext_create_control (mixer_dev, 0, 511, vmix_outvol,
+ MIXT_ENUM,
+ tmp, 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) <
+ 0)
+ return ctl;
+
+ mixer_ext_set_strings (mixer_dev, ctl,
+ "Stereo Multich", 0);
+ }
+
+ sprintf (tmp, "vmix%d-src", mixer->instance_num);
+ if ((ctl = mixer_ext_create_control (mixer_dev, 0, 512, vmix_outvol,
+ MIXT_ENUM,
+ tmp, 7,
+ MIXF_READABLE | MIXF_WRITEABLE)) <
+ 0)
+ return ctl;
+
+ mixer_ext_set_strings (mixer_dev, ctl,
+ "Fast Low Medium High High+ Production OFF", 0);
+ mixer_ext_set_description(mixer_dev, ctl, "Sample rate conversion quality used by the virtual mixer.\n"
+ "\n"
+ "Virtual mixer uses internally a fixed sampling rate that can be set\n"
+ "using the 'vmixctl rate' command (usually 48 kHz by default). Applications\n"
+ "that want to use different rates will be handled by performing automatic\n"
+ "sample rate conversions (SRC) in software. This operation will consume\n"
+ "some additional CPU time depending on the quality. The following\n"
+ "alternatives are availabe:\n"
+ "\n"
+ "Fast: Use fast linear interpolation algorithm (low quality).\n"
+ "Low: Use slightly better linear interpolation\n"
+ "Medium: Use an algorithm that provides good quality with moderate CPU load.\n"
+ "High/High+/Production: Higher quality algorithms that consume more CPU resources.\n"
+ "OFF: No sample rate conversions. Sample rate locked to the master rate.\n"
+ "\n"
+ "'Fast' will work best in most cases. Only users with high end audio\n"
+ "cards and speakers should use the other settings.\n"
+ );
+ ext = mixer_find_ext (mixer_dev, ctl);
+ if (ext != NULL)
+ {
+ int i;
+
+ memset (ext->enum_present, 0, sizeof (ext->enum_present));
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ ext->enum_present[0] |= 0x01; // "Fast" is always present
+#endif
+ ext->enum_present[0] |= 0x040; // As well as "OFF"
+#if CONFIG_OSS_GRC_MAX_QUALITY > 7
+#error CONFIG_OSS_GRC_MAX_QUALITY is out of range
+#endif
+
+ for (i=CONFIG_OSS_GRC_MIN_QUALITY; i <= CONFIG_OSS_GRC_MAX_QUALITY; i++)
+ ext->enum_present[0] |= (1 << i);
+ }
+
+ /*
+ * Create the vmix volume slider and peak meter to the top panel.
+ */
+ if (!(mixer->vmix_flags & VMIX_NOMAINVOL))
+ {
+ sprintf (tmp, "vmix%d-outvol", mixer->instance_num);
+ if ((ctl = mixer_ext_create_control (mixer_dev, 0, 0, vmix_outvol,
+ MIXT_MONOSLIDER16,
+ tmp, DB_SIZE * 5,
+ MIXF_READABLE | MIXF_WRITEABLE |
+ MIXF_CENTIBEL | MIXF_PCMVOL)) < 0)
+ return ctl;
+
+ if (mixer->first_output_mixext == -1)
+ mixer->first_output_mixext = ctl;
+ }
+ else
+ mixer->play_engine.outvol = DB_SIZE * 5;
+
+ sprintf (tmp, "vmix%d-outvu", mixer->instance_num);
+ if ((ctl = mixer_ext_create_control (mixer_dev, 0, 1, vmix_outvol,
+ MIXT_STEREOPEAK,
+ tmp, 144,
+ MIXF_READABLE | MIXF_DECIBEL)) < 0)
+ return ctl;
+
+ if (mixer->first_output_mixext == -1)
+ mixer->first_output_mixext = ctl;
+
+ sprintf (tmp, "vmix%d", mixer->instance_num);
+ if ((mixer->client_mixer_group = mixer_ext_create_group (mixer_dev, 0, tmp)) < 0)
+ return mixer->client_mixer_group;
+
+ return 0;
+}
+
+static int
+create_input_controls (int mixer_dev)
+{
+ int err;
+ vmix_mixer_t *mixer = mixer_devs[mixer_dev]->vmix_devc;
+ char name[32];
+
+ if (!(mixer->vmix_flags & VMIX_NOMAINVOL))
+ {
+ sprintf (name, "vmix%d-invol", mixer->instance_num);
+
+ if ((err = mixer_ext_create_control (mixer_dev, 0, 0, vmix_invol,
+ MIXT_MONOSLIDER16,
+ name, DB_SIZE * 5,
+ MIXF_READABLE | MIXF_WRITEABLE |
+ MIXF_CENTIBEL | MIXF_PCMVOL)) < 0)
+ return err;
+
+ sprintf (name, "vmix%d-invu", mixer->instance_num);
+ if ((err = mixer_ext_create_control (mixer_dev, 0, 1, vmix_invol,
+ MIXT_STEREOPEAK,
+ name, 144,
+ MIXF_READABLE | MIXF_DECIBEL)) < 0)
+ return err;
+
+ if (mixer->first_input_mixext == -1)
+ mixer->first_input_mixext = err;
+ }
+
+ return 0;
+}
+
+static int
+create_duplex_controls (int mixer_dev)
+{
+ int err;
+
+ if ((err=create_output_controls (mixer_dev)) < 0)
+ return err;
+
+ return create_input_controls (mixer_dev);
+}
+
+static int
+vmix_process_chninfo (vmix_channel_map_t * output, oss_chninfo * input,
+ int maxchan)
+{
+ int i, val;
+
+ for (i=0; i < sizeof (vmix_channel_map_t); i++)
+ {
+ val = (*input)[i];
+ if ((val <= 0) || (val > maxchan) || (val == i+1))
+ {
+ (*input)[i] = 0;
+ (*output)[i] = i;
+ }
+ else
+ {
+ (*output)[i] = val-1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Audio virtual device routines
+ */
+
+static int
+vmix_set_rate (int dev, int arg)
+{
+ vmix_mixer_t *mixer = audio_engines[dev]->devc;
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->rate;
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ if (portc->do_src)
+ {
+ /*
+ * Stick with the master device rate if the requested rate is
+ * not inside the range supported by the simple interpolation algorithm.
+ * In that way the rate conversion will be handled by the audio core
+ * with better quality.
+ */
+ if (arg > (mixer->play_engine.rate * 5) / 4) /* At most 1.25*master_rate */
+ arg = mixer->play_engine.rate;
+
+/*
+ * The simple linear interpolation algorithm cannot handle more than 2x rate
+ * boosts reliably so don't permit them.
+ */
+ if (arg < mixer->play_engine.rate / 2)
+ arg = mixer->play_engine.rate;
+
+ return portc->rate = arg;
+ }
+#endif
+
+ return portc->rate = mixer->play_engine.rate;
+}
+
+static short
+vmix_set_channels (int dev, short arg)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+
+ if (portc->dev_type == DT_LOOP)
+ {
+ return portc->mixer->play_engine.channels;
+ }
+
+ if (arg == 0)
+ {
+ return portc->channels;
+ }
+
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if (arg > portc->mixer->play_engine.channels)
+ arg = portc->mixer->play_engine.channels;
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (arg > portc->mixer->record_engine.channels)
+ arg = portc->mixer->record_engine.channels;
+ }
+ return portc->channels = arg;
+}
+
+/*ARGSUSED*/
+static unsigned int
+vmix_set_format (int dev, unsigned int arg)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+
+ if (portc->dev_type == DT_LOOP)
+ return portc->mixer->play_engine.fmt;
+
+ return portc->fmt = AFMT_S16_NE;
+}
+
+static int
+export_names (oss_mixer_enuminfo * ei, char *names)
+{
+ int n = 1, p = 0;
+
+ strcpy (ei->strings, names);
+ names = ei->strings;
+
+ ei->strindex[0] = 0;
+
+ while (names[p] != 0)
+ {
+ if (names[p] == ' ')
+ {
+ names[p] = 0;
+ ei->strindex[n++] = p + 1;
+ }
+ p++;
+ }
+
+ ei->nvalues = n;
+
+ return 0;
+}
+
+static int
+vmix_ioctl_override (int dev, unsigned int cmd, ioctl_arg arg)
+{
+/*
+ * Redirect all mixer (also legacy) ioctl calls to the master device
+ * if the master device driver has exported its ioctl override method.
+ *
+ * All other ioctl calls will be processed in normal way (by oss_audio_core.c)
+ * because we return OSS_EAGAIN.
+ */
+
+ if ((((cmd >> 8) & 0xff) == 'M') || (((cmd >> 8) & 0xff) == 'X'))
+ {
+ vmix_mixer_t *mixer = audio_engines[dev]->devc;
+ adev_t *adev;
+
+ adev=audio_engines[mixer->masterdev];
+
+ if (adev->d->adrv_ioctl_override == NULL)
+ return OSS_EAGAIN;
+
+ return adev->d->adrv_ioctl_override(adev->engine_num, cmd, arg);
+ }
+
+ return OSS_EAGAIN;
+}
+
+static int
+vmix_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ vmix_mixer_t *mixer = audio_engines[dev]->devc;
+ int val, left, right;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_SETPLAYVOL:
+ left = (*arg) & 0xff;
+ if (left > 100)
+ left = 100;
+ left = (left * DB_SIZE * 5) / 100;
+ portc->volume[0] = left;
+
+ right = (*arg >> 8) & 0xff;
+ if (right > 100)
+ right = 100;
+ right = (right * DB_SIZE * 5) / 100;
+ portc->volume[1] = right;
+
+ if (mixer->output_mixer_dev >= 0
+ && mixer->output_mixer_dev < num_mixers)
+ mixer_devs[mixer->output_mixer_dev]->modify_counter++;
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GETPLAYVOL:
+ left = (portc->volume[0] * 100) / (DB_SIZE * 5);
+ right = (portc->volume[1] * 100) / (DB_SIZE * 5);
+ *arg = left | (right << 8);
+ return 0;
+ break;
+
+ case SNDCTL_DSP_GET_PLAYTGT:
+ return *arg = portc->play_choffs / 2;
+ break;
+
+ case SNDCTL_DSP_SET_PLAYTGT:
+ val = (*arg) * 2;
+ if (val < 0)
+ return OSS_EIO;
+ if (val >= mixer->play_engine.channels)
+ return OSS_EIO;
+ portc->play_choffs = val;
+ return *arg = val / 2;
+ break;
+
+ case SNDCTL_DSP_GET_PLAYTGT_NAMES:
+ {
+ oss_mixer_enuminfo *ei = (oss_mixer_enuminfo *) arg;
+ int n, p, i;
+ char *names = NULL;
+
+ names = audio_engines[mixer->masterdev]->outch_names;
+
+ memset (ei, 0, sizeof (*ei));
+
+ n = mixer->play_engine.channels / 2;
+ if (n < 1)
+ n = 1;
+ ei->nvalues = n;
+ p = 0;
+
+ if (n <= 1) /* Only one alternative */
+ {
+ ei->nvalues = 1;
+ ei->strindex[0] = 0;
+ sprintf (ei->strings, "default");
+
+ }
+ else
+ {
+ /* Multiple alternatives */
+
+ if (names != NULL)
+ return export_names (ei, names);
+
+ for (i = 0; i < n; i++)
+ {
+ ei->strindex[i] = p;
+
+ sprintf (&ei->strings[p], "CH%d/%d", i * 2 + 1, i * 2 + 2);
+
+ p += strlen (&ei->strings[p]) + 1;
+ }
+ }
+
+ return 0;
+ }
+ break;
+
+/*
+ * Bypass the recording source and level calls to the master device if only one
+ * recording client is active and if the master device is not a multi channel
+ * one (probably professional device).
+ */
+ case SNDCTL_DSP_GETRECVOL:
+ case SNDCTL_DSP_SETRECVOL:
+
+ if (mixer->inputdev == -1) /* No input device */
+ return OSS_EINVAL;
+ if (mixer->open_inputs < 2 && mixer->record_engine.channels <= 2)
+ return oss_audio_ioctl (mixer->inputdev, NULL, cmd, arg);
+
+ return OSS_EINVAL;
+ break;
+
+/*
+ * Recording source selection. This can be done in two different ways depending
+ * on the hardware capabilities:
+ *
+ * 1) If the input master device has multiple channels then select one of
+ * the stereo pairs. It is likely that such device is a professional
+ * audio card that uses fixed inputs anyway.
+ * 2) For stereo input only devices bypass the RECSRC ioctl calls to the
+ * actual hardware driver. However this cannot be done when multiple
+ * client applications have the same device opened for recording. In
+ * such situation switching the recording source would disturb other
+ * applications that already have recording going on.
+ */
+
+ case SNDCTL_DSP_GET_RECSRC:
+
+ if (mixer->inputdev == -1) /* No input device */
+ return OSS_EINVAL;
+ if (mixer->open_inputs < 2 && mixer->record_engine.channels <= 2)
+ return oss_audio_ioctl (mixer->inputdev, NULL, cmd, arg);
+
+ return *arg = portc->rec_choffs / 2;
+ break;
+
+ case SNDCTL_DSP_SET_RECSRC:
+
+ if (mixer->inputdev == -1) /* No input device */
+ return OSS_EINVAL;
+
+ if (mixer->open_inputs < 2 && mixer->record_engine.channels <= 2)
+ return oss_audio_ioctl (mixer->inputdev, NULL, cmd, arg);
+
+ val = (*arg) * 2;
+ if (val < 0)
+ return OSS_EIO;
+ if (val >= mixer->record_engine.channels)
+ return OSS_EIO;
+ portc->rec_choffs = val;
+ return *arg = val / 2;
+ break;
+
+ case SNDCTL_DSP_GET_RECSRC_NAMES:
+ {
+ oss_mixer_enuminfo *ei = (oss_mixer_enuminfo *) arg;
+ int n, p, i;
+ char *names = NULL;
+
+ if (mixer->inputdev == -1)
+ return OSS_EINVAL;
+
+ if (mixer->open_inputs < 2 && mixer->record_engine.channels <= 2)
+ return oss_audio_ioctl (mixer->inputdev, NULL, cmd, arg);
+
+ names = audio_engines[mixer->inputdev]->inch_names;
+
+ memset (ei, 0, sizeof (*ei));
+
+ n = mixer->record_engine.channels / 2;
+ if (n < 1)
+ n = 1;
+ ei->nvalues = n;
+ p = 0;
+
+ if (n <= 1) /* Only one alternative */
+ {
+ /*
+ * It might be better to get the name of the current recording
+ * source from the master device. However for the time being
+ * we will return just "default".
+ */
+ ei->nvalues = 1;
+ ei->strindex[0] = 0;
+ sprintf (ei->strings, "default");
+ }
+ else
+ {
+ /* Multiple alternatives */
+
+ if (names != NULL)
+ return export_names (ei, names);
+
+ for (i = 0; i < n; i++)
+ {
+ ei->strindex[i] = p;
+
+ sprintf (&ei->strings[p], "CH%d/%d", i * 2 + 1, i * 2 + 2);
+
+ p += strlen (&ei->strings[p]) + 1;
+ }
+ }
+
+ return 0;
+ }
+ break;
+ }
+ return OSS_EINVAL;
+}
+
+static void
+vmix_halt_input (int dev)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ vmix_mixer_t *mixer = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ portc->trigger_bits &= PCM_ENABLE_INPUT;
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+}
+
+static void
+vmix_halt_output (int dev)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ vmix_mixer_t *mixer = audio_engines[dev]->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ portc->trigger_bits &= PCM_ENABLE_OUTPUT;
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+}
+
+static void
+vmix_reset (int dev)
+{
+ vmix_halt_input (dev);
+ vmix_halt_output (dev);
+}
+
+static int
+start_engines (vmix_mixer_t * mixer)
+{
+ int err;
+ int trig;
+
+ adev_t *adev_in, *adev_out;
+ dmap_t *dmap_in, *dmap_out;
+
+ if (mixer->masterdev_opened)
+ {
+ return 0;
+ }
+
+ mixer->master_finfo.mode = OPEN_WRITE;
+ if (mixer->inputdev == mixer->masterdev)
+ mixer->master_finfo.mode |= OPEN_READ;
+ mixer->master_finfo.acc_flags = 0;
+
+ adev_out = adev_in = audio_engines[mixer->masterdev];
+
+ if (mixer->inputdev > -1)
+ adev_in = audio_engines[mixer->inputdev];
+
+ adev_out->cooked_enable = 0;
+
+ if ((err =
+ oss_audio_open_engine (mixer->masterdev, OSS_DEV_DSP,
+ &mixer->master_finfo, 1, OF_SMALLBUF,
+ NULL)) < 0)
+ {
+ return err;
+ }
+
+ dmap_out = adev_out->dmap_out;
+ dmap_in = adev_in->dmap_in;
+ adev_out->cooked_enable = 0;
+
+ if (mixer->inputdev > -1 && mixer->inputdev != mixer->masterdev)
+ {
+ /*
+ * Open input device
+ */
+ adev_in->cooked_enable = 0;
+ mixer->input_finfo.mode = OPEN_READ;
+ mixer->input_finfo.acc_flags = 0;
+ if ((err =
+ oss_audio_open_engine (mixer->inputdev, OSS_DEV_DSP,
+ &mixer->input_finfo, 1, OF_SMALLBUF,
+ NULL)) < 0)
+ {
+ oss_audio_release (mixer->masterdev, &mixer->master_finfo);
+ return err;
+ }
+ strcpy (adev_in->cmd, "VMIX_IN");
+ strcpy (adev_in->label, "VMIX_IN");
+ dmap_in = adev_in->dmap_in;
+ adev_out->pid = 0;
+ adev_out->cooked_enable = 0;
+
+ vmix_setup_record_engine (mixer, adev_in, dmap_in);
+
+ dmap_in->dma_mode = PCM_ENABLE_INPUT;
+ trig = 0;
+ oss_audio_ioctl (mixer->inputdev, NULL, SNDCTL_DSP_SETTRIGGER,
+ (ioctl_arg) & trig);
+ }
+
+ mixer->masterdev_opened = 1;
+ strcpy (adev_out->cmd, "VMIX");
+ strcpy (adev_out->label, "VMIX");
+ adev_out->pid = 0;
+ adev_out->cooked_enable = 0;
+
+ vmix_setup_play_engine (mixer, adev_out, dmap_out);
+
+ trig = 0;
+ dmap_out->dma_mode = PCM_ENABLE_OUTPUT;
+
+ oss_audio_ioctl (mixer->masterdev, NULL, SNDCTL_DSP_SETTRIGGER,
+ (ioctl_arg) & trig);
+ trig = PCM_ENABLE_OUTPUT;
+ if (mixer->masterdev == mixer->inputdev)
+ trig |= PCM_ENABLE_INPUT;
+ else
+ {
+ int trig2 = PCM_ENABLE_INPUT;
+ if (mixer->inputdev > -1)
+ if (oss_audio_ioctl (mixer->inputdev, NULL, SNDCTL_DSP_SETTRIGGER,
+ (ioctl_arg) & trig2) < 0)
+ {
+ cmn_err (CE_WARN, "Trigger (input) failed\n");
+ }
+ }
+
+ if (oss_audio_ioctl (mixer->masterdev, NULL, SNDCTL_DSP_SETTRIGGER,
+ (ioctl_arg) & trig) < 0)
+ {
+ cmn_err (CE_WARN, "Trigger failed\n");
+ }
+
+ return 0;
+}
+
+static void
+stop_engines (vmix_mixer_t * mixer)
+{
+ oss_native_word flags;
+
+ if (mixer->masterdev_opened)
+ {
+ adev_t *adev;
+ dmap_t *dmap;
+
+ adev = audio_engines[mixer->masterdev];
+ dmap = adev->dmap_out;
+
+ oss_audio_ioctl (mixer->masterdev, NULL, SNDCTL_DSP_HALT, 0);
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ dmap->audio_callback = NULL;
+
+ dmap = adev->dmap_in;
+ if (dmap != NULL)
+ {
+ dmap->audio_callback = NULL;
+ }
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+
+ oss_audio_release (mixer->masterdev, &mixer->master_finfo);
+
+ if (mixer->inputdev > -1 && mixer->inputdev != mixer->masterdev)
+ {
+ adev = audio_engines[mixer->inputdev];
+ dmap = adev->dmap_in;
+
+ oss_audio_ioctl (mixer->inputdev, NULL, SNDCTL_DSP_HALT, 0);
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ dmap->audio_callback = NULL;
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+ oss_audio_release (mixer->inputdev, &mixer->input_finfo);
+ }
+
+ mixer->masterdev_opened = 0;
+ }
+}
+
+/*ARGSUSED*/
+static int
+vmix_open (int dev, int mode, int open_flags)
+{
+ adev_t *adev = audio_engines[dev];
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ vmix_mixer_t *mixer = portc->mixer;
+ oss_native_word flags;
+ int start = 0;
+
+ if (mode & portc->disabled_modes)
+ return OSS_EACCES;
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+
+ portc->open_pending = 0; /* Was set to 1 by vmix_create_client */
+
+ if (portc->open_mode != 0)
+ {
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+ portc->trigger_bits = 0;
+ portc->play_mixing_func = NULL;
+ portc->rec_mixing_func = NULL;
+ portc->do_src = 0;
+ portc->play_choffs = 0; /* Left align */
+ portc->rec_choffs = 0; /* Left align */
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ /*
+ * For the time being always enable local linear interpolation to make
+ * vmix devices to work faster.
+ */
+ if (mixer->src_quality == 0)
+ portc->do_src = 1;
+ else
+#endif
+ {
+ adev->src_quality = mixer->src_quality;
+ if (mixer->src_quality == 6) /* SRC=OFF */
+ adev->cooked_enable = 0;
+ else
+ {
+ if (adev->src_quality < 1)
+ adev->src_quality = 1;
+ else if (adev->src_quality > 5)
+ adev->src_quality = 5;
+ }
+ }
+#ifdef CONFIG_OSS_VMIX_FLOAT
+/*
+ * Enable local src (linear interpolation) for mmap applications and SADA
+ * support (sadasupport.c).
+ */
+ if (open_flags & (OF_DEVAUDIO | OF_MMAP))
+ portc->do_src = 1;
+#endif
+
+ /*
+ * However SRC is not supported for input
+ */
+ if (mode == PCM_ENABLE_INPUT)
+ portc->do_src = 0;
+
+ if (mixer->open_devices++ == 0)
+ start = 1;
+
+ if (mode & PCM_ENABLE_INPUT)
+ mixer->open_inputs++;
+
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+
+ if (start)
+ {
+ int err;
+
+ if ((err = start_engines (mixer)) < 0)
+ {
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ mixer->open_devices = 0;
+ mixer->open_inputs=0;
+ portc->open_mode = 0;
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static void
+vmix_close (int dev, int mode)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ vmix_mixer_t *mixer = portc->mixer;
+ oss_native_word flags;
+ int stop = 0;
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ if (mixer->open_devices-- == 1)
+ stop = 1;
+
+ if (mode & PCM_ENABLE_INPUT)
+ mixer->open_inputs--;
+
+ portc->open_mode = 0;
+ portc->trigger_bits = 0;
+ portc->play_mixing_func = NULL;
+ portc->rec_mixing_func = NULL;
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+
+ if (stop)
+ {
+ stop_engines (mixer);
+ }
+
+}
+
+/*ARGSUSED*/
+static void
+vmix_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+}
+
+/*ARGSUSED*/
+static void
+vmix_start_input (int dev, oss_native_word buf, int count, int fragsize,
+ int intrflag)
+{
+}
+
+static void
+vmix_trigger (int dev, int state)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ vmix_mixer_t *mixer = audio_engines[dev]->devc;
+
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits |= state & PCM_ENABLE_OUTPUT;
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits |= state & PCM_ENABLE_INPUT;
+ }
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+}
+
+/*ARGSUSED*/
+static int
+vmix_prepare_for_input (int dev, int bsize, int bcount)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ /* int bytes; */
+
+ switch (portc->fmt)
+ {
+ case AFMT_S16_NE:
+ portc->rec_mixing_func = vmix_rec_export_16ne;
+ /* bytes = 2; */
+ break;
+
+ case AFMT_S16_OE:
+ portc->rec_mixing_func = vmix_rec_export_16oe;
+ /* bytes = 2; */
+ break;
+
+ case AFMT_S32_NE:
+ portc->rec_mixing_func = vmix_rec_export_32ne;
+ /* bytes = 4; */
+ break;
+
+ case AFMT_S32_OE:
+ portc->rec_mixing_func = vmix_rec_export_32oe;
+ /* bytes = 4; */
+ break;
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ case AFMT_FLOAT:
+ portc->rec_mixing_func = vmix_rec_export_float;
+ /* bytes = 4; */
+ break;
+#endif
+ }
+
+ portc->rec_dma_pointer = 0;
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+vmix_prepare_for_output (int dev, int bsize, int bcount)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ /* int bytes; */
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ vmix_mixer_t *mixer = audio_engines[dev]->devc;
+
+ memset (&portc->play_dma_pointer_src, 0, sizeof (portc->play_dma_pointer_src)); /* 0.0 */
+
+ if (portc->rate != mixer->play_engine.rate) /* Sample rate conversions needed */
+ {
+ switch (portc->fmt)
+ {
+ case AFMT_S16_NE:
+ portc->play_mixing_func = vmix_outmix_16ne_src;
+ /* bytes = 2; */
+ break;
+
+ case AFMT_S16_OE:
+ portc->play_mixing_func = vmix_outmix_16oe_src;
+ /* bytes = 2; */
+ break;
+
+ case AFMT_S32_NE:
+ portc->play_mixing_func = vmix_outmix_32ne_src;
+ /* bytes = 4; */
+ break;
+
+ case AFMT_S32_OE:
+ portc->play_mixing_func = vmix_outmix_32oe_src;
+ /* bytes = 4; */
+ break;
+
+ case AFMT_FLOAT:
+ portc->play_mixing_func = vmix_outmix_float_src;
+ /* bytes = 4; */
+ break;
+ }
+ }
+ else
+#endif
+ {
+ switch (portc->fmt)
+ {
+ case AFMT_S16_NE:
+ portc->play_mixing_func = vmix_outmix_16ne;
+ /* bytes = 2; */
+ break;
+
+ case AFMT_S16_OE:
+ portc->play_mixing_func = vmix_outmix_16oe;
+ /* bytes = 2; */
+ break;
+
+ case AFMT_S32_NE:
+ portc->play_mixing_func = vmix_outmix_32ne;
+ /* bytes = 4; */
+ break;
+
+ case AFMT_S32_OE:
+ portc->play_mixing_func = vmix_outmix_32oe;
+ /* bytes = 4; */
+ break;
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ case AFMT_FLOAT:
+ portc->play_mixing_func = vmix_outmix_float;
+ /* bytes = 4; */
+ break;
+#endif
+ }
+ }
+
+ portc->play_dma_pointer = 0;
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+vmix_alloc_buffer (int dev, dmap_t * dmap, int direction)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+
+ /* Loopback devices share the DMA buffer of the master device */
+ if (portc->dev_type == DT_LOOP)
+ {
+ adev_t *adev;
+ adev = audio_engines[portc->mixer->masterdev];
+
+ dmap->dmabuf_phys = 0;
+ dmap->dmabuf = adev->dmap_out->dmabuf;
+ dmap->buffsize = adev->dmap_out->buffsize;
+ dmap->buffer_protected = 1; /* Write protect flag for audio core */
+
+ return 0;
+ }
+
+ if (dmap->dmabuf != NULL)
+ return 0;
+
+#ifdef ALLOW_BUFFER_MAPPING
+ /*
+ * Apps may use mmap() so allocate a buffer that is
+ * suitable for it.
+ */
+ {
+ int err;
+
+ if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0)
+ return err;
+ }
+#else
+#define MY_BUFFSIZE (64*1024)
+ dmap->dmabuf_phys = 0;
+ dmap->dmabuf = KERNEL_MALLOC (MY_BUFFSIZE);
+ if (dmap->dmabuf == NULL)
+ return OSS_ENOSPC;
+ dmap->buffsize = MY_BUFFSIZE;
+#endif
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+vmix_free_buffer (int dev, dmap_t * dmap, int direction)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+
+ /* Loopback devices share the DMA buffer of the master device so don't free it */
+ if (portc->dev_type == DT_LOOP)
+ {
+ dmap->dmabuf_phys = 0;
+ dmap->dmabuf = NULL;
+ dmap->buffsize = 0;
+ return 0;
+ }
+
+ if (dmap->dmabuf == NULL)
+ return 0;
+#ifdef ALLOW_BUFFER_MAPPING
+ oss_free_dmabuf (dev, dmap);
+#else
+ KERNEL_FREE (dmap->dmabuf);
+#endif
+
+ dmap->dmabuf = NULL;
+ dmap->dmabuf_phys = 0;
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+vmix_get_output_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+
+ return portc->play_dma_pointer;
+}
+
+/*ARGSUSED*/
+static int
+vmix_get_input_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+
+ if (portc->dev_type == DT_LOOP)
+ {
+ int p;
+ /* Return the write pointer of the master side */
+ adev_t *adev;
+ adev = audio_engines[portc->mixer->masterdev];
+
+ p = (int) (adev->dmap_out->user_counter % adev->dmap_out->bytes_in_use);
+
+ return p;
+ }
+
+ return portc->rec_dma_pointer;
+}
+
+static int
+vmix_local_qlen (int dev)
+{
+ vmix_portc_t *portc;
+ vmix_mixer_t *mixer;
+ int samplesize;
+ int len = 0;
+
+ portc = audio_engines[dev]->portc;
+ mixer = audio_engines[dev]->devc;
+
+ samplesize = 1;
+ if (portc->bits == AFMT_S16_NE)
+ samplesize *= 2;
+ samplesize *= portc->channels;
+
+ /* Compute the number of samples in the physical device */
+ oss_audio_ioctl (mixer->masterdev, NULL, SNDCTL_DSP_GETODELAY,
+ (ioctl_arg) & len);
+
+ if (mixer->play_engine.bits == 16)
+ len /= 2; /* 16 bit samples */
+ else
+ len /= 4; /* 32 bit samples */
+ len /= mixer->play_engine.channels;
+
+ /* Convert # of samples to local bytes */
+
+ len *= portc->channels;
+
+ if (portc->bits == AFMT_S16_NE)
+ len *= 2;
+
+ return len;
+}
+
+/*ARGSUSED*/
+static void
+vmix_setup_fragments (int dev, dmap_t * dmap, int direction)
+{
+/*
+ * vmix_setup_fragments is used to force fragment/buffer parameters of
+ * loopback devices to match the DMA buffer of the physical device.
+ */
+ vmix_portc_t *portc = audio_engines[dev]->portc;
+ adev_t *adev;
+
+ if (portc->dev_type != DT_LOOP)
+ return;
+
+ adev = audio_engines[portc->mixer->masterdev];
+
+ /* Copy the buffering parameters */
+ dmap->fragment_size = adev->dmap_out->fragment_size;
+ dmap->nfrags = adev->dmap_out->nfrags;
+ dmap->bytes_in_use = adev->dmap_out->bytes_in_use;
+}
+
+static audiodrv_t vmix_driver = {
+ vmix_open,
+ vmix_close,
+ vmix_output_block,
+ vmix_start_input,
+ vmix_ioctl,
+ vmix_prepare_for_input,
+ vmix_prepare_for_output,
+ vmix_reset,
+ vmix_local_qlen,
+ NULL,
+ vmix_halt_input,
+ vmix_halt_output,
+ vmix_trigger,
+ vmix_set_rate,
+ vmix_set_format,
+ vmix_set_channels,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vmix_alloc_buffer,
+ vmix_free_buffer,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ vmix_get_input_buffer_pointer,
+ vmix_get_output_buffer_pointer,
+ NULL,
+ vmix_setup_fragments
+};
+
+/*
+ * Initialization support
+ */
+
+static void
+relink_masterdev (vmix_mixer_t * mixer)
+{
+/*
+ * Insert the VMIX devices to the engine search list of the master device.
+ */
+ adev_t *first_adev, *last_adev, *master_adev;
+ int i, n = mixer->num_clientdevs;
+
+ n = n - 1;
+ if (n < 1)
+ return;
+
+ first_adev = audio_engines[mixer->client_portc[0]->audio_dev];
+ last_adev = audio_engines[mixer->client_portc[n]->audio_dev];
+ master_adev = audio_engines[mixer->masterdev];
+
+/*
+ * Relink client devices in the proper way.
+ */
+
+ for (i=0;i<mixer->num_clientdevs;i++)
+ {
+ adev_t *adev = audio_engines[mixer->client_portc[i]->audio_dev];
+
+ if (i==mixer->num_clientdevs-1)
+ adev->next_out = NULL;
+ else
+ adev->next_out = audio_engines[mixer->client_portc[i+1]->audio_dev];
+ }
+
+ if (master_adev == NULL || master_adev->unloaded || !master_adev->enabled)
+ {
+ cmn_err (CE_WARN, "vmix: master_adev %d is not available\n", mixer->masterdev);
+ cmn_err (CE_CONT, "master_adev=%p, unloaded=%d, enabled=%d\n", master_adev, master_adev->unloaded, master_adev->enabled);
+ return;
+ }
+
+ last_adev->next_out = NULL;
+ master_adev->next_out = first_adev;
+}
+
+static void
+unlink_masterdev (vmix_mixer_t * mixer)
+{
+/*
+ * Remove the VMIX devices from the engine search list of the master device.
+ */
+ adev_t *last_adev, *master_adev;
+ int i, n = mixer->num_clientdevs;
+
+ if (n < 1)
+ return;
+
+ if (n > MAX_CLIENTS)
+ n = MAX_CLIENTS;
+
+ for (i=0;i<n;i++)
+ {
+ /*
+ * Mark all client engines as unloaded
+ */
+ adev_t *adev = audio_engines[mixer->client_portc[i]->audio_dev];
+
+ adev->unloaded = 1;
+ }
+
+ last_adev = audio_engines[mixer->client_portc[n-1]->audio_dev];
+ master_adev = audio_engines[mixer->masterdev];
+
+ if (master_adev == NULL)
+ {
+ cmn_err (CE_WARN, "master_adev == NULL\n");
+ return;
+ }
+
+ master_adev->vmix_mixer=NULL;
+
+ master_adev->next_out = last_adev->next_out;
+ last_adev->next_out = NULL;
+}
+
+static void
+relink_inputdev (vmix_mixer_t * mixer)
+{
+/*
+ * Insert the VMIX devices to the engine search list of the input device.
+ */
+ adev_t *first_adev, *last_adev, *input_adev;
+ int i, n = mixer->num_clientdevs;
+
+/*
+ * Relink client devices in the proper way.
+ */
+
+ for (i=0;i<mixer->num_clientdevs;i++)
+ {
+ adev_t *adev = audio_engines[mixer->client_portc[i]->audio_dev];
+
+ if (i==mixer->num_clientdevs-1)
+ adev->next_in = NULL;
+ else
+ adev->next_in = audio_engines[mixer->client_portc[i+1]->audio_dev];
+ }
+
+ if (n < 1)
+ return;
+
+ n = n - 1;
+
+ first_adev = audio_engines[mixer->client_portc[0]->audio_dev];
+ last_adev = audio_engines[mixer->client_portc[n]->audio_dev];
+ input_adev = audio_engines[mixer->inputdev];
+
+ last_adev->next_in = NULL;
+ input_adev->next_in = first_adev;
+}
+
+static void
+unlink_inputdev (vmix_mixer_t * mixer)
+{
+/*
+ * Remove the VMIX devices from the engine search list of the input device.
+ */
+ adev_t *last_adev, *input_adev;
+ int n = mixer->num_clientdevs;
+
+ if (n < 1)
+ return;
+
+ if (n > MAX_CLIENTS)
+ n = MAX_CLIENTS;
+
+ n = n - 1;
+
+ last_adev = audio_engines[mixer->client_portc[n]->audio_dev];
+ input_adev = audio_engines[mixer->inputdev];
+ input_adev->vmix_mixer=NULL;
+
+ input_adev->next_in = last_adev->next_in;
+ last_adev->next_in = NULL;
+}
+
+static int
+create_vmix_engine (vmix_mixer_t * mixer)
+{
+ vmix_portc_t *portc;
+ int n;
+ char tmp[128];
+ adev_t *adev, *master_adev;
+ int opts = ADEV_VIRTUAL | ADEV_DEFAULT | ADEV_VMIX;
+
+ n = mixer->num_clientdevs;
+
+ /*
+ * ADEV_HIDDEN is used for the VMIX devices because they should not be
+ * made visible to applications. The audio core will automatically
+ * redirect applications opening the master device to use the
+ * VMIX devices.
+ *
+ * However make the client devices visible if requested by vmixctl
+ */
+ if (!(mixer->attach_flags & VMIX_INSTALL_VISIBLE))
+ {
+ opts |= ADEV_HIDDEN;
+
+ if (n > 0)
+ opts |= ADEV_SHADOW;
+ }
+
+ if (mixer->masterdev == -1)
+ return OSS_ENXIO;
+
+ if (n + 1 >= MAX_CLIENTS) /* Cannot create more client engines */
+ return OSS_EBUSY;
+
+ /*
+ * Other than the first instance are unlikely to be default the default
+ * audio device of the system.
+ */
+ if (mixer->instance_num > 0)
+ opts |= ADEV_SPECIAL;
+
+ if ((portc = PMALLOC (mixer->osdev, sizeof (*portc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate portc structure\n");
+ return OSS_ENOMEM;
+ }
+ memset (portc, 0, sizeof (*portc));
+ portc->open_pending = 1; /* Reserve this engine to the client it was created for */
+
+ mixer->num_clientdevs++;
+
+ portc->num = n;
+
+ mixer->client_portc[n] = portc;
+
+ if (mixer->inputdev == -1)
+ opts |= ADEV_NOINPUT;
+ else
+ opts |= ADEV_DUPLEX;
+
+ master_adev = audio_engines[mixer->masterdev];
+
+ sprintf (tmp, "%s (vmix)", master_adev->name);
+
+ if ((portc->audio_dev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
+ mixer->osdev,
+ mixer->master_osdev,
+ tmp,
+ &vmix_driver,
+ sizeof (audiodrv_t),
+ opts,
+ AFMT_S16_NE | AFMT_S32_NE |
+ AFMT_FLOAT, mixer, -1)) < 0)
+ {
+ return portc->audio_dev;
+ }
+
+ adev = audio_engines[portc->audio_dev];
+
+ if (master_adev->d->adrv_ioctl_override != NULL)
+ adev->d->adrv_ioctl_override = vmix_ioctl_override;
+
+ adev->mixer_dev = mixer->output_mixer_dev;
+ adev->portc = portc;
+ adev->min_rate = mixer->play_engine.rate;
+ adev->max_rate = mixer->play_engine.rate;
+ adev->caps |= PCM_CAP_FREERATE | PCM_CAP_BATCH | PCM_CAP_MULTI;
+ adev->min_channels = 2;
+ adev->max_channels = mixer->play_engine.channels;
+
+ adev->rate_source = audio_engines[mixer->masterdev]->rate_source;
+ portc->dev_type = DT_OUT;
+ portc->mixer = mixer;
+ portc->volume[0] = DB_SIZE * 5;
+ portc->volume[1] = DB_SIZE * 5;
+ if (mixer->inputdev == -1)
+ portc->disabled_modes = OPEN_READ;
+
+ portc->fmt = AFMT_S16_NE;
+ portc->bits = 16;
+ portc->channels = 2;
+
+#ifdef VDEV_SUPPORT
+ /* Report device node of the master device */
+ if (opts & ADEV_HIDDEN)
+ strcpy (adev->devnode, master_adev->devnode);
+ oss_add_audio_devlist (OPEN_READ | OPEN_WRITE, master_adev->audio_devfile);
+#endif
+
+ if (n == 0 && mixer->masterdev < num_audio_engines
+ && audio_engines[mixer->masterdev] != NULL)
+ {
+ audio_engines[mixer->masterdev]->redirect_out = portc->audio_dev;
+
+ if (mixer->inputdev > -1)
+ {
+ audio_engines[mixer->inputdev]->redirect_in = portc->audio_dev;
+ audio_engines[mixer->masterdev]->redirect_in = portc->audio_dev;
+ }
+ }
+
+ relink_masterdev (mixer);
+
+ if (mixer->inputdev > -1)
+ relink_inputdev (mixer);
+
+
+ return portc->audio_dev;
+}
+
+static void
+create_loopdev (vmix_mixer_t * mixer)
+{
+ vmix_portc_t *portc;
+ int n;
+ char tmp[128], nick[16];
+ adev_t *adev;
+ int opts = ADEV_VIRTUAL | ADEV_NOOUTPUT;
+
+ if (mixer->masterdev == -1)
+ return;
+
+ if ((portc = PMALLOC (mixer->osdev, sizeof (*portc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot allocate portc structure\n");
+ return;
+ }
+ memset (portc, 0, sizeof (*portc));
+
+ n = mixer->num_loopdevs++;
+ portc->num = n;
+ mixer->loop_portc[n] = portc;
+
+ if (n > 0)
+ opts |= ADEV_SHADOW;
+
+ adev = audio_engines[mixer->masterdev];
+
+ sprintf (tmp, "%s (vmix) loopback record", adev->name);
+ sprintf (nick, "loop%d", n);
+
+ if ((portc->audio_dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION,
+ mixer->osdev,
+ mixer->master_osdev,
+ tmp,
+ &vmix_driver,
+ sizeof (audiodrv_t),
+ opts,
+ mixer->play_engine.fmt,
+ mixer, -1,
+ nick)) < 0)
+ {
+ return;
+ }
+
+ adev = audio_engines[portc->audio_dev];
+
+ adev->mixer_dev = mixer->output_mixer_dev;
+ adev->portc = portc;
+ adev->min_rate = mixer->play_engine.rate;
+ adev->max_rate = mixer->play_engine.rate;
+ adev->min_channels = mixer->play_engine.channels;
+ adev->max_channels = mixer->play_engine.channels;
+ adev->caps |= PCM_CAP_FREERATE | PCM_CAP_HIDDEN | PCM_CAP_BATCH | PCM_CAP_MULTI;
+ portc->mixer = mixer;
+ portc->dev_type = DT_LOOP;
+ portc->disabled_modes = OPEN_WRITE;
+ portc->fmt = AFMT_S16_NE;
+ portc->bits = 16;
+ portc->channels = 2;
+ portc->volume[0] = DB_SIZE * 5;
+ portc->volume[1] = DB_SIZE * 5;
+}
+
+static int
+uninit_vmix_instance(vmix_mixer_t *mixer)
+{
+ if (mixer->master_osdev != NULL)
+ {
+ MUTEX_CLEANUP (mixer->mutex);
+ }
+
+ if (mixer->masterdev < 0 || mixer->masterdev >= num_audio_engines)
+ return OSS_ENXIO;
+
+ if (!mixer->installed_ok)
+ {
+ return OSS_EIO;
+ }
+
+ if (audio_engines[mixer->masterdev] == NULL)
+ return OSS_ENXIO;
+
+/*
+ * Cleanup the engine redirection links
+ */
+
+ audio_engines[mixer->masterdev]->redirect_out = -1;
+
+ if (mixer->inputdev > -1)
+ {
+ audio_engines[mixer->inputdev]->redirect_in = -1;
+ audio_engines[mixer->masterdev]->redirect_in = -1;
+ }
+ unlink_masterdev (mixer);
+
+ if (mixer->inputdev > -1 && mixer->inputdev != mixer->masterdev)
+ {
+ unlink_inputdev (mixer);
+ }
+
+ return 0;
+}
+
+static int
+check_masterdev (void *mx, int reattach)
+{
+ vmix_mixer_t *mixer = mx;
+ adev_t *adev;
+
+ if (mixer->masterdev < 0 || mixer->masterdev >= num_audio_engines)
+ return OSS_ENXIO;
+
+ adev = audio_engines[mixer->masterdev];
+ DDB (cmn_err
+ (CE_CONT, "Check masterdev eng=%d/%s\n", adev->engine_num,
+ adev->name));
+
+ /* Don't accept virtual devices other than loopback ones */
+ if (adev->flags & ADEV_VIRTUAL && !(adev->flags & ADEV_LOOP))
+ return OSS_EIO;
+
+ if (adev->vmix_mixer != NULL) /* Already attached */
+ {
+ cmn_err(CE_CONT, "Vmix already attached to %s\n", adev->devnode);
+ return OSS_EBUSY;
+ }
+
+ if (adev->flags & ADEV_NOOUTPUT)
+ return OSS_EIO;
+
+ if (adev->flags & ADEV_DISABLE_VIRTUAL) /* Not compatible */
+ return OSS_EIO;
+
+ if (adev->vmix_flags & VMIX_DISABLED) /* Not compatible */
+ return OSS_EIO;
+
+ if (adev->max_channels < 2)
+ return OSS_EIO;
+
+ if (!(adev->oformat_mask & SUPPORTED_FORMATS))
+ return OSS_EIO;
+
+ if (mixer->inputdev != -1 && mixer->inputdev != mixer->masterdev)
+ if (audio_engines[mixer->inputdev]->vmix_mixer != NULL) /* Input master already driven */
+ {
+ cmn_err(CE_CONT, "Vmix already attached to %s\n", audio_engines[mixer->inputdev]->devnode);
+ return OSS_EBUSY;
+ }
+
+/*
+ * Good. Initialize all per-mixer variables
+ */
+ mixer->master_osdev = adev->master_osdev;
+ adev->vmix_mixer = mixer;
+ adev->prev_vmix_mixer = mixer;
+//cmn_err(CE_CONT, "Calling MUTEX_INIT mast=%p, mixer=%p, mutex=%p\n", mixer->master_osdev, mixer,mixer->mutex);
+ MUTEX_INIT (mixer->master_osdev, mixer->mutex, MH_DRV + 4);
+//cmn_err(CE_CONT, "OK\n");
+
+ DDB (cmn_err (CE_CONT, "Vmix masterdev=%d\n", mixer->masterdev));
+
+/*
+ * The device is OK. Next check for the input/duplex capability.
+ */
+
+ mixer->vmix_flags = adev->vmix_flags;
+ mixer->max_channels = adev->max_channels;
+
+ if (mixer->attach_flags & VMIX_INSTALL_NOINPUT)
+ mixer->vmix_flags |= VMIX_NOINPUT;
+
+ if (mixer->vmix_flags & VMIX_NOINPUT)
+ mixer->inputdev = -1;
+ else if (!(adev->flags & ADEV_NOINPUT) && (adev->flags & ADEV_DUPLEX))
+ {
+ mixer->inputdev = mixer->masterdev;
+ DDB (cmn_err
+ (CE_CONT, "Vmix masterdev=%d shared for input\n",
+ mixer->masterdev));
+ }
+
+/*
+ * At this point we know the input and output master devices so it's the time
+ * to create the virtual audio devices.
+ */
+
+ adev = audio_engines[mixer->masterdev];
+
+ if (mixer->osdev->first_mixer >= 0)
+ {
+ mixer->output_mixer_dev = mixer->osdev->first_mixer;
+ mixer->input_mixer_dev = mixer->osdev->first_mixer;
+ }
+
+ if (mixer->inputdev != -1 && mixer->inputdev != mixer->masterdev)
+ {
+ adev = audio_engines[mixer->inputdev];
+
+ adev->vmix_mixer = mixer;
+ adev->prev_vmix_mixer = mixer;
+
+ if (adev->mixer_dev != -1)
+ {
+ mixer->input_mixer_dev = adev->mixer_dev;
+ }
+ }
+
+ /*
+ * Warm up the engines so that client device creation knows the defaults
+ */
+ start_engines (mixer);
+ stop_engines (mixer);
+
+ mixer->installed_ok = 1;
+
+
+ if (!reattach)
+ {
+ if (mixer->output_mixer_dev > -1)
+ {
+ if (mixer->output_mixer_dev == mixer->input_mixer_dev)
+ {
+ mixer_ext_set_vmix_init_fn (mixer->output_mixer_dev,
+ create_duplex_controls, 20, mixer);
+ }
+ else
+ {
+ mixer_ext_set_vmix_init_fn (mixer->output_mixer_dev,
+ create_output_controls, 20, mixer);
+ }
+ }
+
+ if (mixer->output_mixer_dev != mixer->input_mixer_dev)
+ if (mixer->inputdev >= 0 &&
+ mixer->input_mixer_dev > -1)
+ {
+ mixer_ext_set_vmix_init_fn (mixer->input_mixer_dev,
+ create_input_controls, 10, mixer);
+ }
+ }
+/*
+ * Crate one client in advance so that that SNDCTL_AUDIOINFO can provide proper info.
+ */
+ if (!(mixer->attach_flags & VMIX_INSTALL_NOPREALLOC))
+ {
+ int cl, n=4;
+
+ if ((mixer->attach_flags & 0xff) != 0) /* Number of clients given */
+ {
+ n = mixer->attach_flags & 0xff;
+ if (n<1) n=1;
+ if (n>=MAX_CLIENTS) n=MAX_CLIENTS-1; /* TODO: Why n=MAX_CLIENTS doesn't work? */
+ }
+
+ for (cl=0;cl<n;cl++)
+ vmix_create_client (mixer);
+
+ /*
+ * Mark the engines to be free for use
+ */
+ for (cl=0;cl<mixer->num_clientdevs;cl++)
+ mixer->client_portc[cl]->open_pending = 0;
+ }
+
+ if (vmix_loopdevs>0)
+ {
+ int i;
+
+ for (i=0;i<vmix_loopdevs;i++)
+ create_loopdev (mixer);
+ }
+
+ DDB (cmn_err (CE_CONT, "Master dev %d is OK\n", adev->engine_num));
+
+ return 0;
+}
+
+int
+vmix_attach_audiodev(oss_device_t *osdev, int masterdev, int inputdev, unsigned int attach_flags)
+{
+/*
+ * Purpose: Create a vmix instance for an audio device.
+ *
+ * This function will be called by OSS drivers after installing the audio devices. It will create
+ * an vmix instance for the device.
+ *
+ * Parameters:
+ *
+ * osdev: The osdev structure of the actual hardware device.
+ * masterdev: The audio engine number of the master (playback or duplex) device.
+ * inputdev: Input master device (if different than masterdev). Value of -1 means that the
+ * masterdev device should also be used as the input master device (if it supports
+ * input).
+ * attach_flags: Flags like VMIX_INSTALL_NOPREALLOC.
+ */
+
+ vmix_mixer_t *mixer=NULL;
+ int reattach=0;
+ int err;
+ int i;
+
+ if (vmix_disabled) /* Vmix not available in the system */
+ return OSS_EIO;
+
+ /*
+ * If the vmix_no_autoattach option is set in osscore.conf then attach
+ * vmix only when 'vmixctl attach' is executed manually (VMIX_INSTALL_MANUAL).
+ */
+ if (vmix_no_autoattach && !(attach_flags & VMIX_INSTALL_MANUAL))
+ return 0;
+
+ if (flat_device_model)
+ {
+ attach_flags |= VMIX_INSTALL_VISIBLE;
+ attach_flags = (attach_flags & ~0xff) | 8; /* Precreate 8 client engines */
+ }
+
+ if (audio_engines[masterdev]->prev_vmix_mixer != NULL)
+ {
+ mixer = audio_engines[masterdev]->prev_vmix_mixer;
+
+ if (mixer->attach_flags != attach_flags) /* Not compatible */
+ mixer=NULL;
+ }
+
+ if (mixer == NULL)
+ {
+ if ((mixer = PMALLOC (osdev, sizeof (*mixer))) == NULL)
+ {
+ cmn_err (CE_CONT, "Cannot allocate memory for instance descriptor\n");
+ return OSS_ENOMEM;
+ }
+
+ memset (mixer, 0, sizeof (*mixer));
+ mixer->instance_num = num_instances++;
+ if (mixer->instance_num > 0)
+ mixer->osdev = osdev_clone (osdev, mixer->instance_num);
+ }
+ else
+ {
+ relink_masterdev (mixer);
+
+ if (mixer->inputdev > -1)
+ relink_inputdev (mixer);
+
+ reattach=1;
+ }
+
+ mixer->osdev = osdev;
+ mixer->first_input_mixext = -1;
+ mixer->first_output_mixext = -1;
+ mixer->src_quality = 0;
+
+ mixer->output_mixer_dev = -1;
+ mixer->input_mixer_dev = -1;
+
+ /*
+ * Mixer default levels
+ */
+ mixer->play_engine.outvol = DB_SIZE * 5;
+ mixer->record_engine.outvol = DB_SIZE * 5;
+#ifndef CONFIG_OSS_VMIX_FLOAT
+ mixer->play_engine.outvol -= 3; /* For overflow protection */
+#endif
+ for (i = 0; i < sizeof (vmix_channel_map_t); i++)
+ mixer->play_engine.channel_order[i] = i;
+
+ mixer->masterdev = masterdev;
+ mixer->inputdev = inputdev;
+ mixer->rate = 48000;
+ mixer->attach_flags = attach_flags;
+
+ DDB (cmn_err (CE_CONT, "Create instance %d\n", num_instances));
+ DDB (cmn_err (CE_CONT, "vmix_masterdev=%d\n", masterdev));
+ DDB (cmn_err (CE_CONT, "vmix_inputdev=%d\n", inputdev));
+ DDB (cmn_err (CE_CONT, "vmix_rate=%d\n", mixer->rate));
+ DDB (cmn_err (CE_CONT, "\n"));
+
+ /*
+ * Insert the newly created mixer to the mixer list.
+ */
+ mixer->next = mixer_list;
+ mixer_list = mixer;
+
+ if (masterdev >= 0)
+ {
+ if (masterdev >= num_audio_engines)
+ {
+ return OSS_ENXIO;
+ }
+
+ masterdev = mixer->masterdev;
+
+ if (mixer->inputdev >= 0)
+ {
+ if (inputdev >= num_audio_engines)
+ inputdev = mixer->inputdev = -1;
+ }
+
+ if ((err=check_masterdev (mixer, reattach))<0)
+ {
+ cmn_err (CE_CONT, "Vmix instance %d: Master device %d is not compatible with vmix (error %d)\n",
+ mixer->instance_num + 1, mixer->masterdev, err);
+ return err;
+ }
+
+ return 0;
+ }
+
+ return OSS_EIO;
+}
+
+int
+vmix_detach_audiodev(int masterdev)
+{
+/*
+ * Purpose: Detach the vmix subsystem from the audio device.
+ *
+ * Most drivers don't call this since vmix instances will be automatically detached when the
+ * master device is removed. However drivers that support dynamically created/deleted devices must
+ * call this when a device is deleted.
+ *
+ * Paramaters:
+ *
+ * masterdev: The audio engine number of the master device (same as in vmix_attach_audiodev).
+ */
+
+ vmix_mixer_t *mixer;
+
+ if (masterdev<0 || masterdev>=num_audio_engines)
+ return OSS_ENXIO;
+
+ mixer = audio_engines[masterdev]->vmix_mixer;
+
+ if (mixer==NULL) /* Not attached */
+ return 0;
+
+ return uninit_vmix_instance(mixer);
+}
+
+int
+vmix_set_master_rate(int masterdev, int rate)
+{
+/*
+ * Purpose: Set the master sampling rate of given vmix instance.
+ *
+ * Paramaters:
+ *
+ * masterdev: The audio engine number of the master device (same as in vmix_attach_audiodev).
+ * rate: The requested new sampling rate.
+ */
+
+ vmix_mixer_t *mixer;
+
+ if (rate < 4000 || rate > 200000)
+ return OSS_EDOM;
+
+ if (masterdev<0 || masterdev>=num_audio_engines)
+ return OSS_ENXIO;
+
+ mixer = audio_engines[masterdev]->vmix_mixer;
+
+ if (mixer==NULL)
+ return OSS_ENXIO;
+
+ mixer->rate = rate;
+
+ return 0;
+}
+
+int
+vmix_set_channel_map (int masterdev, void * map)
+{
+ vmix_mixer_t *mixer;
+
+ if (masterdev < 0 || masterdev >= num_audio_engines)
+ return OSS_ENXIO;
+
+ mixer = audio_engines[masterdev]->vmix_mixer;
+
+ if (mixer == NULL)
+ return OSS_EPERM;
+
+ return vmix_process_chninfo (&mixer->play_engine.channel_order, map,
+ mixer->play_engine.channels);
+}
+
+int
+vmix_create_client(void *mixer_)
+{
+ int engine_num=-1, i;
+ oss_native_word flags;
+ vmix_portc_t *portc;
+ vmix_mixer_t *mixer = mixer_;
+
+ if (mixer->disabled) /* Vmix is disabled for the time being */
+ return OSS_ENXIO;
+
+/*
+ * First check if any of the already created engines is free and available for use.
+ */
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+ for (i=0;i<mixer->num_clientdevs;i++)
+ {
+ portc = mixer->client_portc[i];
+
+ if (portc->open_mode != 0 || portc->open_pending)
+ continue;
+
+ portc->open_pending = 1;
+ engine_num = portc->audio_dev;
+ break;
+ }
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+
+/*
+ * Create a new engine and use it
+ */
+
+ if (engine_num < 0) /* Engine not allocated yet */
+ {
+ if ((engine_num = create_vmix_engine (mixer))<0)
+ {
+ cmn_err(CE_WARN, "Failed to create a vmix engine, error=%d\n", engine_num);
+ return engine_num;
+ }
+
+ portc = audio_engines[engine_num]->portc;
+ create_client_controls (mixer, portc->num);
+ }
+
+ portc = audio_engines[engine_num]->portc;
+
+ /* portc->open_pending = 1; // This was done already by create_vmix_engine() */
+ return engine_num;
+}
+
+void
+vmix_core_init (oss_device_t *osdev)
+{
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ int check;
+/*
+ * Check that the processor is compatible with vmix (has proper FP support).
+ */
+
+ if ((check = oss_fp_check ()) <= 0)
+ {
+ vmix_disabled = 1;
+ cmn_err (CE_WARN,
+ "This processor architecture is not compatible with vmix (info=%d) - Not enabled.\n",
+ check);
+ return;
+ }
+#endif
+}
+
+void
+vmix_core_uninit (void)
+{
+ vmix_mixer_t *mixer = mixer_list;
+ int n = 0;
+
+ while (mixer != NULL && n++ < num_instances)
+ {
+ uninit_vmix_instance(mixer);
+ mixer = mixer->next;
+ }
+
+ mixer_list = NULL; /* Everything removed */
+}
+
+void
+vmix_change_devnames(void *vmix_mixer, const char *name)
+{
+/*
+ * Change audio device names of all client engines.
+ */
+ vmix_mixer_t *mixer = vmix_mixer;
+ int i;
+
+ for (i=0;i<mixer->num_clientdevs;i++)
+ {
+ adev_t *adev = audio_engines[mixer->client_portc[i]->audio_dev];
+
+ sprintf(adev->name, "%s (vmix)", name);
+ }
+}
diff --git a/kernel/framework/vmix_core/vmix_import.inc b/kernel/framework/vmix_core/vmix_import.inc
new file mode 100644
index 0000000..dc4a965
--- /dev/null
+++ b/kernel/framework/vmix_core/vmix_import.inc
@@ -0,0 +1,77 @@
+#ifdef CONFIG_OSS_VMIX_FLOAT
+/*
+ * Purpose: Recording device to local input buffer import routine for vmix
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+int i, ch;
+float vol;
+
+vol = vmix_db_table[eng->outvol / 5];
+
+for (ch = 0; ch < channels; ch++)
+ {
+ float vu;
+ float *chbuf;
+
+ vu = eng->vu[ch % 2];
+ vu = vu / 255.0;
+
+ op = (SAMPLE_TYPE *) inbuf;
+ op += ch;
+
+ chbuf = chbufs[ch];
+
+ for (i = 0; i < samples; i++)
+ {
+ float tmp;
+
+#if 0 && defined(SINE_DEBUG)
+ /* Generate internal sine wave test signal */
+ tmp = 0;
+ if (ch < 2)
+ {
+ tmp = sine_table[sine_phase[ch]];
+ sine_phase[ch] = (sine_phase[ch] + 1) % SINE_SIZE;
+ }
+#else
+ tmp = VMIX_BYTESWAP (*op);
+ tmp /= SAMPLE_RANGE;
+ tmp *= vol;
+
+ if (tmp < -1.0)
+ tmp = -1.0;
+ else if (tmp > 1.0)
+ tmp = 1.0;
+
+#endif
+ op += channels;
+
+ *chbuf++ = tmp;
+
+ /* VU meter */
+ if (tmp < 0.0)
+ tmp = -tmp;
+ if (tmp > vu)
+ vu = tmp;
+ }
+
+ if (ch < 2)
+ {
+ vu = vu * 255.0;
+ eng->vu[ch] = (int)vu;
+ }
+ }
+#else
+#include "vmix_import_int.inc"
+#endif
diff --git a/kernel/framework/vmix_core/vmix_import_int.inc b/kernel/framework/vmix_core/vmix_import_int.inc
new file mode 100644
index 0000000..338078d
--- /dev/null
+++ b/kernel/framework/vmix_core/vmix_import_int.inc
@@ -0,0 +1,56 @@
+/*
+ * Purpose: Recording device to local input buffer import routine for vmix (int)
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+int i, ch;
+int vol;
+
+vol = vmix_db_table[eng->outvol / 5];
+
+for (ch = 0; ch < channels; ch++)
+ {
+ int vu;
+ int *chbuf;
+
+ vu = eng->vu[ch % 2] * 65536;
+
+ op = (SAMPLE_TYPE *) inbuf;
+ op += ch;
+
+ chbuf = chbufs[ch];
+
+ for (i = 0; i < samples; i++)
+ {
+ int tmp;
+
+ tmp = INT_IMPORT (VMIX_BYTESWAP (*op));
+ tmp = (tmp * vol) / VMIX_VOL_SCALE;
+
+ op += channels;
+
+ *chbuf++ = tmp;
+
+ /* VU meter */
+ if (tmp < 0)
+ tmp = -tmp;
+ if (tmp > vu)
+ vu = tmp;
+ }
+
+ if (ch < 2)
+ {
+ vu = vu / 65536;
+ eng->vu[ch] = vu;
+ }
+ }
diff --git a/kernel/framework/vmix_core/vmix_input.c b/kernel/framework/vmix_core/vmix_input.c
new file mode 100644
index 0000000..0e90b48
--- /dev/null
+++ b/kernel/framework/vmix_core/vmix_input.c
@@ -0,0 +1,505 @@
+/*
+ * Purpose: Virtual mixing audio driver recording routines
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#define SWAP_SUPPORT
+#include <oss_config.h>
+#include "vmix.h"
+
+#if 0
+/* Debugging macros */
+extern unsigned char tmp_status;
+# define UP_STATUS(v) OUTB(NULL, (tmp_status=tmp_status|(v)), 0x378)
+# define DOWN_STATUS(v) OUTB(NULL, (tmp_status=tmp_status&~(v)), 0x378)
+#else
+# define UP_STATUS(v)
+# define DOWN_STATUS(v)
+#endif
+
+#undef SINE_DEBUG
+
+#ifndef CONFIG_OSS_VMIX_FLOAT
+#undef SINE_DEBUG
+#endif
+
+#ifdef SINE_DEBUG
+#define SINE_SIZE 48
+static const float sine_table[SINE_SIZE] = {
+ 0.000000, 0.130526, 0.258819, 0.382683,
+ 0.500000, 0.608761, 0.707107, 0.793353,
+ 0.866025, 0.923880, 0.965926, 0.991445,
+ 1.000000, 0.991445, 0.965926, 0.923880,
+ 0.866025, 0.793353, 0.707107, 0.608761,
+ 0.500000, 0.382683, 0.258819, 0.130526,
+ 0.000000, -0.130526, -0.258819, -0.382683,
+ -0.500000, -0.608761, -0.707107, -0.793353,
+ -0.866025, -0.923880, -0.965926, -0.991445,
+ -1.000000, -0.991445, -0.965926, -0.923880,
+ -0.866025, -0.793353, -0.707107, -0.608761,
+ -0.500000, -0.382683, -0.258819, -0.130526
+};
+static int sine_phase[2] = { 0 };
+#endif
+
+/*
+ * Recording import functions (from the physical devices)
+ */
+#undef INT_IMPORT
+#define INT_IMPORT(x) (x * 256)
+
+static void
+import16ne (vmix_engine_t * eng, void *inbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ short *op;
+#define SAMPLE_TYPE short
+#define SAMPLE_RANGE 32768.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+
+#include "vmix_import.inc"
+}
+
+static void
+import16oe (vmix_engine_t * eng, void *inbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ short *op;
+#undef SAMPLE_TYPE
+#undef SAMPLE_RANGE
+#define SAMPLE_TYPE short
+#define SAMPLE_RANGE 32768.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap16(x)
+
+#include "vmix_import.inc"
+}
+
+#undef INT_IMPORT
+#define INT_IMPORT(x) (x / 256)
+
+static void
+import32ne (vmix_engine_t * eng, void *inbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ int *op;
+#undef SAMPLE_TYPE
+#undef SAMPLE_RANGE
+#define SAMPLE_TYPE int
+#define SAMPLE_RANGE 2147483648.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+
+#include "vmix_import.inc"
+}
+
+static void
+import32oe (vmix_engine_t * eng, void *inbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ int *op;
+#define SAMPLE_TYPE int
+#define SAMPLE_RANGE 2147483648.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap32(x)
+
+#include "vmix_import.inc"
+}
+
+/*
+ * recording export functions to virtual devices
+ */
+#undef BUFFER_TYPE
+#define BUFFER_TYPE short *
+
+#undef INT_EXPORT
+#define INT_EXPORT(x) (x / 256)
+
+void
+vmix_rec_export_16ne (vmix_portc_t * portc, int nsamples)
+{
+ short *outp;
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 32767.0;
+#endif
+#include "rec_export.inc"
+}
+
+void
+vmix_rec_export_16oe (vmix_portc_t * portc, int nsamples)
+{
+ short *outp;
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap16(x)
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 32767.0;
+#endif
+#include "rec_export.inc"
+}
+
+#undef BUFFER_TYPE
+#define BUFFER_TYPE int *
+#undef INT_EXPORT
+#define INT_EXPORT(x) (x * 256)
+
+void
+vmix_rec_export_32ne (vmix_portc_t * portc, int nsamples)
+{
+ int *outp;
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 2147483647.0;
+#endif
+#include "rec_export.inc"
+}
+
+void
+vmix_rec_export_32oe (vmix_portc_t * portc, int nsamples)
+{
+ int *outp;
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap32(x)
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 2147483647.0;
+#endif
+#include "rec_export.inc"
+}
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+void
+vmix_rec_export_float (vmix_portc_t * portc, int nsamples)
+{
+ float *outp;
+#undef BUFFER_TYPE
+#define BUFFER_TYPE float *
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+ double range = 1.0;
+#include "rec_export.inc"
+}
+#endif
+
+static void
+vmix_record_callback (int dev, int parm)
+{
+ int i, n;
+ int do_input = 0;
+
+ adev_t *adev = audio_engines[dev];
+ dmap_t *dmap = adev->dmap_in;
+ oss_native_word flags;
+
+ vmix_mixer_t *mixer = adev->vmix_mixer;
+ vmix_engine_t *eng = &mixer->record_engine;
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ fp_env_t fp_buf;
+ short *fp_env = fp_buf;
+ fp_flags_t fp_flags;
+#endif
+
+ if (mixer == NULL) /* Houston, we have a problem. */
+ return;
+
+ /*
+ * Check if any input applications are active. Skip input processing
+ * if it's not needed (to save CPU cycles).
+ */
+
+ for (i = 0; i < mixer->num_clientdevs; i++)
+ if (mixer->client_portc[i]->trigger_bits & PCM_ENABLE_INPUT)
+ do_input = 1;
+
+ if (!do_input) /* Skip all input processing */
+ {
+ n = 0;
+ while (n++ < dmap->nfrags
+ && (int) (dmap->byte_counter - dmap->user_counter) >=
+ dmap->fragment_size)
+ {
+ dmap->user_counter += dmap->fragment_size;
+ }
+ return;
+ }
+
+ UP_STATUS (0x02);
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ {
+ /*
+ * Align the FP save buffer to 16 byte boundary
+ */
+ oss_native_word p;
+ p = (oss_native_word) fp_buf;
+
+ p = ((p + 15ULL) / 16) * 16;
+ fp_env = (short *) p;
+ }
+
+ FP_SAVE (fp_env, fp_flags);
+#endif
+
+ n = 0;
+ while (n++ < dmap->nfrags
+ && (int) (dmap->byte_counter - dmap->user_counter) >=
+ dmap->fragment_size)
+ {
+ int i, p;
+ unsigned char *inbuf;
+
+ if (!do_input)
+ {
+ /*
+ * Just skip the recorded data becaus nobody needs it.
+ */
+ dmap->user_counter += dmap->fragment_size;
+ continue;
+ }
+
+ for (i = 0; i < eng->channels; i++)
+ {
+ memset (eng->chbufs[i], 0, CHBUF_SAMPLES * sizeof (vmix_sample_t));
+ }
+
+ p = (int) (dmap->user_counter % dmap->bytes_in_use);
+ inbuf = dmap->dmabuf + p;
+
+ eng->converter (eng, inbuf, eng->chbufs, eng->channels,
+ eng->samples_per_frag);
+
+ for (i = 0; i < mixer->num_clientdevs; i++)
+ {
+ vmix_portc_t *portc = mixer->client_portc[i];
+
+ if (portc->trigger_bits & PCM_ENABLE_INPUT)
+ {
+ if (portc->rec_mixing_func == NULL)
+ continue;
+ if (portc->rec_choffs + portc->channels >
+ mixer->record_engine.channels)
+ portc->rec_choffs = 0;
+ portc->rec_mixing_func (portc,
+ mixer->record_engine.samples_per_frag);
+ }
+ }
+
+ dmap->user_counter += dmap->fragment_size;
+ }
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ FP_RESTORE (fp_env, fp_flags);
+#endif
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+
+/*
+ * Call oss_audio_inputintr outside FP mode because it may
+ * cause a task switch (under Solaris). Task switch may turn on CR0.TS under
+ * x86 which in turn will cause #nm exception.
+ */
+ for (i = 0; i < mixer->num_clientdevs; i++)
+ if (mixer->client_portc[i]->trigger_bits & PCM_ENABLE_INPUT)
+ {
+ vmix_portc_t *portc = mixer->client_portc[i];
+ oss_audio_inputintr (portc->audio_dev, 0);
+ }
+ DOWN_STATUS (0x02);
+}
+
+void
+finalize_record_engine (vmix_mixer_t * mixer, int fmt, adev_t * adev,
+ dmap_p dmap)
+{
+ int i;
+
+ switch (fmt)
+ {
+ case AFMT_S16_NE:
+ mixer->record_engine.bits = 16;
+ mixer->record_engine.converter = import16ne;
+ break;
+
+ case AFMT_S16_OE:
+ mixer->record_engine.bits = 16;
+ mixer->record_engine.converter = import16oe;
+ break;
+
+ case AFMT_S32_NE:
+ mixer->record_engine.bits = 32;
+ mixer->record_engine.converter = import32ne;
+ break;
+
+ case AFMT_S32_OE:
+ mixer->record_engine.bits = 32;
+ mixer->record_engine.converter = import32oe;
+ break;
+
+ default:
+ cmn_err (CE_CONT, "Unrecognized recording sample format %x\n", fmt);
+ return;
+ }
+
+ mixer->record_engine.fragsize = dmap->fragment_size;
+
+ mixer->record_engine.samples_per_frag =
+ mixer->record_engine.fragsize / mixer->record_engine.channels /
+ (mixer->record_engine.bits / 8);
+
+ if (mixer->record_engine.samples_per_frag > CHBUF_SAMPLES)
+ {
+ cmn_err (CE_WARN, "Too many samples per fragment (%d,%d)\n",
+ mixer->record_engine.samples_per_frag, CHBUF_SAMPLES);
+ return;
+ }
+
+ for (i = 0; i < mixer->record_engine.channels; i++)
+ if (mixer->record_engine.chbufs[i] == NULL) /* Not allocated yet */
+ {
+ mixer->record_engine.chbufs[i] =
+ PMALLOC (mixer->master_osdev,
+ CHBUF_SAMPLES * sizeof (vmix_sample_t));
+ if (mixer->record_engine.chbufs[i] == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ return;
+ }
+ }
+
+ dmap->audio_callback = vmix_record_callback; /* Enable conversions */
+ dmap->callback_parm = mixer->instance_num;
+ dmap->dma_mode = PCM_ENABLE_INPUT;
+
+ if (mixer->num_clientdevs > 1)
+ {
+ adev->redirect_out = mixer->client_portc[0]->audio_dev;
+ adev->vmix_mixer = mixer;
+ }
+ vmix_record_callback (mixer->inputdev, mixer->instance_num);
+}
+
+void
+vmix_setup_record_engine (vmix_mixer_t * mixer, adev_t * adev, dmap_t * dmap)
+{
+ int fmt;
+ int old_min;
+ int frags = 0x7fff0007; /* fragment size of 128 bytes */
+
+/*
+ * Sample format (and endianess) setup
+ *
+ */
+
+ // First make sure a sane format is selected before starting to probe
+ fmt = adev->d->adrv_set_format (mixer->inputdev, AFMT_S16_LE);
+ fmt = adev->d->adrv_set_format (mixer->inputdev, AFMT_S16_NE);
+
+ // Find out the "best" sample format supported by the device
+
+ if (adev->iformat_mask & AFMT_S16_OE)
+ fmt = AFMT_S16_OE;
+ if (adev->iformat_mask & AFMT_S16_NE)
+ fmt = AFMT_S16_NE;
+ if (mixer->multich_enable)
+ {
+ if (adev->iformat_mask & AFMT_S32_OE)
+ fmt = AFMT_S32_OE;
+ if (adev->iformat_mask & AFMT_S32_NE)
+ fmt = AFMT_S32_NE;
+ }
+
+ fmt = adev->d->adrv_set_format (mixer->inputdev, fmt);
+ mixer->record_engine.fmt = fmt;
+
+/*
+ * Number of channels
+ */
+ mixer->record_engine.channels = mixer->max_channels;
+
+ if (mixer->record_engine.channels > MAX_REC_CHANNELS)
+ mixer->record_engine.channels = MAX_REC_CHANNELS;
+
+ if (!mixer->multich_enable)
+ mixer->record_engine.channels = 2;
+
+ /* Force the device to stereo before trying with (possibly) imultiple channels */
+ adev->d->adrv_set_channels (mixer->inputdev, 2);
+
+ mixer->record_engine.channels =
+ adev->d->adrv_set_channels (mixer->inputdev,
+ mixer->record_engine.channels);
+
+ if (mixer->record_engine.channels > MAX_REC_CHANNELS)
+ {
+ cmn_err (CE_WARN,
+ "Number of channels (%d) is larger than maximum (%d)\n",
+ mixer->record_engine.channels, MAX_REC_CHANNELS);
+ return;
+ }
+
+ /*
+ * Try to set the same rate than for playback.
+ */
+ mixer->record_engine.rate =
+ oss_audio_set_rate (mixer->inputdev, mixer->play_engine.rate);
+
+ if (mixer->record_engine.rate <= 22050)
+ frags = 0x7fff0004; /* Use smaller fragments */
+
+ audio_engines[mixer->inputdev]->hw_parms.channels =
+ mixer->record_engine.channels;
+ audio_engines[mixer->inputdev]->hw_parms.rate = mixer->record_engine.rate;
+ audio_engines[mixer->inputdev]->dmap_in->data_rate =
+ mixer->record_engine.rate * mixer->record_engine.channels *
+ mixer->record_engine.bits / 8;
+ audio_engines[mixer->inputdev]->dmap_in->frame_size =
+ mixer->record_engine.channels * mixer->record_engine.bits / 8;
+
+ old_min = adev->min_fragments;
+
+#if 0
+ if ((adev->max_fragments == 0 || adev->max_fragments >= 4)
+ && adev->min_block == 0)
+ adev->min_fragments = 4;
+#endif
+
+ oss_audio_ioctl (mixer->inputdev, NULL, SNDCTL_DSP_SETFRAGMENT,
+ (ioctl_arg) & frags);
+ oss_audio_ioctl (mixer->inputdev, NULL, SNDCTL_DSP_GETBLKSIZE,
+ (ioctl_arg) & mixer->record_engine.fragsize);
+
+ dmap->bytes_in_use = dmap->fragment_size * dmap->nfrags;
+
+ oss_audio_ioctl (mixer->inputdev, NULL, SNDCTL_DSP_GETBLKSIZE,
+ (ioctl_arg) & mixer->record_engine.fragsize);
+ mixer->record_engine.fragsize = dmap->fragment_size;
+ adev->min_fragments = old_min;
+
+ if (mixer->record_engine.channels > 2)
+ {
+ DDB (cmn_err
+ (CE_CONT, "Enabling multi channel rec mode, %d hw channels\n",
+ mixer->record_engine.channels));
+ }
+ else if (mixer->record_engine.channels != 2)
+ {
+ cmn_err (CE_WARN,
+ "Master device doesn't support suitable channel configuration\n");
+
+ return;
+ }
+
+ finalize_record_engine (mixer, fmt, adev, dmap);
+}
diff --git a/kernel/framework/vmix_core/vmix_output.c b/kernel/framework/vmix_core/vmix_output.c
new file mode 100644
index 0000000..47e10ab
--- /dev/null
+++ b/kernel/framework/vmix_core/vmix_output.c
@@ -0,0 +1,687 @@
+/*
+ * Purpose: Virtual mixing audio driver output mixing routines
+ *
+ * This file contains the actual mixing and resampling engine for output.
+ * The actual algorithms are implemented in outexport.inc and playmix.inc.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#define SWAP_SUPPORT
+#include <oss_config.h>
+#include "vmix.h"
+
+#if 0
+/* Debugging macros*/
+extern unsigned char tmp_status;
+# define UP_STATUS(v) OUTB(NULL, (tmp_status=tmp_status|(v)), 0x378)
+# define DOWN_STATUS(v) OUTB(NULL, (tmp_status=tmp_status&~(v)), 0x378)
+#else
+# define UP_STATUS(v)
+# define DOWN_STATUS(v)
+#endif
+
+#undef SINE_DEBUG
+
+#ifndef CONFIG_OSS_VMIX_FLOAT
+#undef SINE_DEBUG
+#endif
+
+#ifdef SINE_DEBUG
+#define SINE_SIZE 48
+static const float sine_table[SINE_SIZE] = {
+ 0.000000, 0.130526, 0.258819, 0.382683,
+ 0.500000, 0.608761, 0.707107, 0.793353,
+ 0.866025, 0.923880, 0.965926, 0.991445,
+ 1.000000, 0.991445, 0.965926, 0.923880,
+ 0.866025, 0.793353, 0.707107, 0.608761,
+ 0.500000, 0.382683, 0.258819, 0.130526,
+ 0.000000, -0.130526, -0.258819, -0.382683,
+ -0.500000, -0.608761, -0.707107, -0.793353,
+ -0.866025, -0.923880, -0.965926, -0.991445,
+ -1.000000, -0.991445, -0.965926, -0.923880,
+ -0.866025, -0.793353, -0.707107, -0.608761,
+ -0.500000, -0.382683, -0.258819, -0.130526
+};
+static int sine_phase[MAX_PLAY_CHANNELS] = { 0 };
+#endif
+
+#ifndef CONFIG_OSS_VMIX_FLOAT
+/*
+ * Simple limiter to prevent overflows when using fixed point computations
+ */
+void
+process_limiter (unsigned int *statevar, int *chbufs[], int nchannels,
+ int nsamples)
+{
+#define Abs(x) ((x) < 0 ? -(x) : (x))
+
+ int k, t;
+ unsigned int q, amp, amp2;
+
+ for (t = 0; t < nsamples; t++)
+ {
+ amp = (unsigned) Abs (chbufs[0][t]);
+
+ for (k = 1; k < nchannels; k++)
+ {
+ amp2 = (unsigned) Abs (chbufs[k][t]);
+ if (amp2 > amp)
+ amp = amp2;
+ }
+
+ amp >>= 8;
+ q = 0x10000;
+
+ if (amp > 0x7FFF)
+ q = 0x7FFF0000 / amp;
+
+ if (*statevar > q)
+ *statevar = q;
+ else
+ {
+ q = *statevar;
+
+ /*
+ * Simplier (linear) tracking algo
+ * (gives less distortion, but more pumping)
+ */
+ *statevar += 2;
+ if (*statevar > 0x10000)
+ *statevar = 0x10000;
+
+ /*
+ * Classic tracking algo
+ * gives more distortion with no-lookahead
+ * *statevar=0x10000-((0x10000-*statevar)*0xFFF4>>16);
+ */
+ }
+
+ for (k = 0; k < nchannels; k++)
+ {
+ int in = chbufs[k][t];
+ int out = 0;
+ unsigned int p;
+
+ if (in >= 0)
+ {
+ p = in;
+ p = ((p & 0xFFFF) * (q >> 4) >> 12) + (p >> 16) * q;
+ out = p;
+ }
+ else
+ {
+ p = -in;
+ p = ((p & 0xFFFF) * (q >> 4) >> 12) + (p >> 16) * q;
+ out = -p;
+ }
+ /* safety code */
+ /* if output after limiter is clamped, then it can be dropped */
+ if (out > 0x7FFFFF)
+ out = 0x7FFFFF;
+ else if (out < -0x7FFFFF)
+ out = -0x7FFFFF;
+
+ chbufs[k][t] = out;
+ }
+ }
+}
+#endif
+
+/*
+ * Output export functions
+ */
+
+#undef INT_EXPORT
+#define INT_EXPORT(x) (x / 256)
+
+static void
+export16ne (vmix_engine_t * eng, void *outbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ short *op;
+#define SAMPLE_TYPE short
+#define SAMPLE_RANGE 32768.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+
+#include "outexport.inc"
+}
+
+static void
+export16oe (vmix_engine_t * eng, void *outbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ short *op;
+#undef SAMPLE_TYPE
+#undef SAMPLE_RANGE
+#define SAMPLE_TYPE short
+#define SAMPLE_RANGE 32768.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap16(x)
+
+#include "outexport.inc"
+}
+
+#undef INT_EXPORT
+#define INT_EXPORT(x) (x * 256)
+
+static void
+export32ne (vmix_engine_t * eng, void *outbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ int *op;
+#undef SAMPLE_TYPE
+#undef SAMPLE_RANGE
+#define SAMPLE_TYPE int
+#define SAMPLE_RANGE 2147483648.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+
+#include "outexport.inc"
+}
+
+static void
+export32oe (vmix_engine_t * eng, void *outbuf, vmix_sample_t * chbufs[],
+ int channels, int samples)
+{
+ int *op;
+#define SAMPLE_TYPE int
+#define SAMPLE_RANGE 2147483648.0
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap32(x)
+
+#include "outexport.inc"
+}
+
+/*
+ * Mixing functions
+ */
+#undef BUFFER_TYPE
+#define BUFFER_TYPE short *
+
+#undef INT_OUTMIX
+#define INT_OUTMIX(x) (x * 256)
+
+void
+vmix_outmix_16ne (vmix_portc_t * portc, int nsamples)
+{
+ short *inp;
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 3.0517578125e-5; /* 1.0 / 32768.0 */
+#endif
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#include "playmix.inc"
+}
+
+void
+vmix_outmix_16oe (vmix_portc_t * portc, int nsamples)
+{
+ short *inp;
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 3.0517578125e-5; /* 1.0 / 32768.0 */
+#endif
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap16(x)
+#include "playmix.inc"
+}
+
+#undef BUFFER_TYPE
+#define BUFFER_TYPE int *
+#undef INT_OUTMIX
+#define INT_OUTMIX(x) (x / 256)
+
+void
+vmix_outmix_32ne (vmix_portc_t * portc, int nsamples)
+{
+ int *inp;
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 4.65661287308e-10; /* 1.0 / 2147483648.0 */
+#endif
+#include "playmix.inc"
+}
+
+void
+vmix_outmix_32oe (vmix_portc_t * portc, int nsamples)
+{
+ int *inp;
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap32(x)
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ double range = 4.65661287308e-10; /* 1.0 / 2147483648.0 */
+#endif
+#include "playmix.inc"
+}
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+void
+vmix_outmix_float (vmix_portc_t * portc, int nsamples)
+{
+ float *inp;
+ double range = 1.0;
+#undef BUFFER_TYPE
+#define BUFFER_TYPE float *
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#include "playmix.inc"
+}
+#endif
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+/*
+ * Mixing functions (with sample rate conversions)
+ */
+#undef BUFFER_TYPE
+#define BUFFER_TYPE short *
+
+#undef INT_OUTMIX
+#define INT_OUTMIX(x) (x * 256)
+
+void
+vmix_outmix_16ne_src (vmix_portc_t * portc, int nsamples)
+{
+ short *inp;
+ double range = 3.0517578125e-5; /* 1.0 / 32768.0 */
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#include "playmix_src.inc"
+}
+
+void
+vmix_outmix_16oe_src (vmix_portc_t * portc, int nsamples)
+{
+ short *inp;
+ double range = 3.0517578125e-5; /* 1.0 / 32768.0 */
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap16(x)
+#include "playmix_src.inc"
+}
+
+#undef BUFFER_TYPE
+#define BUFFER_TYPE int *
+
+#undef INT_OUTMIX
+#define INT_OUTMIX(x) (x / 256)
+
+void
+vmix_outmix_32ne_src (vmix_portc_t * portc, int nsamples)
+{
+ int *inp;
+ double range = 4.65661287308e-10; /* 1.0 / 2147483648.0 */
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#include "playmix_src.inc"
+}
+
+void
+vmix_outmix_32oe_src (vmix_portc_t * portc, int nsamples)
+{
+ int *inp;
+ double range = 4.65661287308e-10; /* 1.0 / 2147483648.0 */
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) bswap32(x)
+#include "playmix_src.inc"
+}
+
+void
+vmix_outmix_float_src (vmix_portc_t * portc, int nsamples)
+{
+ float *inp;
+ double range = 1.0;
+#undef BUFFER_TYPE
+#define BUFFER_TYPE float *
+#undef VMIX_BYTESWAP
+#define VMIX_BYTESWAP(x) x
+#include "playmix_src.inc"
+}
+#endif
+
+static void
+vmix_play_callback (int dev, int parm)
+{
+ int n;
+
+ adev_t *adev = audio_engines[dev];
+ dmap_t *dmap = adev->dmap_out;
+ oss_native_word flags;
+ int i;
+
+ vmix_mixer_t *mixer = adev->vmix_mixer;
+ vmix_engine_t *eng = &mixer->play_engine;
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ fp_env_t fp_buf;
+ short *fp_env = fp_buf;
+ fp_flags_t fp_flags;
+#endif
+
+ UP_STATUS (0x04);
+ if (dmap == NULL || dmap->dmabuf == NULL)
+ return;
+
+ if (dmap->bytes_in_use == 0)
+ {
+ cmn_err (CE_WARN, "Bytes in use=0, eng=%d\n", adev->engine_num);
+ return;
+ }
+
+ MUTEX_ENTER_IRQDISABLE (mixer->mutex, flags);
+
+ if (dmap->dmabuf == NULL)
+ {
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+ return;
+ }
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+
+ {
+ /*
+ * Align the FP save buffer to 16 byte boundary
+ */
+ oss_native_word p;
+ p = (oss_native_word) fp_buf;
+
+ p = ((p + 15ULL) / 16) * 16;
+ fp_env = (short *) p;
+ }
+ FP_SAVE (fp_env, fp_flags);
+#endif
+
+ n = 0;
+ while (n++ < eng->max_playahead
+ && dmap_get_qlen (dmap) < eng->max_playahead)
+ {
+ int p;
+ unsigned char *outbuf;
+ int nstreams = 0;
+
+ for (i = 0; i < eng->channels; i++)
+ memset (eng->chbufs[i], 0, CHBUF_SAMPLES * sizeof (vmix_sample_t));
+ for (i = 0; i < mixer->num_clientdevs; i++)
+ if (mixer->client_portc[i]->trigger_bits & PCM_ENABLE_OUTPUT)
+ {
+ vmix_portc_t *portc = mixer->client_portc[i];
+
+ if (portc->play_mixing_func == NULL)
+ continue;
+ if (portc->play_choffs + portc->channels >
+ mixer->play_engine.channels)
+ portc->play_choffs = 0;
+ portc->play_mixing_func (portc,
+ mixer->play_engine.samples_per_frag);
+ nstreams++; /* Count the number of active output streams */
+ }
+
+ eng->num_active_outputs = (nstreams > 0) ? nstreams : 1;
+
+ /* Export the output mix to the device */
+ p = (int) (dmap->user_counter % dmap->bytes_in_use);
+ outbuf = dmap->dmabuf + p;
+ if (dmap->dmabuf != NULL)
+ {
+#ifndef CONFIG_OSS_VMIX_FLOAT
+ process_limiter (&eng->limiter_statevar, eng->chbufs, eng->channels,
+ eng->samples_per_frag);
+#endif
+ eng->converter (eng, outbuf, eng->chbufs, eng->channels,
+ eng->samples_per_frag);
+ }
+
+ dmap->user_counter += dmap->fragment_size;
+
+ }
+
+#ifdef CONFIG_OSS_VMIX_FLOAT
+ FP_RESTORE (fp_env, fp_flags);
+#endif
+ MUTEX_EXIT_IRQRESTORE (mixer->mutex, flags);
+/*
+ * Call oss_audio_outputintr outside FP mode because it may
+ * cause a task switch (under Solaris). Task switch may turn on CR0.TS under
+ * x86 which in turn will cause #nm exception.
+ */
+ for (i = 0; i < mixer->num_clientdevs; i++)
+ if (mixer->client_portc[i]->trigger_bits & PCM_ENABLE_OUTPUT)
+ {
+ vmix_portc_t *portc = mixer->client_portc[i];
+ oss_audio_outputintr (portc->audio_dev, 1);
+ }
+
+ for (i = 0; i < mixer->num_loopdevs; i++)
+ {
+ if (mixer->loop_portc[i]->trigger_bits & PCM_ENABLE_INPUT)
+ {
+ oss_audio_inputintr (mixer->loop_portc[i]->audio_dev, 0);
+ }
+ }
+ DOWN_STATUS (0x04);
+}
+
+void
+vmix_setup_play_engine (vmix_mixer_t * mixer, adev_t * adev, dmap_t * dmap)
+{
+ int fmt;
+ int frags = 0x7fff0007; /* fragment size of 128 bytes */
+ int i;
+ int old_min;
+
+/*
+ * Sample format (and endianess) setup
+ *
+ */
+
+ /* First make sure a sane format is selected before starting to probe */
+ fmt = adev->d->adrv_set_format (mixer->masterdev, AFMT_S16_LE);
+ fmt = adev->d->adrv_set_format (mixer->masterdev, AFMT_S16_NE);
+
+ /* Find out the "best" sample format supported by the device */
+
+ if (adev->oformat_mask & AFMT_S16_OE)
+ fmt = AFMT_S16_OE;
+ if (adev->oformat_mask & AFMT_S16_NE)
+ fmt = AFMT_S16_NE;
+
+ if (mixer->multich_enable) /* Better quality enabled */
+ {
+ if (adev->oformat_mask & AFMT_S32_OE)
+ fmt = AFMT_S32_OE;
+ if (adev->oformat_mask & AFMT_S32_NE)
+ fmt = AFMT_S32_NE;
+ }
+
+ fmt = adev->d->adrv_set_format (mixer->masterdev, fmt);
+ mixer->play_engine.fmt = fmt;
+
+ switch (fmt)
+ {
+ case AFMT_S16_NE:
+ mixer->play_engine.bits = 16;
+ mixer->play_engine.converter = export16ne;
+ break;
+
+ case AFMT_S16_OE:
+ mixer->play_engine.bits = 16;
+ mixer->play_engine.converter = export16oe;
+ break;
+
+ case AFMT_S32_NE:
+ mixer->play_engine.bits = 32;
+ mixer->play_engine.converter = export32ne;
+ break;
+
+ case AFMT_S32_OE:
+ mixer->play_engine.bits = 32;
+ mixer->play_engine.converter = export32oe;
+ break;
+
+ default:
+ cmn_err (CE_CONT, "Unrecognized sample format %x\n", fmt);
+ return;
+ }
+/*
+ * Number of channels
+ */
+ mixer->play_engine.channels = mixer->max_channels;
+
+ if (mixer->play_engine.channels > MAX_PLAY_CHANNELS)
+ mixer->play_engine.channels = MAX_PLAY_CHANNELS;
+
+ if (!mixer->multich_enable)
+ mixer->play_engine.channels = 2;
+
+ /* Force the device to stereo before trying with (possibly) multiple channels */
+ adev->d->adrv_set_channels (mixer->masterdev, 2);
+
+ mixer->play_engine.channels =
+ adev->d->adrv_set_channels (mixer->masterdev,
+ mixer->play_engine.channels);
+
+ if (mixer->play_engine.channels > MAX_PLAY_CHANNELS)
+ {
+ cmn_err (CE_WARN,
+ "Number of channels (%d) is larger than maximum (%d)\n",
+ mixer->play_engine.channels, MAX_PLAY_CHANNELS);
+ return;
+ }
+
+ if (mixer->play_engine.channels > 2)
+ {
+ DDB (cmn_err
+ (CE_CONT, "Enabling multi channel play mode, %d hw channels\n",
+ mixer->play_engine.channels));
+ }
+ else if (mixer->play_engine.channels != 2)
+ {
+ cmn_err (CE_WARN,
+ "Master device doesn't support suitable channel configuration\n");
+
+ return;
+ }
+
+ mixer->play_engine.rate =
+ oss_audio_set_rate (mixer->masterdev, mixer->rate);
+ mixer->rate = mixer->play_engine.rate;
+
+ if (mixer->play_engine.rate <= 22050)
+ frags = 0x7fff0004; /* Use smaller fragments */
+
+ audio_engines[mixer->masterdev]->hw_parms.channels =
+ mixer->play_engine.channels;
+ audio_engines[mixer->masterdev]->hw_parms.rate = mixer->play_engine.rate;
+ audio_engines[mixer->masterdev]->dmap_out->data_rate =
+ mixer->play_engine.rate * mixer->play_engine.channels *
+ mixer->play_engine.bits / 8;
+ audio_engines[mixer->masterdev]->dmap_out->frame_size =
+ mixer->play_engine.channels * mixer->play_engine.bits / 8;
+
+ old_min = adev->min_fragments;
+#if 0
+ if ((adev->max_fragments == 0 || adev->max_fragments >= 4)
+ && adev->min_block == 0)
+ adev->min_fragments = 4;
+#endif
+
+ oss_audio_ioctl (mixer->masterdev, NULL, SNDCTL_DSP_SETFRAGMENT,
+ (ioctl_arg) & frags);
+ oss_audio_ioctl (mixer->masterdev, NULL, SNDCTL_DSP_GETBLKSIZE,
+ (ioctl_arg) & mixer->play_engine.fragsize);
+
+ dmap->bytes_in_use = dmap->fragment_size * dmap->nfrags;
+
+ oss_audio_ioctl (mixer->masterdev, NULL, SNDCTL_DSP_GETBLKSIZE,
+ (ioctl_arg) & mixer->play_engine.fragsize);
+ adev->min_fragments = old_min;
+
+ mixer->play_engine.fragsize = dmap->fragment_size;
+
+/*
+ * Determine how many fragments we need to keep filled.
+ */
+ if (adev->vmix_flags & VMIX_MULTIFRAG)
+ mixer->play_engine.max_playahead = 32;
+ else
+ mixer->play_engine.max_playahead = 4;
+
+ if (mixer->play_engine.max_playahead >
+ audio_engines[mixer->masterdev]->dmap_out->nfrags)
+ mixer->play_engine.max_playahead =
+ audio_engines[mixer->masterdev]->dmap_out->nfrags;
+
+/*
+ * Try to keep one empty fragment after the one currently being played
+ * by the device. Writing too close to the playback point may cause
+ * massive clicking with some devices.
+ */
+ if (dmap->nfrags > 2 && mixer->play_engine.max_playahead == dmap->nfrags)
+ mixer->play_engine.max_playahead--;
+
+ mixer->play_engine.samples_per_frag =
+ mixer->play_engine.fragsize / mixer->play_engine.channels /
+ (mixer->play_engine.bits / 8);
+
+ if (mixer->play_engine.samples_per_frag > CHBUF_SAMPLES)
+ {
+ cmn_err (CE_WARN, "Too many samples per fragment (%d,%d)\n",
+ mixer->play_engine.samples_per_frag, CHBUF_SAMPLES);
+ return;
+ }
+
+ for (i = 0; i < mixer->play_engine.channels; i++)
+ {
+ if (mixer->play_engine.chbufs[i] == NULL) /* Not allocated yet */
+ {
+ mixer->play_engine.chbufs[i] =
+ PMALLOC (mixer->master_portc,
+ CHBUF_SAMPLES * sizeof (vmix_sample_t));
+ if (mixer->play_engine.chbufs[i] == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ return;
+ }
+ }
+
+ if (mixer->play_engine.channel_order[i] >= mixer->play_engine.channels)
+ mixer->play_engine.channel_order[i] = i;
+ }
+
+ dmap->audio_callback = vmix_play_callback; /* Enable conversions */
+ dmap->callback_parm = mixer->instance_num;
+ dmap->dma_mode = PCM_ENABLE_OUTPUT;
+
+ if (mixer->inputdev == mixer->masterdev)
+ {
+ mixer->record_engine.rate = mixer->play_engine.rate;
+ mixer->record_engine.bits = mixer->play_engine.bits;
+ mixer->record_engine.fragsize = mixer->play_engine.fragsize;
+ mixer->record_engine.channels = mixer->play_engine.channels;
+ mixer->record_engine.samples_per_frag =
+ mixer->play_engine.samples_per_frag;
+ }
+
+ if (mixer->num_clientdevs > 1)
+ {
+ adev->redirect_out = mixer->client_portc[0]->audio_dev;
+ }
+
+ /*
+ * Fill in the initial playback data (silence) to avoid underruns
+ */
+ vmix_play_callback (mixer->masterdev, mixer->instance_num);
+
+ if (mixer->masterdev == mixer->inputdev)
+ finalize_record_engine (mixer, fmt, audio_engines[mixer->inputdev],
+ audio_engines[mixer->inputdev]->dmap_in);
+}