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