midictl.c revision 1.1.2.1 1 1.1.2.1 chap /* $NetBSD: midictl.c,v 1.1.2.1 2006/06/06 21:33:16 chap Exp $ */
2 1.1.2.1 chap
3 1.1.2.1 chap /*-
4 1.1.2.1 chap * Copyright (c) 2006 The NetBSD Foundation, Inc.
5 1.1.2.1 chap * All rights reserved.
6 1.1.2.1 chap *
7 1.1.2.1 chap * This code is derived from software contributed to The NetBSD Foundation
8 1.1.2.1 chap * by Chapman Flack.
9 1.1.2.1 chap *
10 1.1.2.1 chap * Redistribution and use in source and binary forms, with or without
11 1.1.2.1 chap * modification, are permitted provided that the following conditions
12 1.1.2.1 chap * are met:
13 1.1.2.1 chap * 1. Redistributions of source code must retain the above copyright
14 1.1.2.1 chap * notice, this list of conditions and the following disclaimer.
15 1.1.2.1 chap * 2. Redistributions in binary form must reproduce the above copyright
16 1.1.2.1 chap * notice, this list of conditions and the following disclaimer in the
17 1.1.2.1 chap * documentation and/or other materials provided with the distribution.
18 1.1.2.1 chap * 3. All advertising materials mentioning features or use of this software
19 1.1.2.1 chap * must display the following acknowledgement:
20 1.1.2.1 chap * This product includes software developed by the NetBSD
21 1.1.2.1 chap * Foundation, Inc. and its contributors.
22 1.1.2.1 chap * 4. Neither the name of The NetBSD Foundation nor the names of its
23 1.1.2.1 chap * contributors may be used to endorse or promote products derived
24 1.1.2.1 chap * from this software without specific prior written permission.
25 1.1.2.1 chap *
26 1.1.2.1 chap * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 1.1.2.1 chap * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 1.1.2.1 chap * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 1.1.2.1 chap * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 1.1.2.1 chap * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 1.1.2.1 chap * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 1.1.2.1 chap * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 1.1.2.1 chap * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 1.1.2.1 chap * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 1.1.2.1 chap * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 1.1.2.1 chap * POSSIBILITY OF SUCH DAMAGE.
37 1.1.2.1 chap */
38 1.1.2.1 chap #include <sys/cdefs.h>
39 1.1.2.1 chap __KERNEL_RCSID(0, "$NetBSD: midictl.c,v 1.1.2.1 2006/06/06 21:33:16 chap Exp $");
40 1.1.2.1 chap
41 1.1.2.1 chap /*
42 1.1.2.1 chap * See midictl.h for an overview of the purpose and use of this module.
43 1.1.2.1 chap */
44 1.1.2.1 chap
45 1.1.2.1 chap #if defined(_KERNEL)
46 1.1.2.1 chap #define _MIDICTL_ASSERT(x) KASSERT(x)
47 1.1.2.1 chap #define _MIDICTL_MALLOC(s,t) malloc((s), (t), M_WAITOK)
48 1.1.2.1 chap #define _MIDICTL_FREE(s,t) free((s), (t))
49 1.1.2.1 chap #include <sys/systm.h>
50 1.1.2.1 chap #include <sys/types.h>
51 1.1.2.1 chap #else
52 1.1.2.1 chap #include <assert.h>
53 1.1.2.1 chap #include <stdio.h>
54 1.1.2.1 chap #include <stdlib.h>
55 1.1.2.1 chap #define _MIDICTL_ASSERT(x) assert(x)
56 1.1.2.1 chap #define _MIDICTL_MALLOC(s,t) malloc((s))
57 1.1.2.1 chap #define _MIDICTL_FREE(s,t) free((s))
58 1.1.2.1 chap #endif
59 1.1.2.1 chap
60 1.1.2.1 chap #include "midictl.h"
61 1.1.2.1 chap
62 1.1.2.1 chap /*
63 1.1.2.1 chap * The upper part of this file is MIDI-aware, and deals with things like
64 1.1.2.1 chap * decoding MIDI Control Change messages, dealing with the ones that require
65 1.1.2.1 chap * special handling as mode messages or parameter updates, and so on.
66 1.1.2.1 chap *
67 1.1.2.1 chap * It relies on a "store" layer (implemented in the lower part of this file)
68 1.1.2.1 chap * that only must be able to stash away 2-, 8-, or 16-bit quantities (which
69 1.1.2.1 chap * it may pack into larger units as it sees fit) and find them again given
70 1.1.2.1 chap * a class, channel, and key (controller/parameter number).
71 1.1.2.1 chap *
72 1.1.2.1 chap * The MIDI controllers can have 1-, 7-, or 14-bit values; the parameters are
73 1.1.2.1 chap * also 14-bit. The 14-bit values have to be set in two MIDI messages, 7 bits
74 1.1.2.1 chap * at a time. The MIDI layer uses store-managed 2- or 8-bit slots for the
75 1.1.2.1 chap * smaller types, and uses the free high bit to indicate that it has explicitly
76 1.1.2.1 chap * set the value. (Because the store is allowed to pack things, it may 'find'
77 1.1.2.1 chap * a zero entry for a value we never set, because it shares a word with a
78 1.1.2.1 chap * different value that has been set. We know it is not a real value because
79 1.1.2.1 chap * the high bit is clear.)
80 1.1.2.1 chap *
81 1.1.2.1 chap * The 14-bit values are handled similarly: 16-bit store slots are used to hold
82 1.1.2.1 chap * them, with the two free high bits indicating independently whether the MSB
83 1.1.2.1 chap * and the LSB have been explicitly set--as two separate MIDI messages are
84 1.1.2.1 chap * required. If such a control is queried when only one half has been explicitly
85 1.1.2.1 chap * set, the result is as if it had been set to the specified default value
86 1.1.2.1 chap * before the explicit set.
87 1.1.2.1 chap */
88 1.1.2.1 chap typedef struct bucket bucket; /* the store layer completes this type */
89 1.1.2.1 chap
90 1.1.2.1 chap typedef enum { CTL1, CTL7, CTL14, RPN, NRPN } class;
91 1.1.2.1 chap
92 1.1.2.1 chap /*
93 1.1.2.1 chap * assert(does_not_apply(KNFNamespaceArgumentAgainstNamesInPrototypes,
94 1.1.2.1 chap * PrototypesOfStaticFunctionsWithinNonIncludedFile));
95 1.1.2.1 chap */
96 1.1.2.1 chap static void reset_all_controllers(midictl *mc, uint_fast8_t chan);
97 1.1.2.1 chap static void enter14(midictl *mc, uint_fast8_t chan, class c,
98 1.1.2.1 chap uint_fast16_t key, _Bool islsb, uint8_t val);
99 1.1.2.1 chap static uint_fast16_t read14(midictl *mc, uint_fast8_t chan, class c,
100 1.1.2.1 chap uint_fast16_t key, uint_fast16_t dflt);
101 1.1.2.1 chap static class classify(uint_fast16_t *key, _Bool *islsb);
102 1.1.2.1 chap static midictl_notify notify_no_one;
103 1.1.2.1 chap
104 1.1.2.1 chap static midictl_store *store_init(void);
105 1.1.2.1 chap static void store_done(midictl_store *s);
106 1.1.2.1 chap static bucket *store_locate(midictl_store *s, class c,
107 1.1.2.1 chap uint_fast8_t chan, uint_fast16_t key);
108 1.1.2.1 chap static uint16_t store_extract(bucket *b, class c,
109 1.1.2.1 chap uint_fast8_t chan, uint_fast16_t key);
110 1.1.2.1 chap static void store_update(midictl_store *s, bucket *b, class c,
111 1.1.2.1 chap uint_fast8_t chan, uint_fast16_t key, uint16_t value);
112 1.1.2.1 chap
113 1.1.2.1 chap #define PN_SET 0x8000 /* a parameter number has been explicitly set */
114 1.1.2.1 chap #define C14MSET 0x8000 /* MSB of a 14-bit val has been set */
115 1.1.2.1 chap #define C14LSET 0x4000 /* LSB of a 14-bit val has been set */
116 1.1.2.1 chap #define C7_SET 0x80 /* a 7-bit ctl has been set */
117 1.1.2.1 chap #define C1_SET 2 /* a 1-bit ctl has been set */
118 1.1.2.1 chap
119 1.1.2.1 chap #if defined(_MIDICTL_MAIN)
120 1.1.2.1 chap #define XS(s) [MIDICTL_##s]=#s
121 1.1.2.1 chap char const * const evt_strings[] = {
122 1.1.2.1 chap XS(CTLR), XS(RPN), XS(NRPN), XS(RESET), XS(NOTES_OFF),
123 1.1.2.1 chap XS(SOUND_OFF), XS(LOCAL), XS(MODE)
124 1.1.2.1 chap };
125 1.1.2.1 chap #undef XS
126 1.1.2.1 chap
127 1.1.2.1 chap void
128 1.1.2.1 chap dbgnotify(void *cookie, midictl_evt e, uint_fast8_t chan, uint_fast16_t key)
129 1.1.2.1 chap {
130 1.1.2.1 chap printf("NFY %p %s chan %u #%u\n", cookie, evt_strings[e], chan, key);
131 1.1.2.1 chap }
132 1.1.2.1 chap
133 1.1.2.1 chap midictl mc = {
134 1.1.2.1 chap .accept_any_ctl_rpn = 0,
135 1.1.2.1 chap .accept_any_nrpn = 0,
136 1.1.2.1 chap .base_channel = 16,
137 1.1.2.1 chap .cookie = NULL,
138 1.1.2.1 chap .notify = dbgnotify
139 1.1.2.1 chap };
140 1.1.2.1 chap
141 1.1.2.1 chap int
142 1.1.2.1 chap main(int argc, char **argv)
143 1.1.2.1 chap {
144 1.1.2.1 chap int cnt, a, b, c;
145 1.1.2.1 chap
146 1.1.2.1 chap midictl_open(&mc);
147 1.1.2.1 chap do {
148 1.1.2.1 chap cnt = scanf("%i %i %i", &a, &b, &c);
149 1.1.2.1 chap if ( 3 == cnt ) {
150 1.1.2.1 chap midictl_change(&mc, a, (uint8_t[]){b,c});
151 1.1.2.1 chap }
152 1.1.2.1 chap } while ( EOF != cnt );
153 1.1.2.1 chap midictl_close(&mc);
154 1.1.2.1 chap return 0;
155 1.1.2.1 chap }
156 1.1.2.1 chap #endif /* defined(_MIDICTL_MAIN) */
157 1.1.2.1 chap
158 1.1.2.1 chap void
159 1.1.2.1 chap midictl_open(midictl *mc)
160 1.1.2.1 chap {
161 1.1.2.1 chap if ( NULL == mc->notify )
162 1.1.2.1 chap mc->notify = notify_no_one;
163 1.1.2.1 chap mc->store = store_init();
164 1.1.2.1 chap }
165 1.1.2.1 chap
166 1.1.2.1 chap void
167 1.1.2.1 chap midictl_close(midictl *mc)
168 1.1.2.1 chap {
169 1.1.2.1 chap store_done(mc->store);
170 1.1.2.1 chap }
171 1.1.2.1 chap
172 1.1.2.1 chap void
173 1.1.2.1 chap midictl_change(midictl *mc, uint_fast8_t chan, uint8_t *ctlval)
174 1.1.2.1 chap {
175 1.1.2.1 chap class c;
176 1.1.2.1 chap uint_fast16_t key, val;
177 1.1.2.1 chap _Bool islsb;
178 1.1.2.1 chap bucket *bkt;
179 1.1.2.1 chap
180 1.1.2.1 chap switch ( ctlval[0] ) {
181 1.1.2.1 chap /*
182 1.1.2.1 chap * Channel mode messages:
183 1.1.2.1 chap */
184 1.1.2.1 chap case MIDI_CTRL_OMNI_OFF:
185 1.1.2.1 chap case MIDI_CTRL_OMNI_ON:
186 1.1.2.1 chap case MIDI_CTRL_POLY_OFF:
187 1.1.2.1 chap case MIDI_CTRL_POLY_ON:
188 1.1.2.1 chap if ( chan != mc->base_channel )
189 1.1.2.1 chap return; /* ignored - not on base channel */
190 1.1.2.1 chap else
191 1.1.2.1 chap return; /* XXX ignored anyway - not implemented yet */
192 1.1.2.1 chap case MIDI_CTRL_NOTES_OFF:
193 1.1.2.1 chap mc->notify(mc->cookie, MIDICTL_NOTES_OFF, chan, 0);
194 1.1.2.1 chap return;
195 1.1.2.1 chap case MIDI_CTRL_LOCAL:
196 1.1.2.1 chap mc->notify(mc->cookie, MIDICTL_LOCAL, chan, ctlval[1]);
197 1.1.2.1 chap return;
198 1.1.2.1 chap case MIDI_CTRL_SOUND_OFF:
199 1.1.2.1 chap mc->notify(mc->cookie, MIDICTL_SOUND_OFF, chan, 0);
200 1.1.2.1 chap return;
201 1.1.2.1 chap case MIDI_CTRL_RESET:
202 1.1.2.1 chap reset_all_controllers(mc, chan);
203 1.1.2.1 chap return;
204 1.1.2.1 chap /*
205 1.1.2.1 chap * Control changes to be handled specially:
206 1.1.2.1 chap */
207 1.1.2.1 chap case MIDI_CTRL_RPN_LSB:
208 1.1.2.1 chap mc-> rpn |= PN_SET | (0x7f & ctlval[1]);
209 1.1.2.1 chap mc->nrpn &= ~PN_SET;
210 1.1.2.1 chap return;
211 1.1.2.1 chap case MIDI_CTRL_RPN_MSB:
212 1.1.2.1 chap mc-> rpn |= PN_SET | (0x7f & ctlval[1])<<7;
213 1.1.2.1 chap mc->nrpn &= ~PN_SET;
214 1.1.2.1 chap return;
215 1.1.2.1 chap case MIDI_CTRL_NRPN_LSB:
216 1.1.2.1 chap mc->nrpn |= PN_SET | (0x7f & ctlval[1]);
217 1.1.2.1 chap mc-> rpn &= ~PN_SET;
218 1.1.2.1 chap return;
219 1.1.2.1 chap case MIDI_CTRL_NRPN_MSB:
220 1.1.2.1 chap mc->nrpn |= PN_SET | (0x7f & ctlval[1])<<7;
221 1.1.2.1 chap mc-> rpn &= ~PN_SET;
222 1.1.2.1 chap return;
223 1.1.2.1 chap case MIDI_CTRL_DATA_ENTRY_LSB:
224 1.1.2.1 chap islsb = 1;
225 1.1.2.1 chap goto whichparm;
226 1.1.2.1 chap case MIDI_CTRL_DATA_ENTRY_MSB:
227 1.1.2.1 chap islsb = 0;
228 1.1.2.1 chap whichparm:
229 1.1.2.1 chap if ( 0 == ( (mc->rpn ^ mc->nrpn) & PN_SET ) )
230 1.1.2.1 chap return; /* exactly one must be current */
231 1.1.2.1 chap if ( mc->rpn & PN_SET ) {
232 1.1.2.1 chap key = mc->rpn;
233 1.1.2.1 chap c = RPN;
234 1.1.2.1 chap } else {
235 1.1.2.1 chap key = mc->nrpn;
236 1.1.2.1 chap c = NRPN;
237 1.1.2.1 chap }
238 1.1.2.1 chap key &= 0x3fff;
239 1.1.2.1 chap if ( 0x3fff == key ) /* 'null' parm# to lock out changes */
240 1.1.2.1 chap return;
241 1.1.2.1 chap enter14(mc, chan, c, key, islsb, ctlval[1]);
242 1.1.2.1 chap return;
243 1.1.2.1 chap case MIDI_CTRL_RPN_INCREMENT: /* XXX for later - these are a PITA to */
244 1.1.2.1 chap case MIDI_CTRL_RPN_DECREMENT: /* get right - 'right' varies by param */
245 1.1.2.1 chap return;
246 1.1.2.1 chap }
247 1.1.2.1 chap
248 1.1.2.1 chap /*
249 1.1.2.1 chap * Channel mode, RPN, and NRPN operations have been ruled out.
250 1.1.2.1 chap * This is an ordinary control change.
251 1.1.2.1 chap */
252 1.1.2.1 chap
253 1.1.2.1 chap key = ctlval[0];
254 1.1.2.1 chap c = classify(&key, &islsb);
255 1.1.2.1 chap
256 1.1.2.1 chap switch ( c ) {
257 1.1.2.1 chap case CTL14:
258 1.1.2.1 chap enter14(mc, chan, c, key, islsb, ctlval[1]);
259 1.1.2.1 chap break;
260 1.1.2.1 chap case CTL7:
261 1.1.2.1 chap bkt = store_locate(mc->store, c, chan, key);
262 1.1.2.1 chap if ( !mc->accept_any_ctl_rpn ) {
263 1.1.2.1 chap if ( NULL == bkt )
264 1.1.2.1 chap break;
265 1.1.2.1 chap val = store_extract(bkt, c, chan, key);
266 1.1.2.1 chap if ( !(val&C7_SET) )
267 1.1.2.1 chap break;
268 1.1.2.1 chap }
269 1.1.2.1 chap store_update(mc->store, bkt, c, chan, key,
270 1.1.2.1 chap C7_SET | (0x7f & ctlval[1]));
271 1.1.2.1 chap mc->notify(mc->cookie, MIDICTL_CTLR, chan, key);
272 1.1.2.1 chap break;
273 1.1.2.1 chap case CTL1:
274 1.1.2.1 chap bkt = store_locate(mc->store, c, chan, key);
275 1.1.2.1 chap if ( !mc->accept_any_ctl_rpn ) {
276 1.1.2.1 chap if ( NULL == bkt )
277 1.1.2.1 chap break;
278 1.1.2.1 chap val = store_extract(bkt, c, chan, key);
279 1.1.2.1 chap if ( !(val&C1_SET) )
280 1.1.2.1 chap break;
281 1.1.2.1 chap }
282 1.1.2.1 chap store_update(mc->store, bkt, c, chan, key,
283 1.1.2.1 chap C1_SET | (ctlval[1]>63));
284 1.1.2.1 chap mc->notify(mc->cookie, MIDICTL_CTLR, chan, key);
285 1.1.2.1 chap break;
286 1.1.2.1 chap case RPN:
287 1.1.2.1 chap case NRPN:
288 1.1.2.1 chap break; /* won't see these - sop for gcc */
289 1.1.2.1 chap }
290 1.1.2.1 chap }
291 1.1.2.1 chap
292 1.1.2.1 chap uint_fast16_t
293 1.1.2.1 chap midictl_read(midictl *mc, uint_fast8_t chan, uint_fast8_t ctlr,
294 1.1.2.1 chap uint_fast16_t dflt)
295 1.1.2.1 chap {
296 1.1.2.1 chap bucket *bkt;
297 1.1.2.1 chap uint_fast16_t key, val;
298 1.1.2.1 chap class c;
299 1.1.2.1 chap _Bool islsb;
300 1.1.2.1 chap
301 1.1.2.1 chap key = ctlr;
302 1.1.2.1 chap c = classify(&key, &islsb);
303 1.1.2.1 chap switch ( c ) {
304 1.1.2.1 chap case CTL1:
305 1.1.2.1 chap bkt = store_locate(mc->store, c, chan, key);
306 1.1.2.1 chap if ( NULL == bkt ||
307 1.1.2.1 chap !(C1_SET&(val = store_extract(bkt, c, chan, key))) ) {
308 1.1.2.1 chap val = C1_SET | (dflt > 63);
309 1.1.2.1 chap store_update(mc->store, bkt, c, chan, key, val);
310 1.1.2.1 chap }
311 1.1.2.1 chap return (val & 1) ? 127 : 0;
312 1.1.2.1 chap case CTL7:
313 1.1.2.1 chap bkt = store_locate(mc->store, c, chan, key);
314 1.1.2.1 chap if ( NULL == bkt ||
315 1.1.2.1 chap !(C7_SET&(val = store_extract(bkt, c, chan, key))) ) {
316 1.1.2.1 chap val = C7_SET | (dflt & 0x7f);
317 1.1.2.1 chap store_update(mc->store, bkt, c, chan, key, val);
318 1.1.2.1 chap }
319 1.1.2.1 chap return val & 0x7f;
320 1.1.2.1 chap case CTL14:
321 1.1.2.1 chap _MIDICTL_ASSERT(!islsb);
322 1.1.2.1 chap return read14(mc, chan, c, key, dflt);
323 1.1.2.1 chap case RPN:
324 1.1.2.1 chap case NRPN:
325 1.1.2.1 chap break; /* sop for gcc */
326 1.1.2.1 chap }
327 1.1.2.1 chap return 0; /* sop for gcc */
328 1.1.2.1 chap }
329 1.1.2.1 chap
330 1.1.2.1 chap uint_fast16_t
331 1.1.2.1 chap midictl_rpn_read(midictl *mc, uint_fast8_t chan, uint_fast16_t ctlr,
332 1.1.2.1 chap uint_fast16_t dflt)
333 1.1.2.1 chap {
334 1.1.2.1 chap return read14(mc, chan, RPN, ctlr, dflt);
335 1.1.2.1 chap }
336 1.1.2.1 chap
337 1.1.2.1 chap uint_fast16_t
338 1.1.2.1 chap midictl_nrpn_read(midictl *mc, uint_fast8_t chan, uint_fast16_t ctlr,
339 1.1.2.1 chap uint_fast16_t dflt)
340 1.1.2.1 chap {
341 1.1.2.1 chap return read14(mc, chan, NRPN, ctlr, dflt);
342 1.1.2.1 chap }
343 1.1.2.1 chap
344 1.1.2.1 chap static void
345 1.1.2.1 chap reset_all_controllers(midictl *mc, uint_fast8_t chan)
346 1.1.2.1 chap {
347 1.1.2.1 chap uint_fast16_t ctlr, key;
348 1.1.2.1 chap class c;
349 1.1.2.1 chap _Bool islsb;
350 1.1.2.1 chap bucket *bkt;
351 1.1.2.1 chap
352 1.1.2.1 chap for ( ctlr = 0 ; ; ++ ctlr ) {
353 1.1.2.1 chap switch ( ctlr ) {
354 1.1.2.1 chap /*
355 1.1.2.1 chap * exempt by http://www.midi.org/about-midi/rp15.shtml:
356 1.1.2.1 chap */
357 1.1.2.1 chap case MIDI_CTRL_BANK_SELECT_MSB: /* 0 */
358 1.1.2.1 chap case MIDI_CTRL_CHANNEL_VOLUME_MSB: /* 7 */
359 1.1.2.1 chap case MIDI_CTRL_PAN_MSB: /* 10 */
360 1.1.2.1 chap continue;
361 1.1.2.1 chap case MIDI_CTRL_BANK_SELECT_LSB: /* 32 */
362 1.1.2.1 chap ctlr += 31; /* skip all these LSBs anyway */
363 1.1.2.1 chap continue;
364 1.1.2.1 chap case MIDI_CTRL_SOUND_VARIATION: /* 70 */
365 1.1.2.1 chap ctlr += 9; /* skip all Sound Controllers */
366 1.1.2.1 chap continue;
367 1.1.2.1 chap case MIDI_CTRL_EFFECT_DEPTH_1: /* 91 */
368 1.1.2.1 chap goto loop_exit; /* nothing more gets reset */
369 1.1.2.1 chap /*
370 1.1.2.1 chap * exempt for our own personal reasons:
371 1.1.2.1 chap */
372 1.1.2.1 chap case MIDI_CTRL_DATA_ENTRY_MSB: /* 6 */
373 1.1.2.1 chap continue; /* doesn't go to the store */
374 1.1.2.1 chap }
375 1.1.2.1 chap
376 1.1.2.1 chap key = ctlr;
377 1.1.2.1 chap c = classify(&key, &islsb);
378 1.1.2.1 chap
379 1.1.2.1 chap bkt = store_locate(mc->store, c, chan, key);
380 1.1.2.1 chap if ( NULL == bkt )
381 1.1.2.1 chap continue;
382 1.1.2.1 chap store_update(mc->store, bkt, c, chan, key, 0); /* no C*SET */
383 1.1.2.1 chap }
384 1.1.2.1 chap loop_exit:
385 1.1.2.1 chap mc->notify(mc->cookie, MIDICTL_RESET, chan, 0);
386 1.1.2.1 chap }
387 1.1.2.1 chap
388 1.1.2.1 chap static void
389 1.1.2.1 chap enter14(midictl *mc, uint_fast8_t chan, class c, uint_fast16_t key,
390 1.1.2.1 chap _Bool islsb, uint8_t val)
391 1.1.2.1 chap {
392 1.1.2.1 chap bucket *bkt;
393 1.1.2.1 chap uint16_t stval;
394 1.1.2.1 chap
395 1.1.2.1 chap bkt = store_locate(mc->store, c, chan, key);
396 1.1.2.1 chap stval = (NULL == bkt) ? 0 : store_extract(bkt, c, chan, key);
397 1.1.2.1 chap if ( !(stval&(C14MSET|C14LSET)) ) {
398 1.1.2.1 chap if ( !((NRPN==c)? mc->accept_any_nrpn: mc->accept_any_ctl_rpn) )
399 1.1.2.1 chap return;
400 1.1.2.1 chap }
401 1.1.2.1 chap if ( islsb )
402 1.1.2.1 chap stval = C14LSET | val | ( stval & ~0x7f );
403 1.1.2.1 chap else
404 1.1.2.1 chap stval = C14MSET | ( val << 7 ) | ( stval & ~0x3f80 );
405 1.1.2.1 chap store_update(mc->store, bkt, c, chan, key, stval);
406 1.1.2.1 chap mc->notify(mc->cookie, CTL14 == c ? MIDICTL_CTLR
407 1.1.2.1 chap : RPN == c ? MIDICTL_RPN
408 1.1.2.1 chap : MIDICTL_NRPN, chan, key);
409 1.1.2.1 chap }
410 1.1.2.1 chap
411 1.1.2.1 chap static uint_fast16_t
412 1.1.2.1 chap read14(midictl *mc, uint_fast8_t chan, class c, uint_fast16_t key,
413 1.1.2.1 chap uint_fast16_t dflt)
414 1.1.2.1 chap {
415 1.1.2.1 chap bucket *bkt;
416 1.1.2.1 chap uint16_t val;
417 1.1.2.1 chap
418 1.1.2.1 chap bkt = store_locate(mc->store, c, chan, key);
419 1.1.2.1 chap if ( NULL == bkt )
420 1.1.2.1 chap goto neitherset;
421 1.1.2.1 chap
422 1.1.2.1 chap val = store_extract(bkt, c, chan, key);
423 1.1.2.1 chap switch ( val & (C14MSET|C14LSET) ) {
424 1.1.2.1 chap case C14MSET|C14LSET:
425 1.1.2.1 chap return val & 0x3fff;
426 1.1.2.1 chap case C14MSET:
427 1.1.2.1 chap val = C14LSET | (val & ~0x7f) | (dflt & 0x7f);
428 1.1.2.1 chap break;
429 1.1.2.1 chap case C14LSET:
430 1.1.2.1 chap val = C14MSET | (val & ~0x3f8) | (dflt & 0x3f8);
431 1.1.2.1 chap break;
432 1.1.2.1 chap neitherset:
433 1.1.2.1 chap case 0:
434 1.1.2.1 chap val = C14MSET|C14LSET | (dflt & 0x3fff);
435 1.1.2.1 chap }
436 1.1.2.1 chap store_update(mc->store, bkt, c, chan, key, val);
437 1.1.2.1 chap return val & 0x3fff;
438 1.1.2.1 chap }
439 1.1.2.1 chap
440 1.1.2.1 chap /*
441 1.1.2.1 chap * Determine the controller class; ranges based on
442 1.1.2.1 chap * http://www.midi.org/about-midi/table3.shtml dated 1995/1999/2002
443 1.1.2.1 chap * and viewed 2 June 2006.
444 1.1.2.1 chap */
445 1.1.2.1 chap static class
446 1.1.2.1 chap classify(uint_fast16_t *key, _Bool *islsb) {
447 1.1.2.1 chap if ( *key < 32 ) {
448 1.1.2.1 chap *islsb = 0;
449 1.1.2.1 chap return CTL14;
450 1.1.2.1 chap } else if ( *key < 64 ) {
451 1.1.2.1 chap *islsb = 1;
452 1.1.2.1 chap *key -= 32;
453 1.1.2.1 chap return CTL14;
454 1.1.2.1 chap } else if ( *key < 70 ) {
455 1.1.2.1 chap *key -= 64;
456 1.1.2.1 chap return CTL1;
457 1.1.2.1 chap } /* 70-84 defined, 85-90 undef'd, 91-95 def'd */
458 1.1.2.1 chap return CTL7; /* 96-101,120- handled above, 102-119 all undef'd */
459 1.1.2.1 chap /* treat them all as CTL7 */
460 1.1.2.1 chap }
461 1.1.2.1 chap
462 1.1.2.1 chap static void
463 1.1.2.1 chap notify_no_one(void *cookie, midictl_evt evt, uint_fast8_t chan, uint_fast16_t k)
464 1.1.2.1 chap {
465 1.1.2.1 chap }
466 1.1.2.1 chap
467 1.1.2.1 chap #undef PN_SET
468 1.1.2.1 chap #undef C14MSET
469 1.1.2.1 chap #undef C14LSET
470 1.1.2.1 chap #undef C7_SET
471 1.1.2.1 chap #undef C1_SET
472 1.1.2.1 chap
473 1.1.2.1 chap /*
474 1.1.2.1 chap * I M P L E M E N T A T I O N O F T H E S T O R E :
475 1.1.2.1 chap *
476 1.1.2.1 chap * MIDI defines a metric plethora of possible controllers, registered
477 1.1.2.1 chap * parameters, and nonregistered parameters: a bit more than 32k possible words
478 1.1.2.1 chap * to store. The saving grace is that only a handful are likely to appear in
479 1.1.2.1 chap * typical MIDI data, and only a handful are likely implemented by or
480 1.1.2.1 chap * interesting to a typical client. So the store implementation needs to be
481 1.1.2.1 chap * suited to a largish but quite sparse data set.
482 1.1.2.1 chap *
483 1.1.2.1 chap * For greatest efficiency, this could be implemented over the hash API.
484 1.1.2.1 chap * For now, it is implemented over libprop, which is not a perfect fit,
485 1.1.2.1 chap * but because that API does so much more than the hash API, this code
486 1.1.2.1 chap * has to do less, and simplicity is worth something.
487 1.1.2.1 chap *
488 1.1.2.1 chap * prop_numbers are uintmax_t's, which are wider than anything we store, and
489 1.1.2.1 chap * to reduce waste we want to fill them. The choice is to fill an entry
490 1.1.2.1 chap * with values for the same controller across some consecutive channels
491 1.1.2.1 chap * (rather than for consecutive controllers on a channel) because very few
492 1.1.2.1 chap * controllers are likely to be used, but those that are will probably be used
493 1.1.2.1 chap * on more than one channel.
494 1.1.2.1 chap */
495 1.1.2.1 chap
496 1.1.2.1 chap #include <prop/proplib.h>
497 1.1.2.1 chap #include <sys/malloc.h>
498 1.1.2.1 chap
499 1.1.2.1 chap #define KEYSTRSIZE 8
500 1.1.2.1 chap static void tokeystr(char s[static KEYSTRSIZE],
501 1.1.2.1 chap class c, uint_fast8_t chan, uint_fast16_t key);
502 1.1.2.1 chap
503 1.1.2.1 chap static uint_fast8_t const packing[] = {
504 1.1.2.1 chap [CTL1 ] = 4*sizeof(uintmax_t)/sizeof(uint8_t),
505 1.1.2.1 chap [CTL7 ] = sizeof(uintmax_t)/sizeof(uint8_t),
506 1.1.2.1 chap [CTL14] = sizeof(uintmax_t)/sizeof(uint16_t),
507 1.1.2.1 chap [RPN ] = sizeof(uintmax_t)/sizeof(uint16_t),
508 1.1.2.1 chap [NRPN ] = sizeof(uintmax_t)/sizeof(uint16_t)
509 1.1.2.1 chap };
510 1.1.2.1 chap
511 1.1.2.1 chap struct bucket {
512 1.1.2.1 chap union {
513 1.1.2.1 chap uintmax_t val;
514 1.1.2.1 chap uint8_t c7[sizeof(uintmax_t)/sizeof(uint8_t)];
515 1.1.2.1 chap uint16_t c14[sizeof(uintmax_t)/sizeof(uint16_t)];
516 1.1.2.1 chap } __packed un;
517 1.1.2.1 chap midictl_store *ms;
518 1.1.2.1 chap };
519 1.1.2.1 chap
520 1.1.2.1 chap struct midictl_store {
521 1.1.2.1 chap prop_dictionary_t pd;
522 1.1.2.1 chap bucket bkt; /* assume any one client nonreentrant (for now?) */
523 1.1.2.1 chap };
524 1.1.2.1 chap
525 1.1.2.1 chap static midictl_store *
526 1.1.2.1 chap store_init(void)
527 1.1.2.1 chap {
528 1.1.2.1 chap midictl_store *s;
529 1.1.2.1 chap
530 1.1.2.1 chap s = _MIDICTL_MALLOC(sizeof *s, M_DEVBUF);
531 1.1.2.1 chap s->pd = prop_dictionary_create();
532 1.1.2.1 chap s->bkt.ms = s;
533 1.1.2.1 chap return s;
534 1.1.2.1 chap }
535 1.1.2.1 chap
536 1.1.2.1 chap static void
537 1.1.2.1 chap store_done(midictl_store *s)
538 1.1.2.1 chap {
539 1.1.2.1 chap prop_object_release(s->pd);
540 1.1.2.1 chap _MIDICTL_FREE(s, M_DEVBUF);
541 1.1.2.1 chap }
542 1.1.2.1 chap
543 1.1.2.1 chap static bucket *
544 1.1.2.1 chap store_locate(midictl_store *s, class c, uint_fast8_t chan, uint_fast16_t key)
545 1.1.2.1 chap {
546 1.1.2.1 chap char buf[8];
547 1.1.2.1 chap prop_number_t pn;
548 1.1.2.1 chap
549 1.1.2.1 chap tokeystr(buf, c, chan, key);
550 1.1.2.1 chap pn = (prop_number_t)prop_dictionary_get(s->pd, buf);
551 1.1.2.1 chap if ( NULL == pn ) {
552 1.1.2.1 chap s->bkt.un.val = 0;
553 1.1.2.1 chap return NULL;
554 1.1.2.1 chap }
555 1.1.2.1 chap s->bkt.un.val = prop_number_integer_value(pn);
556 1.1.2.1 chap return &s->bkt;
557 1.1.2.1 chap }
558 1.1.2.1 chap
559 1.1.2.1 chap static uint16_t
560 1.1.2.1 chap store_extract(bucket *b, class c, uint_fast8_t chan, uint_fast16_t key)
561 1.1.2.1 chap {
562 1.1.2.1 chap chan %= packing[c];
563 1.1.2.1 chap switch ( c ) {
564 1.1.2.1 chap case CTL1:
565 1.1.2.1 chap return 3 & (b->un.c7[chan/4]>>(chan%4)*2);
566 1.1.2.1 chap case CTL7:
567 1.1.2.1 chap return b->un.c7[chan];
568 1.1.2.1 chap case CTL14:
569 1.1.2.1 chap case RPN:
570 1.1.2.1 chap case NRPN:
571 1.1.2.1 chap break;
572 1.1.2.1 chap }
573 1.1.2.1 chap return b->un.c14[chan];
574 1.1.2.1 chap }
575 1.1.2.1 chap
576 1.1.2.1 chap static void
577 1.1.2.1 chap store_update(midictl_store *s, bucket *b, class c, uint_fast8_t chan,
578 1.1.2.1 chap uint_fast16_t key, uint16_t value)
579 1.1.2.1 chap {
580 1.1.2.1 chap uintmax_t orig;
581 1.1.2.1 chap char buf[KEYSTRSIZE];
582 1.1.2.1 chap prop_number_t pn;
583 1.1.2.1 chap boolean_t success;
584 1.1.2.1 chap uint_fast8_t ent;
585 1.1.2.1 chap
586 1.1.2.1 chap if ( NULL == b ) {
587 1.1.2.1 chap b = &s->bkt;
588 1.1.2.1 chap orig = 0;
589 1.1.2.1 chap } else
590 1.1.2.1 chap orig = b->un.val;
591 1.1.2.1 chap
592 1.1.2.1 chap ent = chan % packing[c];
593 1.1.2.1 chap
594 1.1.2.1 chap switch ( c ) {
595 1.1.2.1 chap case CTL1:
596 1.1.2.1 chap b->un.c7[ent/4] &= ~(3<<(ent%4)*2);
597 1.1.2.1 chap b->un.c7[ent/4] |= (3&value)<<(ent%4)*2;
598 1.1.2.1 chap break;
599 1.1.2.1 chap case CTL7:
600 1.1.2.1 chap b->un.c7[ent] = value;
601 1.1.2.1 chap break;
602 1.1.2.1 chap case CTL14:
603 1.1.2.1 chap case RPN:
604 1.1.2.1 chap case NRPN:
605 1.1.2.1 chap b->un.c14[ent] = value;
606 1.1.2.1 chap break;
607 1.1.2.1 chap }
608 1.1.2.1 chap
609 1.1.2.1 chap if ( orig == b->un.val )
610 1.1.2.1 chap return;
611 1.1.2.1 chap
612 1.1.2.1 chap tokeystr(buf, c, chan, key);
613 1.1.2.1 chap
614 1.1.2.1 chap if ( 0 == b->un.val )
615 1.1.2.1 chap prop_dictionary_remove(s->pd, buf);
616 1.1.2.1 chap else {
617 1.1.2.1 chap pn = prop_number_create_integer(b->un.val);
618 1.1.2.1 chap _MIDICTL_ASSERT(NULL != pn);
619 1.1.2.1 chap success = prop_dictionary_set(s->pd, buf, pn);
620 1.1.2.1 chap _MIDICTL_ASSERT(success);
621 1.1.2.1 chap prop_object_release(pn);
622 1.1.2.1 chap }
623 1.1.2.1 chap }
624 1.1.2.1 chap
625 1.1.2.1 chap static void
626 1.1.2.1 chap tokeystr(char s[static KEYSTRSIZE],
627 1.1.2.1 chap class c, uint_fast8_t chan, uint_fast16_t key)
628 1.1.2.1 chap {
629 1.1.2.1 chap snprintf(s, KEYSTRSIZE, "%x%x%x", c, chan/packing[c], key);
630 1.1.2.1 chap }
631 1.1.2.1 chap
632 1.1.2.1 chap #if defined(_MIDICTL_MAIN)
633 1.1.2.1 chap void
634 1.1.2.1 chap dumpstore(void)
635 1.1.2.1 chap {
636 1.1.2.1 chap char *s = prop_dictionary_externalize(mc.store->pd);
637 1.1.2.1 chap printf("%s", s);
638 1.1.2.1 chap free(s);
639 1.1.2.1 chap }
640 1.1.2.1 chap #endif
641