Home | History | Annotate | Line # | Download | only in dev
midictl.c revision 1.1.2.2
      1  1.1.2.2  chap /* $NetBSD: midictl.c,v 1.1.2.2 2006/06/07 00:09:39 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.2  chap __KERNEL_RCSID(0, "$NetBSD: midictl.c,v 1.1.2.2 2006/06/07 00:09:39 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.2  chap 			/* see http://www.midi.org/about-midi/rp18.shtml */
    246  1.1.2.1  chap 		return;
    247  1.1.2.1  chap 	}
    248  1.1.2.1  chap 
    249  1.1.2.1  chap 	/*
    250  1.1.2.1  chap 	 * Channel mode, RPN, and NRPN operations have been ruled out.
    251  1.1.2.1  chap 	 * This is an ordinary control change.
    252  1.1.2.1  chap 	 */
    253  1.1.2.1  chap 
    254  1.1.2.1  chap 	key = ctlval[0];
    255  1.1.2.1  chap 	c = classify(&key, &islsb);
    256  1.1.2.1  chap 
    257  1.1.2.1  chap 	switch ( c ) {
    258  1.1.2.1  chap 	case CTL14:
    259  1.1.2.1  chap 		enter14(mc, chan, c, key, islsb, ctlval[1]);
    260  1.1.2.1  chap 		break;
    261  1.1.2.1  chap 	case CTL7:
    262  1.1.2.1  chap 		bkt = store_locate(mc->store, c, chan, key);
    263  1.1.2.1  chap 		if ( !mc->accept_any_ctl_rpn ) {
    264  1.1.2.1  chap 			if ( NULL == bkt )
    265  1.1.2.1  chap 				break;
    266  1.1.2.1  chap 			val = store_extract(bkt, c, chan, key);
    267  1.1.2.1  chap 			if ( !(val&C7_SET) )
    268  1.1.2.1  chap 				break;
    269  1.1.2.1  chap 		}
    270  1.1.2.1  chap 		store_update(mc->store, bkt, c, chan, key,
    271  1.1.2.1  chap 		    C7_SET | (0x7f & ctlval[1]));
    272  1.1.2.1  chap 		mc->notify(mc->cookie, MIDICTL_CTLR, chan, key);
    273  1.1.2.1  chap 		break;
    274  1.1.2.1  chap 	case CTL1:
    275  1.1.2.1  chap 		bkt = store_locate(mc->store, c, chan, key);
    276  1.1.2.1  chap 		if ( !mc->accept_any_ctl_rpn ) {
    277  1.1.2.1  chap 			if ( NULL == bkt )
    278  1.1.2.1  chap 				break;
    279  1.1.2.1  chap 			val = store_extract(bkt, c, chan, key);
    280  1.1.2.1  chap 			if ( !(val&C1_SET) )
    281  1.1.2.1  chap 				break;
    282  1.1.2.1  chap 		}
    283  1.1.2.1  chap 		store_update(mc->store, bkt, c, chan, key,
    284  1.1.2.1  chap 		    C1_SET | (ctlval[1]>63));
    285  1.1.2.1  chap 		mc->notify(mc->cookie, MIDICTL_CTLR, chan, key);
    286  1.1.2.1  chap 		break;
    287  1.1.2.1  chap 	case RPN:
    288  1.1.2.1  chap 	case NRPN:
    289  1.1.2.1  chap 		break; /* won't see these - sop for gcc */
    290  1.1.2.1  chap 	}
    291  1.1.2.1  chap }
    292  1.1.2.1  chap 
    293  1.1.2.1  chap uint_fast16_t
    294  1.1.2.1  chap midictl_read(midictl *mc, uint_fast8_t chan, uint_fast8_t ctlr,
    295  1.1.2.1  chap              uint_fast16_t dflt)
    296  1.1.2.1  chap {
    297  1.1.2.1  chap 	bucket *bkt;
    298  1.1.2.1  chap 	uint_fast16_t key, val;
    299  1.1.2.1  chap 	class c;
    300  1.1.2.1  chap 	_Bool islsb;
    301  1.1.2.1  chap 
    302  1.1.2.1  chap 	key = ctlr;
    303  1.1.2.1  chap 	c = classify(&key, &islsb);
    304  1.1.2.1  chap 	switch ( c ) {
    305  1.1.2.1  chap 	case CTL1:
    306  1.1.2.1  chap 		bkt = store_locate(mc->store, c, chan, key);
    307  1.1.2.1  chap 		if ( NULL == bkt ||
    308  1.1.2.1  chap 		    !(C1_SET&(val = store_extract(bkt, c, chan, key))) ) {
    309  1.1.2.1  chap 			val = C1_SET | (dflt > 63);
    310  1.1.2.1  chap 			store_update(mc->store, bkt, c, chan, key, val);
    311  1.1.2.1  chap 		}
    312  1.1.2.1  chap 		return (val & 1) ? 127 : 0;
    313  1.1.2.1  chap 	case CTL7:
    314  1.1.2.1  chap 		bkt = store_locate(mc->store, c, chan, key);
    315  1.1.2.1  chap 		if ( NULL == bkt ||
    316  1.1.2.1  chap 		    !(C7_SET&(val = store_extract(bkt, c, chan, key))) ) {
    317  1.1.2.1  chap 			val = C7_SET | (dflt & 0x7f);
    318  1.1.2.1  chap 			store_update(mc->store, bkt, c, chan, key, val);
    319  1.1.2.1  chap 		}
    320  1.1.2.1  chap 		return val & 0x7f;
    321  1.1.2.1  chap 	case CTL14:
    322  1.1.2.1  chap 		_MIDICTL_ASSERT(!islsb);
    323  1.1.2.1  chap 		return read14(mc, chan, c, key, dflt);
    324  1.1.2.1  chap 	case RPN:
    325  1.1.2.1  chap 	case NRPN:
    326  1.1.2.1  chap 		break; /* sop for gcc */
    327  1.1.2.1  chap 	}
    328  1.1.2.1  chap 	return 0; /* sop for gcc */
    329  1.1.2.1  chap }
    330  1.1.2.1  chap 
    331  1.1.2.1  chap uint_fast16_t
    332  1.1.2.1  chap midictl_rpn_read(midictl *mc, uint_fast8_t chan, uint_fast16_t ctlr,
    333  1.1.2.1  chap                  uint_fast16_t dflt)
    334  1.1.2.1  chap {
    335  1.1.2.1  chap 	return read14(mc, chan, RPN, ctlr, dflt);
    336  1.1.2.1  chap }
    337  1.1.2.1  chap 
    338  1.1.2.1  chap uint_fast16_t
    339  1.1.2.1  chap midictl_nrpn_read(midictl *mc, uint_fast8_t chan, uint_fast16_t ctlr,
    340  1.1.2.1  chap                   uint_fast16_t dflt)
    341  1.1.2.1  chap {
    342  1.1.2.1  chap 	return read14(mc, chan, NRPN, ctlr, dflt);
    343  1.1.2.1  chap }
    344  1.1.2.1  chap 
    345  1.1.2.1  chap static void
    346  1.1.2.1  chap reset_all_controllers(midictl *mc, uint_fast8_t chan)
    347  1.1.2.1  chap {
    348  1.1.2.1  chap 	uint_fast16_t ctlr, key;
    349  1.1.2.1  chap 	class c;
    350  1.1.2.1  chap 	_Bool islsb;
    351  1.1.2.1  chap 	bucket *bkt;
    352  1.1.2.1  chap 
    353  1.1.2.1  chap 	for ( ctlr = 0 ; ; ++ ctlr ) {
    354  1.1.2.1  chap 		switch ( ctlr ) {
    355  1.1.2.1  chap 		/*
    356  1.1.2.1  chap 		 * exempt by http://www.midi.org/about-midi/rp15.shtml:
    357  1.1.2.1  chap 		 */
    358  1.1.2.1  chap 		case MIDI_CTRL_BANK_SELECT_MSB:		/* 0 */
    359  1.1.2.1  chap 		case MIDI_CTRL_CHANNEL_VOLUME_MSB:	/* 7 */
    360  1.1.2.1  chap 		case MIDI_CTRL_PAN_MSB:			/* 10 */
    361  1.1.2.1  chap 			continue;
    362  1.1.2.1  chap 		case MIDI_CTRL_BANK_SELECT_LSB:		/* 32 */
    363  1.1.2.1  chap 			ctlr += 31; /* skip all these LSBs anyway */
    364  1.1.2.1  chap 			continue;
    365  1.1.2.1  chap 		case MIDI_CTRL_SOUND_VARIATION:		/* 70 */
    366  1.1.2.1  chap 			ctlr += 9; /* skip all Sound Controllers */
    367  1.1.2.1  chap 			continue;
    368  1.1.2.1  chap 		case MIDI_CTRL_EFFECT_DEPTH_1:		/* 91 */
    369  1.1.2.1  chap 			goto loop_exit; /* nothing more gets reset */
    370  1.1.2.1  chap 		/*
    371  1.1.2.1  chap 		 * exempt for our own personal reasons:
    372  1.1.2.1  chap 		 */
    373  1.1.2.1  chap 		case MIDI_CTRL_DATA_ENTRY_MSB:		/* 6 */
    374  1.1.2.1  chap 			continue; /* doesn't go to the store */
    375  1.1.2.1  chap 		}
    376  1.1.2.1  chap 
    377  1.1.2.1  chap 		key = ctlr;
    378  1.1.2.1  chap 		c = classify(&key, &islsb);
    379  1.1.2.1  chap 
    380  1.1.2.1  chap 		bkt = store_locate(mc->store, c, chan, key);
    381  1.1.2.1  chap 		if ( NULL == bkt )
    382  1.1.2.1  chap 			continue;
    383  1.1.2.1  chap 		store_update(mc->store, bkt, c, chan, key, 0); /* no C*SET */
    384  1.1.2.1  chap 	}
    385  1.1.2.1  chap loop_exit:
    386  1.1.2.1  chap 	mc->notify(mc->cookie, MIDICTL_RESET, chan, 0);
    387  1.1.2.1  chap }
    388  1.1.2.1  chap 
    389  1.1.2.1  chap static void
    390  1.1.2.1  chap enter14(midictl *mc, uint_fast8_t chan, class c, uint_fast16_t key,
    391  1.1.2.1  chap         _Bool islsb, uint8_t val)
    392  1.1.2.1  chap {
    393  1.1.2.1  chap 	bucket *bkt;
    394  1.1.2.1  chap 	uint16_t stval;
    395  1.1.2.1  chap 
    396  1.1.2.1  chap 	bkt = store_locate(mc->store, c, chan, key);
    397  1.1.2.1  chap 	stval = (NULL == bkt) ? 0 : store_extract(bkt, c, chan, key);
    398  1.1.2.1  chap 	if ( !(stval&(C14MSET|C14LSET)) ) {
    399  1.1.2.1  chap 		if ( !((NRPN==c)? mc->accept_any_nrpn: mc->accept_any_ctl_rpn) )
    400  1.1.2.1  chap 			return;
    401  1.1.2.1  chap 	}
    402  1.1.2.1  chap 	if ( islsb )
    403  1.1.2.1  chap 		stval = C14LSET | val | ( stval & ~0x7f );
    404  1.1.2.1  chap 	else
    405  1.1.2.1  chap 		stval = C14MSET | ( val << 7 ) | ( stval & ~0x3f80 );
    406  1.1.2.1  chap 	store_update(mc->store, bkt, c, chan, key, stval);
    407  1.1.2.1  chap 	mc->notify(mc->cookie, CTL14 == c ? MIDICTL_CTLR
    408  1.1.2.1  chap 		             : RPN   == c ? MIDICTL_RPN
    409  1.1.2.1  chap 			     : MIDICTL_NRPN, chan, key);
    410  1.1.2.1  chap }
    411  1.1.2.1  chap 
    412  1.1.2.1  chap static uint_fast16_t
    413  1.1.2.1  chap read14(midictl *mc, uint_fast8_t chan, class c, uint_fast16_t key,
    414  1.1.2.1  chap        uint_fast16_t dflt)
    415  1.1.2.1  chap {
    416  1.1.2.1  chap 	bucket *bkt;
    417  1.1.2.1  chap 	uint16_t val;
    418  1.1.2.1  chap 
    419  1.1.2.1  chap 	bkt = store_locate(mc->store, c, chan, key);
    420  1.1.2.1  chap 	if ( NULL == bkt )
    421  1.1.2.1  chap 		goto neitherset;
    422  1.1.2.1  chap 
    423  1.1.2.1  chap 	val = store_extract(bkt, c, chan, key);
    424  1.1.2.1  chap 	switch ( val & (C14MSET|C14LSET) ) {
    425  1.1.2.1  chap 	case C14MSET|C14LSET:
    426  1.1.2.1  chap 		return val & 0x3fff;
    427  1.1.2.1  chap 	case C14MSET:
    428  1.1.2.1  chap 		val = C14LSET | (val & ~0x7f) | (dflt & 0x7f);
    429  1.1.2.1  chap 		break;
    430  1.1.2.1  chap 	case C14LSET:
    431  1.1.2.1  chap 		val = C14MSET | (val & ~0x3f8) | (dflt & 0x3f8);
    432  1.1.2.1  chap 		break;
    433  1.1.2.1  chap neitherset:
    434  1.1.2.1  chap 	case 0:
    435  1.1.2.1  chap 		val = C14MSET|C14LSET | (dflt & 0x3fff);
    436  1.1.2.1  chap 	}
    437  1.1.2.1  chap 	store_update(mc->store, bkt, c, chan, key, val);
    438  1.1.2.1  chap 	return val & 0x3fff;
    439  1.1.2.1  chap }
    440  1.1.2.1  chap 
    441  1.1.2.1  chap /*
    442  1.1.2.1  chap  * Determine the controller class; ranges based on
    443  1.1.2.1  chap  * http://www.midi.org/about-midi/table3.shtml dated 1995/1999/2002
    444  1.1.2.1  chap  * and viewed 2 June 2006.
    445  1.1.2.1  chap  */
    446  1.1.2.1  chap static class
    447  1.1.2.1  chap classify(uint_fast16_t *key, _Bool *islsb) {
    448  1.1.2.1  chap 	if ( *key < 32 ) {
    449  1.1.2.1  chap 		*islsb = 0;
    450  1.1.2.1  chap 		return CTL14;
    451  1.1.2.1  chap 	} else if ( *key < 64 ) {
    452  1.1.2.1  chap 		*islsb = 1;
    453  1.1.2.1  chap 		*key -= 32;
    454  1.1.2.1  chap 		return CTL14;
    455  1.1.2.1  chap 	} else if ( *key < 70 ) {
    456  1.1.2.1  chap 		*key -= 64;
    457  1.1.2.1  chap 		return CTL1;
    458  1.1.2.1  chap 	}	  	/* 70-84 defined, 85-90 undef'd, 91-95 def'd */
    459  1.1.2.1  chap 	return CTL7;	/* 96-101,120- handled above, 102-119 all undef'd */
    460  1.1.2.1  chap 		  	/* treat them all as CTL7 */
    461  1.1.2.1  chap }
    462  1.1.2.1  chap 
    463  1.1.2.1  chap static void
    464  1.1.2.1  chap notify_no_one(void *cookie, midictl_evt evt, uint_fast8_t chan, uint_fast16_t k)
    465  1.1.2.1  chap {
    466  1.1.2.1  chap }
    467  1.1.2.1  chap 
    468  1.1.2.1  chap #undef PN_SET
    469  1.1.2.1  chap #undef C14MSET
    470  1.1.2.1  chap #undef C14LSET
    471  1.1.2.1  chap #undef C7_SET
    472  1.1.2.1  chap #undef C1_SET
    473  1.1.2.1  chap 
    474  1.1.2.1  chap /*
    475  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 :
    476  1.1.2.1  chap  *
    477  1.1.2.1  chap  * MIDI defines a metric plethora of possible controllers, registered
    478  1.1.2.1  chap  * parameters, and nonregistered parameters: a bit more than 32k possible words
    479  1.1.2.1  chap  * to store. The saving grace is that only a handful are likely to appear in
    480  1.1.2.1  chap  * typical MIDI data, and only a handful are likely implemented by or
    481  1.1.2.1  chap  * interesting to a typical client. So the store implementation needs to be
    482  1.1.2.1  chap  * suited to a largish but quite sparse data set.
    483  1.1.2.1  chap  *
    484  1.1.2.1  chap  * For greatest efficiency, this could be implemented over the hash API.
    485  1.1.2.1  chap  * For now, it is implemented over libprop, which is not a perfect fit,
    486  1.1.2.1  chap  * but because that API does so much more than the hash API, this code
    487  1.1.2.1  chap  * has to do less, and simplicity is worth something.
    488  1.1.2.1  chap  *
    489  1.1.2.1  chap  * prop_numbers are uintmax_t's, which are wider than anything we store, and
    490  1.1.2.1  chap  * to reduce waste we want to fill them. The choice is to fill an entry
    491  1.1.2.1  chap  * with values for the same controller across some consecutive channels
    492  1.1.2.1  chap  * (rather than for consecutive controllers on a channel) because very few
    493  1.1.2.1  chap  * controllers are likely to be used, but those that are will probably be used
    494  1.1.2.1  chap  * on more than one channel.
    495  1.1.2.1  chap  */
    496  1.1.2.1  chap 
    497  1.1.2.1  chap #include <prop/proplib.h>
    498  1.1.2.1  chap #include <sys/malloc.h>
    499  1.1.2.1  chap 
    500  1.1.2.1  chap #define KEYSTRSIZE 8
    501  1.1.2.1  chap static void tokeystr(char s[static KEYSTRSIZE],
    502  1.1.2.1  chap                      class c, uint_fast8_t chan, uint_fast16_t key);
    503  1.1.2.1  chap 
    504  1.1.2.1  chap static uint_fast8_t const packing[] = {
    505  1.1.2.1  chap 	[CTL1 ] = 4*sizeof(uintmax_t)/sizeof(uint8_t),
    506  1.1.2.1  chap 	[CTL7 ] =   sizeof(uintmax_t)/sizeof(uint8_t),
    507  1.1.2.1  chap 	[CTL14] =   sizeof(uintmax_t)/sizeof(uint16_t),
    508  1.1.2.1  chap 	[RPN  ] =   sizeof(uintmax_t)/sizeof(uint16_t),
    509  1.1.2.1  chap 	[NRPN ] =   sizeof(uintmax_t)/sizeof(uint16_t)
    510  1.1.2.1  chap };
    511  1.1.2.1  chap 
    512  1.1.2.1  chap struct bucket {
    513  1.1.2.1  chap 	union {
    514  1.1.2.1  chap 		uintmax_t val;
    515  1.1.2.1  chap 		uint8_t   c7[sizeof(uintmax_t)/sizeof(uint8_t)];
    516  1.1.2.1  chap 		uint16_t c14[sizeof(uintmax_t)/sizeof(uint16_t)];
    517  1.1.2.1  chap 	} __packed un;
    518  1.1.2.1  chap 	midictl_store *ms;
    519  1.1.2.1  chap };
    520  1.1.2.1  chap 
    521  1.1.2.1  chap struct midictl_store {
    522  1.1.2.1  chap 	prop_dictionary_t pd;
    523  1.1.2.1  chap 	bucket bkt; /* assume any one client nonreentrant (for now?) */
    524  1.1.2.1  chap };
    525  1.1.2.1  chap 
    526  1.1.2.1  chap static midictl_store *
    527  1.1.2.1  chap store_init(void)
    528  1.1.2.1  chap {
    529  1.1.2.1  chap 	midictl_store *s;
    530  1.1.2.1  chap 
    531  1.1.2.1  chap 	s = _MIDICTL_MALLOC(sizeof *s, M_DEVBUF);
    532  1.1.2.1  chap 	s->pd = prop_dictionary_create();
    533  1.1.2.1  chap 	s->bkt.ms = s;
    534  1.1.2.1  chap 	return s;
    535  1.1.2.1  chap }
    536  1.1.2.1  chap 
    537  1.1.2.1  chap static void
    538  1.1.2.1  chap store_done(midictl_store *s)
    539  1.1.2.1  chap {
    540  1.1.2.1  chap 	prop_object_release(s->pd);
    541  1.1.2.1  chap 	_MIDICTL_FREE(s, M_DEVBUF);
    542  1.1.2.1  chap }
    543  1.1.2.1  chap 
    544  1.1.2.1  chap static bucket *
    545  1.1.2.1  chap store_locate(midictl_store *s, class c, uint_fast8_t chan, uint_fast16_t key)
    546  1.1.2.1  chap {
    547  1.1.2.1  chap 	char buf[8];
    548  1.1.2.1  chap 	prop_number_t pn;
    549  1.1.2.1  chap 
    550  1.1.2.1  chap 	tokeystr(buf, c, chan, key);
    551  1.1.2.1  chap 	pn = (prop_number_t)prop_dictionary_get(s->pd, buf);
    552  1.1.2.1  chap 	if ( NULL == pn ) {
    553  1.1.2.1  chap 		s->bkt.un.val = 0;
    554  1.1.2.1  chap 		return NULL;
    555  1.1.2.1  chap 	}
    556  1.1.2.1  chap 	s->bkt.un.val = prop_number_integer_value(pn);
    557  1.1.2.1  chap 	return &s->bkt;
    558  1.1.2.1  chap }
    559  1.1.2.1  chap 
    560  1.1.2.1  chap static uint16_t
    561  1.1.2.1  chap store_extract(bucket *b, class c, uint_fast8_t chan, uint_fast16_t key)
    562  1.1.2.1  chap {
    563  1.1.2.1  chap 	chan %= packing[c];
    564  1.1.2.1  chap 	switch ( c ) {
    565  1.1.2.1  chap 	case CTL1:
    566  1.1.2.1  chap 		return 3 & (b->un.c7[chan/4]>>(chan%4)*2);
    567  1.1.2.1  chap 	case CTL7:
    568  1.1.2.1  chap 		return b->un.c7[chan];
    569  1.1.2.1  chap 	case CTL14:
    570  1.1.2.1  chap 	case RPN:
    571  1.1.2.1  chap 	case NRPN:
    572  1.1.2.1  chap 		break;
    573  1.1.2.1  chap 	}
    574  1.1.2.1  chap 	return b->un.c14[chan];
    575  1.1.2.1  chap }
    576  1.1.2.1  chap 
    577  1.1.2.1  chap static void
    578  1.1.2.1  chap store_update(midictl_store *s, bucket *b, class c, uint_fast8_t chan,
    579  1.1.2.1  chap 	     uint_fast16_t key, uint16_t value)
    580  1.1.2.1  chap {
    581  1.1.2.1  chap 	uintmax_t orig;
    582  1.1.2.1  chap 	char buf[KEYSTRSIZE];
    583  1.1.2.1  chap 	prop_number_t pn;
    584  1.1.2.1  chap 	boolean_t success;
    585  1.1.2.1  chap 	uint_fast8_t ent;
    586  1.1.2.1  chap 
    587  1.1.2.1  chap 	if ( NULL == b ) {
    588  1.1.2.1  chap 		b = &s->bkt;
    589  1.1.2.1  chap 		orig = 0;
    590  1.1.2.1  chap 	} else
    591  1.1.2.1  chap 		orig = b->un.val;
    592  1.1.2.1  chap 
    593  1.1.2.1  chap 	ent = chan % packing[c];
    594  1.1.2.1  chap 
    595  1.1.2.1  chap 	switch ( c ) {
    596  1.1.2.1  chap 	case CTL1:
    597  1.1.2.1  chap 		b->un.c7[ent/4] &= ~(3<<(ent%4)*2);
    598  1.1.2.1  chap 		b->un.c7[ent/4] |= (3&value)<<(ent%4)*2;
    599  1.1.2.1  chap 		break;
    600  1.1.2.1  chap 	case CTL7:
    601  1.1.2.1  chap 		b->un.c7[ent] = value;
    602  1.1.2.1  chap 		break;
    603  1.1.2.1  chap 	case CTL14:
    604  1.1.2.1  chap 	case RPN:
    605  1.1.2.1  chap 	case NRPN:
    606  1.1.2.1  chap 		b->un.c14[ent] = value;
    607  1.1.2.1  chap 		break;
    608  1.1.2.1  chap 	}
    609  1.1.2.1  chap 
    610  1.1.2.1  chap 	if ( orig == b->un.val )
    611  1.1.2.1  chap 		return;
    612  1.1.2.1  chap 
    613  1.1.2.1  chap 	tokeystr(buf, c, chan, key);
    614  1.1.2.1  chap 
    615  1.1.2.1  chap 	if ( 0 == b->un.val )
    616  1.1.2.1  chap 		prop_dictionary_remove(s->pd, buf);
    617  1.1.2.1  chap 	else {
    618  1.1.2.1  chap 		pn = prop_number_create_integer(b->un.val);
    619  1.1.2.1  chap 		_MIDICTL_ASSERT(NULL != pn);
    620  1.1.2.1  chap 		success = prop_dictionary_set(s->pd, buf, pn);
    621  1.1.2.1  chap 		_MIDICTL_ASSERT(success);
    622  1.1.2.1  chap 		prop_object_release(pn);
    623  1.1.2.1  chap 	}
    624  1.1.2.1  chap }
    625  1.1.2.1  chap 
    626  1.1.2.1  chap static void
    627  1.1.2.1  chap tokeystr(char s[static KEYSTRSIZE],
    628  1.1.2.1  chap          class c, uint_fast8_t chan, uint_fast16_t key)
    629  1.1.2.1  chap {
    630  1.1.2.1  chap 	snprintf(s, KEYSTRSIZE, "%x%x%x", c, chan/packing[c], key);
    631  1.1.2.1  chap }
    632  1.1.2.1  chap 
    633  1.1.2.1  chap #if defined(_MIDICTL_MAIN)
    634  1.1.2.1  chap void
    635  1.1.2.1  chap dumpstore(void)
    636  1.1.2.1  chap {
    637  1.1.2.1  chap 	char *s = prop_dictionary_externalize(mc.store->pd);
    638  1.1.2.1  chap 	printf("%s", s);
    639  1.1.2.1  chap 	free(s);
    640  1.1.2.1  chap }
    641  1.1.2.1  chap #endif
    642