diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2013-05-03 21:08:42 +0400 |
commit | 1058def8e7827e56ce4a70afb4aeacb5dc44148f (patch) | |
tree | 4495d23e7b54ab5700e3839081e797c1eafe0db9 /kernel/drv/oss_midimix/oss_midimix.c | |
download | oss4-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.c | 346 |
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; +} |