123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- /*
- * OSS compatible sequencer driver
- *
- * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include "seq_oss_device.h"
- #include "seq_oss_synth.h"
- #include "seq_oss_midi.h"
- #include "seq_oss_event.h"
- #include "seq_oss_timer.h"
- #include <sound/seq_oss_legacy.h>
- #include "seq_oss_readq.h"
- #include "seq_oss_writeq.h"
- /*
- * prototypes
- */
- static int extended_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);
- static int chn_voice_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
- static int chn_common_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
- static int timing_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
- static int local_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);
- static int old_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);
- static int note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev);
- static int note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev);
- static int set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev);
- static int set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev);
- static int set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct snd_seq_event *ev);
- /*
- * convert an OSS event to ALSA event
- * return 0 : enqueued
- * non-zero : invalid - ignored
- */
- int
- snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
- {
- switch (q->s.code) {
- case SEQ_EXTENDED:
- return extended_event(dp, q, ev);
- case EV_CHN_VOICE:
- return chn_voice_event(dp, q, ev);
- case EV_CHN_COMMON:
- return chn_common_event(dp, q, ev);
- case EV_TIMING:
- return timing_event(dp, q, ev);
- case EV_SEQ_LOCAL:
- return local_event(dp, q, ev);
- case EV_SYSEX:
- return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev);
- case SEQ_MIDIPUTC:
- if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
- return -EINVAL;
- /* put a midi byte */
- if (! is_write_mode(dp->file_mode))
- break;
- if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))
- break;
- if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)
- return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);
- break;
- case SEQ_ECHO:
- if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
- return -EINVAL;
- return set_echo_event(dp, q, ev);
- case SEQ_PRIVATE:
- if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
- return -EINVAL;
- return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev);
- default:
- if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
- return -EINVAL;
- return old_event(dp, q, ev);
- }
- return -EINVAL;
- }
- /* old type events: mode1 only */
- static int
- old_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
- {
- switch (q->s.code) {
- case SEQ_NOTEOFF:
- return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
- case SEQ_NOTEON:
- return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);
- case SEQ_WAIT:
- /* skip */
- break;
- case SEQ_PGMCHANGE:
- return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE,
- q->n.chn, 0, q->n.note, ev);
- case SEQ_SYNCTIMER:
- return snd_seq_oss_timer_reset(dp->timer);
- }
- return -EINVAL;
- }
- /* 8bytes extended event: mode1 only */
- static int
- extended_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
- {
- int val;
- switch (q->e.cmd) {
- case SEQ_NOTEOFF:
- return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
- case SEQ_NOTEON:
- return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);
- case SEQ_PGMCHANGE:
- return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
- q->e.chn, 0, q->e.p1, ev);
- case SEQ_AFTERTOUCH:
- return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS,
- q->e.chn, 0, q->e.p1, ev);
- case SEQ_BALANCE:
- /* convert -128:127 to 0:127 */
- val = (char)q->e.p1;
- val = (val + 128) / 2;
- return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER,
- q->e.chn, CTL_PAN, val, ev);
- case SEQ_CONTROLLER:
- val = ((short)q->e.p3 << 8) | (short)q->e.p2;
- switch (q->e.p1) {
- case CTRL_PITCH_BENDER: /* SEQ1 V2 control */
- /* -0x2000:0x1fff */
- return set_control_event(dp, q->e.dev,
- SNDRV_SEQ_EVENT_PITCHBEND,
- q->e.chn, 0, val, ev);
- case CTRL_PITCH_BENDER_RANGE:
- /* conversion: 100/semitone -> 128/semitone */
- return set_control_event(dp, q->e.dev,
- SNDRV_SEQ_EVENT_REGPARAM,
- q->e.chn, 0, val*128/100, ev);
- default:
- return set_control_event(dp, q->e.dev,
- SNDRV_SEQ_EVENT_CONTROL14,
- q->e.chn, q->e.p1, val, ev);
- }
- case SEQ_VOLMODE:
- return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev);
- }
- return -EINVAL;
- }
- /* channel voice events: mode1 and 2 */
- static int
- chn_voice_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
- {
- if (q->v.chn >= 32)
- return -EINVAL;
- switch (q->v.cmd) {
- case MIDI_NOTEON:
- return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
- case MIDI_NOTEOFF:
- return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);
- case MIDI_KEY_PRESSURE:
- return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS,
- q->v.chn, q->v.note, q->v.parm, ev);
- }
- return -EINVAL;
- }
- /* channel common events: mode1 and 2 */
- static int
- chn_common_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
- {
- if (q->l.chn >= 32)
- return -EINVAL;
- switch (q->l.cmd) {
- case MIDI_PGM_CHANGE:
- return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE,
- q->l.chn, 0, q->l.p1, ev);
- case MIDI_CTL_CHANGE:
- return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER,
- q->l.chn, q->l.p1, q->l.val, ev);
- case MIDI_PITCH_BEND:
- /* conversion: 0:0x3fff -> -0x2000:0x1fff */
- return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND,
- q->l.chn, 0, q->l.val - 8192, ev);
-
- case MIDI_CHN_PRESSURE:
- return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS,
- q->l.chn, 0, q->l.val, ev);
- }
- return -EINVAL;
- }
- /* timer events: mode1 and mode2 */
- static int
- timing_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
- {
- switch (q->t.cmd) {
- case TMR_ECHO:
- if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
- return set_echo_event(dp, q, ev);
- else {
- union evrec tmp;
- memset(&tmp, 0, sizeof(tmp));
- /* XXX: only for little-endian! */
- tmp.echo = (q->t.time << 8) | SEQ_ECHO;
- return set_echo_event(dp, &tmp, ev);
- }
- case TMR_STOP:
- if (dp->seq_mode)
- return snd_seq_oss_timer_stop(dp->timer);
- return 0;
- case TMR_CONTINUE:
- if (dp->seq_mode)
- return snd_seq_oss_timer_continue(dp->timer);
- return 0;
- case TMR_TEMPO:
- if (dp->seq_mode)
- return snd_seq_oss_timer_tempo(dp->timer, q->t.time);
- return 0;
- }
- return -EINVAL;
- }
- /* local events: mode1 and 2 */
- static int
- local_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)
- {
- return -EINVAL;
- }
- /*
- * process note-on event for OSS synth
- * three different modes are available:
- * - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode)
- * Accept note 255 as volume change.
- * - SNDRV_SEQ_OSS_PASS_EVENTS
- * Pass all events to lowlevel driver anyway
- * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000)
- * Use key-pressure if note >= 128
- */
- static int
- note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
- {
- struct seq_oss_synthinfo *info;
- if (!snd_seq_oss_synth_is_valid(dp, dev))
- return -ENXIO;
- info = &dp->synths[dev];
- switch (info->arg.event_passing) {
- case SNDRV_SEQ_OSS_PROCESS_EVENTS:
- if (! info->ch || ch < 0 || ch >= info->nr_voices) {
- /* pass directly */
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
- }
- if (note == 255 && info->ch[ch].note >= 0) {
- /* volume control */
- int type;
- //if (! vel)
- /* set volume to zero -- note off */
- // type = SNDRV_SEQ_EVENT_NOTEOFF;
- //else
- if (info->ch[ch].vel)
- /* sample already started -- volume change */
- type = SNDRV_SEQ_EVENT_KEYPRESS;
- else
- /* sample not started -- start now */
- type = SNDRV_SEQ_EVENT_NOTEON;
- info->ch[ch].vel = vel;
- return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
- } else if (note >= 128)
- return -EINVAL; /* invalid */
- if (note != info->ch[ch].note && info->ch[ch].note >= 0)
- /* note changed - note off at beginning */
- set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev);
- /* set current status */
- info->ch[ch].note = note;
- info->ch[ch].vel = vel;
- if (vel) /* non-zero velocity - start the note now */
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
- return -EINVAL;
-
- case SNDRV_SEQ_OSS_PASS_EVENTS:
- /* pass the event anyway */
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
- case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
- if (note >= 128) /* key pressure: shifted by 128 */
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev);
- else /* normal note-on event */
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
- }
- return -EINVAL;
- }
- /*
- * process note-off event for OSS synth
- */
- static int
- note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
- {
- struct seq_oss_synthinfo *info;
- if (!snd_seq_oss_synth_is_valid(dp, dev))
- return -ENXIO;
- info = &dp->synths[dev];
- switch (info->arg.event_passing) {
- case SNDRV_SEQ_OSS_PROCESS_EVENTS:
- if (! info->ch || ch < 0 || ch >= info->nr_voices) {
- /* pass directly */
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
- }
- if (info->ch[ch].note >= 0) {
- note = info->ch[ch].note;
- info->ch[ch].vel = 0;
- info->ch[ch].note = -1;
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
- }
- return -EINVAL; /* invalid */
- case SNDRV_SEQ_OSS_PASS_EVENTS:
- case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:
- /* pass the event anyway */
- return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);
- }
- return -EINVAL;
- }
- /*
- * create a note event
- */
- static int
- set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev)
- {
- if (! snd_seq_oss_synth_is_valid(dp, dev))
- return -ENXIO;
-
- ev->type = type;
- snd_seq_oss_synth_addr(dp, dev, ev);
- ev->data.note.channel = ch;
- ev->data.note.note = note;
- ev->data.note.velocity = vel;
- return 0;
- }
- /*
- * create a control event
- */
- static int
- set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev)
- {
- if (! snd_seq_oss_synth_is_valid(dp, dev))
- return -ENXIO;
-
- ev->type = type;
- snd_seq_oss_synth_addr(dp, dev, ev);
- ev->data.control.channel = ch;
- ev->data.control.param = param;
- ev->data.control.value = val;
- return 0;
- }
- /*
- * create an echo event
- */
- static int
- set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct snd_seq_event *ev)
- {
- ev->type = SNDRV_SEQ_EVENT_ECHO;
- /* echo back to itself */
- snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port);
- memcpy(&ev->data, rec, LONG_EVENT_SIZE);
- return 0;
- }
- /*
- * event input callback from ALSA sequencer:
- * the echo event is processed here.
- */
- int
- snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data,
- int atomic, int hop)
- {
- struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
- union evrec *rec;
- if (ev->type != SNDRV_SEQ_EVENT_ECHO)
- return snd_seq_oss_midi_input(ev, direct, private_data);
- if (ev->source.client != dp->cseq)
- return 0; /* ignored */
- rec = (union evrec*)&ev->data;
- if (rec->s.code == SEQ_SYNCTIMER) {
- /* sync echo back */
- snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time);
-
- } else {
- /* echo back event */
- if (dp->readq == NULL)
- return 0;
- snd_seq_oss_readq_put_event(dp->readq, rec);
- }
- return 0;
- }
|