• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#import "MIDIMessage.h"
18
19const uint8_t kMIDINoChannel = -1;
20
21MIDIMessageType MIDIMessageTypeFromStatus(MIDIByte status) {
22  if (status < MIDIMessageSysEx) {
23    return (status & 0xF0) >> 4;
24  } else {
25    return status;
26  }
27}
28
29MIDIChannel MIDIChannelFromStatus(MIDIByte status) {
30  if (status < MIDIMessageSysEx) {
31    return (status & 0x0F) + 1;
32  } else {
33    return -1;
34  }
35}
36
37NSData *MIDIMessageBody(NSData *message) {
38  if (message.length == 0) {
39    return nil;
40  }
41
42  const MIDIByte *bytes = (const MIDIByte *)message.bytes;
43
44  // Slice off any header/trailer bytes.
45  if (MIDIMessageTypeFromStatus(bytes[0]) == MIDIMessageSysEx) {
46    NSCAssert(bytes[message.length - 1] == MIDIMessageSysExEnd, @"SysEx message without trailer.");
47    return [message subdataWithRange:NSMakeRange(1, message.length - 2)];
48  } else {
49    return [message subdataWithRange:NSMakeRange(1, message.length - 1)];
50  }
51}
52
53MIDIByte MIDIStatusByte(MIDIMessageType type, MIDIChannel channel) {
54  if (type >= MIDIMessageSysEx) {
55    return type;
56  } else {
57    return (type << 4) | (channel - 1);
58  }
59}
60
61NSData *MIDIChannelMessageCreate(MIDIMessageType type, MIDIChannel channel, NSData *body) {
62  NSMutableData *message =
63      [[NSMutableData alloc] initWithCapacity:body.length + 2];  // +2 for status and SysEx trailer
64
65  const MIDIByte status = MIDIStatusByte(type, channel);
66  [message appendBytes:&status length:1];
67  [message appendData:body];
68
69  if (type == MIDIMessageSysEx) {
70    const MIDIByte trailer = MIDIMessageSysEx;
71    [message appendBytes:&trailer length:1];
72  }
73
74  return message;
75}
76
77NSData *MIDIMessageCreateSimple1(MIDIMessageType type, MIDIChannel channel, MIDIByte first) {
78  NSCAssert(type != MIDIMessageSysEx, @"MIDIMessageCreateSimple1 cannot create SysEx messages.");
79
80  NSMutableData *message = [[NSMutableData alloc] initWithCapacity:2];  // Status + Data
81
82  const MIDIByte status = MIDIStatusByte(type, channel);
83  [message appendBytes:&status length:1];
84  [message appendBytes:&first length:1];
85
86  return message;
87}
88
89NSData *MIDIMessageCreateSimple2(MIDIMessageType type,
90                                 MIDIChannel channel,
91                                 MIDIByte first,
92                                 MIDIByte second) {
93  NSCAssert(type != MIDIMessageSysEx, @"MIDIMessageCreateSimple2 cannot create SysEx messages.");
94
95  NSMutableData *message = [[NSMutableData alloc] initWithCapacity:3];  // Status + Data + Data
96
97  const MIDIByte status = MIDIStatusByte(type, channel);
98  [message appendBytes:&status length:1];
99  [message appendBytes:&first length:1];
100  [message appendBytes:&second length:1];
101
102  return message;
103}
104