1 /* 2 * Copyright 2003-2009, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Matthijs Hollemans 7 * Christian Packmann 8 * Jerome Leveque 9 * Philippe Houdoin 10 * Pete Goodeve 11 */ 12 13 14 #include "PortDrivers.h" 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <unistd.h> 19 #include <errno.h> 20 21 #include <String.h> 22 23 MidiPortConsumer::MidiPortConsumer(int fd, const char* name) 24 : BMidiLocalConsumer(name), 25 fFileDescriptor(fd) 26 { 27 } 28 29 30 void 31 MidiPortConsumer::Data(uchar* data, size_t length, 32 bool atomic, bigtime_t time) 33 { 34 snooze_until(time - Latency(), B_SYSTEM_TIMEBASE); 35 36 if (write(fFileDescriptor, data, length) == -1) { 37 perror("Error sending data to driver"); 38 } 39 } 40 41 42 // #pragma mark - 43 44 45 MidiPortProducer::MidiPortProducer(int fd, const char *name) 46 : BMidiLocalProducer(name), 47 fFileDescriptor(fd), fKeepRunning(true) 48 49 { 50 BString tmp = name; 51 tmp << " reader"; 52 53 fReaderThread = spawn_thread( 54 _ReaderThread, tmp.String(), B_URGENT_PRIORITY, this); 55 56 resume_thread(fReaderThread); 57 } 58 59 60 MidiPortProducer::~MidiPortProducer() 61 { 62 fKeepRunning = false; 63 64 status_t dummy; 65 wait_for_thread(fReaderThread, &dummy); 66 } 67 68 69 int32 70 MidiPortProducer::_ReaderThread(void* data) 71 { 72 return ((MidiPortProducer*) data)->GetData(); 73 } 74 75 76 int32 77 MidiPortProducer::GetData() 78 { 79 uint8 msgBuf[3]; 80 uint8* sysexBuf = NULL; 81 82 uint8* msgPtr = NULL; 83 size_t msgSize = 0; 84 size_t needed = 0; 85 uint8 runningStatus = 0; 86 87 bool haveSysEx = false; 88 size_t sysexAlloc = 0; 89 size_t sysexSize = 0; 90 91 uint8 next = 0; 92 93 while (fKeepRunning) { 94 if (read(fFileDescriptor, &next, 1) != 1) { 95 if (errno == B_CANCELED) 96 fKeepRunning = false; 97 else 98 perror("Error reading data from driver"); 99 break; 100 } 101 102 bigtime_t timestamp = system_time(); 103 104 if (haveSysEx) { 105 // System Exclusive mode 106 if (next < 0x80) { 107 // System Exclusive data byte 108 sysexBuf[sysexSize++] = next; 109 if (sysexSize == sysexAlloc) { 110 sysexAlloc *= 2; 111 sysexBuf = (uint8*) realloc(sysexBuf, sysexAlloc); 112 } 113 continue; 114 } else if ((next & 0xF8) == 0xF8) { 115 // System Realtime interleaved in System Exclusive sequence 116 SpraySystemRealTime(next, timestamp); 117 continue; 118 } else { 119 // Whatever byte, this one ends the running SysEx sequence 120 SpraySystemExclusive(sysexBuf, sysexSize, timestamp); 121 haveSysEx = false; 122 if (next == B_SYS_EX_END) { 123 // swallow SysEx end byte 124 continue; 125 } 126 // any other byte, while ending the SysEx sequence, 127 // should be handled, not dropped 128 } 129 } 130 131 if ((next & 0xF8) == 0xF8) { 132 // System Realtime 133 SpraySystemRealTime(next, timestamp); 134 } else if ((next & 0xF0) == 0xF0) { 135 // System Common 136 runningStatus = 0; 137 msgBuf[0] = next; 138 msgPtr = msgBuf + 1; 139 switch (next) { 140 case B_SYS_EX_START: 141 sysexAlloc = 4096; 142 sysexBuf = (uint8*) malloc(sysexAlloc); 143 sysexSize = 0; 144 haveSysEx = true; 145 break; 146 147 case B_SONG_POSITION: 148 needed = 2; 149 msgSize = 3; 150 break; 151 152 case B_MIDI_TIME_CODE: 153 case B_SONG_SELECT: 154 case B_CABLE_MESSAGE: 155 needed = 1; 156 msgSize = 2; 157 break; 158 159 case B_SYS_EX_END: 160 // Unpaired with B_SYS_EX_START, but pass it anyway... 161 case B_TUNE_REQUEST: 162 SpraySystemCommon(next, 0, 0, timestamp); 163 break; 164 } 165 } else if ((next & 0x80) == 0x80) { 166 // Voice message 167 runningStatus = next; 168 msgBuf[0] = next; 169 msgPtr = msgBuf + 1; 170 switch (next & 0xF0) { 171 case B_NOTE_OFF: 172 case B_NOTE_ON: 173 case B_KEY_PRESSURE: 174 case B_CONTROL_CHANGE: 175 case B_PITCH_BEND: 176 needed = 2; 177 msgSize = 3; 178 break; 179 180 case B_PROGRAM_CHANGE: 181 case B_CHANNEL_PRESSURE: 182 needed = 1; 183 msgSize = 2; 184 break; 185 } 186 } else if (needed > 0) { 187 // Data bytes to complete message 188 *msgPtr++ = next; 189 if (--needed == 0) { 190 switch (msgBuf[0] & 0xF0) { 191 case B_NOTE_OFF: 192 SprayNoteOff(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], 193 timestamp); 194 break; 195 196 case B_NOTE_ON: 197 SprayNoteOn(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], 198 timestamp); 199 break; 200 201 case B_KEY_PRESSURE: 202 SprayKeyPressure(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], 203 timestamp); 204 break; 205 206 case B_CONTROL_CHANGE: 207 SprayControlChange(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], 208 timestamp); 209 break; 210 211 case B_PROGRAM_CHANGE: 212 SprayProgramChange(msgBuf[0] & 0x0F, msgBuf[1], 213 timestamp); 214 break; 215 216 case B_CHANNEL_PRESSURE: 217 SprayChannelPressure(msgBuf[0] & 0x0F, msgBuf[1], 218 timestamp); 219 break; 220 221 case B_PITCH_BEND: 222 SprayPitchBend(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], 223 timestamp); 224 break; 225 } 226 227 switch (msgBuf[0]) { 228 case B_SONG_POSITION: 229 SpraySystemCommon(msgBuf[0], msgBuf[1], msgBuf[2], 230 timestamp); 231 break; 232 233 case B_MIDI_TIME_CODE: 234 case B_SONG_SELECT: 235 case B_CABLE_MESSAGE: 236 SpraySystemCommon(msgBuf[0], msgBuf[1], 0, timestamp); 237 break; 238 } 239 } 240 } else if (runningStatus != 0) { 241 // Repeated voice command 242 msgBuf[0] = runningStatus; 243 msgBuf[1] = next; 244 msgPtr = msgBuf + 2; 245 needed = msgSize - 2; 246 } 247 } // while fKeepRunning 248 249 if (haveSysEx) 250 free(sysexBuf); 251 252 return fKeepRunning ? errno : B_OK; 253 } 254