213 lines
10 KiB
C
213 lines
10 KiB
C
|
/* $NetBSD: midictl.h,v 1.2 2006/06/30 13:56:25 chap Exp $ */
|
||
|
|
||
|
/*-
|
||
|
* Copyright (c) 2006 The NetBSD Foundation, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||
|
* by Chapman Flack.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions
|
||
|
* are met:
|
||
|
* 1. Redistributions of source code must retain the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||
|
* notice, this list of conditions and the following disclaimer in the
|
||
|
* documentation and/or other materials provided with the distribution.
|
||
|
* 3. All advertising materials mentioning features or use of this software
|
||
|
* must display the following acknowledgement:
|
||
|
* This product includes software developed by the NetBSD
|
||
|
* Foundation, Inc. and its contributors.
|
||
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||
|
* contributors may be used to endorse or promote products derived
|
||
|
* from this software without specific prior written permission.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#ifndef _SYS_DEV_MIDICTL_H_
|
||
|
#define _SYS_DEV_MIDICTL_H_
|
||
|
|
||
|
/*
|
||
|
* General support for MIDI controllers, registered parameters, and
|
||
|
* nonregistered parameters. It interprets MIDI messages that update
|
||
|
* these values, maintains the associated state, and provides an API
|
||
|
* for obtaining the current value of any controller or parameter and
|
||
|
* tracking changes to it.
|
||
|
*
|
||
|
* One function provides the interface for message parsing. When a message
|
||
|
* is received, once it has been determined to be a MIDI_CTL_CHANGE message,
|
||
|
* simply call midictl_change(&mc, chan, ctlval) where chan is the channel
|
||
|
* extracted from the first byte, and ctlval points to the remaining two
|
||
|
* bytes of the message received.
|
||
|
*
|
||
|
* The API for reading the state is equally simple. Use
|
||
|
* midictl_read(&mc, chan, ctlr, dflt)
|
||
|
* midictl_rpn_read(&mc, chan, rpn, dflt)
|
||
|
* midictl_nrpn_read(&mc, chan, nrpn, dflt)
|
||
|
* to read the current value of controller #ctlr, RP #rpn, or NRP #nrpn,
|
||
|
* respectively. (For 14-bit controllers, use the MSB number as ctlr, not
|
||
|
* the LSB number.) You get the complete current value; for 14-bit controllers
|
||
|
* and parameters you get a single 14-bit integer without fussing about the
|
||
|
* multiple MIDI messages needed to set it. If you read a controller or
|
||
|
* parameter that no MIDI message has yet written, you get back the value dflt.
|
||
|
* If you read one whose MSB or LSB only has been written, you get what you
|
||
|
* would get if the value had been dflt before the write.
|
||
|
*
|
||
|
* The functions may be called from any context but reentrant calls operating
|
||
|
* on the same midictl are unsupported, with one exception: calls back into
|
||
|
* midictl from a notify handler it has called are permitted. If you are
|
||
|
* calling midictl_change in a driver function called by midi(4), you are ok
|
||
|
* as midi(4) itself serializes its calls into the driver. For other uses,
|
||
|
* avoiding reentrant calls is up to you.
|
||
|
*
|
||
|
* A strict division of labor limits complexity. This module knows as little
|
||
|
* about the meanings of different MIDI parameters and controllers as possible
|
||
|
* to do its job: it knows which controllers are overloaded to serve as
|
||
|
* channel mode messages, and which are overloaded to provide access to the
|
||
|
* RPN and NRPN space. It knows which controllers are 14-bit, 7-bit, or 1-bit
|
||
|
* according to the table online at midi.org. (All parameters are treated as
|
||
|
* 14-bit.) It does not know or care about the specified default values;
|
||
|
* client code is expected to know those defaults for whatever controls it
|
||
|
* actually implements, and supply them when calling midictl_*read(). That
|
||
|
* avoids the need for a large table of specified values for things most
|
||
|
* clients will never read. A header file defining the official defaults could
|
||
|
* be useful for clients to include for use when calling midictl_*read, but
|
||
|
* is not part of this module. Reset All Controllers is simply handled by
|
||
|
* forgetting controllers have been written at all, so the next read by
|
||
|
* the client will return the client's supplied default.
|
||
|
*
|
||
|
* An incoming MIDI stream may refer to many controllers and parameters the
|
||
|
* client does not implement. To limit memory use, messages are ignored by
|
||
|
* default if they target a controller or parameter the client has never
|
||
|
* read. To indicate which controllers/parameters it supports, the client
|
||
|
* should simply read them when starting.
|
||
|
*
|
||
|
* Where the task is to generically process some MIDI stream without losing
|
||
|
* data, accept_any_ctl_rpn can be set to 1 in the midictl structure, and
|
||
|
* state will be kept for any incoming controller or RPN update. The separate
|
||
|
* flag accept_any_nrpn enables the same behavior for nonregistered parameters.
|
||
|
*
|
||
|
* Whenever a change is made to any value for which state is being kept, the
|
||
|
* notify function will be called with MIDICTL_CTLR, MIDICTL_RPN, or
|
||
|
* MIDICTL_NRPN, the channel, and the controller, rp, or nrp number,
|
||
|
* respectively. The controller number will never refer to the LSB of a 14-bit
|
||
|
* controller. The new /value/ is not included; if the change is of interest,
|
||
|
* the client reads the value and thereby supplies the default (which can still
|
||
|
* be needed if the update is to half of a 14-bit value). The notify function
|
||
|
* is also called, with appropriate evt codes, on receipt of channel mode
|
||
|
* messages.
|
||
|
*
|
||
|
* Reset All Controllers:
|
||
|
*
|
||
|
* The Reset All Controllers message will cause this module to forget settings
|
||
|
* for all controllers on the affected channel other than those specifically
|
||
|
* excepted by MIDI RP-015. Registered and nonregistered parameters are not
|
||
|
* affected. The notify function is then called with evt = MIDICTL_RESET.
|
||
|
*
|
||
|
* The client's response to MIDICTL_RESET should include reading all
|
||
|
* controllers it cares about, to ensure (if the accept_any_ctl_rpn flag is not
|
||
|
* set) that they will continue to be tracked. The client must also reset to
|
||
|
* defaults the following pieces of channel state that are not managed by this
|
||
|
* module, but required by RP-015 to be reset:
|
||
|
* Pitch Bend
|
||
|
* Channel Pressure
|
||
|
* Key Pressure (for all keys on channel)
|
||
|
* The client does NOT reset the current Program.
|
||
|
*/
|
||
|
#include <sys/midiio.h>
|
||
|
#include <sys/stdint.h>
|
||
|
|
||
|
/*
|
||
|
* Events that may be reported via a midictl_notify function.
|
||
|
* Enum starts at 1<<16 so that enum|key can be used as a switch expression.
|
||
|
* Key is 0 except where shown below.
|
||
|
*/
|
||
|
typedef enum {
|
||
|
MIDICTL_CTLR = 1<<16, /* key=ctlr */
|
||
|
MIDICTL_RPN = 2<<16, /* key=rpn */
|
||
|
MIDICTL_NRPN = 3<<16, /* key=nrpn */
|
||
|
MIDICTL_RESET = 4<<16, /* Reset All Controllers received */
|
||
|
MIDICTL_NOTES_OFF = 5<<16, /* All Notes Off received */
|
||
|
MIDICTL_SOUND_OFF = 6<<16, /* All Sound Off received */
|
||
|
MIDICTL_LOCAL = 7<<16, /* if (key) localIsOn else localIsOff */
|
||
|
MIDICTL_MODE = 8<<16 /* key=mode(1-4)? TBD unimplemented */
|
||
|
} midictl_evt;
|
||
|
|
||
|
/*
|
||
|
* midictl_notify(void *cookie, midictl_evt evt,
|
||
|
* uint_fast8_t chan, uint_fast16_t key)
|
||
|
*/
|
||
|
typedef void
|
||
|
midictl_notify(void *, midictl_evt, uint_fast8_t, uint_fast16_t);
|
||
|
|
||
|
typedef struct midictl_store midictl_store;
|
||
|
|
||
|
typedef struct {
|
||
|
uint_fast8_t accept_any_ctl_rpn:1; /* 0 ==> ignore chgs for unqueried */
|
||
|
uint_fast8_t accept_any_nrpn:1; /* likewise for NRPNs */
|
||
|
uint_fast8_t base_channel; /* set >= 16 to ignore any MODE messages */
|
||
|
void *cookie; /* this value will be passed to notify */
|
||
|
midictl_notify *notify;
|
||
|
/* */
|
||
|
uint16_t rpn;
|
||
|
uint16_t nrpn;
|
||
|
midictl_store *store;
|
||
|
} midictl;
|
||
|
|
||
|
extern void
|
||
|
midictl_open(midictl *);
|
||
|
|
||
|
extern void
|
||
|
midictl_close(midictl *);
|
||
|
|
||
|
/*
|
||
|
* Called on receipt of a Control Change message. Updates the controller,
|
||
|
* RPN, or NRPN value as appropriate. When updating a controller or RPN that
|
||
|
* is defined in the spec as boolean, all values that by definition represent
|
||
|
* false are coerced to zero. Fires the callback if a value of interest has
|
||
|
* been changed.
|
||
|
* ctlval: points to the second byte of the message (therefore, to a two-
|
||
|
* byte array: controller number and value).
|
||
|
* midictl_change(midictl *mc, uint_fast8_t chan, uint8_t *ctlval);
|
||
|
*/
|
||
|
extern void
|
||
|
midictl_change(midictl *, uint_fast8_t, uint8_t *);
|
||
|
|
||
|
/*
|
||
|
* Read the current value of controller ctlr for channel chan.
|
||
|
* If this is the first time querying this controller on this channel,
|
||
|
* and accept_any_ctl_rpn is false, any earlier change message for it
|
||
|
* will have been ignored, so it will be given the value dflt, which is
|
||
|
* also returned, and future change messages for it will take effect.
|
||
|
* If the controller has a two-byte value and only one has been explicitly set
|
||
|
* at the time of the first query, the effect is as if the value had been
|
||
|
* first set to dflt, then the explicitly-set byte updated.
|
||
|
* midictl_read(midictl *mc, uint_fast8_t chan,
|
||
|
* uint_fast8_t ctlr, uint_fast16_t dflt);
|
||
|
*/
|
||
|
extern uint_fast16_t
|
||
|
midictl_read(midictl *, uint_fast8_t, uint_fast8_t, uint_fast16_t);
|
||
|
|
||
|
/*
|
||
|
* As for midictl_read, but for registered parameters or nonregistered
|
||
|
* parameters, respectively.
|
||
|
*/
|
||
|
extern uint_fast16_t
|
||
|
midictl_rpn_read(midictl *mc, uint_fast8_t, uint_fast16_t, uint_fast16_t);
|
||
|
extern uint_fast16_t
|
||
|
midictl_nrpn_read(midictl *mc, uint_fast8_t, uint_fast16_t, uint_fast16_t);
|
||
|
|
||
|
#endif /* _SYS_DEV_MIDICTL_H_ */
|