448 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * OSS compatible sequencer driver4 *5 * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>6 */7 8#include "seq_oss_device.h"9#include "seq_oss_synth.h"10#include "seq_oss_midi.h"11#include "seq_oss_event.h"12#include "seq_oss_timer.h"13#include <sound/seq_oss_legacy.h>14#include "seq_oss_readq.h"15#include "seq_oss_writeq.h"16#include <linux/nospec.h>17 18 19/*20 * prototypes21 */22static int extended_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);23static int chn_voice_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);24static int chn_common_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);25static int timing_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);26static int local_event(struct seq_oss_devinfo *dp, union evrec *event_rec, struct snd_seq_event *ev);27static int old_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev);28static int note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev);29static int note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev);30static int set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev);31static int set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev);32static int set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct snd_seq_event *ev);33 34 35/*36 * convert an OSS event to ALSA event37 * return 0 : enqueued38 * non-zero : invalid - ignored39 */40 41int42snd_seq_oss_process_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)43{44 switch (q->s.code) {45 case SEQ_EXTENDED:46 return extended_event(dp, q, ev);47 48 case EV_CHN_VOICE:49 return chn_voice_event(dp, q, ev);50 51 case EV_CHN_COMMON:52 return chn_common_event(dp, q, ev);53 54 case EV_TIMING:55 return timing_event(dp, q, ev);56 57 case EV_SEQ_LOCAL:58 return local_event(dp, q, ev);59 60 case EV_SYSEX:61 return snd_seq_oss_synth_sysex(dp, q->x.dev, q->x.buf, ev);62 63 case SEQ_MIDIPUTC:64 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)65 return -EINVAL;66 /* put a midi byte */67 if (! is_write_mode(dp->file_mode))68 break;69 if (snd_seq_oss_midi_open(dp, q->s.dev, SNDRV_SEQ_OSS_FILE_WRITE))70 break;71 if (snd_seq_oss_midi_filemode(dp, q->s.dev) & SNDRV_SEQ_OSS_FILE_WRITE)72 return snd_seq_oss_midi_putc(dp, q->s.dev, q->s.parm1, ev);73 break;74 75 case SEQ_ECHO:76 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)77 return -EINVAL;78 return set_echo_event(dp, q, ev);79 80 case SEQ_PRIVATE:81 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)82 return -EINVAL;83 return snd_seq_oss_synth_raw_event(dp, q->c[1], q->c, ev);84 85 default:86 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)87 return -EINVAL;88 return old_event(dp, q, ev);89 }90 return -EINVAL;91}92 93/* old type events: mode1 only */94static int95old_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)96{97 switch (q->s.code) {98 case SEQ_NOTEOFF:99 return note_off_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);100 101 case SEQ_NOTEON:102 return note_on_event(dp, 0, q->n.chn, q->n.note, q->n.vel, ev);103 104 case SEQ_WAIT:105 /* skip */106 break;107 108 case SEQ_PGMCHANGE:109 return set_control_event(dp, 0, SNDRV_SEQ_EVENT_PGMCHANGE,110 q->n.chn, 0, q->n.note, ev);111 112 case SEQ_SYNCTIMER:113 return snd_seq_oss_timer_reset(dp->timer);114 }115 116 return -EINVAL;117}118 119/* 8bytes extended event: mode1 only */120static int121extended_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)122{123 int val;124 125 switch (q->e.cmd) {126 case SEQ_NOTEOFF:127 return note_off_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);128 129 case SEQ_NOTEON:130 return note_on_event(dp, q->e.dev, q->e.chn, q->e.p1, q->e.p2, ev);131 132 case SEQ_PGMCHANGE:133 return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_PGMCHANGE,134 q->e.chn, 0, q->e.p1, ev);135 136 case SEQ_AFTERTOUCH:137 return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CHANPRESS,138 q->e.chn, 0, q->e.p1, ev);139 140 case SEQ_BALANCE:141 /* convert -128:127 to 0:127 */142 val = (char)q->e.p1;143 val = (val + 128) / 2;144 return set_control_event(dp, q->e.dev, SNDRV_SEQ_EVENT_CONTROLLER,145 q->e.chn, CTL_PAN, val, ev);146 147 case SEQ_CONTROLLER:148 val = ((short)q->e.p3 << 8) | (short)q->e.p2;149 switch (q->e.p1) {150 case CTRL_PITCH_BENDER: /* SEQ1 V2 control */151 /* -0x2000:0x1fff */152 return set_control_event(dp, q->e.dev,153 SNDRV_SEQ_EVENT_PITCHBEND,154 q->e.chn, 0, val, ev);155 case CTRL_PITCH_BENDER_RANGE:156 /* conversion: 100/semitone -> 128/semitone */157 return set_control_event(dp, q->e.dev,158 SNDRV_SEQ_EVENT_REGPARAM,159 q->e.chn, 0, val*128/100, ev);160 default:161 return set_control_event(dp, q->e.dev,162 SNDRV_SEQ_EVENT_CONTROL14,163 q->e.chn, q->e.p1, val, ev);164 }165 166 case SEQ_VOLMODE:167 return snd_seq_oss_synth_raw_event(dp, q->e.dev, q->c, ev);168 169 }170 return -EINVAL;171}172 173/* channel voice events: mode1 and 2 */174static int175chn_voice_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)176{177 if (q->v.chn >= 32)178 return -EINVAL;179 switch (q->v.cmd) {180 case MIDI_NOTEON:181 return note_on_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);182 183 case MIDI_NOTEOFF:184 return note_off_event(dp, q->v.dev, q->v.chn, q->v.note, q->v.parm, ev);185 186 case MIDI_KEY_PRESSURE:187 return set_note_event(dp, q->v.dev, SNDRV_SEQ_EVENT_KEYPRESS,188 q->v.chn, q->v.note, q->v.parm, ev);189 190 }191 return -EINVAL;192}193 194/* channel common events: mode1 and 2 */195static int196chn_common_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)197{198 if (q->l.chn >= 32)199 return -EINVAL;200 switch (q->l.cmd) {201 case MIDI_PGM_CHANGE:202 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PGMCHANGE,203 q->l.chn, 0, q->l.p1, ev);204 205 case MIDI_CTL_CHANGE:206 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CONTROLLER,207 q->l.chn, q->l.p1, q->l.val, ev);208 209 case MIDI_PITCH_BEND:210 /* conversion: 0:0x3fff -> -0x2000:0x1fff */211 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_PITCHBEND,212 q->l.chn, 0, q->l.val - 8192, ev);213 214 case MIDI_CHN_PRESSURE:215 return set_control_event(dp, q->l.dev, SNDRV_SEQ_EVENT_CHANPRESS,216 q->l.chn, 0, q->l.val, ev);217 }218 return -EINVAL;219}220 221/* timer events: mode1 and mode2 */222static int223timing_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)224{225 switch (q->t.cmd) {226 case TMR_ECHO:227 if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)228 return set_echo_event(dp, q, ev);229 else {230 union evrec tmp;231 memset(&tmp, 0, sizeof(tmp));232 /* XXX: only for little-endian! */233 tmp.echo = (q->t.time << 8) | SEQ_ECHO;234 return set_echo_event(dp, &tmp, ev);235 } 236 237 case TMR_STOP:238 if (dp->seq_mode)239 return snd_seq_oss_timer_stop(dp->timer);240 return 0;241 242 case TMR_CONTINUE:243 if (dp->seq_mode)244 return snd_seq_oss_timer_continue(dp->timer);245 return 0;246 247 case TMR_TEMPO:248 if (dp->seq_mode)249 return snd_seq_oss_timer_tempo(dp->timer, q->t.time);250 return 0;251 }252 253 return -EINVAL;254}255 256/* local events: mode1 and 2 */257static int258local_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev)259{260 return -EINVAL;261}262 263/*264 * process note-on event for OSS synth265 * three different modes are available:266 * - SNDRV_SEQ_OSS_PROCESS_EVENTS (for one-voice per channel mode)267 * Accept note 255 as volume change.268 * - SNDRV_SEQ_OSS_PASS_EVENTS269 * Pass all events to lowlevel driver anyway270 * - SNDRV_SEQ_OSS_PROCESS_KEYPRESS (mostly for Emu8000)271 * Use key-pressure if note >= 128272 */273static int274note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)275{276 struct seq_oss_synthinfo *info;277 278 info = snd_seq_oss_synth_info(dp, dev);279 if (!info)280 return -ENXIO;281 282 switch (info->arg.event_passing) {283 case SNDRV_SEQ_OSS_PROCESS_EVENTS:284 if (! info->ch || ch < 0 || ch >= info->nr_voices) {285 /* pass directly */286 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);287 }288 289 ch = array_index_nospec(ch, info->nr_voices);290 if (note == 255 && info->ch[ch].note >= 0) {291 /* volume control */292 int type;293 //if (! vel)294 /* set volume to zero -- note off */295 // type = SNDRV_SEQ_EVENT_NOTEOFF;296 //else297 if (info->ch[ch].vel)298 /* sample already started -- volume change */299 type = SNDRV_SEQ_EVENT_KEYPRESS;300 else301 /* sample not started -- start now */302 type = SNDRV_SEQ_EVENT_NOTEON;303 info->ch[ch].vel = vel;304 return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);305 } else if (note >= 128)306 return -EINVAL; /* invalid */307 308 if (note != info->ch[ch].note && info->ch[ch].note >= 0)309 /* note changed - note off at beginning */310 set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, info->ch[ch].note, 0, ev);311 /* set current status */312 info->ch[ch].note = note;313 info->ch[ch].vel = vel;314 if (vel) /* non-zero velocity - start the note now */315 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);316 return -EINVAL;317 318 case SNDRV_SEQ_OSS_PASS_EVENTS:319 /* pass the event anyway */320 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);321 322 case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:323 if (note >= 128) /* key pressure: shifted by 128 */324 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_KEYPRESS, ch, note - 128, vel, ev);325 else /* normal note-on event */326 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);327 }328 return -EINVAL;329}330 331/*332 * process note-off event for OSS synth333 */334static int335note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)336{337 struct seq_oss_synthinfo *info;338 339 info = snd_seq_oss_synth_info(dp, dev);340 if (!info)341 return -ENXIO;342 343 switch (info->arg.event_passing) {344 case SNDRV_SEQ_OSS_PROCESS_EVENTS:345 if (! info->ch || ch < 0 || ch >= info->nr_voices) {346 /* pass directly */347 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);348 }349 350 ch = array_index_nospec(ch, info->nr_voices);351 if (info->ch[ch].note >= 0) {352 note = info->ch[ch].note;353 info->ch[ch].vel = 0;354 info->ch[ch].note = -1;355 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);356 }357 return -EINVAL; /* invalid */358 359 case SNDRV_SEQ_OSS_PASS_EVENTS:360 case SNDRV_SEQ_OSS_PROCESS_KEYPRESS:361 /* pass the event anyway */362 return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEOFF, ch, note, vel, ev);363 364 }365 return -EINVAL;366}367 368/*369 * create a note event370 */371static int372set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev)373{374 if (!snd_seq_oss_synth_info(dp, dev))375 return -ENXIO;376 377 ev->type = type;378 snd_seq_oss_synth_addr(dp, dev, ev);379 ev->data.note.channel = ch;380 ev->data.note.note = note;381 ev->data.note.velocity = vel;382 383 return 0;384}385 386/*387 * create a control event388 */389static int390set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev)391{392 if (!snd_seq_oss_synth_info(dp, dev))393 return -ENXIO;394 395 ev->type = type;396 snd_seq_oss_synth_addr(dp, dev, ev);397 ev->data.control.channel = ch;398 ev->data.control.param = param;399 ev->data.control.value = val;400 401 return 0;402}403 404/*405 * create an echo event406 */407static int408set_echo_event(struct seq_oss_devinfo *dp, union evrec *rec, struct snd_seq_event *ev)409{410 ev->type = SNDRV_SEQ_EVENT_ECHO;411 /* echo back to itself */412 snd_seq_oss_fill_addr(dp, ev, dp->addr.client, dp->addr.port);413 memcpy(&ev->data, rec, LONG_EVENT_SIZE);414 return 0;415}416 417/*418 * event input callback from ALSA sequencer:419 * the echo event is processed here.420 */421int422snd_seq_oss_event_input(struct snd_seq_event *ev, int direct, void *private_data,423 int atomic, int hop)424{425 struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;426 union evrec *rec;427 428 if (ev->type != SNDRV_SEQ_EVENT_ECHO)429 return snd_seq_oss_midi_input(ev, direct, private_data);430 431 if (ev->source.client != dp->cseq)432 return 0; /* ignored */433 434 rec = (union evrec*)&ev->data;435 if (rec->s.code == SEQ_SYNCTIMER) {436 /* sync echo back */437 snd_seq_oss_writeq_wakeup(dp->writeq, rec->t.time);438 439 } else {440 /* echo back event */441 if (dp->readq == NULL)442 return 0;443 snd_seq_oss_readq_put_event(dp->readq, rec);444 }445 return 0;446}447 448