summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_midimix/oss_midimix.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2013-05-03 21:08:42 +0400
commit1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch)
tree4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/drv/oss_midimix/oss_midimix.c
downloadoss4-upstream.tar.gz
Imported Upstream version 4.2-build2006upstream/4.2-build2006upstream
Diffstat (limited to 'kernel/drv/oss_midimix/oss_midimix.c')
-rw-r--r--kernel/drv/oss_midimix/oss_midimix.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/kernel/drv/oss_midimix/oss_midimix.c b/kernel/drv/oss_midimix/oss_midimix.c
new file mode 100644
index 0000000..9f7cc4c
--- /dev/null
+++ b/kernel/drv/oss_midimix/oss_midimix.c
@@ -0,0 +1,346 @@
+/*
+ * Purpose: MIDI mixer pseudo driver
+ *
+ * This driver creates a pseudo MIDI port device that can be used for
+ * real-time mixer volume changes. The pseudo MIDI device will return MIDI
+ * control change messages when a mixer setting changes. Application using the
+ * device may also do mixer changes (cross fading, etc) by sending (or
+ * playing back) control messages.
+ */
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "oss_midimix_cfg.h"
+#include "midi_core.h"
+#include "midiparser.h"
+
+#define MAX_INSTANCES 1
+
+typedef struct
+{
+ oss_device_t *osdev;
+ oss_mutex_t mutex;
+ int midi_dev;
+ int open_mode;
+ oss_midi_inputbuf_t inputbuf;
+ midiparser_common_p parser;
+ int modify_counters[16];
+ int update_counters[16][128];
+ timeout_id_t timeout_id;
+} midimix_devc;
+
+static midimix_devc midimix_devs[MAX_INSTANCES] = {{ 0 }};
+static int ndevs = 0;
+
+static void
+midimix_close (int dev, int mode)
+{
+ midimix_devc *devc = midi_devs[dev]->devc;
+ oss_native_word flags;
+
+ midiparser_unalloc (devc->parser);
+ devc->parser = NULL;
+ untimeout (devc->timeout_id);
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ devc->open_mode = 0;
+ devc->inputbuf = NULL;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+void
+parser_cb (void *context, int category, unsigned char msg, unsigned char ch,
+ unsigned char *parms, int len)
+{
+
+ if (category == CAT_CHN && msg == MIDI_CTL_CHANGE)
+ {
+ oss_mixext *ext;
+ oss_mixer_value rec;
+
+ int dev = ch;
+ int ctl = parms[0];
+ int val = parms[1];
+
+ if ((ext = mixer_find_ext (dev, ctl)) == NULL)
+ return; /* Not found */
+
+ if (!(ext->flags & MIXF_READABLE) || !(ext->flags & MIXF_WRITEABLE))
+ return; /* Not accessible */
+
+ if (ext->maxvalue > 127)
+ val = (val * ext->maxvalue + 63) / 127;
+
+ if (val < 0 || val > ext->maxvalue)
+ return; /* Value out of range */
+
+ switch (ext->type)
+ {
+ case MIXT_MONOSLIDER:
+ case MIXT_STEREOSLIDER:
+ val = val & 0xff;
+ val = val | (val << 8);
+ break;
+
+ case MIXT_ONOFF:
+ val = !!val;
+ break;
+
+ case MIXT_ENUM:
+ if (val >= ext->maxvalue)
+ return; /* Out of range */
+ break;
+
+ case MIXT_VALUE:
+ case MIXT_HEXVALUE:
+ case MIXT_SLIDER:
+ /* Accept as-is */
+ break;
+
+ default:
+ return; /* Type not supported */
+ }
+
+ memset (&rec, 0, sizeof (rec));
+ rec.dev = dev;
+ rec.ctrl = ctl;
+ rec.value = val;
+ rec.timestamp = ext->timestamp;
+ oss_mixer_ext (dev, OSS_DEV_MIXER, SNDCTL_MIX_WRITE, (ioctl_arg) & rec);
+ }
+
+}
+
+static void
+check_for_updates (midimix_devc * devc, int force)
+{
+ int dev, ctl;
+
+ for (dev = 0; dev < 16 && dev < num_mixers; dev++)
+ {
+ touch_mixer (dev);
+
+ if (force
+ || (mixer_devs[dev]->modify_counter > devc->modify_counters[dev]))
+ {
+ devc->modify_counters[dev] = mixer_devs[dev]->modify_counter;
+ for (ctl = 0; ctl < 128; ctl++)
+ {
+ oss_mixext *ext;
+ oss_mixer_value rec;
+ int val;
+ unsigned char midibuf[3];
+
+ if ((ext = mixer_find_ext (dev, ctl)) == NULL)
+ break; /* All controls processed I think */
+ if (!(ext->flags & MIXF_READABLE)
+ || !(ext->flags & MIXF_WRITEABLE))
+ continue; /* Not accessible */
+
+ if (!force
+ && devc->update_counters[dev][ctl] == ext->update_counter)
+ continue; /* No change */
+ devc->update_counters[dev][ctl] = ext->update_counter;
+
+ switch (ext->type)
+ {
+ case MIXT_MONOSLIDER:
+ case MIXT_STEREOSLIDER:
+ case MIXT_ONOFF:
+ case MIXT_ENUM:
+ case MIXT_VALUE:
+ case MIXT_HEXVALUE:
+ case MIXT_SLIDER:
+ /* Type OK */
+ break;
+
+ default:
+ continue; /* Type not supported */
+ }
+
+ memset (&rec, 0, sizeof (rec));
+ rec.dev = dev;
+ rec.ctrl = ctl;
+ rec.timestamp = ext->timestamp;
+
+ if (oss_mixer_ext
+ (dev, OSS_DEV_MIXER, SNDCTL_MIX_READ,
+ (ioctl_arg) & rec) < 0)
+ {
+ continue; /* Read failed */
+ }
+
+ val = rec.value & 0xff;
+ if (ext->maxvalue > 127)
+ val = (val * 127 + (ext->maxvalue / 2)) / ext->maxvalue;
+
+ if (val < 0)
+ val = 0;
+ if (val > 127)
+ val = 127;
+
+ midibuf[0] = 0xb0 | dev;
+ midibuf[1] = ctl;
+ midibuf[2] = val;
+
+ devc->inputbuf (devc->midi_dev, midibuf, 3);
+ }
+ }
+ }
+}
+
+static void
+timer_callback (void *arg)
+{
+ midimix_devc *devc = arg;
+ check_for_updates (devc, 0);
+ devc->timeout_id = timeout (timer_callback, devc, OSS_HZ / 10); /* 0.1s */
+}
+
+static int
+midimix_open (int dev, int mode, oss_midi_inputbyte_t inputbyte,
+ oss_midi_inputbuf_t inputbuf, oss_midi_outputintr_t outputintr)
+{
+ midimix_devc *devc = midi_devs[dev]->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (devc->open_mode)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+ devc->open_mode = mode;
+ devc->inputbuf = inputbuf;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ if ((devc->parser = midiparser_create (parser_cb, devc)) == NULL)
+ {
+ devc->open_mode = 0;
+ devc->inputbuf = NULL;
+ cmn_err (CE_WARN, "Cannot create MIDI parser\n");
+ return OSS_ENOMEM;
+ }
+
+ devc->timeout_id = timeout (timer_callback, devc, OSS_HZ / 10); /* 0.1s */
+ return 0;
+}
+
+static int
+midimix_ioctl (int dev, unsigned cmd, ioctl_arg arg)
+{
+ return OSS_EINVAL;
+}
+
+static int
+midimix_out (int dev, unsigned char midi_byte)
+{
+ midimix_devc *devc = midi_devs[dev]->devc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ midiparser_input (devc->parser, midi_byte);
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return 1;
+}
+
+static int
+midimix_bulk_out (int dev, unsigned char *buf, int len)
+{
+ midimix_devc *devc = midi_devs[dev]->devc;
+ int i;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ for (i = 0; i < len; i++)
+ {
+ midiparser_input (devc->parser, buf[i]);
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return len;
+}
+
+static void
+midimix_init_device (int dev)
+{
+ midimix_devc *devc = midi_devs[dev]->devc;
+
+ if (devc->open_mode & OPEN_READ)
+ check_for_updates (devc, 1);
+}
+
+static midi_driver_t midimix_driver = {
+ midimix_open,
+ midimix_close,
+ midimix_ioctl,
+ midimix_out,
+ midimix_bulk_out,
+ MIDI_PAYLOAD_SIZE,
+ NULL,
+ NULL,
+ NULL,
+ midimix_init_device
+};
+
+static void
+attach_midimix_dev (oss_device_t * osdev)
+{
+ midimix_devc *devc = &midimix_devs[ndevs++];
+
+ devc->osdev = osdev;
+
+ MUTEX_INIT (osdev, devc->mutex, MH_DRV);
+
+ devc->midi_dev =
+ oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "MIX",
+ "Mixer access MIDI device", &midimix_driver,
+ sizeof (midi_driver_t),
+ MFLAG_VIRTUAL | MFLAG_QUIET, devc, devc->osdev);
+}
+
+int
+oss_midimix_attach (oss_device_t * osdev)
+{
+ int midimix_instances = 1;
+ int i;
+
+ oss_register_device (osdev, "OSS MIDI mixer driver");
+
+ if (midimix_instances > MAX_INSTANCES)
+ midimix_instances = MAX_INSTANCES;
+ for (i = 0; i < midimix_instances; i++)
+ {
+ attach_midimix_dev (osdev);
+ }
+
+ return 1;
+}
+
+int
+oss_midimix_detach (oss_device_t * osdev)
+{
+ int i, err;
+
+ if ((err = oss_disable_device (osdev)) < 0)
+ return 0;
+
+ for (i = 0; i < ndevs; i++)
+ {
+ midimix_devc *devc;
+
+ devc = &midimix_devs[i];
+ }
+
+ oss_unregister_device (osdev);
+
+ return 1;
+}