1 /* $NetBSD: opl.c,v 1.36 2009/03/14 21:04:20 dsl Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (augustss@NetBSD.org).
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 * The OPL3 (YMF262) manual can be found at
34 * ftp://ftp.yamahayst.com/Fax_Back_Doc/sound/YMF262.PDF
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: opl.c,v 1.36 2009/03/14 21:04:20 dsl Exp $");
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/malloc.h>
52 #include <sys/audioio.h>
53 #include <sys/midiio.h>
54 #include <dev/audio_if.h>
56 #include <dev/midi_if.h>
57 #include <dev/midivar.h>
58 #include <dev/midisynvar.h>
60 #include <dev/ic/oplreg.h>
61 #include <dev/ic/oplvar.h>
64 #define DPRINTF(x) if (opldebug) printf x
65 #define DPRINTFN(n,x) if (opldebug >= (n)) printf x
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 */
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, },
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, },
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, },
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, },
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, }
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
);
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);
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
);
129 struct midisyn_methods opl3_midi
= {
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
,
141 opl_attach(struct opl_softc
*sc
)
146 printf("\nopl: find failed\n");
150 sc
->syn
.mets
= &opl3_midi
;
151 snprintf(sc
->syn
.name
, sizeof(sc
->syn
.name
), "%sYamaha OPL%d",
152 sc
->syn
.name
, sc
->model
);
154 sc
->syn
.nvoice
= sc
->model
== OPL_2
? OPL2_NVOICE
: OPL3_NVOICE
;
155 midisyn_attach(&sc
->mididev
, &sc
->syn
);
157 /* Set up voice table */
158 for (i
= 0; i
< OPL3_NVOICE
; i
++)
159 sc
->voices
[i
] = voicetab
[i
];
163 aprint_normal(": model OPL%d", sc
->model
);
166 sc
->panl
= OPL_VOICE_TO_LEFT
;
167 sc
->panr
= OPL_VOICE_TO_RIGHT
;
168 if (sc
->model
== OPL_3
&&
169 device_cfdata(sc
->mididev
.dev
)->cf_flags
& OPL_FLAGS_SWAP_LR
) {
170 sc
->panl
= OPL_VOICE_TO_RIGHT
;
171 sc
->panr
= OPL_VOICE_TO_LEFT
;
172 aprint_normal(": LR swapped");
179 midi_attach_mi(&midisyn_hw_if
, &sc
->syn
, sc
->mididev
.dev
);
183 opl_detach(struct opl_softc
*sc
, int flags
)
187 if (sc
->sc_mididev
!= NULL
)
188 rv
= config_detach(sc
->sc_mididev
, flags
);
194 opl_command(struct opl_softc
*sc
, int offs
, int addr
, int data
)
196 DPRINTFN(4, ("opl_command: sc=%p, offs=%d addr=0x%02x data=0x%02x\n",
197 sc
, offs
, addr
, data
));
199 bus_space_write_1(sc
->iot
, sc
->ioh
, OPL_ADDR
+offs
, addr
);
200 if (sc
->model
== OPL_2
)
204 bus_space_write_1(sc
->iot
, sc
->ioh
, OPL_DATA
+offs
, data
);
205 if (sc
->model
== OPL_2
)
212 opl_match(bus_space_tag_t iot
, bus_space_handle_t ioh
, int offs
)
214 struct opl_softc
*sc
;
217 sc
= malloc(sizeof(*sc
), M_TEMP
, M_WAITOK
|M_ZERO
);
227 opl_find(struct opl_softc
*sc
)
229 u_int8_t status1
, status2
;
231 DPRINTFN(2,("opl_find: ioh=0x%x\n", (int)sc
->ioh
));
232 sc
->model
= OPL_2
; /* worst case assumption */
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
);
240 /* get status bits */
241 status1
= bus_space_read_1(sc
->iot
,sc
->ioh
,OPL_STATUS
+OPL_L
+sc
->offs
);
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 */
248 /* get status bits again */
249 status2
= bus_space_read_1(sc
->iot
,sc
->ioh
,OPL_STATUS
+OPL_L
+sc
->offs
);
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
);
255 DPRINTFN(2,("opl_find: %02x %02x\n", status1
, status2
));
257 if ((status1
& OPL_STATUS_MASK
) != 0 ||
258 (status2
& OPL_STATUS_MASK
) != (OPL_STATUS_IRQ
| OPL_STATUS_FT1
))
273 DPRINTFN(2,("opl_find: OPL%d at 0x%x detected\n",
274 sc
->model
, (int)sc
->ioh
));
279 * idea: opl_command does a lot of busywaiting, and the driver typically sets
280 * a lot of registers each time a voice-attack happens. some kind of
281 * caching to remember what was last written to each register could save
282 * a lot of cpu. It would have to be smart enough not to interfere with
283 * any necessary sequences of register access expected by the hardware...
286 opl_set_op_reg(struct opl_softc
*sc
, int base
, int voice
, int op
, u_char value
)
288 struct opl_voice
*v
= &sc
->voices
[voice
];
289 opl_command(sc
, v
->iooffs
, base
+ v
->op
[op
], value
);
293 opl_set_ch_reg(struct opl_softc
*sc
, int base
, int voice
, u_char value
)
295 struct opl_voice
*v
= &sc
->voices
[voice
];
296 opl_command(sc
, v
->iooffs
, base
+ v
->voiceno
, value
);
301 opl_load_patch(struct opl_softc
*sc
, int v
)
303 const struct opl_operators
*p
= sc
->voices
[v
].patch
;
305 opl_set_op_reg(sc
, OPL_AM_VIB
, v
, 0, p
->ops
[OO_CHARS
+0]);
306 opl_set_op_reg(sc
, OPL_AM_VIB
, v
, 1, p
->ops
[OO_CHARS
+1]);
307 opl_set_op_reg(sc
, OPL_KSL_LEVEL
, v
, 0, p
->ops
[OO_KSL_LEV
+0]);
308 opl_set_op_reg(sc
, OPL_KSL_LEVEL
, v
, 1, p
->ops
[OO_KSL_LEV
+1]);
309 opl_set_op_reg(sc
, OPL_ATTACK_DECAY
, v
, 0, p
->ops
[OO_ATT_DEC
+0]);
310 opl_set_op_reg(sc
, OPL_ATTACK_DECAY
, v
, 1, p
->ops
[OO_ATT_DEC
+1]);
311 opl_set_op_reg(sc
, OPL_SUSTAIN_RELEASE
, v
, 0, p
->ops
[OO_SUS_REL
+0]);
312 opl_set_op_reg(sc
, OPL_SUSTAIN_RELEASE
, v
, 1, p
->ops
[OO_SUS_REL
+1]);
313 opl_set_op_reg(sc
, OPL_WAVE_SELECT
, v
, 0, p
->ops
[OO_WAV_SEL
+0]);
314 opl_set_op_reg(sc
, OPL_WAVE_SELECT
, v
, 1, p
->ops
[OO_WAV_SEL
+1]);
315 opl_set_ch_reg(sc
, OPL_FEEDBACK_CONNECTION
, v
, p
->ops
[OO_FB_CONN
]);
319 opl_get_block_fnum(midipitch_t mp
)
326 * We can get to about note 30 before needing to switch from block 0.
327 * Thereafter, switch block every octave; that will keep f_num in the
328 * upper end of its range, making the most bits available for
331 block
= ( mp
- MIDIPITCH_FROM_KEY(19) ) / MIDIPITCH_OCTAVE
;
332 if ( block
> 7 ) /* subtract wrapped */
335 * Could subtract block*MIDIPITCH_OCTAVE here, or >>block later. Later.
338 hz18
= MIDIPITCH_TO_HZ18(mp
);
342 * The formula in the manual is f_num = ((hz<<19)/fs)>>(block-1) (though
343 * block==0 implies >>-1 which is a C unspecified result). As we already
344 * have hz<<18 and I omitted the -1 when shifting above, what's left to
345 * do now is multiply by 4 and divide by fs, the sampling frequency of
346 * the chip. fs is the master clock frequency fM / 288, fM is 14.32 MHz
347 * so fs is a goofy number around 49.7kHz. The 5th convergent of the
348 * continued fraction matches 4/fs to 9+ significant figures. Doing the
349 * shift first (above) ensures there's room in hz18 to multiply by 9.
352 f_num
= (9 * hz18
) / 111875;
353 return ((block
<< 10) | f_num
);
358 opl_reset(struct opl_softc
*sc
)
362 for (i
= 1; i
<= OPL_MAXREG
; i
++)
363 opl_command(sc
, OPL_L
, OPL_KEYON_BLOCK
+ i
, 0);
365 opl_command(sc
, OPL_L
, OPL_TEST
, OPL_ENABLE_WAVE_SELECT
);
366 opl_command(sc
, OPL_L
, OPL_PERCUSSION
, 0);
367 if (sc
->model
== OPL_3
) {
368 opl_command(sc
, OPL_R
, OPL_MODE
, OPL3_ENABLE
);
369 opl_command(sc
, OPL_R
,OPL_CONNECTION_SELECT
,OPL_NOCONNECTION
);
372 for (i
= 0; i
< MIDI_MAX_CHANS
; i
++)
373 sc
->pan
[i
] = OPL_VOICE_TO_LEFT
| OPL_VOICE_TO_RIGHT
;
377 oplsyn_open(midisyn
*ms
, int flags
)
379 struct opl_softc
*sc
= ms
->data
;
381 DPRINTFN(2, ("oplsyn_open: %d\n", flags
));
383 #ifndef AUDIO_NO_POWER_CTL
385 sc
->powerctl(sc
->powerarg
, 1);
389 sc
->spkrctl(sc
->spkrarg
, 1);
394 oplsyn_close(midisyn
*ms
)
396 struct opl_softc
*sc
= ms
->data
;
398 DPRINTFN(2, ("oplsyn_close:\n"));
400 /*opl_reset(ms->data);*/
402 sc
->spkrctl(sc
->spkrarg
, 0);
403 #ifndef AUDIO_NO_POWER_CTL
405 sc
->powerctl(sc
->powerarg
, 0);
411 oplsyn_getinfo(void *addr
, struct synth_dev
*sd
)
413 struct opl_softc
*sc
= addr
;
415 sd
->name
= sc
->model
== OPL_2
? "Yamaha OPL2" : "Yamaha OPL3";
416 sd
->type
= SYNTH_TYPE_FM
;
417 sd
->subtype
= sc
->model
== OPL_2
? SYNTH_SUB_FM_TYPE_ADLIB
418 : SYNTH_SUB_FM_TYPE_OPL3
;
419 sd
->capabilities
= 0;
424 oplsyn_reset(void *addr
)
426 struct opl_softc
*sc
= addr
;
427 DPRINTFN(3, ("oplsyn_reset:\n"));
432 opl_calc_vol(int regbyte
, int16_t level_cB
)
434 int level
= regbyte
& OPL_TOTAL_LEVEL_MASK
;
437 * level is a six-bit attenuation, from 0 (full output)
438 * to -48dB (but without the minus sign) in steps of .75 dB.
439 * We'll just add level_cB, after scaling it because it's
440 * in centibels instead and has the customary minus sign.
443 level
+= ( -4 * level_cB
) / 30;
445 if (level
> OPL_TOTAL_LEVEL_MASK
)
446 level
= OPL_TOTAL_LEVEL_MASK
;
450 return level
& OPL_TOTAL_LEVEL_MASK
;
453 #define OPLACT_ARTICULATE 1
454 #define OPLACT_PITCH 2
455 #define OPLACT_LEVEL 4
458 oplsyn_attackv(midisyn
*ms
,
459 uint_fast16_t voice
, midipitch_t mp
, int16_t level_cB
)
461 oplsyn_setv(ms
, voice
, mp
, level_cB
,
462 OPLACT_ARTICULATE
| OPLACT_PITCH
| OPLACT_LEVEL
);
466 oplsyn_repitchv(midisyn
*ms
, uint_fast16_t voice
, midipitch_t mp
)
468 oplsyn_setv(ms
, voice
, mp
, 0, OPLACT_PITCH
);
472 oplsyn_relevelv(midisyn
*ms
, uint_fast16_t voice
, int16_t level_cB
)
474 oplsyn_setv(ms
, voice
, 0, level_cB
, OPLACT_LEVEL
);
478 oplsyn_setv(midisyn
*ms
,
479 uint_fast16_t voice
, midipitch_t mp
, int16_t level_cB
, int act
)
481 struct opl_softc
*sc
= ms
->data
;
483 const struct opl_operators
*p
;
484 u_int32_t block_fnum
;
488 u_int8_t chars0
, chars1
, ksl0
, ksl1
, fbc
;
489 u_int8_t r20m
, r20c
, r40m
, r40c
, rA0
, rB0
;
492 DPRINTFN(3, ("%s: %p %d %u %d\n", __func__
, sc
, voice
,
496 if (voice
>= sc
->syn
.nvoice
) {
497 printf("%s: bad voice %d\n", __func__
, voice
);
501 v
= &sc
->voices
[voice
];
503 if ( act
& OPLACT_ARTICULATE
) {
504 /* Turn off old note */
505 opl_set_op_reg(sc
, OPL_KSL_LEVEL
, voice
, 0, 0xff);
506 opl_set_op_reg(sc
, OPL_KSL_LEVEL
, voice
, 1, 0xff);
507 opl_set_ch_reg(sc
, OPL_KEYON_BLOCK
, voice
, 0);
509 chan
= MS_GETCHAN(&ms
->voices
[voice
]);
510 p
= &opl2_instrs
[ms
->pgms
[chan
]];
512 opl_load_patch(sc
, voice
);
514 fbc
= p
->ops
[OO_FB_CONN
];
515 if (sc
->model
== OPL_3
) {
516 fbc
&= ~OPL_STEREO_BITS
;
517 fbc
|= sc
->pan
[chan
];
519 opl_set_ch_reg(sc
, OPL_FEEDBACK_CONNECTION
, voice
, fbc
);
523 if ( act
& OPLACT_LEVEL
) {
525 ksl0
= p
->ops
[OO_KSL_LEV
+0];
526 ksl1
= p
->ops
[OO_KSL_LEV
+1];
527 if (p
->ops
[OO_FB_CONN
] & 0x01) {
528 vol0
= opl_calc_vol(ksl0
, level_cB
);
529 vol1
= opl_calc_vol(ksl1
, level_cB
);
532 vol1
= opl_calc_vol(ksl1
, level_cB
);
534 r40m
= (ksl0
& OPL_KSL_MASK
) | vol0
;
535 r40c
= (ksl1
& OPL_KSL_MASK
) | vol1
;
537 opl_set_op_reg(sc
, OPL_KSL_LEVEL
, voice
, 0, r40m
);
538 opl_set_op_reg(sc
, OPL_KSL_LEVEL
, voice
, 1, r40c
);
541 if ( act
& OPLACT_PITCH
) {
543 if ( mp
> MIDIPITCH_FROM_KEY(114) ) { /* out of mult 1 range */
544 mult
= 4; /* will cover remaining MIDI range */
545 mp
-= 2*MIDIPITCH_OCTAVE
;
548 block_fnum
= opl_get_block_fnum(mp
);
550 chars0
= p
->ops
[OO_CHARS
+0];
551 chars1
= p
->ops
[OO_CHARS
+1];
552 m_mult
= (chars0
& OPL_MULTIPLE_MASK
) * mult
;
553 c_mult
= (chars1
& OPL_MULTIPLE_MASK
) * mult
;
556 if ( 0 == m_mult
) /* The OPL uses 0 to represent .5 */
557 m_mult
= 2; /* but of course 0*mult above did */
558 if ( 0 == c_mult
) /* not DTRT */
562 if ((m_mult
> 15) || (c_mult
> 15)) {
563 printf("%s: frequency out of range %u (mult %d)\n",
567 r20m
= (chars0
&~ OPL_MULTIPLE_MASK
) | m_mult
;
568 r20c
= (chars1
&~ OPL_MULTIPLE_MASK
) | c_mult
;
570 rA0
= block_fnum
& 0xFF;
571 rB0
= (block_fnum
>> 8) | OPL_KEYON_BIT
;
575 opl_set_op_reg(sc
, OPL_AM_VIB
, voice
, 0, r20m
);
576 opl_set_op_reg(sc
, OPL_AM_VIB
, voice
, 1, r20c
);
578 opl_set_ch_reg(sc
, OPL_FNUM_LOW
, voice
, rA0
);
579 opl_set_ch_reg(sc
, OPL_KEYON_BLOCK
, voice
, rB0
);
584 oplsyn_releasev(midisyn
*ms
, uint_fast16_t voice
, uint_fast8_t vel
)
586 struct opl_softc
*sc
= ms
->data
;
589 DPRINTFN(1, ("%s: %p %d\n", __func__
, sc
, voice
));
592 if (voice
>= sc
->syn
.nvoice
) {
593 printf("oplsyn_noteoff: bad voice %d\n", voice
);
597 v
= &sc
->voices
[voice
];
598 opl_set_ch_reg(sc
, 0xB0, voice
, v
->rB0
& ~OPL_KEYON_BIT
);
602 oplsyn_ctlnotice(midisyn
*ms
,
603 midictl_evt evt
, uint_fast8_t chan
, uint_fast16_t key
)
606 DPRINTFN(1, ("%s: %p %d\n", __func__
, ms
->data
, chan
));
610 oplsyn_panhandler(ms
, chan
);
615 case MIDI_CTRL_PAN_MSB
:
616 oplsyn_panhandler(ms
, chan
);
625 /* PROGRAM CHANGE midi event: */
627 oplsyn_programchange(midisyn
*ms
, uint_fast8_t chan
, uint_fast8_t prog
)
630 if (chan
>= MIDI_MAX_CHANS
)
633 ms
->pgms
[chan
] = prog
;
637 oplsyn_loadpatch(midisyn
*ms
, struct sysex_info
*sysex
,
641 struct opl_softc
*sc
= ms
->data
;
642 struct sbi_instrument ins
;
644 DPRINTFN(1, ("oplsyn_loadpatch: %p\n", sc
));
646 memcpy(&ins
, sysex
, sizeof *sysex
);
647 if (uio
->uio_resid
>= sizeof ins
- sizeof *sysex
)
649 uiomove((char *)&ins
+ sizeof *sysex
, sizeof ins
- sizeof *sysex
, uio
);
655 oplsyn_panhandler(midisyn
*ms
, uint_fast8_t chan
)
657 struct opl_softc
*sc
= ms
->data
;
658 uint_fast16_t setting
;
660 setting
= midictl_read(&ms
->ctl
, chan
, MIDI_CTRL_PAN_MSB
, 8192);
661 setting
>>= 7; /* we used to treat it as MSB only */
663 (setting
<= OPL_MIDI_CENTER_MAX
? sc
->panl
: 0) |
664 (setting
>= OPL_MIDI_CENTER_MIN
? sc
->panr
: 0);