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