forked from mixedinkey-opensource/MIKMIDI
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MIKMIDICommand.h
259 lines (230 loc) · 11.3 KB
/
MIKMIDICommand.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//
// MIKMIDICommand.h
// MIDI Testbed
//
// Created by Andrew Madsen on 3/7/13.
// Copyright (c) 2013 Mixed In Key. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreMIDI/CoreMIDI.h>
#import "MIKMIDICompilerCompatibility.h"
/**
* Types of MIDI messages. These values correspond directly to the MIDI command type values
* found in MIDI message data.
*
* @note Not all of these MIDI message types are currently explicitly supported by MIKMIDI.
*/
typedef NS_ENUM(NSUInteger, MIKMIDICommandType) {
/** Note off command. */
MIKMIDICommandTypeNoteOff = 0x8f,
/** Note on command. */
MIKMIDICommandTypeNoteOn = 0x9f,
/** Polyphonic key pressure command. */
MIKMIDICommandTypePolyphonicKeyPressure = 0xaf,
/** Control change command. This is the most common command sent by MIDI controllers. */
MIKMIDICommandTypeControlChange = 0xbf,
/** Program change command. */
MIKMIDICommandTypeProgramChange = 0xcf,
/** Channel pressure command. */
MIKMIDICommandTypeChannelPressure = 0xdf,
/** Pitch wheel change command. */
MIKMIDICommandTypePitchWheelChange = 0xef,
/** System message command. */
MIKMIDICommandTypeSystemMessage = 0xff,
/** System message command. */
MIKMIDICommandTypeSystemExclusive = 0xf0,
/** System exclusive (SysEx) command. */
MIKMIDICommandTypeSystemTimecodeQuarterFrame = 0xf1,
/** System song position pointer command. */
MIKMIDICommandTypeSystemSongPositionPointer = 0xf2,
/** System song select command. */
MIKMIDICommandTypeSystemSongSelect = 0xf3,
/** System tune request command. */
MIKMIDICommandTypeSystemTuneRequest = 0xf6,
/** System timing clock command. */
MIKMIDICommandTypeSystemTimingClock = 0xf8,
/** System timing clock command. */
MIKMIDICommandTypeSystemStartSequence = 0xfa,
/** System start sequence command. */
MIKMIDICommandTypeSystemContinueSequence = 0xfb,
/** System continue sequence command. */
MIKMIDICommandTypeSystemStopSequence = 0xfc,
/** System keep alive message. */
MIKMIDICommandTypeSystemKeepAlive = 0xfe,
};
@class MIKMIDIMappingItem;
NS_ASSUME_NONNULL_BEGIN
/**
* In MIKMIDI, MIDI messages are objects. Specifically, they are instances of MIKMIDICommand or one of its
* subclasses. MIKMIDICommand's subclasses each represent a specific type of MIDI message, for example,
* control change command messages will be instances of MIKMIDIControlChangeCommand.
* MIKMIDICommand includes properties for getting information and data common to all MIDI messages.
* Its subclasses implement additional method and properties specific to messages of their associated type.
*
* MIKMIDICommand is also available in mutable variants, most useful for creating commands to be sent out
* by your application.
*
* To create a new command, typically, you should use +commandForCommandType:.
*
* Subclass MIKMIDICommand
* -----------------------
*
* Support for the various MIDI message types is provided by type-specific subclasses of MIKMIDICommand.
* For example, Control Change messages are represented using MIKMIDIControlChangeCommand. MIKMIDI
* includes a limited number of MIKMIDICommand subclasses to support the most common MIDI message types.
* To support a new command type, you should create a new subclass of MIKMIDICommand (and please consider
* contributing it to the main MIKMIDI repository!). If you implement this subclass according to the rules
* explained below, it will automatically be used to represent incoming MIDI commands matching its MIDI command type.
*
* To successfully subclass MIKMIDICommand, you *must* override at least the following methods:
*
* - `+supportedMIDICommandTypes:` - Return an array of one or more MIKMIDICommandTypes that your subclass supports.
* - `+immutableCounterPartClass` - Return the subclass itself (eg. `return [MIKMIDINewTypeCommand class];`)
* - `+mutableCounterPartClass` - Return the mutable counterpart class (eg. `return [MIKMIDIMutableNewTypeCommand class;]`)
*
* Optionally, override `-additionalCommandDescription` to provide an additional, type-specific description string.
*
* You must also implement `+load` and call `[MIKMIDICommand registerSubclass:self]` to register your subclass with
* the MIKMIDICommand machinery.
*
* When creating a subclass of MIKMIDICommand, you should also create a mutable variant which is itself
* a subclass of your type-specific MIKMIDICommand subclass. The mutable subclass should override `+isMutable`
* and return YES.
*
* If your subclass adds additional properties, beyond those supported by MIKMIDICommand itself, those properties
* should only be settable on instances of the mutable variant class. The preferred way to accomplish this is to
* implement the setters on the *immutable*, base subclass. In their implementations, check to see if self is
* mutable, and if not, raise an exception. Use the following line of code:
*
* if (![[self class] isMutable]) return MIKMIDI_RAISE_MUTATION_ATTEMPT_EXCEPTION;
*
* For a straightforward example of a MIKMIDICommand subclass, see MIKMIDINoteOnCommand.
*
*/
@interface MIKMIDICommand : NSObject <NSCopying>
/**
* Convenience method for creating a new MIKMIDICommand instance from a MIDIPacket as received or created
* using CoreMIDI functions. For command types for which there is a specific MIKMIDICommand subclass,
* an instance of the appropriate subclass will be returned.
*
* @note This method is used by MIKMIDI's internal machinery, and its use by MIKMIDI
* clients, while not disallowed, is not typical. Normally, +commandForCommandType: should be used.
*
* @param packet A pointer to an MIDIPacket struct.
*
* @return For supported command types, an initialized MIKMIDICommand subclass. Otherwise, an instance
* of MIKMIDICommand itself. nil if there is an error.
*
* @see +commandForCommandType:
*/
+ (instancetype)commandWithMIDIPacket:(MIDIPacket *)packet;
/**
* Convenience method for creating a new MIKMIDICommand instance from a MIDIPacket as received or created
* using CoreMIDI functions. For command types for which there is a specific MIKMIDICommand subclass,
* an instance of the appropriate subclass will be returned.
*
* @note This method is used by MIKMIDI's internal machinery, and its use by MIKMIDI
* clients, while not disallowed, is not typical. Normally, +commandForCommandType: should be used.
*
* @param packet A pointer to an MIDIPacket struct.
*
* @return An NSArray containing initialized MIKMIDICommand subclass instances for each MIDI
* message of a supported command type. For unsupported command types, an instance of
* MIKMIDICommand itself will be used. Returns nil if there is an error.
*
* @see +commandForCommandType:
*/
+ (MIKArrayOf(MIKMIDICommand *) *)commandsWithMIDIPacket:(MIDIPacket *)packet;
+ (BOOL)onlyOneSingleCommandInPacket:(MIDIPacket *)inputPacket;
+ (MIKMIDICommand *)extractSingleCommandWithMIDIPacket:(MIDIPacket *)inputPacket;
/**
* Convenience method for creating a new MIKMIDICommand. For command types for which there is a
* specific MIKMIDICommand subclass, an instance of the appropriate subclass will be returned.
*
* @param commandType The type of MIDI command to create. See MIKMIDICommandType for a list
* of possible values.
*
* @return For supported command types, an initialized MIKMIDICommand subclass. Otherwise, an instance
* of MIKMIDICommand itself. nil if there is an error.
*/
+ (instancetype)commandForCommandType:(MIKMIDICommandType)commandType; // Most useful for mutable commands
/**
* The time at which the MIDI message was received. Will be set for commands received from a connected MIDI source. For commands
* to be sent (ie. created by the MIKMIDI-using application), this must be set manually.
*/
@property (nonatomic, strong, readonly) NSDate *timestamp;
/**
* The receiver's command type. See MIKMIDICommandType for a list of possible values.
*/
@property (nonatomic, readonly) MIKMIDICommandType commandType;
/**
* The MIDI status byte. The exact meaning of the contents
* of this byte differ for different command types. See
* http://www.midi.org/techspecs/midimessages.php for a information
* about the contents of this value.
*/
@property (nonatomic, readonly) UInt8 statusByte;
/**
* The first byte of the MIDI data (after the command type).
*/
@property (nonatomic, readonly) UInt8 dataByte1;
/**
* The second byte of the MIDI data (after the command type).
*/
@property (nonatomic, readonly) UInt8 dataByte2;
/**
* The timestamp for the receiver, expressed as a host clock time. This is the timestamp
* used by CoreMIDI. Usually the timestamp property, which returns an NSDate, will be more useful.
*
* @see -timestamp
*/
@property (nonatomic, readonly) MIDITimeStamp midiTimestamp;
/**
* The raw data that makes up the receiver.
*/
@property (nonatomic, copy, readonly, null_resettable) NSData *data;
/**
* Optional mapping item used to route the command. This must be set by client code that handles
* receiving MIDI commands. Allows responders to understand how a command was mapped, especially
* useful to determine interaction type so that responders can interpret the command correctly.
*/
@property (nonatomic, strong, nullable) MIKMIDIMappingItem *mappingItem;
@end
/**
* Mutable subclass of MIKMIDICommand. All MIKMIDICommand subclasses have mutable variants.
*/
@interface MIKMutableMIDICommand : MIKMIDICommand
@property (nonatomic, strong, readwrite) NSDate *timestamp;
@property (nonatomic, readwrite) MIKMIDICommandType commandType;
@property (nonatomic, readwrite) UInt8 dataByte1;
@property (nonatomic, readwrite) UInt8 dataByte2;
@property (nonatomic, readwrite) MIDITimeStamp midiTimestamp;
@property (nonatomic, copy, readwrite) NSData *data;
@end
/**
* Allocates and returns (by reference) a CoreMIDI MIDIPacketList created from an array of MIKMIDICommand instances.
* The created MIDIPacketList will be sized according to the number of commands and their contents. Ownership is
* transfered to the caller which becomes responsible for freeing the allocated memory.
* Used by MIKMIDI when sending commands. Typically, this is not needed by clients of MIKMIDI.
*
* @param outPacketList A pointer to a pointer to a MIDIPacketList structure which will point to the created MIDIPacketList
* upon success.
* @param commands An array of MIKMIDICommand instances.
*
* @return YES if creating the packet list was successful, NO if an error occurred.
*/
BOOL MIKCreateMIDIPacketListFromCommands(MIDIPacketList * _Nonnull * _Nonnull outPacketList, MIKArrayOf(MIKMIDICommand *) *commands);
/**
* Allocates and returns (by reference) a CoreMIDI MIDIPacketList created from a single MIKMIDICommand instance.
* The created MIDIPacketList will be sized according to the command and its contents. Ownership is
* transfered to the caller which becomes responsible for freeing the allocated memory.
* Used by MIKMIDI when sending commands. Typically, this is not needed by clients of MIKMIDI.
*
* @param outPacketList A pointer to a pointer to a MIDIPacketList structure which will point to the created MIDIPacketList
* upon success.
* @param commands One MIKMIDICommand instance.
*
* @return YES if creating the packet list was successful, NO if an error occurred.
*/
BOOL MIKCreateMIDIPacketListFromCommand(MIDIPacketList * _Nonnull * _Nonnull outPacketList, MIKMIDICommand *command);
NS_ASSUME_NONNULL_END