1 /* $NetBSD: midictl.c,v 1.5 2008/04/28 20:23:47 martin Exp $ */
4 * Copyright (c) 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
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.
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: midictl.c,v 1.5 2008/04/28 20:23:47 martin Exp $");
35 * See midictl.h for an overview of the purpose and use of this module.
39 #define _MIDICTL_ASSERT(x) KASSERT(x)
40 #define _MIDICTL_MALLOC(s,t) malloc((s), (t), M_WAITOK)
41 #define _MIDICTL_ZMALLOC(s,t) malloc((s), (t), M_WAITOK|M_ZERO)
42 #define _MIDICTL_IMZMALLOC(s,t) malloc((s), (t), M_NOWAIT|M_ZERO)
43 #define _MIDICTL_PANIC(...) panic(__VA_ARGS__)
44 #define _MIDICTL_FREE(s,t) free((s), (t))
45 #include <sys/systm.h>
46 #include <sys/types.h>
53 #define _MIDICTL_ASSERT(x) assert(x)
54 #define _MIDICTL_MALLOC(s,t) malloc((s))
55 #define _MIDICTL_ZMALLOC(s,t) calloc(1,(s))
56 #define _MIDICTL_IMZMALLOC(s,t) calloc(1,(s))
57 #define _MIDICTL_PANIC(...) errx(1,__VA_ARGS__)
58 #define _MIDICTL_FREE(s,t) free((s))
64 * The upper part of this file is MIDI-aware, and deals with things like
65 * decoding MIDI Control Change messages, dealing with the ones that require
66 * special handling as mode messages or parameter updates, and so on.
68 * It relies on a "store" layer (implemented in the lower part of this file)
69 * that only must be able to stash away 2-, 8-, or 16-bit quantities (which
70 * it may pack into larger units as it sees fit) and find them again given
71 * a class, channel, and key (controller/parameter number).
73 * The MIDI controllers can have 1-, 7-, or 14-bit values; the parameters are
74 * also 14-bit. The 14-bit values have to be set in two MIDI messages, 7 bits
75 * at a time. The MIDI layer uses store-managed 2- or 8-bit slots for the
76 * smaller types, and uses the free high bit to indicate that it has explicitly
77 * set the value. (Because the store is allowed to pack things, it may 'find'
78 * a zero entry for a value we never set, because it shares a word with a
79 * different value that has been set. We know it is not a real value because
80 * the high bit is clear.)
82 * The 14-bit values are handled similarly: 16-bit store slots are used to hold
83 * them, with the two free high bits indicating independently whether the MSB
84 * and the LSB have been explicitly set--as two separate MIDI messages are
85 * required. If such a control is queried when only one half has been explicitly
86 * set, the result is as if it had been set to the specified default value
87 * before the explicit set.
90 typedef enum { CTL1
, CTL7
, CTL14
, RPN
, NRPN
} class;
93 * assert(does_not_apply(KNFNamespaceArgumentAgainstNamesInPrototypes,
94 * PrototypesOfStaticFunctionsWithinNonIncludedFile));
96 static void reset_all_controllers(midictl
*mc
, uint_fast8_t chan
);
97 static void enter14(midictl
*mc
, uint_fast8_t chan
, class c
,
98 uint_fast16_t key
, _Bool islsb
, uint8_t val
);
99 static uint_fast16_t read14(midictl
*mc
, uint_fast8_t chan
, class c
,
100 uint_fast16_t key
, uint_fast16_t dflt
);
101 static class classify(uint_fast16_t *key
, _Bool
*islsb
);
102 static midictl_notify notify_no_one
;
104 static midictl_store
*store_init(void);
105 static void store_done(midictl_store
*s
);
106 static _Bool
store_locate(midictl_store
*s
, class c
,
107 uint_fast8_t chan
, uint_fast16_t key
);
109 * store_extract and store_update operate on the bucket most recently found
110 * by store_locate on this store. That works because reentrancy of midictl
111 * functions is limited: they /can/ be reentered during midictl_notify
112 * callbacks, but not at other arbitrary times. We never call notify /during/
113 * a locate/extract/update transaction.
115 static uint16_t store_extract(midictl_store
*s
, class c
,
116 uint_fast8_t chan
, uint_fast16_t key
);
117 static void store_update(midictl_store
*s
, class c
,
118 uint_fast8_t chan
, uint_fast16_t key
, uint16_t value
);
120 #define PN_SET 0x8000 /* a parameter number has been explicitly set */
121 #define C14MSET 0x8000 /* MSB of a 14-bit val has been set */
122 #define C14LSET 0x4000 /* LSB of a 14-bit val has been set */
123 #define C7_SET 0x80 /* a 7-bit ctl has been set */
124 #define C1_SET 2 /* a 1-bit ctl has been set */
126 #if defined(_MIDICTL_MAIN)
127 #define XS(s) [MIDICTL_##s]=#s
128 char const * const evt_strings
[] = {
129 XS(CTLR
), XS(RPN
), XS(NRPN
), XS(RESET
), XS(NOTES_OFF
),
130 XS(SOUND_OFF
), XS(LOCAL
), XS(MODE
)
135 dbgnotify(void *cookie
, midictl_evt e
, uint_fast8_t chan
, uint_fast16_t key
)
137 printf("NFY %p %s chan %u #%u\n", cookie
, evt_strings
[e
], chan
, key
);
141 .accept_any_ctl_rpn
= 0,
142 .accept_any_nrpn
= 0,
149 main(int argc
, char **argv
)
155 cnt
= scanf("%i %i %i", &a
, &b
, &c
);
157 midictl_change(&mc
, a
, (uint8_t[]){b
,c
});
159 } while ( EOF
!= cnt
);
163 #endif /* defined(_MIDICTL_MAIN) */
166 midictl_open(midictl
*mc
)
168 if ( NULL
== mc
->notify
)
169 mc
->notify
= notify_no_one
;
170 mc
->store
= store_init();
174 midictl_close(midictl
*mc
)
176 store_done(mc
->store
);
180 midictl_change(midictl
*mc
, uint_fast8_t chan
, uint8_t *ctlval
)
183 uint_fast16_t key
, val
;
184 _Bool islsb
, present
;
186 switch ( ctlval
[0] ) {
188 * Channel mode messages:
190 case MIDI_CTRL_OMNI_OFF
:
191 case MIDI_CTRL_OMNI_ON
:
192 case MIDI_CTRL_POLY_OFF
:
193 case MIDI_CTRL_POLY_ON
:
194 if ( chan
!= mc
->base_channel
)
195 return; /* ignored - not on base channel */
197 return; /* XXX ignored anyway - not implemented yet */
198 case MIDI_CTRL_NOTES_OFF
:
199 mc
->notify(mc
->cookie
, MIDICTL_NOTES_OFF
, chan
, 0);
201 case MIDI_CTRL_LOCAL
:
202 mc
->notify(mc
->cookie
, MIDICTL_LOCAL
, chan
, ctlval
[1]);
204 case MIDI_CTRL_SOUND_OFF
:
205 mc
->notify(mc
->cookie
, MIDICTL_SOUND_OFF
, chan
, 0);
207 case MIDI_CTRL_RESET
:
208 reset_all_controllers(mc
, chan
);
211 * Control changes to be handled specially:
213 case MIDI_CTRL_RPN_LSB
:
215 mc
-> rpn
|= PN_SET
| (0x7f & ctlval
[1]);
218 case MIDI_CTRL_RPN_MSB
:
219 mc
-> rpn
&= ~0x7f<<7;
220 mc
-> rpn
|= PN_SET
| (0x7f & ctlval
[1])<<7;
223 case MIDI_CTRL_NRPN_LSB
:
225 mc
->nrpn
|= PN_SET
| (0x7f & ctlval
[1]);
228 case MIDI_CTRL_NRPN_MSB
:
229 mc
->nrpn
&= ~0x7f<<7;
230 mc
->nrpn
|= PN_SET
| (0x7f & ctlval
[1])<<7;
233 case MIDI_CTRL_DATA_ENTRY_LSB
:
236 case MIDI_CTRL_DATA_ENTRY_MSB
:
239 if ( 0 == ( (mc
->rpn
^ mc
->nrpn
) & PN_SET
) )
240 return; /* exactly one must be current */
241 if ( mc
->rpn
& PN_SET
) {
249 if ( 0x3fff == key
) /* 'null' parm# to lock out changes */
251 enter14(mc
, chan
, c
, key
, islsb
, ctlval
[1]);
253 case MIDI_CTRL_RPN_INCREMENT
: /* XXX for later - these are a PITA to */
254 case MIDI_CTRL_RPN_DECREMENT
: /* get right - 'right' varies by param */
255 /* see http://www.midi.org/about-midi/rp18.shtml */
260 * Channel mode, RPN, and NRPN operations have been ruled out.
261 * This is an ordinary control change.
265 c
= classify(&key
, &islsb
);
269 enter14(mc
, chan
, c
, key
, islsb
, ctlval
[1]);
272 present
= store_locate(mc
->store
, c
, chan
, key
);
273 if ( !mc
->accept_any_ctl_rpn
) {
276 val
= store_extract(mc
->store
, c
, chan
, key
);
280 store_update(mc
->store
, c
, chan
, key
,
281 C7_SET
| (0x7f & ctlval
[1]));
282 mc
->notify(mc
->cookie
, MIDICTL_CTLR
, chan
, key
);
285 present
= store_locate(mc
->store
, c
, chan
, key
);
286 if ( !mc
->accept_any_ctl_rpn
) {
289 val
= store_extract(mc
->store
, c
, chan
, key
);
293 store_update(mc
->store
, c
, chan
, key
,
294 C1_SET
| (ctlval
[1]>63));
295 mc
->notify(mc
->cookie
, MIDICTL_CTLR
, chan
, key
);
299 return; /* won't see these - sop for gcc */
304 midictl_read(midictl
*mc
, uint_fast8_t chan
, uint_fast8_t ctlr
,
307 uint_fast16_t key
, val
;
309 _Bool islsb
, present
;
312 c
= classify(&key
, &islsb
);
315 present
= store_locate(mc
->store
, c
, chan
, key
);
317 !(C1_SET
&(val
= store_extract(mc
->store
, c
, chan
, key
))) ) {
318 val
= C1_SET
| (dflt
> 63); /* convert to boolean */
319 store_update(mc
->store
, c
, chan
, key
, val
);
321 return (val
& 1) ? 127 : 0;
323 present
= store_locate(mc
->store
, c
, chan
, key
);
325 !(C7_SET
&(val
= store_extract(mc
->store
, c
, chan
, key
))) ) {
326 val
= C7_SET
| (dflt
& 0x7f);
327 store_update(mc
->store
, c
, chan
, key
, val
);
331 _MIDICTL_ASSERT(!islsb
);
332 return read14(mc
, chan
, c
, key
, dflt
);
335 break; /* sop for gcc */
337 return 0; /* sop for gcc */
341 midictl_rpn_read(midictl
*mc
, uint_fast8_t chan
, uint_fast16_t ctlr
,
344 return read14(mc
, chan
, RPN
, ctlr
, dflt
);
348 midictl_nrpn_read(midictl
*mc
, uint_fast8_t chan
, uint_fast16_t ctlr
,
351 return read14(mc
, chan
, NRPN
, ctlr
, dflt
);
355 reset_all_controllers(midictl
*mc
, uint_fast8_t chan
)
357 uint_fast16_t ctlr
, key
;
359 _Bool islsb
, present
;
361 for ( ctlr
= 0 ; ; ++ ctlr
) {
364 * exempt by http://www.midi.org/about-midi/rp15.shtml:
366 case MIDI_CTRL_BANK_SELECT_MSB
: /* 0 */
367 case MIDI_CTRL_CHANNEL_VOLUME_MSB
: /* 7 */
368 case MIDI_CTRL_PAN_MSB
: /* 10 */
370 case MIDI_CTRL_BANK_SELECT_LSB
: /* 32 */
371 ctlr
+= 31; /* skip all these LSBs anyway */
373 case MIDI_CTRL_SOUND_VARIATION
: /* 70 */
374 ctlr
+= 9; /* skip all Sound Controllers */
376 case MIDI_CTRL_EFFECT_DEPTH_1
: /* 91 */
377 goto loop_exit
; /* nothing more gets reset */
379 * exempt for our own personal reasons:
381 case MIDI_CTRL_DATA_ENTRY_MSB
: /* 6 */
382 continue; /* doesn't go to the store */
386 c
= classify(&key
, &islsb
);
388 present
= store_locate(mc
->store
, c
, chan
, key
);
391 store_update(mc
->store
, c
, chan
, key
, 0); /* no C*SET */
394 mc
->notify(mc
->cookie
, MIDICTL_RESET
, chan
, 0);
398 enter14(midictl
*mc
, uint_fast8_t chan
, class c
, uint_fast16_t key
,
399 _Bool islsb
, uint8_t val
)
404 present
= store_locate(mc
->store
, c
, chan
, key
);
405 stval
= (present
) ? store_extract(mc
->store
, c
, chan
, key
) : 0;
406 if ( !( stval
& (C14MSET
|C14LSET
) ) ) {
407 if ( !((NRPN
==c
)? mc
->accept_any_nrpn
: mc
->accept_any_ctl_rpn
) )
411 stval
= C14LSET
| val
| ( stval
& ~0x7f );
413 stval
= C14MSET
| ( val
<< 7 ) | ( stval
& ~0x3f80 );
414 store_update(mc
->store
, c
, chan
, key
, stval
);
415 mc
->notify(mc
->cookie
, CTL14
== c
? MIDICTL_CTLR
416 : RPN
== c
? MIDICTL_RPN
417 : MIDICTL_NRPN
, chan
, key
);
421 read14(midictl
*mc
, uint_fast8_t chan
, class c
, uint_fast16_t key
,
427 present
= store_locate(mc
->store
, c
, chan
, key
);
431 val
= store_extract(mc
->store
, c
, chan
, key
);
432 switch ( val
& (C14MSET
|C14LSET
) ) {
433 case C14MSET
|C14LSET
:
436 val
= C14LSET
| (val
& ~0x7f) | (dflt
& 0x7f);
439 val
= C14MSET
| (val
& ~0x3f8) | (dflt
& 0x3f8);
443 val
= C14MSET
|C14LSET
| (dflt
& 0x3fff);
445 store_update(mc
->store
, c
, chan
, key
, val
);
450 * Determine the controller class; ranges based on
451 * http://www.midi.org/about-midi/table3.shtml dated 1995/1999/2002
452 * and viewed 2 June 2006.
455 classify(uint_fast16_t *key
, _Bool
*islsb
) {
459 } else if ( *key
< 64 ) {
463 } else if ( *key
< 70 ) {
465 } /* 70-84 defined, 85-90 undef'd, 91-95 def'd */
466 return CTL7
; /* 96-101,120- handled above, 102-119 all undef'd */
467 /* treat them all as CTL7 */
471 notify_no_one(void *cookie
, midictl_evt evt
,
472 uint_fast8_t chan
, uint_fast16_t k
)
483 * I M P L E M E N T A T I O N O F T H E S T O R E :
485 * MIDI defines a metric plethora of possible controllers, registered
486 * parameters, and nonregistered parameters: a bit more than 32k possible words
487 * to store. The saving grace is that only a handful are likely to appear in
488 * typical MIDI data, and only a handful are likely implemented by or
489 * interesting to a typical client. So the store implementation needs to be
490 * suited to a largish but quite sparse data set.
492 * A double-hashed, open address table is used here. Each slot is a uint64
493 * that contains the match key (control class|channel|ctl-or-PN-number) as
494 * well as the values for two or more channels. CTL14s, RPNs, and NRPNs can
495 * be packed two channels to the slot; CTL7s, six channels; and CTL1s get all
496 * 16 channels into one slot. The channel value used in the key is the lowest
497 * channel stored in the slot. Open addressing is appropriate here because the
498 * link fields in a chained approach would be at least 100% overhead, and also,
499 * we don't delete (MIDICTL_RESET is the only event that logically deletes
500 * things, and at the moment it does not remove anything from the table, but
501 * zeroes the stored value). If wanted, the deletion algorithm for open
502 * addressing could be used, with shrinking/rehashing when the load factor
503 * drops below 3/8 (3/4 is the current threshold for expansion), and the
504 * rehashing would relieve the fills-with-DELETED problem in most cases. But
505 * for now the table never shrinks while the device is open.
508 #include <sys/malloc.h>
510 #define INITIALLGCAPACITY 6 /* initial capacity 1<<6 */
511 #define IS_USED 1<<15
512 #define IS_CTL7 1<<14
514 #define CTL1SHIFT(chan) (23+((chan)<<1))
515 #define CTL7SHIFT(chan) (16+((chan)<<3))
516 #define CTLESHIFT(chan) (23+((chan)<<4))
518 static uint_fast8_t const packing
[] = {
519 [CTL1
] = 16, /* 16 * 2 bits ==> 32 bits, all chns in one bucket */
520 [CTL7
] = 6, /* 6 * 8 bits ==> 48 bits, 6 chns in one bucket */
521 [CTL14
] = 2, /* 2 *16 bits ==> 32 bits, 2 chns in one bucket */
526 struct midictl_store
{
534 static uint32_t store_idx(uint32_t lgcapacity
,
536 uint64_t key
, uint64_t mask
);
537 static void store_rehash(midictl_store
*s
);
539 static midictl_store
*
544 s
= _MIDICTL_MALLOC(sizeof *s
, M_DEVBUF
);
546 s
->lgcapacity
= INITIALLGCAPACITY
;
547 s
->table
= _MIDICTL_ZMALLOC(sizeof *s
->table
<<s
->lgcapacity
, M_DEVBUF
);
552 store_done(midictl_store
*s
)
554 _MIDICTL_FREE(s
->table
, M_DEVBUF
);
555 _MIDICTL_FREE(s
, M_DEVBUF
);
559 store_locate(midictl_store
*s
, class c
, uint_fast8_t chan
, uint_fast16_t key
)
563 if ( s
->used
>= 1 << s
->lgcapacity
)
564 _MIDICTL_PANIC("%s: repeated attempts to expand table failed, "
565 "plumb ran out of space", __func__
);
567 chan
= packing
[c
] * (chan
/packing
[c
]);
569 if ( CTL7
== c
) { /* only 16 bits here (key's only 7) */
570 s
->key
= IS_USED
| IS_CTL7
| (chan
<< 7) | key
;
572 } else { /* use 23 bits (key could be 14) */
573 s
->key
= (c
<< 20) | (chan
<< 16) | IS_USED
| key
;
577 s
->idx
= store_idx(s
->lgcapacity
, s
->table
, s
->key
, mask
);
579 if ( !(s
->table
[s
->idx
] & IS_USED
) )
586 store_extract(midictl_store
*s
, class c
, uint_fast8_t chan
,
592 return 3 & (s
->table
[s
->idx
]>>CTL1SHIFT(chan
));
594 return 0xff & (s
->table
[s
->idx
]>>CTL7SHIFT(chan
));
600 return 0xffff & (s
->table
[s
->idx
]>>CTLESHIFT(chan
));
604 store_update(midictl_store
*s
, class c
, uint_fast8_t chan
,
605 uint_fast16_t key
, uint16_t value
)
609 orig
= s
->table
[s
->idx
];
610 if ( !(orig
& IS_USED
) ) {
619 orig
&= ~(((uint64_t)3)<<CTL1SHIFT(chan
));
620 orig
|= ((uint64_t)(3 & value
)) << CTL1SHIFT(chan
);
623 orig
&= ~(((uint64_t)0xff)<<CTL7SHIFT(chan
));
624 orig
|= ((uint64_t)(0xff & value
)) << CTL7SHIFT(chan
);
629 orig
&= ~(((uint64_t)0xffff)<<CTLESHIFT(chan
));
630 orig
|= ((uint64_t)value
) << CTLESHIFT(chan
);
634 s
->table
[s
->idx
] = orig
;
635 if ( s
->used
* 4 >= 3 << s
->lgcapacity
)
640 store_idx(uint32_t lgcapacity
, uint64_t *table
,
641 uint64_t key
, uint64_t mask
)
649 h1
= ((k
* 0x61c88646) >> (32-lgcapacity
)) & ((1<<lgcapacity
) - 1);
650 h2
= ((k
* 0x9e3779b9) >> (32-lgcapacity
)) & ((1<<lgcapacity
) - 1);
653 for ( idx
= h1
;; idx
-= h2
) {
655 idx
+= 1<<lgcapacity
;
656 val
= (uint32_t)(table
[idx
] & mask
);
659 if ( !(val
& IS_USED
) )
667 store_rehash(midictl_store
*s
)
669 uint64_t *newtbl
, mask
;
670 uint32_t newlgcap
, oidx
, nidx
;
672 newlgcap
= 1 + s
->lgcapacity
;
673 newtbl
= _MIDICTL_IMZMALLOC(sizeof *newtbl
<< newlgcap
, M_DEVBUF
);
676 * Because IMZMALLOC can't sleep, it might have returned NULL.
677 * We rehash when there is some capacity left in the table, so
678 * just leave it alone; we'll get another chance on the next insertion.
679 * Nothing to panic about unless the load factor really hits 1.
681 if ( NULL
== newtbl
)
684 for ( oidx
= 1<<s
->lgcapacity
; oidx
--> 0 ; ) {
685 if ( !(s
->table
[oidx
] & IS_USED
) )
687 if ( s
->table
[oidx
] & IS_CTL7
)
691 nidx
= store_idx(newlgcap
, newtbl
, s
->table
[oidx
]&mask
, mask
);
692 newtbl
[nidx
] = s
->table
[oidx
];
695 _MIDICTL_FREE(s
->table
, M_DEVBUF
);
697 s
->lgcapacity
= newlgcap
;
700 #if defined(_MIDICTL_MAIN)
714 printf("store capacity %u, used %u\n", 1<<s
->lgcapacity
, s
->used
);
717 for ( i
= 1<<s
->lgcapacity
; i
--> 0; ) {
718 if ( !(s
->table
[i
] & IS_USED
) )
721 if ( s
->table
[i
] & IS_CTL7
) {
723 chan
= 0xf & (s
->table
[i
]>>7);
724 key
= 0x7f & s
->table
[i
];
726 c
= 0x7 & (s
->table
[i
]>>20);
727 chan
= 0xf & (s
->table
[i
]>>16);
728 key
= 0x3fff & s
->table
[i
];
733 val
= s
->table
[i
] >> CTL1SHIFT(0);
737 val
= s
->table
[i
] >> CTL7SHIFT(0);
741 val
= s
->table
[i
] >> CTLESHIFT(0);
745 val
= s
->table
[i
] >> CTLESHIFT(0);
749 val
= s
->table
[i
] >> CTLESHIFT(0);
757 printf("[%7u] %5s chans %x-%x key %5u: %"PRIx64
"\n",
758 i
, lbl
, chan
, chan
+packing
[c
]-1, key
, val
);
762 printf("remain == %u ??\n", remain
);