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