1 1.4 jmcneill /* $NetBSD: midictl.h,v 1.4 2011/11/23 23:07:31 jmcneill Exp $ */ 2 1.2 chap 3 1.2 chap /*- 4 1.4 jmcneill * Copyright (c) 2006, 2008 The NetBSD Foundation, Inc. 5 1.2 chap * All rights reserved. 6 1.2 chap * 7 1.2 chap * This code is derived from software contributed to The NetBSD Foundation 8 1.4 jmcneill * by Chapman Flack and by Andrew Doran. 9 1.2 chap * 10 1.2 chap * Redistribution and use in source and binary forms, with or without 11 1.2 chap * modification, are permitted provided that the following conditions 12 1.2 chap * are met: 13 1.2 chap * 1. Redistributions of source code must retain the above copyright 14 1.2 chap * notice, this list of conditions and the following disclaimer. 15 1.2 chap * 2. Redistributions in binary form must reproduce the above copyright 16 1.2 chap * notice, this list of conditions and the following disclaimer in the 17 1.2 chap * documentation and/or other materials provided with the distribution. 18 1.2 chap * 19 1.2 chap * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.2 chap * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.2 chap * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.2 chap * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.2 chap * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.2 chap * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.2 chap * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.2 chap * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.2 chap * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.2 chap * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.2 chap * POSSIBILITY OF SUCH DAMAGE. 30 1.2 chap */ 31 1.2 chap 32 1.2 chap #ifndef _SYS_DEV_MIDICTL_H_ 33 1.2 chap #define _SYS_DEV_MIDICTL_H_ 34 1.2 chap 35 1.2 chap /* 36 1.2 chap * General support for MIDI controllers, registered parameters, and 37 1.2 chap * nonregistered parameters. It interprets MIDI messages that update 38 1.2 chap * these values, maintains the associated state, and provides an API 39 1.2 chap * for obtaining the current value of any controller or parameter and 40 1.2 chap * tracking changes to it. 41 1.2 chap * 42 1.2 chap * One function provides the interface for message parsing. When a message 43 1.2 chap * is received, once it has been determined to be a MIDI_CTL_CHANGE message, 44 1.2 chap * simply call midictl_change(&mc, chan, ctlval) where chan is the channel 45 1.2 chap * extracted from the first byte, and ctlval points to the remaining two 46 1.2 chap * bytes of the message received. 47 1.2 chap * 48 1.2 chap * The API for reading the state is equally simple. Use 49 1.2 chap * midictl_read(&mc, chan, ctlr, dflt) 50 1.2 chap * midictl_rpn_read(&mc, chan, rpn, dflt) 51 1.2 chap * midictl_nrpn_read(&mc, chan, nrpn, dflt) 52 1.2 chap * to read the current value of controller #ctlr, RP #rpn, or NRP #nrpn, 53 1.2 chap * respectively. (For 14-bit controllers, use the MSB number as ctlr, not 54 1.2 chap * the LSB number.) You get the complete current value; for 14-bit controllers 55 1.2 chap * and parameters you get a single 14-bit integer without fussing about the 56 1.2 chap * multiple MIDI messages needed to set it. If you read a controller or 57 1.2 chap * parameter that no MIDI message has yet written, you get back the value dflt. 58 1.2 chap * If you read one whose MSB or LSB only has been written, you get what you 59 1.2 chap * would get if the value had been dflt before the write. 60 1.2 chap * 61 1.2 chap * The functions may be called from any context but reentrant calls operating 62 1.2 chap * on the same midictl are unsupported, with one exception: calls back into 63 1.2 chap * midictl from a notify handler it has called are permitted. If you are 64 1.2 chap * calling midictl_change in a driver function called by midi(4), you are ok 65 1.2 chap * as midi(4) itself serializes its calls into the driver. For other uses, 66 1.2 chap * avoiding reentrant calls is up to you. 67 1.2 chap * 68 1.2 chap * A strict division of labor limits complexity. This module knows as little 69 1.2 chap * about the meanings of different MIDI parameters and controllers as possible 70 1.2 chap * to do its job: it knows which controllers are overloaded to serve as 71 1.2 chap * channel mode messages, and which are overloaded to provide access to the 72 1.2 chap * RPN and NRPN space. It knows which controllers are 14-bit, 7-bit, or 1-bit 73 1.2 chap * according to the table online at midi.org. (All parameters are treated as 74 1.2 chap * 14-bit.) It does not know or care about the specified default values; 75 1.2 chap * client code is expected to know those defaults for whatever controls it 76 1.2 chap * actually implements, and supply them when calling midictl_*read(). That 77 1.2 chap * avoids the need for a large table of specified values for things most 78 1.2 chap * clients will never read. A header file defining the official defaults could 79 1.2 chap * be useful for clients to include for use when calling midictl_*read, but 80 1.2 chap * is not part of this module. Reset All Controllers is simply handled by 81 1.2 chap * forgetting controllers have been written at all, so the next read by 82 1.2 chap * the client will return the client's supplied default. 83 1.2 chap * 84 1.2 chap * An incoming MIDI stream may refer to many controllers and parameters the 85 1.2 chap * client does not implement. To limit memory use, messages are ignored by 86 1.2 chap * default if they target a controller or parameter the client has never 87 1.2 chap * read. To indicate which controllers/parameters it supports, the client 88 1.2 chap * should simply read them when starting. 89 1.2 chap * 90 1.2 chap * Where the task is to generically process some MIDI stream without losing 91 1.2 chap * data, accept_any_ctl_rpn can be set to 1 in the midictl structure, and 92 1.2 chap * state will be kept for any incoming controller or RPN update. The separate 93 1.2 chap * flag accept_any_nrpn enables the same behavior for nonregistered parameters. 94 1.2 chap * 95 1.2 chap * Whenever a change is made to any value for which state is being kept, the 96 1.2 chap * notify function will be called with MIDICTL_CTLR, MIDICTL_RPN, or 97 1.2 chap * MIDICTL_NRPN, the channel, and the controller, rp, or nrp number, 98 1.2 chap * respectively. The controller number will never refer to the LSB of a 14-bit 99 1.2 chap * controller. The new /value/ is not included; if the change is of interest, 100 1.2 chap * the client reads the value and thereby supplies the default (which can still 101 1.2 chap * be needed if the update is to half of a 14-bit value). The notify function 102 1.2 chap * is also called, with appropriate evt codes, on receipt of channel mode 103 1.2 chap * messages. 104 1.2 chap * 105 1.2 chap * Reset All Controllers: 106 1.2 chap * 107 1.2 chap * The Reset All Controllers message will cause this module to forget settings 108 1.2 chap * for all controllers on the affected channel other than those specifically 109 1.2 chap * excepted by MIDI RP-015. Registered and nonregistered parameters are not 110 1.2 chap * affected. The notify function is then called with evt = MIDICTL_RESET. 111 1.2 chap * 112 1.2 chap * The client's response to MIDICTL_RESET should include reading all 113 1.2 chap * controllers it cares about, to ensure (if the accept_any_ctl_rpn flag is not 114 1.2 chap * set) that they will continue to be tracked. The client must also reset to 115 1.2 chap * defaults the following pieces of channel state that are not managed by this 116 1.2 chap * module, but required by RP-015 to be reset: 117 1.2 chap * Pitch Bend 118 1.2 chap * Channel Pressure 119 1.2 chap * Key Pressure (for all keys on channel) 120 1.2 chap * The client does NOT reset the current Program. 121 1.2 chap */ 122 1.2 chap #include <sys/midiio.h> 123 1.2 chap #include <sys/stdint.h> 124 1.2 chap 125 1.2 chap /* 126 1.2 chap * Events that may be reported via a midictl_notify function. 127 1.2 chap * Enum starts at 1<<16 so that enum|key can be used as a switch expression. 128 1.2 chap * Key is 0 except where shown below. 129 1.2 chap */ 130 1.2 chap typedef enum { 131 1.2 chap MIDICTL_CTLR = 1<<16, /* key=ctlr */ 132 1.2 chap MIDICTL_RPN = 2<<16, /* key=rpn */ 133 1.2 chap MIDICTL_NRPN = 3<<16, /* key=nrpn */ 134 1.2 chap MIDICTL_RESET = 4<<16, /* Reset All Controllers received */ 135 1.2 chap MIDICTL_NOTES_OFF = 5<<16, /* All Notes Off received */ 136 1.2 chap MIDICTL_SOUND_OFF = 6<<16, /* All Sound Off received */ 137 1.2 chap MIDICTL_LOCAL = 7<<16, /* if (key) localIsOn else localIsOff */ 138 1.2 chap MIDICTL_MODE = 8<<16 /* key=mode(1-4)? TBD unimplemented */ 139 1.2 chap } midictl_evt; 140 1.2 chap 141 1.2 chap /* 142 1.2 chap * midictl_notify(void *cookie, midictl_evt evt, 143 1.2 chap * uint_fast8_t chan, uint_fast16_t key) 144 1.2 chap */ 145 1.2 chap typedef void 146 1.2 chap midictl_notify(void *, midictl_evt, uint_fast8_t, uint_fast16_t); 147 1.2 chap 148 1.2 chap typedef struct midictl_store midictl_store; 149 1.2 chap 150 1.2 chap typedef struct { 151 1.2 chap uint_fast8_t accept_any_ctl_rpn:1; /* 0 ==> ignore chgs for unqueried */ 152 1.2 chap uint_fast8_t accept_any_nrpn:1; /* likewise for NRPNs */ 153 1.2 chap uint_fast8_t base_channel; /* set >= 16 to ignore any MODE messages */ 154 1.2 chap void *cookie; /* this value will be passed to notify */ 155 1.2 chap midictl_notify *notify; 156 1.2 chap /* */ 157 1.2 chap uint16_t rpn; 158 1.2 chap uint16_t nrpn; 159 1.2 chap midictl_store *store; 160 1.4 jmcneill kmutex_t *lock; 161 1.2 chap } midictl; 162 1.2 chap 163 1.4 jmcneill extern int 164 1.2 chap midictl_open(midictl *); 165 1.2 chap 166 1.2 chap extern void 167 1.2 chap midictl_close(midictl *); 168 1.2 chap 169 1.2 chap /* 170 1.2 chap * Called on receipt of a Control Change message. Updates the controller, 171 1.2 chap * RPN, or NRPN value as appropriate. When updating a controller or RPN that 172 1.2 chap * is defined in the spec as boolean, all values that by definition represent 173 1.2 chap * false are coerced to zero. Fires the callback if a value of interest has 174 1.2 chap * been changed. 175 1.2 chap * ctlval: points to the second byte of the message (therefore, to a two- 176 1.2 chap * byte array: controller number and value). 177 1.2 chap * midictl_change(midictl *mc, uint_fast8_t chan, uint8_t *ctlval); 178 1.2 chap */ 179 1.2 chap extern void 180 1.2 chap midictl_change(midictl *, uint_fast8_t, uint8_t *); 181 1.2 chap 182 1.2 chap /* 183 1.2 chap * Read the current value of controller ctlr for channel chan. 184 1.2 chap * If this is the first time querying this controller on this channel, 185 1.2 chap * and accept_any_ctl_rpn is false, any earlier change message for it 186 1.2 chap * will have been ignored, so it will be given the value dflt, which is 187 1.2 chap * also returned, and future change messages for it will take effect. 188 1.2 chap * If the controller has a two-byte value and only one has been explicitly set 189 1.2 chap * at the time of the first query, the effect is as if the value had been 190 1.2 chap * first set to dflt, then the explicitly-set byte updated. 191 1.2 chap * midictl_read(midictl *mc, uint_fast8_t chan, 192 1.2 chap * uint_fast8_t ctlr, uint_fast16_t dflt); 193 1.2 chap */ 194 1.2 chap extern uint_fast16_t 195 1.2 chap midictl_read(midictl *, uint_fast8_t, uint_fast8_t, uint_fast16_t); 196 1.2 chap 197 1.2 chap /* 198 1.2 chap * As for midictl_read, but for registered parameters or nonregistered 199 1.2 chap * parameters, respectively. 200 1.2 chap */ 201 1.2 chap extern uint_fast16_t 202 1.2 chap midictl_rpn_read(midictl *mc, uint_fast8_t, uint_fast16_t, uint_fast16_t); 203 1.2 chap extern uint_fast16_t 204 1.2 chap midictl_nrpn_read(midictl *mc, uint_fast8_t, uint_fast16_t, uint_fast16_t); 205 1.2 chap 206 1.2 chap #endif /* _SYS_DEV_MIDICTL_H_ */ 207