xref: /netbsd-src/sys/dev/ic/opl.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: opl.c,v 1.21 2004/04/22 00:17:11 itojun 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.21 2004/04/22 00:17:11 itojun 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 	snprintf(sc->syn.name, sizeof(sc->syn.name), "%sYamaha OPL%d",
162 	    sc->syn.name, sc->model);
163 	sc->syn.data = sc;
164 	sc->syn.nvoice = sc->model == OPL_2 ? OPL2_NVOICE : OPL3_NVOICE;
165 	sc->syn.flags =  MS_DOALLOC | MS_FREQXLATE;
166 	midisyn_attach(&sc->mididev, &sc->syn);
167 
168 	/* Set up voice table */
169 	for (i = 0; i < OPL3_NVOICE; i++)
170 		sc->voices[i] = voicetab[i];
171 
172 	opl_reset(sc);
173 
174 	printf(": model OPL%d", sc->model);
175 
176 	/* Set up panpot */
177 	sc->panl = OPL_VOICE_TO_LEFT;
178 	sc->panr = OPL_VOICE_TO_RIGHT;
179 	if (sc->model == OPL_3 &&
180 	    sc->mididev.dev.dv_cfdata->cf_flags & OPL_FLAGS_SWAP_LR) {
181 		sc->panl = OPL_VOICE_TO_RIGHT;
182 		sc->panr = OPL_VOICE_TO_LEFT;
183 		printf(": LR swapped");
184 	}
185 
186 	printf("\n");
187 
188 	sc->sc_mididev =
189 	    midi_attach_mi(&midisyn_hw_if, &sc->syn, &sc->mididev.dev);
190 }
191 
192 int
193 opl_detach(sc, flags)
194 	struct opl_softc *sc;
195 	int flags;
196 {
197 	int rv = 0;
198 
199 	if (sc->sc_mididev != NULL)
200 		rv = config_detach(sc->sc_mididev, flags);
201 
202 	return(rv);
203 }
204 
205 static void
206 opl_command(sc, offs, addr, data)
207 	struct opl_softc *sc;
208 	int offs;
209 	int addr, data;
210 {
211 	DPRINTFN(4, ("opl_command: sc=%p, offs=%d addr=0x%02x data=0x%02x\n",
212 		     sc, offs, addr, data));
213 	offs += sc->offs;
214 	bus_space_write_1(sc->iot, sc->ioh, OPL_ADDR+offs, addr);
215 	if (sc->model == OPL_2)
216 		delay(10);
217 	else
218 		delay(6);
219 	bus_space_write_1(sc->iot, sc->ioh, OPL_DATA+offs, data);
220 	if (sc->model == OPL_2)
221 		delay(30);
222 	else
223 		delay(6);
224 }
225 
226 int
227 opl_find(sc)
228 	struct opl_softc *sc;
229 {
230 	u_int8_t status1, status2;
231 
232 	DPRINTFN(2,("opl_find: ioh=0x%x\n", (int)sc->ioh));
233 	sc->model = OPL_2;	/* worst case assumption */
234 
235 	/* Reset timers 1 and 2 */
236 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL,
237 		    OPL_TIMER1_MASK | OPL_TIMER2_MASK);
238 	/* Reset the IRQ of the FM chip */
239 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL, OPL_IRQ_RESET);
240 
241 	/* get status bits */
242 	status1 = bus_space_read_1(sc->iot,sc->ioh,OPL_STATUS+OPL_L+sc->offs);
243 
244 	opl_command(sc, OPL_L, OPL_TIMER1, -2); /* wait 2 ticks */
245 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL, /* start timer1 */
246 		    OPL_TIMER1_START | OPL_TIMER2_MASK);
247 	delay(1000);		/* wait for timer to expire */
248 
249 	/* get status bits again */
250 	status2 = bus_space_read_1(sc->iot,sc->ioh,OPL_STATUS+OPL_L+sc->offs);
251 
252 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL,
253 		    OPL_TIMER1_MASK | OPL_TIMER2_MASK);
254 	opl_command(sc, OPL_L, OPL_TIMER_CONTROL, OPL_IRQ_RESET);
255 
256 	DPRINTFN(2,("opl_find: %02x %02x\n", status1, status2));
257 
258 	if ((status1 & OPL_STATUS_MASK) != 0 ||
259 	    (status2 & OPL_STATUS_MASK) != (OPL_STATUS_IRQ | OPL_STATUS_FT1))
260 		return (0);
261 
262 	switch(status1) {
263 	case 0x00:
264 	case 0x0f:
265 		sc->model = OPL_3;
266 		break;
267 	case 0x06:
268 		sc->model = OPL_2;
269 		break;
270 	default:
271 		return (0);
272 	}
273 
274 	DPRINTFN(2,("opl_find: OPL%d at 0x%x detected\n",
275 		    sc->model, (int)sc->ioh));
276 	return (1);
277 }
278 
279 void
280 opl_set_op_reg(sc, base, voice, op, value)
281 	struct opl_softc *sc;
282 	int base;
283 	int voice;
284 	int op;
285 	u_char value;
286 {
287 	struct opl_voice *v = &sc->voices[voice];
288 	opl_command(sc, v->iooffs, base + v->op[op], value);
289 }
290 
291 void
292 opl_set_ch_reg(sc, base, voice, value)
293 	struct opl_softc *sc;
294 	int base;
295 	int voice;
296 	u_char value;
297 {
298 	struct opl_voice *v = &sc->voices[voice];
299 	opl_command(sc, v->iooffs, base + v->voiceno, value);
300 }
301 
302 
303 void
304 opl_load_patch(sc, v)
305 	struct opl_softc *sc;
306 	int v;
307 {
308 	const struct opl_operators *p = sc->voices[v].patch;
309 
310 	opl_set_op_reg(sc, OPL_AM_VIB,          v, 0, p->ops[OO_CHARS+0]);
311 	opl_set_op_reg(sc, OPL_AM_VIB,          v, 1, p->ops[OO_CHARS+1]);
312 	opl_set_op_reg(sc, OPL_KSL_LEVEL,       v, 0, p->ops[OO_KSL_LEV+0]);
313 	opl_set_op_reg(sc, OPL_KSL_LEVEL,       v, 1, p->ops[OO_KSL_LEV+1]);
314 	opl_set_op_reg(sc, OPL_ATTACK_DECAY,    v, 0, p->ops[OO_ATT_DEC+0]);
315 	opl_set_op_reg(sc, OPL_ATTACK_DECAY,    v, 1, p->ops[OO_ATT_DEC+1]);
316 	opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 0, p->ops[OO_SUS_REL+0]);
317 	opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 1, p->ops[OO_SUS_REL+1]);
318 	opl_set_op_reg(sc, OPL_WAVE_SELECT,     v, 0, p->ops[OO_WAV_SEL+0]);
319 	opl_set_op_reg(sc, OPL_WAVE_SELECT,     v, 1, p->ops[OO_WAV_SEL+1]);
320 	opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, v, p->ops[OO_FB_CONN]);
321 }
322 
323 #define OPL_FNUM_FAIL 0xffff
324 u_int32_t
325 opl_get_block_fnum(freq)
326 	int freq;
327 {
328 	u_int32_t f_num = freq / 3125;
329 	u_int32_t  block = 0;
330 
331 	while (f_num > 0x3ff && block < 8) {
332 		block++;
333 		f_num >>= 1;
334 	}
335 
336 	if (block > 7)
337 		return (OPL_FNUM_FAIL);
338 	else
339 		return ((block << 10) | f_num);
340 }
341 
342 
343 void
344 opl_reset(sc)
345 	struct opl_softc *sc;
346 {
347 	int i;
348 
349 	for (i = 1; i <= OPL_MAXREG; i++)
350 		opl_command(sc, OPL_L, OPL_KEYON_BLOCK + i, 0);
351 
352 	opl_command(sc, OPL_L, OPL_TEST, OPL_ENABLE_WAVE_SELECT);
353 	opl_command(sc, OPL_L, OPL_PERCUSSION, 0);
354 	if (sc->model == OPL_3) {
355 		opl_command(sc, OPL_R, OPL_MODE, OPL3_ENABLE);
356 		opl_command(sc, OPL_R,OPL_CONNECTION_SELECT,OPL_NOCONNECTION);
357 	}
358 
359 	sc->volume = 64;
360 
361 	for (i = 0; i < MIDI_MAX_CHANS; i++)
362 		sc->pan[i] = OPL_VOICE_TO_LEFT | OPL_VOICE_TO_RIGHT;
363 }
364 
365 int
366 oplsyn_open(ms, flags)
367 	midisyn *ms;
368 	int flags;
369 {
370 	struct opl_softc *sc = ms->data;
371 
372 	DPRINTFN(2, ("oplsyn_open: %d\n", flags));
373 
374 #ifndef AUDIO_NO_POWER_CTL
375 	if (sc->powerctl)
376 		sc->powerctl(sc->powerarg, 1);
377 #endif
378 	opl_reset(ms->data);
379 	if (sc->spkrctl)
380 		sc->spkrctl(sc->spkrarg, 1);
381 	return (0);
382 }
383 
384 void
385 oplsyn_close(ms)
386 	midisyn *ms;
387 {
388 	struct opl_softc *sc = ms->data;
389 
390 	DPRINTFN(2, ("oplsyn_close:\n"));
391 
392 	/*opl_reset(ms->data);*/
393 	if (sc->spkrctl)
394 		sc->spkrctl(sc->spkrarg, 0);
395 #ifndef AUDIO_NO_POWER_CTL
396 	if (sc->powerctl)
397 		sc->powerctl(sc->powerarg, 0);
398 #endif
399 }
400 
401 #if 0
402 void
403 oplsyn_getinfo(addr, sd)
404 	void *addr;
405 	struct synth_dev *sd;
406 {
407 	struct opl_softc *sc = addr;
408 
409 	sd->name = sc->model == OPL_2 ? "Yamaha OPL2" : "Yamaha OPL3";
410 	sd->type = SYNTH_TYPE_FM;
411 	sd->subtype = sc->model == OPL_2 ? SYNTH_SUB_FM_TYPE_ADLIB
412 		: SYNTH_SUB_FM_TYPE_OPL3;
413 	sd->capabilities = 0;
414 }
415 #endif
416 
417 void
418 oplsyn_reset(addr)
419 	void *addr;
420 {
421 	struct opl_softc *sc = addr;
422 	DPRINTFN(3, ("oplsyn_reset:\n"));
423 	opl_reset(sc);
424 }
425 
426 const int8_t opl_volume_table[128] =
427     {-64, -48, -40, -35, -32, -29, -27, -26,
428      -24, -23, -21, -20, -19, -18, -18, -17,
429      -16, -15, -15, -14, -13, -13, -12, -12,
430      -11, -11, -10, -10, -10, -9, -9, -8,
431      -8, -8, -7, -7, -7, -6, -6, -6,
432      -5, -5, -5, -5, -4, -4, -4, -4,
433      -3, -3, -3, -3, -2, -2, -2, -2,
434      -2, -1, -1, -1, -1, 0, 0, 0,
435      0, 0, 0, 1, 1, 1, 1, 1,
436      1, 2, 2, 2, 2, 2, 2, 2,
437      3, 3, 3, 3, 3, 3, 3, 4,
438      4, 4, 4, 4, 4, 4, 4, 5,
439      5, 5, 5, 5, 5, 5, 5, 5,
440      6, 6, 6, 6, 6, 6, 6, 6,
441      6, 7, 7, 7, 7, 7, 7, 7,
442      7, 7, 7, 8, 8, 8, 8, 8};
443 
444 int
445 opl_calc_vol(regbyte, volume, mainvol)
446 	int regbyte;
447 	int volume;
448 	int mainvol;
449 {
450 	int level = ~regbyte & OPL_TOTAL_LEVEL_MASK;
451 
452 	if (mainvol > 127)
453 		mainvol = 127;
454 
455 	volume = (volume * mainvol) / 127;
456 
457 	if (level)
458 		level += opl_volume_table[volume];
459 
460 	if (level > OPL_TOTAL_LEVEL_MASK)
461 		level = OPL_TOTAL_LEVEL_MASK;
462 	if (level < 0)
463 		level = 0;
464 
465 	return (~level & OPL_TOTAL_LEVEL_MASK);
466 }
467 
468 void
469 oplsyn_noteon(ms, voice, freq, vel)
470 	midisyn *ms;
471 	u_int32_t voice, freq, vel;
472 {
473 	struct opl_softc *sc = ms->data;
474 	struct opl_voice *v;
475 	const struct opl_operators *p;
476 	u_int32_t block_fnum;
477 	int mult;
478 	int c_mult, m_mult;
479 	u_int32_t chan;
480 	u_int8_t chars0, chars1, ksl0, ksl1, fbc;
481 	u_int8_t r20m, r20c, r40m, r40c, rA0, rB0;
482 	u_int8_t vol0, vol1;
483 
484 	DPRINTFN(3, ("oplsyn_noteon: %p %d %d\n", sc, voice,
485 		     MIDISYN_FREQ_TO_HZ(freq)));
486 
487 #ifdef DIAGNOSTIC
488 	if (voice < 0 || voice >= sc->syn.nvoice) {
489 		printf("oplsyn_noteon: bad voice %d\n", voice);
490 		return;
491 	}
492 #endif
493 	/* Turn off old note */
494 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 0, 0xff);
495 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 1, 0xff);
496 	opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice,    0);
497 
498 	v = &sc->voices[voice];
499 
500 	chan = MS_GETCHAN(&ms->voices[voice]);
501 	p = &opl2_instrs[ms->pgms[chan]];
502 	v->patch = p;
503 	opl_load_patch(sc, voice);
504 
505 	mult = 1;
506 	for (;;) {
507 		block_fnum = opl_get_block_fnum(freq / mult);
508 		if (block_fnum != OPL_FNUM_FAIL)
509 			break;
510 		mult *= 2;
511 		if (mult == 16)
512 			mult = 15;
513 	}
514 
515 	chars0 = p->ops[OO_CHARS+0];
516 	chars1 = p->ops[OO_CHARS+1];
517 	m_mult = (chars0 & OPL_MULTIPLE_MASK) * mult;
518 	c_mult = (chars1 & OPL_MULTIPLE_MASK) * mult;
519 	if ((block_fnum == OPL_FNUM_FAIL) || (m_mult > 15) || (c_mult > 15)) {
520 		printf("oplsyn_noteon: frequency out of range %d\n",
521 		       MIDISYN_FREQ_TO_HZ(freq));
522 		return;
523 	}
524 	r20m = (chars0 &~ OPL_MULTIPLE_MASK) | m_mult;
525 	r20c = (chars1 &~ OPL_MULTIPLE_MASK) | c_mult;
526 
527 	/* 2 voice */
528 	ksl0 = p->ops[OO_KSL_LEV+0];
529 	ksl1 = p->ops[OO_KSL_LEV+1];
530 	if (p->ops[OO_FB_CONN] & 0x01) {
531 		vol0 = opl_calc_vol(ksl0, vel, sc->volume);
532 		vol1 = opl_calc_vol(ksl1, vel, sc->volume);
533 	} else {
534 		vol0 = ksl0;
535 		vol1 = opl_calc_vol(ksl1, vel, sc->volume);
536 	}
537 	r40m = (ksl0 & OPL_KSL_MASK) | vol0;
538 	r40c = (ksl1 & OPL_KSL_MASK) | vol1;
539 
540 	rA0  = block_fnum & 0xFF;
541 	rB0  = (block_fnum >> 8) | OPL_KEYON_BIT;
542 
543 	v->rB0 = rB0;
544 
545 	fbc = p->ops[OO_FB_CONN];
546 	if (sc->model == OPL_3) {
547 		fbc &= ~OPL_STEREO_BITS;
548 		fbc |= sc->pan[chan];
549 	}
550 	opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, voice, fbc);
551 
552 	opl_set_op_reg(sc, OPL_AM_VIB,      voice, 0, r20m);
553 	opl_set_op_reg(sc, OPL_AM_VIB,      voice, 1, r20c);
554 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 0, r40m);
555 	opl_set_op_reg(sc, OPL_KSL_LEVEL,   voice, 1, r40c);
556 	opl_set_ch_reg(sc, OPL_FNUM_LOW,    voice,    rA0);
557 	opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice,    rB0);
558 }
559 
560 void
561 oplsyn_noteoff(ms, voice, note, vel)
562 	midisyn *ms;
563 	u_int32_t voice, note, vel;
564 {
565 	struct opl_softc *sc = ms->data;
566 	struct opl_voice *v;
567 
568 	DPRINTFN(3, ("oplsyn_noteoff: %p %d %d\n", sc, voice,
569 		     MIDISYN_FREQ_TO_HZ(note)));
570 
571 #ifdef DIAGNOSTIC
572 	if (voice < 0 || voice >= sc->syn.nvoice) {
573 		printf("oplsyn_noteoff: bad voice %d\n", voice);
574 		return;
575 	}
576 #endif
577 	v = &sc->voices[voice];
578 	opl_set_ch_reg(sc, 0xB0, voice, v->rB0 & ~OPL_KEYON_BIT);
579 }
580 
581 void
582 oplsyn_keypressure(ms, voice, note, vel)
583 	midisyn *ms;
584 	u_int32_t voice, note, vel;
585 {
586 #ifdef AUDIO_DEBUG
587 	struct opl_softc *sc = ms->data;
588 	DPRINTFN(1, ("oplsyn_keypressure: %p %d\n", sc, note));
589 #endif
590 }
591 
592 void
593 oplsyn_ctlchange(ms, chan, parm, w14)
594 	midisyn *ms;
595 	u_int32_t chan, parm, w14;
596 {
597 	struct opl_softc *sc = ms->data;
598 
599 	DPRINTFN(1, ("oplsyn_ctlchange: %p %d\n", sc, chan));
600 	switch (parm) {
601 	case MIDI_CTRL_PAN_MSB:
602 		sc->pan[chan] =
603 		    (w14 <= OPL_MIDI_CENTER_MAX ? sc->panl : 0) |
604 		    (w14 >= OPL_MIDI_CENTER_MIN ? sc->panr : 0);
605 		break;
606 	}
607 }
608 
609 /* PROGRAM CHANGE midi event: */
610 void
611 oplsyn_programchange(ms, chan, prog)
612 	midisyn *ms;
613 	u_int32_t chan;
614 	u_int32_t prog;
615 {
616 	/* sanity checks */
617 	if (chan >= MIDI_MAX_CHANS || prog >= OPL_NINSTR)
618 		return;
619 
620 	ms->pgms[chan] = prog;
621 }
622 
623 void
624 oplsyn_pitchbend(ms, voice, parm, x)
625 	midisyn *ms;
626 	u_int32_t voice, parm, x;
627 {
628 #ifdef AUDIO_DEBUG
629 	struct opl_softc *sc = ms->data;
630 	DPRINTFN(1, ("oplsyn_pitchbend: %p %d\n", sc, voice));
631 #endif
632 }
633 
634 void
635 oplsyn_loadpatch(ms, sysex, uio)
636 	midisyn *ms;
637 	struct sysex_info *sysex;
638 	struct uio *uio;
639 {
640 #if 0
641 	struct opl_softc *sc = ms->data;
642 	struct sbi_instrument ins;
643 
644 	DPRINTFN(1, ("oplsyn_loadpatch: %p\n", sc));
645 
646 	memcpy(&ins, sysex, sizeof *sysex);
647 	if (uio->uio_resid >= sizeof ins - sizeof *sysex)
648 		return EINVAL;
649 	uiomove((char *)&ins + sizeof *sysex, sizeof ins - sizeof *sysex, uio);
650 	/* XXX */
651 #endif
652 }
653