1 | /* $NetBSD: opl.c,v 1.42 2016/07/14 10:19:06 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1998, 2008 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), and by Andrew Doran. |
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 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * The OPL3 (YMF262) manual can be found at |
34 | * ftp://ftp.yamahayst.com/Fax_Back_Doc/sound/YMF262.PDF |
35 | */ |
36 | |
37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: opl.c,v 1.42 2016/07/14 10:19:06 msaitoh Exp $" ); |
39 | |
40 | #include <sys/param.h> |
41 | #include <sys/systm.h> |
42 | #include <sys/errno.h> |
43 | #include <sys/ioctl.h> |
44 | #include <sys/syslog.h> |
45 | #include <sys/device.h> |
46 | #include <sys/select.h> |
47 | #include <sys/kmem.h> |
48 | |
49 | #include <sys/cpu.h> |
50 | #include <sys/bus.h> |
51 | |
52 | #include <sys/audioio.h> |
53 | #include <sys/midiio.h> |
54 | #include <dev/audio_if.h> |
55 | |
56 | #include <dev/midi_if.h> |
57 | #include <dev/midivar.h> |
58 | #include <dev/midisynvar.h> |
59 | |
60 | #include <dev/ic/oplreg.h> |
61 | #include <dev/ic/oplvar.h> |
62 | |
63 | #ifdef AUDIO_DEBUG |
64 | #define DPRINTF(x) if (opldebug) printf x |
65 | #define DPRINTFN(n,x) if (opldebug >= (n)) printf x |
66 | int opldebug = 0; |
67 | #else |
68 | #define DPRINTF(x) |
69 | #define DPRINTFN(n,x) |
70 | #endif |
71 | |
72 | struct real_voice { |
73 | u_int8_t voice_num; |
74 | u_int8_t voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ |
75 | u_int8_t iooffs; /* I/O port (left or right side) */ |
76 | u_int8_t op[4]; /* Operator offsets */ |
77 | }; |
78 | |
79 | const struct opl_voice voicetab[] = { |
80 | /* No I/O offs OP1 OP2 OP3 OP4 */ |
81 | /* --------------------------------------------- */ |
82 | { 0, OPL_L, {0x00, 0x03, 0x08, 0x0b}, NULL, 0, }, |
83 | { 1, OPL_L, {0x01, 0x04, 0x09, 0x0c}, NULL, 0, }, |
84 | { 2, OPL_L, {0x02, 0x05, 0x0a, 0x0d}, NULL, 0, }, |
85 | |
86 | { 3, OPL_L, {0x08, 0x0b, 0x00, 0x00}, NULL, 0, }, |
87 | { 4, OPL_L, {0x09, 0x0c, 0x00, 0x00}, NULL, 0, }, |
88 | { 5, OPL_L, {0x0a, 0x0d, 0x00, 0x00}, NULL, 0, }, |
89 | |
90 | { 6, OPL_L, {0x10, 0x13, 0x00, 0x00}, NULL, 0, }, |
91 | { 7, OPL_L, {0x11, 0x14, 0x00, 0x00}, NULL, 0, }, |
92 | { 8, OPL_L, {0x12, 0x15, 0x00, 0x00}, NULL, 0, }, |
93 | |
94 | { 0, OPL_R, {0x00, 0x03, 0x08, 0x0b}, NULL, 0, }, |
95 | { 1, OPL_R, {0x01, 0x04, 0x09, 0x0c}, NULL, 0, }, |
96 | { 2, OPL_R, {0x02, 0x05, 0x0a, 0x0d}, NULL, 0, }, |
97 | { 3, OPL_R, {0x08, 0x0b, 0x00, 0x00}, NULL, 0, }, |
98 | { 4, OPL_R, {0x09, 0x0c, 0x00, 0x00}, NULL, 0, }, |
99 | { 5, OPL_R, {0x0a, 0x0d, 0x00, 0x00}, NULL, 0, }, |
100 | |
101 | { 6, OPL_R, {0x10, 0x13, 0x00, 0x00}, NULL, 0, }, |
102 | { 7, OPL_R, {0x11, 0x14, 0x00, 0x00}, NULL, 0, }, |
103 | { 8, OPL_R, {0x12, 0x15, 0x00, 0x00}, NULL, 0, } |
104 | }; |
105 | |
106 | static void opl_command(struct opl_softc *, int, int, int); |
107 | void opl_reset(struct opl_softc *); |
108 | void opl_freq_to_fnum (int freq, int *block, int *fnum); |
109 | |
110 | int oplsyn_open(midisyn *ms, int); |
111 | void oplsyn_close(midisyn *); |
112 | void oplsyn_reset(void *); |
113 | void oplsyn_attackv(midisyn *, uint_fast16_t, midipitch_t, int16_t); |
114 | static void oplsyn_repitchv(midisyn *, uint_fast16_t, midipitch_t); |
115 | static void oplsyn_relevelv(midisyn *, uint_fast16_t, int16_t); |
116 | static void oplsyn_setv(midisyn *, uint_fast16_t, midipitch_t, int16_t, int); |
117 | void oplsyn_releasev(midisyn *, uint_fast16_t, uint_fast8_t); |
118 | int oplsyn_ctlnotice(midisyn *, midictl_evt, uint_fast8_t, uint_fast16_t); |
119 | void oplsyn_programchange(midisyn *, uint_fast8_t, uint_fast8_t); |
120 | void oplsyn_loadpatch(midisyn *, struct sysex_info *, struct uio *); |
121 | static void oplsyn_panhandler(midisyn *, uint_fast8_t); |
122 | |
123 | void opl_set_op_reg(struct opl_softc *, int, int, int, u_char); |
124 | void opl_set_ch_reg(struct opl_softc *, int, int, u_char); |
125 | void opl_load_patch(struct opl_softc *, int); |
126 | u_int32_t opl_get_block_fnum(midipitch_t mp); |
127 | int opl_calc_vol(int regbyte, int16_t level_cB); |
128 | |
129 | struct midisyn_methods opl3_midi = { |
130 | .open = oplsyn_open, |
131 | .close = oplsyn_close, |
132 | .attackv = oplsyn_attackv, |
133 | .repitchv = oplsyn_repitchv, |
134 | .relevelv = oplsyn_relevelv, |
135 | .releasev = oplsyn_releasev, |
136 | .pgmchg = oplsyn_programchange, |
137 | .ctlnotice = oplsyn_ctlnotice, |
138 | }; |
139 | |
140 | void |
141 | opl_attach(struct opl_softc *sc) |
142 | { |
143 | int i; |
144 | |
145 | KASSERT(sc->dev != NULL); |
146 | KASSERT(sc->lock != NULL); |
147 | |
148 | mutex_enter(sc->lock); |
149 | i = opl_find(sc); |
150 | mutex_exit(sc->lock); |
151 | if (i == 0) { |
152 | aprint_error("\nopl: find failed\n" ); |
153 | return; |
154 | } |
155 | |
156 | mutex_enter(sc->lock); |
157 | opl_reset(sc); |
158 | mutex_exit(sc->lock); |
159 | |
160 | sc->syn.mets = &opl3_midi; |
161 | size_t len = strlen(sc->syn.name); |
162 | snprintf(sc->syn.name + len, sizeof(sc->syn.name) - len, |
163 | "Yamaha OPL%d" , sc->model); |
164 | sc->syn.data = sc; |
165 | sc->syn.nvoice = sc->model == OPL_2 ? OPL2_NVOICE : OPL3_NVOICE; |
166 | sc->syn.lock = sc->lock; |
167 | midisyn_init(&sc->syn); |
168 | |
169 | /* Set up voice table */ |
170 | for (i = 0; i < OPL3_NVOICE; i++) |
171 | sc->voices[i] = voicetab[i]; |
172 | |
173 | aprint_normal(": 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 | device_cfdata(sc->dev)->cf_flags & OPL_FLAGS_SWAP_LR) { |
180 | sc->panl = OPL_VOICE_TO_RIGHT; |
181 | sc->panr = OPL_VOICE_TO_LEFT; |
182 | aprint_normal(": LR swapped" ); |
183 | } |
184 | |
185 | aprint_normal("\n" ); |
186 | aprint_naive("\n" ); |
187 | |
188 | sc->sc_mididev = |
189 | midi_attach_mi(&midisyn_hw_if, &sc->syn, sc->dev); |
190 | } |
191 | |
192 | int |
193 | opl_detach(struct opl_softc *sc, int flags) |
194 | { |
195 | int rv = 0; |
196 | |
197 | if (sc->sc_mididev != NULL) |
198 | rv = config_detach(sc->sc_mididev, flags); |
199 | |
200 | return(rv); |
201 | } |
202 | |
203 | static void |
204 | opl_command(struct opl_softc *sc, int offs, int addr, int data) |
205 | { |
206 | DPRINTFN(4, ("opl_command: sc=%p, offs=%d addr=0x%02x data=0x%02x\n" , |
207 | sc, offs, addr, data)); |
208 | |
209 | KASSERT(!sc->lock || mutex_owned(sc->lock)); |
210 | |
211 | offs += sc->offs; |
212 | bus_space_write_1(sc->iot, sc->ioh, OPL_ADDR+offs, addr); |
213 | if (sc->model == OPL_2) |
214 | delay(10); |
215 | else |
216 | delay(6); |
217 | bus_space_write_1(sc->iot, sc->ioh, OPL_DATA+offs, data); |
218 | if (sc->model == OPL_2) |
219 | delay(30); |
220 | else |
221 | delay(6); |
222 | } |
223 | |
224 | int |
225 | opl_match(bus_space_tag_t iot, bus_space_handle_t ioh, int offs) |
226 | { |
227 | struct opl_softc *sc; |
228 | int rv; |
229 | |
230 | sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); |
231 | sc->iot = iot; |
232 | sc->ioh = ioh; |
233 | sc->offs = offs; |
234 | rv = opl_find(sc); |
235 | kmem_free(sc, sizeof(*sc)); |
236 | return rv; |
237 | } |
238 | |
239 | int |
240 | opl_find(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(struct opl_softc *sc, int base, int voice, int op, u_char value) |
300 | { |
301 | struct opl_voice *v = &sc->voices[voice]; |
302 | |
303 | KASSERT(mutex_owned(sc->lock)); |
304 | |
305 | opl_command(sc, v->iooffs, base + v->op[op], value); |
306 | } |
307 | |
308 | void |
309 | opl_set_ch_reg(struct opl_softc *sc, int base, int voice, u_char value) |
310 | { |
311 | struct opl_voice *v = &sc->voices[voice]; |
312 | |
313 | KASSERT(mutex_owned(sc->lock)); |
314 | |
315 | opl_command(sc, v->iooffs, base + v->voiceno, value); |
316 | } |
317 | |
318 | |
319 | void |
320 | opl_load_patch(struct opl_softc *sc, int v) |
321 | { |
322 | const struct opl_operators *p = sc->voices[v].patch; |
323 | |
324 | KASSERT(mutex_owned(sc->lock)); |
325 | |
326 | opl_set_op_reg(sc, OPL_AM_VIB, v, 0, p->ops[OO_CHARS+0]); |
327 | opl_set_op_reg(sc, OPL_AM_VIB, v, 1, p->ops[OO_CHARS+1]); |
328 | opl_set_op_reg(sc, OPL_KSL_LEVEL, v, 0, p->ops[OO_KSL_LEV+0]); |
329 | opl_set_op_reg(sc, OPL_KSL_LEVEL, v, 1, p->ops[OO_KSL_LEV+1]); |
330 | opl_set_op_reg(sc, OPL_ATTACK_DECAY, v, 0, p->ops[OO_ATT_DEC+0]); |
331 | opl_set_op_reg(sc, OPL_ATTACK_DECAY, v, 1, p->ops[OO_ATT_DEC+1]); |
332 | opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 0, p->ops[OO_SUS_REL+0]); |
333 | opl_set_op_reg(sc, OPL_SUSTAIN_RELEASE, v, 1, p->ops[OO_SUS_REL+1]); |
334 | opl_set_op_reg(sc, OPL_WAVE_SELECT, v, 0, p->ops[OO_WAV_SEL+0]); |
335 | opl_set_op_reg(sc, OPL_WAVE_SELECT, v, 1, p->ops[OO_WAV_SEL+1]); |
336 | opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, v, p->ops[OO_FB_CONN]); |
337 | } |
338 | |
339 | uint32_t |
340 | opl_get_block_fnum(midipitch_t mp) |
341 | { |
342 | midihz18_t hz18; |
343 | uint32_t block; |
344 | uint32_t f_num; |
345 | |
346 | /* |
347 | * We can get to about note 30 before needing to switch from block 0. |
348 | * Thereafter, switch block every octave; that will keep f_num in the |
349 | * upper end of its range, making the most bits available for |
350 | * resolution. |
351 | */ |
352 | block = ( mp - MIDIPITCH_FROM_KEY(19) ) / MIDIPITCH_OCTAVE; |
353 | if ( block > 7 ) /* subtract wrapped */ |
354 | block = 0; |
355 | /* |
356 | * Could subtract block*MIDIPITCH_OCTAVE here, or >>block later. Later. |
357 | */ |
358 | |
359 | hz18 = MIDIPITCH_TO_HZ18(mp); |
360 | hz18 >>= block; |
361 | |
362 | /* |
363 | * The formula in the manual is f_num = ((hz<<19)/fs)>>(block-1) (though |
364 | * block==0 implies >>-1 which is a C unspecified result). As we already |
365 | * have hz<<18 and I omitted the -1 when shifting above, what's left to |
366 | * do now is multiply by 4 and divide by fs, the sampling frequency of |
367 | * the chip. fs is the master clock frequency fM / 288, fM is 14.32 MHz |
368 | * so fs is a goofy number around 49.7kHz. The 5th convergent of the |
369 | * continued fraction matches 4/fs to 9+ significant figures. Doing the |
370 | * shift first (above) ensures there's room in hz18 to multiply by 9. |
371 | */ |
372 | |
373 | f_num = (9 * hz18) / 111875; |
374 | return ((block << 10) | f_num); |
375 | } |
376 | |
377 | |
378 | void |
379 | opl_reset(struct opl_softc *sc) |
380 | { |
381 | int i; |
382 | |
383 | KASSERT(mutex_owned(sc->lock)); |
384 | |
385 | for (i = 1; i <= OPL_MAXREG; i++) |
386 | opl_command(sc, OPL_L, OPL_KEYON_BLOCK + i, 0); |
387 | |
388 | opl_command(sc, OPL_L, OPL_TEST, OPL_ENABLE_WAVE_SELECT); |
389 | opl_command(sc, OPL_L, OPL_PERCUSSION, 0); |
390 | if (sc->model == OPL_3) { |
391 | opl_command(sc, OPL_R, OPL_MODE, OPL3_ENABLE); |
392 | opl_command(sc, OPL_R,OPL_CONNECTION_SELECT,OPL_NOCONNECTION); |
393 | } |
394 | |
395 | for (i = 0; i < MIDI_MAX_CHANS; i++) |
396 | sc->pan[i] = OPL_VOICE_TO_LEFT | OPL_VOICE_TO_RIGHT; |
397 | } |
398 | |
399 | int |
400 | oplsyn_open(midisyn *ms, int flags) |
401 | { |
402 | struct opl_softc *sc = ms->data; |
403 | |
404 | KASSERT(mutex_owned(sc->lock)); |
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(midisyn *ms) |
420 | { |
421 | struct opl_softc *sc = ms->data; |
422 | |
423 | DPRINTFN(2, ("oplsyn_close:\n" )); |
424 | |
425 | KASSERT(mutex_owned(sc->lock)); |
426 | |
427 | /*opl_reset(ms->data);*/ |
428 | if (sc->spkrctl) |
429 | sc->spkrctl(sc->spkrarg, 0); |
430 | #ifndef AUDIO_NO_POWER_CTL |
431 | if (sc->powerctl) |
432 | sc->powerctl(sc->powerarg, 0); |
433 | #endif |
434 | } |
435 | |
436 | #if 0 |
437 | void |
438 | oplsyn_getinfo(void *addr, struct synth_dev *sd) |
439 | { |
440 | struct opl_softc *sc = addr; |
441 | |
442 | sd->name = sc->model == OPL_2 ? "Yamaha OPL2" : "Yamaha OPL3" ; |
443 | sd->type = SYNTH_TYPE_FM; |
444 | sd->subtype = sc->model == OPL_2 ? SYNTH_SUB_FM_TYPE_ADLIB |
445 | : SYNTH_SUB_FM_TYPE_OPL3; |
446 | sd->capabilities = 0; |
447 | } |
448 | #endif |
449 | |
450 | void |
451 | oplsyn_reset(void *addr) |
452 | { |
453 | struct opl_softc *sc = addr; |
454 | |
455 | KASSERT(mutex_owned(sc->lock)); |
456 | |
457 | DPRINTFN(3, ("oplsyn_reset:\n" )); |
458 | opl_reset(sc); |
459 | } |
460 | |
461 | int |
462 | opl_calc_vol(int regbyte, int16_t level_cB) |
463 | { |
464 | int level = regbyte & OPL_TOTAL_LEVEL_MASK; |
465 | |
466 | /* |
467 | * level is a six-bit attenuation, from 0 (full output) |
468 | * to -48dB (but without the minus sign) in steps of .75 dB. |
469 | * We'll just add level_cB, after scaling it because it's |
470 | * in centibels instead and has the customary minus sign. |
471 | */ |
472 | |
473 | level += ( -4 * level_cB ) / 30; |
474 | |
475 | if (level > OPL_TOTAL_LEVEL_MASK) |
476 | level = OPL_TOTAL_LEVEL_MASK; |
477 | if (level < 0) |
478 | level = 0; |
479 | |
480 | return level & OPL_TOTAL_LEVEL_MASK; |
481 | } |
482 | |
483 | #define OPLACT_ARTICULATE 1 |
484 | #define OPLACT_PITCH 2 |
485 | #define OPLACT_LEVEL 4 |
486 | |
487 | void |
488 | oplsyn_attackv(midisyn *ms, |
489 | uint_fast16_t voice, midipitch_t mp, int16_t level_cB) |
490 | { |
491 | oplsyn_setv(ms, voice, mp, level_cB, |
492 | OPLACT_ARTICULATE | OPLACT_PITCH | OPLACT_LEVEL); |
493 | } |
494 | |
495 | static void |
496 | oplsyn_repitchv(midisyn *ms, uint_fast16_t voice, midipitch_t mp) |
497 | { |
498 | oplsyn_setv(ms, voice, mp, 0, OPLACT_PITCH); |
499 | } |
500 | |
501 | static void |
502 | oplsyn_relevelv(midisyn *ms, uint_fast16_t voice, int16_t level_cB) |
503 | { |
504 | oplsyn_setv(ms, voice, 0, level_cB, OPLACT_LEVEL); |
505 | } |
506 | |
507 | static void |
508 | oplsyn_setv(midisyn *ms, |
509 | uint_fast16_t voice, midipitch_t mp, int16_t level_cB, int act) |
510 | { |
511 | struct opl_softc *sc = ms->data; |
512 | struct opl_voice *v; |
513 | const struct opl_operators *p; |
514 | u_int32_t block_fnum; |
515 | int mult; |
516 | int c_mult, m_mult; |
517 | u_int32_t chan; |
518 | u_int8_t chars0, chars1, ksl0, ksl1, fbc; |
519 | u_int8_t r20m, r20c, r40m, r40c, rA0, rB0; |
520 | u_int8_t vol0, vol1; |
521 | |
522 | KASSERT(mutex_owned(sc->lock)); |
523 | |
524 | DPRINTFN(3, ("%s: %p %d %u %d\n" , __func__, sc, voice, |
525 | mp, level_cB)); |
526 | |
527 | #ifdef DIAGNOSTIC |
528 | if (voice >= sc->syn.nvoice) { |
529 | printf("%s: bad voice %d\n" , __func__, voice); |
530 | return; |
531 | } |
532 | #endif |
533 | v = &sc->voices[voice]; |
534 | |
535 | if ( act & OPLACT_ARTICULATE ) { |
536 | /* Turn off old note */ |
537 | opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 0, 0xff); |
538 | opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 1, 0xff); |
539 | opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice, 0); |
540 | |
541 | chan = MS_GETCHAN(&ms->voices[voice]); |
542 | p = &opl2_instrs[ms->pgms[chan]]; |
543 | v->patch = p; |
544 | opl_load_patch(sc, voice); |
545 | |
546 | fbc = p->ops[OO_FB_CONN]; |
547 | if (sc->model == OPL_3) { |
548 | fbc &= ~OPL_STEREO_BITS; |
549 | fbc |= sc->pan[chan]; |
550 | } |
551 | opl_set_ch_reg(sc, OPL_FEEDBACK_CONNECTION, voice, fbc); |
552 | } else |
553 | p = v->patch; |
554 | |
555 | if ( act & OPLACT_LEVEL ) { |
556 | /* 2 voice */ |
557 | ksl0 = p->ops[OO_KSL_LEV+0]; |
558 | ksl1 = p->ops[OO_KSL_LEV+1]; |
559 | if (p->ops[OO_FB_CONN] & 0x01) { |
560 | vol0 = opl_calc_vol(ksl0, level_cB); |
561 | vol1 = opl_calc_vol(ksl1, level_cB); |
562 | } else { |
563 | vol0 = ksl0; |
564 | vol1 = opl_calc_vol(ksl1, level_cB); |
565 | } |
566 | r40m = (ksl0 & OPL_KSL_MASK) | vol0; |
567 | r40c = (ksl1 & OPL_KSL_MASK) | vol1; |
568 | |
569 | opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 0, r40m); |
570 | opl_set_op_reg(sc, OPL_KSL_LEVEL, voice, 1, r40c); |
571 | } |
572 | |
573 | if ( act & OPLACT_PITCH ) { |
574 | mult = 1; |
575 | if ( mp > MIDIPITCH_FROM_KEY(114) ) { /* out of mult 1 range */ |
576 | mult = 4; /* will cover remaining MIDI range */ |
577 | mp -= 2*MIDIPITCH_OCTAVE; |
578 | } |
579 | |
580 | block_fnum = opl_get_block_fnum(mp); |
581 | |
582 | chars0 = p->ops[OO_CHARS+0]; |
583 | chars1 = p->ops[OO_CHARS+1]; |
584 | m_mult = (chars0 & OPL_MULTIPLE_MASK) * mult; |
585 | c_mult = (chars1 & OPL_MULTIPLE_MASK) * mult; |
586 | |
587 | if ( 4 == mult ) { |
588 | if ( 0 == m_mult ) /* The OPL uses 0 to represent .5 */ |
589 | m_mult = 2; /* but of course 0*mult above did */ |
590 | if ( 0 == c_mult ) /* not DTRT */ |
591 | c_mult = 2; |
592 | } |
593 | |
594 | if ((m_mult > 15) || (c_mult > 15)) { |
595 | printf("%s: frequency out of range %u (mult %d)\n" , |
596 | __func__, mp, mult); |
597 | return; |
598 | } |
599 | r20m = (chars0 &~ OPL_MULTIPLE_MASK) | m_mult; |
600 | r20c = (chars1 &~ OPL_MULTIPLE_MASK) | c_mult; |
601 | |
602 | rA0 = block_fnum & 0xFF; |
603 | rB0 = (block_fnum >> 8) | OPL_KEYON_BIT; |
604 | |
605 | v->rB0 = rB0; |
606 | |
607 | opl_set_op_reg(sc, OPL_AM_VIB, voice, 0, r20m); |
608 | opl_set_op_reg(sc, OPL_AM_VIB, voice, 1, r20c); |
609 | |
610 | opl_set_ch_reg(sc, OPL_FNUM_LOW, voice, rA0); |
611 | opl_set_ch_reg(sc, OPL_KEYON_BLOCK, voice, rB0); |
612 | } |
613 | } |
614 | |
615 | void |
616 | oplsyn_releasev(midisyn *ms, uint_fast16_t voice, uint_fast8_t vel) |
617 | { |
618 | struct opl_softc *sc = ms->data; |
619 | struct opl_voice *v; |
620 | |
621 | KASSERT(mutex_owned(sc->lock)); |
622 | |
623 | DPRINTFN(1, ("%s: %p %d\n" , __func__, sc, voice)); |
624 | |
625 | #ifdef DIAGNOSTIC |
626 | if (voice >= sc->syn.nvoice) { |
627 | printf("oplsyn_noteoff: bad voice %d\n" , voice); |
628 | return; |
629 | } |
630 | #endif |
631 | v = &sc->voices[voice]; |
632 | opl_set_ch_reg(sc, 0xB0, voice, v->rB0 & ~OPL_KEYON_BIT); |
633 | } |
634 | |
635 | int |
636 | oplsyn_ctlnotice(midisyn *ms, |
637 | midictl_evt evt, uint_fast8_t chan, uint_fast16_t key) |
638 | { |
639 | |
640 | DPRINTFN(1, ("%s: %p %d\n" , __func__, ms->data, chan)); |
641 | |
642 | switch (evt) { |
643 | case MIDICTL_RESET: |
644 | oplsyn_panhandler(ms, chan); |
645 | return 1; |
646 | |
647 | case MIDICTL_CTLR: |
648 | switch (key) { |
649 | case MIDI_CTRL_PAN_MSB: |
650 | oplsyn_panhandler(ms, chan); |
651 | return 1; |
652 | } |
653 | return 0; |
654 | default: |
655 | return 0; |
656 | } |
657 | } |
658 | |
659 | /* PROGRAM CHANGE midi event: */ |
660 | void |
661 | oplsyn_programchange(midisyn *ms, uint_fast8_t chan, uint_fast8_t prog) |
662 | { |
663 | /* sanity checks */ |
664 | if (chan >= MIDI_MAX_CHANS) |
665 | return; |
666 | |
667 | ms->pgms[chan] = prog; |
668 | } |
669 | |
670 | void |
671 | oplsyn_loadpatch(midisyn *ms, struct sysex_info *sysex, struct uio *uio) |
672 | { |
673 | #if 0 |
674 | struct opl_softc *sc = ms->data; |
675 | struct sbi_instrument ins; |
676 | |
677 | DPRINTFN(1, ("oplsyn_loadpatch: %p\n" , sc)); |
678 | |
679 | memcpy(&ins, sysex, sizeof *sysex); |
680 | if (uio->uio_resid >= sizeof ins - sizeof *sysex) |
681 | return EINVAL; |
682 | uiomove((char *)&ins + sizeof *sysex, sizeof ins - sizeof *sysex, uio); |
683 | /* XXX */ |
684 | #endif |
685 | } |
686 | |
687 | static void |
688 | oplsyn_panhandler(midisyn *ms, uint_fast8_t chan) |
689 | { |
690 | struct opl_softc *sc = ms->data; |
691 | uint_fast16_t setting; |
692 | |
693 | setting = midictl_read(&ms->ctl, chan, MIDI_CTRL_PAN_MSB, 8192); |
694 | setting >>= 7; /* we used to treat it as MSB only */ |
695 | sc->pan[chan] = |
696 | (setting <= OPL_MIDI_CENTER_MAX ? sc->panl : 0) | |
697 | (setting >= OPL_MIDI_CENTER_MIN ? sc->panr : 0); |
698 | } |
699 | |