FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/opl.c
1 /* $NetBSD: opl.c,v 1.31 2006/11/16 01:32:52 christos 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.31 2006/11/16 01:32:52 christos 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 #include <sys/malloc.h>
55
56 #include <machine/cpu.h>
57 #include <machine/bus.h>
58
59 #include <sys/audioio.h>
60 #include <sys/midiio.h>
61 #include <dev/audio_if.h>
62
63 #include <dev/midi_if.h>
64 #include <dev/midivar.h>
65 #include <dev/midisynvar.h>
66
67 #include <dev/ic/oplreg.h>
68 #include <dev/ic/oplvar.h>
69
70 #ifdef AUDIO_DEBUG
71 #define DPRINTF(x) if (opldebug) printf x
72 #define DPRINTFN(n,x) if (opldebug >= (n)) printf x
73 int opldebug = 0;
74 #else
75 #define DPRINTF(x)
76 #define DPRINTFN(n,x)
77 #endif
78
79 struct real_voice {
80 u_int8_t voice_num;
81 u_int8_t voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
82 u_int8_t iooffs; /* I/O port (left or right side) */
83 u_int8_t op[4]; /* Operator offsets */
84 };
85
86 const struct opl_voice voicetab[] = {
87 /* No I/O offs OP1 OP2 OP3 OP4 */
88 /* --------------------------------------------- */
89 { 0, OPL_L, {0x00, 0x03, 0x08, 0x0b}, NULL, 0, },
90 { 1, OPL_L, {0x01, 0x04, 0x09, 0x0c}, NULL, 0, },
91 { 2, OPL_L, {0x02, 0x05, 0x0a, 0x0d}, NULL, 0, },
92
93 { 3, OPL_L, {0x08, 0x0b, 0x00, 0x00}, NULL, 0, },
94 { 4, OPL_L, {0x09, 0x0c, 0x00, 0x00}, NULL, 0, },
95 { 5, OPL_L, {0x0a, 0x0d, 0x00, 0x00}, NULL, 0, },
96
97 { 6, OPL_L, {0x10, 0x13, 0x00, 0x00}, NULL, 0, },
98 { 7, OPL_L, {0x11, 0x14, 0x00, 0x00}, NULL, 0, },
99 { 8, OPL_L, {0x12, 0x15, 0x00, 0x00}, NULL, 0, },
100
101 { 0, OPL_R, {0x00, 0x03, 0x08, 0x0b}, NULL, 0, },
102 { 1, OPL_R, {0x01, 0x04, 0x09, 0x0c}, NULL, 0, },
103 { 2, OPL_R, {0x02, 0x05, 0x0a, 0x0d}, NULL, 0, },
104 { 3, OPL_R, {0x08, 0x0b, 0x00, 0x00}, NULL, 0, },
105 { 4, OPL_R, {0x09, 0x0c, 0x00, 0x00}, NULL, 0, },
106 { 5, OPL_R, {0x0a, 0x0d, 0x00, 0x00}, NULL, 0, },
107
108 { 6, OPL_R, {0x10, 0x13, 0x00, 0x00}, NULL, 0, },
109 { 7, OPL_R, {0x11, 0x14, 0x00, 0x00}, NULL, 0, },
110 { 8, OPL_R, {0x12, 0x15, 0x00, 0x00}, NULL, 0, }
111 };
112
113 static void opl_command(struct opl_softc *, int, int, int);
114 void opl_reset(struct opl_softc *);
115 void opl_freq_to_fnum (int freq, int *block, int *fnum);
116
117 int oplsyn_open(midisyn *ms, int);
118 void oplsyn_close(midisyn *);
119 void oplsyn_reset(void *);
120 void oplsyn_attackv(midisyn *, uint_fast16_t, midipitch_t, int16_t);
121 static void oplsyn_repitchv(midisyn *, uint_fast16_t, midipitch_t);
122 static void oplsyn_relevelv(midisyn *, uint_fast16_t, int16_t);
123 static void oplsyn_setv(midisyn *, uint_fast16_t, midipitch_t, int16_t, int);
124 void oplsyn_releasev(midisyn *, uint_fast16_t, uint_fast8_t);
125 int oplsyn_ctlnotice(midisyn *, midictl_evt, uint_fast8_t, uint_fast16_t);
126 void oplsyn_programchange(midisyn *, uint_fast8_t, uint_fast8_t);
127 void oplsyn_loadpatch(midisyn *, struct sysex_info *, struct uio *);
128 static void oplsyn_panhandler(midisyn *, uint_fast8_t);
129
130 void opl_set_op_reg(struct opl_softc *, int, int, int, u_char);
131 void opl_set_ch_reg(struct opl_softc *, int, int, u_char);
132 void opl_load_patch(struct opl_softc *, int);
133 u_int32_t opl_get_block_fnum(midipitch_t mp);
134 int opl_calc_vol(int regbyte, int16_t level_cB);
135
136 struct midisyn_methods opl3_midi = {
137 .open = oplsyn_open,
138 .close = oplsyn_close,
139 .attackv = oplsyn_attackv,
140 .repitchv = oplsyn_repitchv,
141 .relevelv = oplsyn_relevelv,
142 .releasev = oplsyn_releasev,
143 .pgmchg = oplsyn_programchange,
144 .ctlnotice = oplsyn_ctlnotice,
145 };
146
147 void
148 opl_attach(sc)
149 struct opl_softc *sc;
150 {
151 int i;
152
153 if (!opl_find(sc)) {
154 printf("\nopl: find failed\n");
155 return;
156 }
157
158 sc->syn.mets = &opl3_midi;
159 snprintf(sc->syn.name, sizeof(sc->syn.name), "%sYamaha OPL%d",
160 sc->syn.name, sc->model);
161 sc->syn.data = sc;
162 sc->syn.nvoice = sc->model == OPL_2 ? OPL2_NVOICE : OPL3_NVOICE;
163 midisyn_attach(&sc->mididev, &sc->syn);
164
165 /* Set up voice table */
166 for (i = 0; i < OPL3_NVOICE; i++)
167 sc->voices[i] = voicetab[i];
168
169 opl_reset(sc);
170
171 printf(": model OPL%d", sc->model);
172
173 /* Set up panpot */
174 sc->panl = OPL_VOICE_TO_LEFT;
175 sc->panr = OPL_VOICE_TO_RIGHT;
176 if (sc->model == OPL_3 &&
177 device_cfdata(&sc->mididev.dev)->cf_flags & OPL_FLAGS_SWAP_LR) {
178 sc->panl = OPL_VOICE_TO_RIGHT;
179 sc->panr = OPL_VOICE_TO_LEFT;
180 printf(": LR swapped");
181 }
182
183 printf("\n");
184
185 sc->sc_mididev =
186 midi_attach_mi(&midisyn_hw_if, &sc->syn, &sc->mididev.dev);
187 }
188
189 int
190 opl_detach(sc, flags)
191 struct opl_softc *sc;
192 int flags;
193 {
194 int rv = 0;
195
196 if (sc->sc_mididev != NULL)
197 rv = config_detach(sc->sc_mididev, flags);
198
199 return(rv);
200 }
201
202 static void
203 opl_command(sc, offs, addr, data)
204 struct opl_softc *sc;
205 int offs;
206 int addr, data;
207 {
208 DPRINTFN(4, ("opl_command: sc=%p, offs=%d addr=0x%02x data=0x%02x\n",
209 sc, offs, addr, data));
210 offs += sc->offs;
211 bus_space_write_1(sc->iot, sc->ioh, OPL_ADDR+offs, addr);
212 if (sc->model == OPL_2)
213 delay(10);
214 else
215 delay(6);
216 bus_space_write_1(sc->iot, sc->ioh, OPL_DATA+offs, data);
217 if (sc->model == OPL_2)
218 delay(30);
219 else
220 delay(6);
221 }
222
223 int
224 opl_match(bus_space_tag_t iot, bus_space_handle_t ioh, int offs)
225 {
226 struct opl_softc *sc;
227 int rv;
228
229 sc = malloc(sizeof(*sc), M_TEMP, M_WAITOK|M_ZERO);
230 sc->iot = iot;
231 sc->ioh = ioh;
232 sc->offs = offs;
233 rv = opl_find(sc);
234 free(sc, M_TEMP);
235 return rv;
236 }
237
238 int
239 opl_find(sc)
240 struct opl_softc *sc;
241 {
242 u_int8_t status1, status2;
243
244 DPRINTFN(2,("opl_find: ioh=0x%x\n", (int)sc->ioh));
245 sc->model = OPL_2; /* worst case assumption */
246
247 /* Reset timers 1 and 2 */
248 opl_command(sc, OPL_L, OPL_TIMER_CONTROL,
249 OPL_TIMER1_MASK | OPL_TIMER2_MASK);
250 /* Reset the IRQ of the FM chip */
251 opl_command(sc, OPL_L, OPL_TIMER_CONTROL, OPL_IRQ_RESET);
252
253 /* get status bits */
254 status1 = bus_space_read_1(sc->iot,sc->ioh,OPL_STATUS+OPL_L+sc->offs);
255
256 opl_command(sc, OPL_L, OPL_TIMER1, -2); /* wait 2 ticks */
257 opl_command(sc, OPL_L, OPL_TIMER_CONTROL, /* start timer1 */
258 OPL_TIMER1_START | OPL_TIMER2_MASK);
259 delay(1000); /* wait for timer to expire */
260
261 /* get status bits again */
262 status2 = bus_space_read_1(sc->iot,sc->ioh,OPL_STATUS+OPL_L+sc->offs);
263
264 opl_command(sc, OPL_L, OPL_TIMER_CONTROL,
265 OPL_TIMER1_MASK | OPL_TIMER2_MASK);
266 opl_command(sc, OPL_L, OPL_TIMER_CONTROL, OPL_IRQ_RESET);
267
268 DPRINTFN(2,("opl_find: %02x %02x\n", status1, status2));
269
270 if ((status1 & OPL_STATUS_MASK) != 0 ||
271 (status2 & OPL_STATUS_MASK) != (OPL_STATUS_IRQ | OPL_STATUS_FT1))
272 return (0);
273
274 switch(status1) {
275 case 0x00:
276 case 0x0f:
277 sc->model = OPL_3;
278 break;
279 case 0x06:
280 sc->model = OPL_2;
281 break;
282 default:
283 return (0);
284 }
285
286 DPRINTFN(2,("opl_find: OPL%d at 0x%x detected\n",
287 sc->model, (int)sc->ioh));
288 return (1);
289 }
290
291 /*
292 * idea: opl_command does a lot of busywaiting, and the driver typically sets
293 * a lot of registers each time a voice-attack happens. some kind of
294 * caching to remember what was last written to each register could save
295 * a lot of cpu. It would have to be smart enough not to interfere with
296 * any necessary sequences of register access expected by the hardware...
297 */
298 void
299 opl_set_op_reg(sc, base, voice, op, value)
300 struct opl_softc *sc;
301 int base;
302 int voice;
303 int op;
304 u_char value;
305 {
306 struct opl_voice *v = &sc->voices[voice];
307 opl_command(sc, v->iooffs, base + v->op[op], value);
308 }
309
310 void
311 opl_set_ch_reg(sc, base, voice, value)
312 struct opl_softc *sc;
313 int base;
314 int voice;
315 u_char value;
316 {
317 struct opl_voice *v = &sc->voices[voice];
318 opl_command(sc, v->iooffs, base + v->voiceno, value);
319 }
320
321
322 void
323 opl_load_patch(sc, v)
324 struct opl_softc *sc;
325 int v;
326 {
327 const struct opl_operators *p = sc->voices[v].patch;
328
329 opl_set_op_reg(sc, OPL_AM_VIB, v, 0, p->ops[OO_CHARS+0]);
330 opl_set_op_reg(sc, OPL_AM_VIB, v, 1, p->ops[OO_CHARS+1]);
331 opl_set_op_reg(sc, OPL_KSL_LEVEL, v, 0, p->ops[OO_KSL_LEV+0]);
332 opl_set_op_reg(sc, OPL_KSL_LEVEL, v, 1, p->ops[OO_KSL_LEV+1]);
333 opl_set_op_reg(sc, OPL_ATTACK_DECAY, v, 0, p->ops[OO_ATT_DEC+0]);
334 opl_set_op_reg(sc, OPL_ATTACK_DECAY, v, 1, p->ops[OO_ATT_DEC+1]);
335 opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 0, p->ops[OO_SUS_REL+0]);
336 opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 1, p->ops[OO_SUS_REL+1]);
337 opl_set_op_reg(sc, OPL_WAVE_SELECT, v, 0, p->ops[OO_WAV_SEL+0]);
338 opl_set_op_reg(sc, OPL_WAVE_SELECT, v, 1, p->ops[OO_WAV_SEL+1]);
339 opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, v, p->ops[OO_FB_CONN]);
340 }
341
342 uint32_t
343 opl_get_block_fnum(midipitch_t mp)
344 {
345 midihz18_t hz18;
346 uint32_t block;
347 uint32_t f_num;
348
349 /*
350 * We can get to about note 30 before needing to switch from block 0.
351 * Thereafter, switch block every octave; that will keep f_num in the
352 * upper end of its range, making the most bits available for
353 * resolution.
354 */
355 block = ( mp - MIDIPITCH_FROM_KEY(19) ) / MIDIPITCH_OCTAVE;
356 if ( block > 7 ) /* subtract wrapped */
357 block = 0;
358 /*
359 * Could subtract block*MIDIPITCH_OCTAVE here, or >>block later. Later.
360 */
361
362 hz18 = MIDIPITCH_TO_HZ18(mp);
363 hz18 >>= block;
364
365 /*
366 * The formula in the manual is f_num = ((hz<<19)/fs)>>(block-1) (though
367 * block==0 implies >>-1 which is a C unspecified result). As we already
368 * have hz<<18 and I omitted the -1 when shifting above, what's left to
369 * do now is multiply by 4 and divide by fs, the sampling frequency of
370 * the chip. fs is the master clock frequency fM / 288, fM is 14.32 MHz
371 * so fs is a goofy number around 49.7kHz. The 5th convergent of the
372 * continued fraction matches 4/fs to 9+ significant figures. Doing the
373 * shift first (above) ensures there's room in hz18 to multiply by 9.
374 */
375
376 f_num = (9 * hz18) / 111875;
377 return ((block << 10) | f_num);
378 }
379
380
381 void
382 opl_reset(sc)
383 struct opl_softc *sc;
384 {
385 int i;
386
387 for (i = 1; i <= OPL_MAXREG; i++)
388 opl_command(sc, OPL_L, OPL_KEYON_BLOCK + i, 0);
389
390 opl_command(sc, OPL_L, OPL_TEST, OPL_ENABLE_WAVE_SELECT);
391 opl_command(sc, OPL_L, OPL_PERCUSSION, 0);
392 if (sc->model == OPL_3) {
393 opl_command(sc, OPL_R, OPL_MODE, OPL3_ENABLE);
394 opl_command(sc, OPL_R,OPL_CONNECTION_SELECT,OPL_NOCONNECTION);
395 }
396
397 for (i = 0; i < MIDI_MAX_CHANS; i++)
398 sc->pan[i] = OPL_VOICE_TO_LEFT | OPL_VOICE_TO_RIGHT;
399 }
400
401 int
402 oplsyn_open(midisyn *ms, int flags)
403 {
404 struct opl_softc *sc = ms->data;
405
406 DPRINTFN(2, ("oplsyn_open: %d\n", flags));
407
408 #ifndef AUDIO_NO_POWER_CTL
409 if (sc->powerctl)
410 sc->powerctl(sc->powerarg, 1);
411 #endif
412 opl_reset(ms->data);
413 if (sc->spkrctl)
414 sc->spkrctl(sc->spkrarg, 1);
415 return (0);
416 }
417
418 void
419 oplsyn_close(ms)
420 midisyn *ms;
421 {
422 struct opl_softc *sc = ms->data;
423
424 DPRINTFN(2, ("oplsyn_close:\n"));
425
426 /*opl_reset(ms->data);*/
427 if (sc->spkrctl)
428 sc->spkrctl(sc->spkrarg, 0);
429 #ifndef AUDIO_NO_POWER_CTL
430 if (sc->powerctl)
431 sc->powerctl(sc->powerarg, 0);
432 #endif
433 }
434
435 #if 0
436 void
437 oplsyn_getinfo(addr, sd)
438 void *addr;
439 struct synth_dev *sd;
440 {
441 struct opl_softc *sc = addr;
442
443 sd->name = sc->model == OPL_2 ? "Yamaha OPL2" : "Yamaha OPL3";
444 sd->type = SYNTH_TYPE_FM;
445 sd->subtype = sc->model == OPL_2 ? SYNTH_SUB_FM_TYPE_ADLIB
446 : SYNTH_SUB_FM_TYPE_OPL3;
447 sd->capabilities = 0;
448 }
449 #endif
450
451 void
452 oplsyn_reset(addr)
453 void *addr;
454 {
455 struct opl_softc *sc = addr;
456 DPRINTFN(3, ("oplsyn_reset:\n"));
457 opl_reset(sc);
458 }
459
460 int
461 opl_calc_vol(int regbyte, int16_t level_cB)
462 {
463 int level = regbyte & OPL_TOTAL_LEVEL_MASK;
464
465 /*
466 * level is a six-bit attenuation, from 0 (full output)
467 * to -48dB (but without the minus sign) in steps of .75 dB.
468 * We'll just add level_cB, after scaling it because it's
469 * in centibels instead and has the customary minus sign.
470 */
471
472 level += ( -4 * level_cB ) / 30;
473
474 if (level > OPL_TOTAL_LEVEL_MASK)
475 level = OPL_TOTAL_LEVEL_MASK;
476 if (level < 0)
477 level = 0;
478
479 return level & OPL_TOTAL_LEVEL_MASK;
480 }
481
482 #define OPLACT_ARTICULATE 1
483 #define OPLACT_PITCH 2
484 #define OPLACT_LEVEL 4
485
486 void
487 oplsyn_attackv(midisyn *ms,
488 uint_fast16_t voice, midipitch_t mp, int16_t level_cB)
489 {
490 oplsyn_setv(ms, voice, mp, level_cB,
491 OPLACT_ARTICULATE | OPLACT_PITCH | OPLACT_LEVEL);
492 }
493
494 static void
495 oplsyn_repitchv(midisyn *ms, uint_fast16_t voice, midipitch_t mp)
496 {
497 oplsyn_setv(ms, voice, mp, 0, OPLACT_PITCH);
498 }
499
500 static void
501 oplsyn_relevelv(midisyn *ms, uint_fast16_t voice, int16_t level_cB)
502 {
503 oplsyn_setv(ms, voice, 0, level_cB, OPLACT_LEVEL);
504 }
505
506 static void
507 oplsyn_setv(midisyn *ms,
508 uint_fast16_t voice, midipitch_t mp, int16_t level_cB, int act)
509 {
510 struct opl_softc *sc = ms->data;
511 struct opl_voice *v;
512 const struct opl_operators *p;
513 u_int32_t block_fnum;
514 int mult;
515 int c_mult, m_mult;
516 u_int32_t chan;
517 u_int8_t chars0, chars1, ksl0, ksl1, fbc;
518 u_int8_t r20m, r20c, r40m, r40c, rA0, rB0;
519 u_int8_t vol0, vol1;
520
521 DPRINTFN(3, ("%s: %p %d %u %d\n", __func__, sc, voice,
522 mp, level_cB));
523
524 #ifdef DIAGNOSTIC
525 if (voice >= sc->syn.nvoice) {
526 printf("%s: bad voice %d\n", __func__, voice);
527 return;
528 }
529 #endif
530 v = &sc->voices[voice];
531
532 if ( act & OPLACT_ARTICULATE ) {
533 /* Turn off old note */
534 opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 0, 0xff);
535 opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 1, 0xff);
536 opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice, 0);
537
538 chan = MS_GETCHAN(&ms->voices[voice]);
539 p = &opl2_instrs[ms->pgms[chan]];
540 v->patch = p;
541 opl_load_patch(sc, voice);
542
543 fbc = p->ops[OO_FB_CONN];
544 if (sc->model == OPL_3) {
545 fbc &= ~OPL_STEREO_BITS;
546 fbc |= sc->pan[chan];
547 }
548 opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, voice, fbc);
549 } else
550 p = v->patch;
551
552 if ( act & OPLACT_LEVEL ) {
553 /* 2 voice */
554 ksl0 = p->ops[OO_KSL_LEV+0];
555 ksl1 = p->ops[OO_KSL_LEV+1];
556 if (p->ops[OO_FB_CONN] & 0x01) {
557 vol0 = opl_calc_vol(ksl0, level_cB);
558 vol1 = opl_calc_vol(ksl1, level_cB);
559 } else {
560 vol0 = ksl0;
561 vol1 = opl_calc_vol(ksl1, level_cB);
562 }
563 r40m = (ksl0 & OPL_KSL_MASK) | vol0;
564 r40c = (ksl1 & OPL_KSL_MASK) | vol1;
565
566 opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 0, r40m);
567 opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 1, r40c);
568 }
569
570 if ( act & OPLACT_PITCH ) {
571 mult = 1;
572 if ( mp > MIDIPITCH_FROM_KEY(114) ) { /* out of mult 1 range */
573 mult = 4; /* will cover remaining MIDI range */
574 mp -= 2*MIDIPITCH_OCTAVE;
575 }
576
577 block_fnum = opl_get_block_fnum(mp);
578
579 chars0 = p->ops[OO_CHARS+0];
580 chars1 = p->ops[OO_CHARS+1];
581 m_mult = (chars0 & OPL_MULTIPLE_MASK) * mult;
582 c_mult = (chars1 & OPL_MULTIPLE_MASK) * mult;
583
584 if ( 4 == mult ) {
585 if ( 0 == m_mult ) /* The OPL uses 0 to represent .5 */
586 m_mult = 2; /* but of course 0*mult above did */
587 if ( 0 == c_mult ) /* not DTRT */
588 c_mult = 2;
589 }
590
591 if ((m_mult > 15) || (c_mult > 15)) {
592 printf("%s: frequency out of range %u (mult %d)\n",
593 __func__, mp, mult);
594 return;
595 }
596 r20m = (chars0 &~ OPL_MULTIPLE_MASK) | m_mult;
597 r20c = (chars1 &~ OPL_MULTIPLE_MASK) | c_mult;
598
599 rA0 = block_fnum & 0xFF;
600 rB0 = (block_fnum >> 8) | OPL_KEYON_BIT;
601
602 v->rB0 = rB0;
603
604 opl_set_op_reg(sc, OPL_AM_VIB, voice, 0, r20m);
605 opl_set_op_reg(sc, OPL_AM_VIB, voice, 1, r20c);
606
607 opl_set_ch_reg(sc, OPL_FNUM_LOW, voice, rA0);
608 opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice, rB0);
609 }
610 }
611
612 void
613 oplsyn_releasev(midisyn *ms, uint_fast16_t voice, uint_fast8_t vel)
614 {
615 struct opl_softc *sc = ms->data;
616 struct opl_voice *v;
617
618 DPRINTFN(1, ("%s: %p %d\n", __func__, sc, voice));
619
620 #ifdef DIAGNOSTIC
621 if (voice >= sc->syn.nvoice) {
622 printf("oplsyn_noteoff: bad voice %d\n", voice);
623 return;
624 }
625 #endif
626 v = &sc->voices[voice];
627 opl_set_ch_reg(sc, 0xB0, voice, v->rB0 & ~OPL_KEYON_BIT);
628 }
629
630 int
631 oplsyn_ctlnotice(midisyn *ms,
632 midictl_evt evt, uint_fast8_t chan, uint_fast16_t key)
633 {
634
635 DPRINTFN(1, ("%s: %p %d\n", __func__, ms->data, chan));
636
637 switch (evt) {
638 case MIDICTL_RESET:
639 oplsyn_panhandler(ms, chan);
640 return 1;
641
642 case MIDICTL_CTLR:
643 switch (key) {
644 case MIDI_CTRL_PAN_MSB:
645 oplsyn_panhandler(ms, chan);
646 return 1;
647 }
648 return 0;
649 default:
650 return 0;
651 }
652 }
653
654 /* PROGRAM CHANGE midi event: */
655 void
656 oplsyn_programchange(midisyn *ms, uint_fast8_t chan, uint_fast8_t prog)
657 {
658 /* sanity checks */
659 if (chan >= MIDI_MAX_CHANS)
660 return;
661
662 ms->pgms[chan] = prog;
663 }
664
665 void
666 oplsyn_loadpatch(midisyn *ms, struct sysex_info *sysex,
667 struct uio *uio)
668 {
669 #if 0
670 struct opl_softc *sc = ms->data;
671 struct sbi_instrument ins;
672
673 DPRINTFN(1, ("oplsyn_loadpatch: %p\n", sc));
674
675 memcpy(&ins, sysex, sizeof *sysex);
676 if (uio->uio_resid >= sizeof ins - sizeof *sysex)
677 return EINVAL;
678 uiomove((char *)&ins + sizeof *sysex, sizeof ins - sizeof *sysex, uio);
679 /* XXX */
680 #endif
681 }
682
683 static void
684 oplsyn_panhandler(midisyn *ms, uint_fast8_t chan)
685 {
686 struct opl_softc *sc = ms->data;
687 uint_fast16_t setting;
688
689 setting = midictl_read(&ms->ctl, chan, MIDI_CTRL_PAN_MSB, 8192);
690 setting >>= 7; /* we used to treat it as MSB only */
691 sc->pan[chan] =
692 (setting <= OPL_MIDI_CENTER_MAX ? sc->panl : 0) |
693 (setting >= OPL_MIDI_CENTER_MIN ? sc->panr : 0);
694 }
Cache object: ccdfc8ff790f487e7dbfda1b091ef2e4
|