xref: /netbsd-src/sys/dev/ic/opl.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: opl.c,v 1.18 2003/02/09 09:28:21 itohy Exp $	*/
2 
3 /*
4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Lennart Augustsson (augustss@netbsd.org).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * The OPL3 (YMF262) manual can be found at
41  * ftp://ftp.yamahayst.com/Fax_Back_Doc/sound/YMF262.PDF
42  */
43 
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: opl.c,v 1.18 2003/02/09 09:28:21 itohy Exp $");
46 
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/errno.h>
50 #include <sys/ioctl.h>
51 #include <sys/syslog.h>
52 #include <sys/device.h>
53 #include <sys/select.h>
54 
55 #include <machine/cpu.h>
56 #include <machine/bus.h>
57 
58 #include <sys/audioio.h>
59 #include <sys/midiio.h>
60 #include <dev/audio_if.h>
61 
62 #include <dev/midi_if.h>
63 #include <dev/midivar.h>
64 #include <dev/midisynvar.h>
65 
66 #include <dev/ic/oplreg.h>
67 #include <dev/ic/oplvar.h>
68 
69 #ifdef AUDIO_DEBUG
70 #define DPRINTF(x)	if (opldebug) printf x
71 #define DPRINTFN(n,x)	if (opldebug >= (n)) printf x
72 int	opldebug = 0;
73 #else
74 #define DPRINTF(x)
75 #define DPRINTFN(n,x)
76 #endif
77 
78 struct real_voice {
79 	u_int8_t voice_num;
80 	u_int8_t voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
81 	u_int8_t iooffs; /* I/O port (left or right side) */
82 	u_int8_t op[4]; /* Operator offsets */
83 };
84 
85 const struct opl_voice voicetab[] = {
86 /*       No    I/O offs	OP1	OP2	OP3   OP4	*/
87 /*	---------------------------------------------	*/
88 	{ 0,   OPL_L,	{0x00,	0x03,	0x08, 0x0b}},
89 	{ 1,   OPL_L,	{0x01,	0x04,	0x09, 0x0c}},
90 	{ 2,   OPL_L,	{0x02,	0x05,	0x0a, 0x0d}},
91 
92 	{ 3,   OPL_L,	{0x08,	0x0b,	0x00, 0x00}},
93 	{ 4,   OPL_L,	{0x09,	0x0c,	0x00, 0x00}},
94 	{ 5,   OPL_L,	{0x0a,	0x0d,	0x00, 0x00}},
95 
96 	{ 6,   OPL_L,	{0x10,	0x13,	0x00, 0x00}},
97 	{ 7,   OPL_L,	{0x11,	0x14,	0x00, 0x00}},
98 	{ 8,   OPL_L,	{0x12,	0x15,	0x00, 0x00}},
99 
100 	{ 0,   OPL_R,	{0x00,	0x03,	0x08, 0x0b}},
101 	{ 1,   OPL_R,	{0x01,	0x04,	0x09, 0x0c}},
102 	{ 2,   OPL_R,	{0x02,	0x05,	0x0a, 0x0d}},
103 	{ 3,   OPL_R,	{0x08,	0x0b,	0x00, 0x00}},
104 	{ 4,   OPL_R,	{0x09,	0x0c,	0x00, 0x00}},
105 	{ 5,   OPL_R,	{0x0a,	0x0d,	0x00, 0x00}},
106 
107 	{ 6,   OPL_R,	{0x10,	0x13,	0x00, 0x00}},
108 	{ 7,   OPL_R,	{0x11,	0x14,	0x00, 0x00}},
109 	{ 8,   OPL_R,	{0x12,	0x15,	0x00, 0x00}}
110 };
111 
112 static void opl_command(struct opl_softc *, int, int, int);
113 void opl_reset(struct opl_softc *);
114 void opl_freq_to_fnum (int freq, int *block, int *fnum);
115 
116 int oplsyn_open __P((midisyn *ms, int));
117 void oplsyn_close __P((midisyn *));
118 void oplsyn_reset __P((void *));
119 void oplsyn_noteon __P((midisyn *, u_int32_t, u_int32_t, u_int32_t));
120 void oplsyn_noteoff __P((midisyn *, u_int32_t, u_int32_t, u_int32_t));
121 void oplsyn_keypressure __P((midisyn *, u_int32_t, u_int32_t, u_int32_t));
122 void oplsyn_ctlchange __P((midisyn *, u_int32_t, u_int32_t, u_int32_t));
123 void oplsyn_programchange __P((midisyn *, u_int32_t, u_int32_t));
124 void oplsyn_pitchbend __P((midisyn *, u_int32_t, u_int32_t, u_int32_t));
125 void oplsyn_loadpatch __P((midisyn *, struct sysex_info *, struct uio *));
126 
127 
128 void opl_set_op_reg __P((struct opl_softc *, int, int, int, u_char));
129 void opl_set_ch_reg __P((struct opl_softc *, int, int, u_char));
130 void opl_load_patch __P((struct opl_softc *, int));
131 u_int32_t opl_get_block_fnum __P((int freq));
132 int opl_calc_vol __P((int regbyte, int volume, int main_vol));
133 
134 struct midisyn_methods opl3_midi = {
135 	oplsyn_open,
136 	oplsyn_close,
137 	0,
138 	0,
139 	oplsyn_noteon,
140 	oplsyn_noteoff,
141 	oplsyn_keypressure,
142 	oplsyn_ctlchange,
143 	oplsyn_programchange,
144 	0,
145 	oplsyn_pitchbend,
146 	0
147 };
148 
149 void
150 opl_attach(sc)
151 	struct opl_softc *sc;
152 {
153 	int i;
154 
155 	if (!opl_find(sc)) {
156 		printf("\nopl: find failed\n");
157 		return;
158 	}
159 
160 	sc->syn.mets = &opl3_midi;
161 	sprintf(sc->syn.name, "%sYamaha OPL%d", sc->syn.name, sc->model);
162 	sc->syn.data = sc;
163 	sc->syn.nvoice = sc->model == OPL_2 ? OPL2_NVOICE : OPL3_NVOICE;
164 	sc->syn.flags =  MS_DOALLOC | MS_FREQXLATE;
165 	midisyn_attach(&sc->mididev, &sc->syn);
166 
167 	/* Set up voice table */
168 	for (i = 0; i < OPL3_NVOICE; i++)
169 		sc->voices[i] = voicetab[i];
170 
171 	opl_reset(sc);
172 
173 	printf(": model OPL%d", sc->model);
174 
175 	/* Set up panpot */
176 	sc->panl = OPL_VOICE_TO_LEFT;
177 	sc->panr = OPL_VOICE_TO_RIGHT;
178 	if (sc->model == OPL_3 &&
179 	    sc->mididev.dev.dv_cfdata->cf_flags & OPL_FLAGS_SWAP_LR) {
180 		sc->panl = OPL_VOICE_TO_RIGHT;
181 		sc->panr = OPL_VOICE_TO_LEFT;
182 		printf(": LR swapped");
183 	}
184 
185 	printf("\n");
186 
187 	sc->sc_mididev =
188 	    midi_attach_mi(&midisyn_hw_if, &sc->syn, &sc->mididev.dev);
189 }
190 
191 int
192 opl_detach(sc, flags)
193 	struct opl_softc *sc;
194 	int flags;
195 {
196 	int rv = 0;
197 
198 	if (sc->sc_mididev != NULL)
199 		rv = config_detach(sc->sc_mididev, flags);
200 
201 	return(rv);
202 }
203 
204 static void
205 opl_command(sc, offs, addr, data)
206 	struct opl_softc *sc;
207 	int offs;
208 	int addr, data;
209 {
210 	DPRINTFN(4, ("opl_command: sc=%p, offs=%d addr=0x%02x data=0x%02x\n",
211 		     sc, offs, addr, data));
212 	offs += sc->offs;
213 	bus_space_write_1(sc->iot, sc->ioh, OPL_ADDR+offs, addr);
214 	if (sc->model == OPL_2)
215 		delay(10);
216 	else
217 		delay(6);
218 	bus_space_write_1(sc->iot, sc->ioh, OPL_DATA+offs, data);
219 	if (sc->model == OPL_2)
220 		delay(30);
221 	else
222 		delay(6);
223 }
224 
225 int
226 opl_find(sc)
227 	struct opl_softc *sc;
228 {
229 	u_int8_t status1, status2;
230 
231 	DPRINTFN(2,("opl_find: ioh=0x%x\n", (int)sc->ioh));
232 	sc->model = OPL_2;	/* worst case assumtion */
233 
234 	/* Reset timers 1 and 2 */
235 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL,
236 		    OPL_TIMER1_MASK | OPL_TIMER2_MASK);
237 	/* Reset the IRQ of the FM chip */
238 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL, OPL_IRQ_RESET);
239 
240 	/* get status bits */
241 	status1 = bus_space_read_1(sc->iot,sc->ioh,OPL_STATUS+OPL_L+sc->offs);
242 
243 	opl_command(sc, OPL_L, OPL_TIMER1, -2); /* wait 2 ticks */
244 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL, /* start timer1 */
245 		    OPL_TIMER1_START | OPL_TIMER2_MASK);
246 	delay(1000);		/* wait for timer to expire */
247 
248 	/* get status bits again */
249 	status2 = bus_space_read_1(sc->iot,sc->ioh,OPL_STATUS+OPL_L+sc->offs);
250 
251 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL,
252 		    OPL_TIMER1_MASK | OPL_TIMER2_MASK);
253 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL, OPL_IRQ_RESET);
254 
255 	DPRINTFN(2,("opl_find: %02x %02x\n", status1, status2));
256 
257 	if ((status1 & OPL_STATUS_MASK) != 0 ||
258 	    (status2 & OPL_STATUS_MASK) != (OPL_STATUS_IRQ | OPL_STATUS_FT1))
259 		return (0);
260 
261 	switch(status1) {
262 	case 0x00:
263 	case 0x0f:
264 		sc->model = OPL_3;
265 		break;
266 	case 0x06:
267 		sc->model = OPL_2;
268 		break;
269 	default:
270 		return (0);
271 	}
272 
273 	DPRINTFN(2,("opl_find: OPL%d at 0x%x detected\n",
274 		    sc->model, (int)sc->ioh));
275 	return (1);
276 }
277 
278 void
279 opl_set_op_reg(sc, base, voice, op, value)
280 	struct opl_softc *sc;
281 	int base;
282 	int voice;
283 	int op;
284 	u_char value;
285 {
286 	struct opl_voice *v = &sc->voices[voice];
287 	opl_command(sc, v->iooffs, base + v->op[op], value);
288 }
289 
290 void
291 opl_set_ch_reg(sc, base, voice, value)
292 	struct opl_softc *sc;
293 	int base;
294 	int voice;
295 	u_char value;
296 {
297 	struct opl_voice *v = &sc->voices[voice];
298 	opl_command(sc, v->iooffs, base + v->voiceno, value);
299 }
300 
301 
302 void
303 opl_load_patch(sc, v)
304 	struct opl_softc *sc;
305 	int v;
306 {
307 	const struct opl_operators *p = sc->voices[v].patch;
308 
309 	opl_set_op_reg(sc, OPL_AM_VIB,          v, 0, p->ops[OO_CHARS+0]);
310 	opl_set_op_reg(sc, OPL_AM_VIB,          v, 1, p->ops[OO_CHARS+1]);
311 	opl_set_op_reg(sc, OPL_KSL_LEVEL,       v, 0, p->ops[OO_KSL_LEV+0]);
312 	opl_set_op_reg(sc, OPL_KSL_LEVEL,       v, 1, p->ops[OO_KSL_LEV+1]);
313 	opl_set_op_reg(sc, OPL_ATTACK_DECAY,    v, 0, p->ops[OO_ATT_DEC+0]);
314 	opl_set_op_reg(sc, OPL_ATTACK_DECAY,    v, 1, p->ops[OO_ATT_DEC+1]);
315 	opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 0, p->ops[OO_SUS_REL+0]);
316 	opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 1, p->ops[OO_SUS_REL+1]);
317 	opl_set_op_reg(sc, OPL_WAVE_SELECT,     v, 0, p->ops[OO_WAV_SEL+0]);
318 	opl_set_op_reg(sc, OPL_WAVE_SELECT,     v, 1, p->ops[OO_WAV_SEL+1]);
319 	opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, v, p->ops[OO_FB_CONN]);
320 }
321 
322 #define OPL_FNUM_FAIL 0xffff
323 u_int32_t
324 opl_get_block_fnum(freq)
325 	int freq;
326 {
327 	u_int32_t f_num = freq / 3125;
328 	u_int32_t  block = 0;
329 
330 	while (f_num > 0x3ff && block < 8) {
331 		block++;
332 		f_num >>= 1;
333 	}
334 
335 	if (block > 7)
336 		return (OPL_FNUM_FAIL);
337 	else
338 		return ((block << 10) | f_num);
339 }
340 
341 
342 void
343 opl_reset(sc)
344 	struct opl_softc *sc;
345 {
346 	int i;
347 
348 	for (i = 1; i <= OPL_MAXREG; i++)
349 		opl_command(sc, OPL_L, OPL_KEYON_BLOCK + i, 0);
350 
351 	opl_command(sc, OPL_L, OPL_TEST, OPL_ENABLE_WAVE_SELECT);
352 	opl_command(sc, OPL_L, OPL_PERCUSSION, 0);
353 	if (sc->model == OPL_3) {
354 		opl_command(sc, OPL_R, OPL_MODE, OPL3_ENABLE);
355 		opl_command(sc, OPL_R,OPL_CONNECTION_SELECT,OPL_NOCONNECTION);
356 	}
357 
358 	sc->volume = 64;
359 
360 	for (i = 0; i < MIDI_MAX_CHANS; i++)
361 		sc->pan[i] = OPL_VOICE_TO_LEFT | OPL_VOICE_TO_RIGHT;
362 }
363 
364 int
365 oplsyn_open(ms, flags)
366 	midisyn *ms;
367 	int flags;
368 {
369 	struct opl_softc *sc = ms->data;
370 
371 	DPRINTFN(2, ("oplsyn_open: %d\n", flags));
372 
373 #ifndef AUDIO_NO_POWER_CTL
374 	if (sc->powerctl)
375 		sc->powerctl(sc->powerarg, 1);
376 #endif
377 	opl_reset(ms->data);
378 	if (sc->spkrctl)
379 		sc->spkrctl(sc->spkrarg, 1);
380 	return (0);
381 }
382 
383 void
384 oplsyn_close(ms)
385 	midisyn *ms;
386 {
387 	struct opl_softc *sc = ms->data;
388 
389 	DPRINTFN(2, ("oplsyn_close:\n"));
390 
391 	/*opl_reset(ms->data);*/
392 	if (sc->spkrctl)
393 		sc->spkrctl(sc->spkrarg, 0);
394 #ifndef AUDIO_NO_POWER_CTL
395 	if (sc->powerctl)
396 		sc->powerctl(sc->powerarg, 0);
397 #endif
398 }
399 
400 #if 0
401 void
402 oplsyn_getinfo(addr, sd)
403 	void *addr;
404 	struct synth_dev *sd;
405 {
406 	struct opl_softc *sc = addr;
407 
408 	sd->name = sc->model == OPL_2 ? "Yamaha OPL2" : "Yamaha OPL3";
409 	sd->type = SYNTH_TYPE_FM;
410 	sd->subtype = sc->model == OPL_2 ? SYNTH_SUB_FM_TYPE_ADLIB
411 		: SYNTH_SUB_FM_TYPE_OPL3;
412 	sd->capabilities = 0;
413 }
414 #endif
415 
416 void
417 oplsyn_reset(addr)
418 	void *addr;
419 {
420 	struct opl_softc *sc = addr;
421 	DPRINTFN(3, ("oplsyn_reset:\n"));
422 	opl_reset(sc);
423 }
424 
425 const int8_t opl_volume_table[128] =
426     {-64, -48, -40, -35, -32, -29, -27, -26,
427      -24, -23, -21, -20, -19, -18, -18, -17,
428      -16, -15, -15, -14, -13, -13, -12, -12,
429      -11, -11, -10, -10, -10, -9, -9, -8,
430      -8, -8, -7, -7, -7, -6, -6, -6,
431      -5, -5, -5, -5, -4, -4, -4, -4,
432      -3, -3, -3, -3, -2, -2, -2, -2,
433      -2, -1, -1, -1, -1, 0, 0, 0,
434      0, 0, 0, 1, 1, 1, 1, 1,
435      1, 2, 2, 2, 2, 2, 2, 2,
436      3, 3, 3, 3, 3, 3, 3, 4,
437      4, 4, 4, 4, 4, 4, 4, 5,
438      5, 5, 5, 5, 5, 5, 5, 5,
439      6, 6, 6, 6, 6, 6, 6, 6,
440      6, 7, 7, 7, 7, 7, 7, 7,
441      7, 7, 7, 8, 8, 8, 8, 8};
442 
443 int
444 opl_calc_vol(regbyte, volume, mainvol)
445 	int regbyte;
446 	int volume;
447 	int mainvol;
448 {
449 	int level = ~regbyte & OPL_TOTAL_LEVEL_MASK;
450 
451 	if (mainvol > 127)
452 		mainvol = 127;
453 
454 	volume = (volume * mainvol) / 127;
455 
456 	if (level)
457 		level += opl_volume_table[volume];
458 
459 	if (level > OPL_TOTAL_LEVEL_MASK)
460 		level = OPL_TOTAL_LEVEL_MASK;
461 	if (level < 0)
462 		level = 0;
463 
464 	return (~level & OPL_TOTAL_LEVEL_MASK);
465 }
466 
467 void
468 oplsyn_noteon(ms, voice, freq, vel)
469 	midisyn *ms;
470 	u_int32_t voice, freq, vel;
471 {
472 	struct opl_softc *sc = ms->data;
473 	struct opl_voice *v;
474 	const struct opl_operators *p;
475 	u_int32_t block_fnum;
476 	int mult;
477 	int c_mult, m_mult;
478 	u_int32_t chan;
479 	u_int8_t chars0, chars1, ksl0, ksl1, fbc;
480 	u_int8_t r20m, r20c, r40m, r40c, rA0, rB0;
481 	u_int8_t vol0, vol1;
482 
483 	DPRINTFN(3, ("oplsyn_noteon: %p %d %d\n", sc, voice,
484 		     MIDISYN_FREQ_TO_HZ(freq)));
485 
486 #ifdef DIAGNOSTIC
487 	if (voice < 0 || voice >= sc->syn.nvoice) {
488 		printf("oplsyn_noteon: bad voice %d\n", voice);
489 		return;
490 	}
491 #endif
492 	/* Turn off old note */
493 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 0, 0xff);
494 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 1, 0xff);
495 	opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice,    0);
496 
497 	v = &sc->voices[voice];
498 
499 	chan = MS_GETCHAN(&ms->voices[voice]);
500 	p = &opl2_instrs[ms->pgms[chan]];
501 	v->patch = p;
502 	opl_load_patch(sc, voice);
503 
504 	mult = 1;
505 	for (;;) {
506 		block_fnum = opl_get_block_fnum(freq / mult);
507 		if (block_fnum != OPL_FNUM_FAIL)
508 			break;
509 		mult *= 2;
510 		if (mult == 16)
511 			mult = 15;
512 	}
513 
514 	chars0 = p->ops[OO_CHARS+0];
515 	chars1 = p->ops[OO_CHARS+1];
516 	m_mult = (chars0 & OPL_MULTIPLE_MASK) * mult;
517 	c_mult = (chars1 & OPL_MULTIPLE_MASK) * mult;
518 	if ((block_fnum == OPL_FNUM_FAIL) || (m_mult > 15) || (c_mult > 15)) {
519 		printf("oplsyn_noteon: frequency out of range %d\n",
520 		       MIDISYN_FREQ_TO_HZ(freq));
521 		return;
522 	}
523 	r20m = (chars0 &~ OPL_MULTIPLE_MASK) | m_mult;
524 	r20c = (chars1 &~ OPL_MULTIPLE_MASK) | c_mult;
525 
526 	/* 2 voice */
527 	ksl0 = p->ops[OO_KSL_LEV+0];
528 	ksl1 = p->ops[OO_KSL_LEV+1];
529 	if (p->ops[OO_FB_CONN] & 0x01) {
530 		vol0 = opl_calc_vol(ksl0, vel, sc->volume);
531 		vol1 = opl_calc_vol(ksl1, vel, sc->volume);
532 	} else {
533 		vol0 = ksl0;
534 		vol1 = opl_calc_vol(ksl1, vel, sc->volume);
535 	}
536 	r40m = (ksl0 & OPL_KSL_MASK) | vol0;
537 	r40c = (ksl1 & OPL_KSL_MASK) | vol1;
538 
539 	rA0  = block_fnum & 0xFF;
540 	rB0  = (block_fnum >> 8) | OPL_KEYON_BIT;
541 
542 	v->rB0 = rB0;
543 
544 	fbc = p->ops[OO_FB_CONN];
545 	if (sc->model == OPL_3) {
546 		fbc &= ~OPL_STEREO_BITS;
547 		fbc |= sc->pan[chan];
548 	}
549 	opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, voice, fbc);
550 
551 	opl_set_op_reg(sc, OPL_AM_VIB,      voice, 0, r20m);
552 	opl_set_op_reg(sc, OPL_AM_VIB,      voice, 1, r20c);
553 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 0, r40m);
554 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 1, r40c);
555 	opl_set_ch_reg(sc, OPL_FNUM_LOW,    voice,    rA0);
556 	opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice,    rB0);
557 }
558 
559 void
560 oplsyn_noteoff(ms, voice, note, vel)
561 	midisyn *ms;
562 	u_int32_t voice, note, vel;
563 {
564 	struct opl_softc *sc = ms->data;
565 	struct opl_voice *v;
566 
567 	DPRINTFN(3, ("oplsyn_noteoff: %p %d %d\n", sc, voice,
568 		     MIDISYN_FREQ_TO_HZ(note)));
569 
570 #ifdef DIAGNOSTIC
571 	if (voice < 0 || voice >= sc->syn.nvoice) {
572 		printf("oplsyn_noteoff: bad voice %d\n", voice);
573 		return;
574 	}
575 #endif
576 	v = &sc->voices[voice];
577 	opl_set_ch_reg(sc, 0xB0, voice, v->rB0 & ~OPL_KEYON_BIT);
578 }
579 
580 void
581 oplsyn_keypressure(ms, voice, note, vel)
582 	midisyn *ms;
583 	u_int32_t voice, note, vel;
584 {
585 #ifdef AUDIO_DEBUG
586 	struct opl_softc *sc = ms->data;
587 	DPRINTFN(1, ("oplsyn_keypressure: %p %d\n", sc, note));
588 #endif
589 }
590 
591 void
592 oplsyn_ctlchange(ms, chan, parm, w14)
593 	midisyn *ms;
594 	u_int32_t chan, parm, w14;
595 {
596 	struct opl_softc *sc = ms->data;
597 
598 	DPRINTFN(1, ("oplsyn_ctlchange: %p %d\n", sc, chan));
599 	switch (parm) {
600 	case MIDI_CTRL_PAN_MSB:
601 		sc->pan[chan] =
602 		    (w14 <= OPL_MIDI_CENTER_MAX ? sc->panl : 0) |
603 		    (w14 >= OPL_MIDI_CENTER_MIN ? sc->panr : 0);
604 		break;
605 	}
606 }
607 
608 /* PROGRAM CHANGE midi event: */
609 void
610 oplsyn_programchange(ms, chan, prog)
611 	midisyn *ms;
612 	u_int32_t chan;
613 	u_int32_t prog;
614 {
615 	/* sanity checks */
616 	if (chan >= MIDI_MAX_CHANS || prog >= OPL_NINSTR)
617 		return;
618 
619 	ms->pgms[chan] = prog;
620 }
621 
622 void
623 oplsyn_pitchbend(ms, voice, parm, x)
624 	midisyn *ms;
625 	u_int32_t voice, parm, x;
626 {
627 #ifdef AUDIO_DEBUG
628 	struct opl_softc *sc = ms->data;
629 	DPRINTFN(1, ("oplsyn_pitchbend: %p %d\n", sc, voice));
630 #endif
631 }
632 
633 void
634 oplsyn_loadpatch(ms, sysex, uio)
635 	midisyn *ms;
636 	struct sysex_info *sysex;
637 	struct uio *uio;
638 {
639 #if 0
640 	struct opl_softc *sc = ms->data;
641 	struct sbi_instrument ins;
642 
643 	DPRINTFN(1, ("oplsyn_loadpatch: %p\n", sc));
644 
645 	memcpy(&ins, sysex, sizeof *sysex);
646 	if (uio->uio_resid >= sizeof ins - sizeof *sysex)
647 		return EINVAL;
648 	uiomove((char *)&ins + sizeof *sysex, sizeof ins - sizeof *sysex, uio);
649 	/* XXX */
650 #endif
651 }
652