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
MidiPortConsumer(int fd,const char * name)23 MidiPortConsumer::MidiPortConsumer(int fd, const char* name)
24 : BMidiLocalConsumer(name),
25 fFileDescriptor(fd)
26 {
27 }
28
29
30 void
Data(uchar * data,size_t length,bool atomic,bigtime_t time)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
MidiPortProducer(int fd,const char * name)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
~MidiPortProducer()60 MidiPortProducer::~MidiPortProducer()
61 {
62 fKeepRunning = false;
63
64 status_t dummy;
65 wait_for_thread(fReaderThread, &dummy);
66 }
67
68
69 int32
_ReaderThread(void * data)70 MidiPortProducer::_ReaderThread(void* data)
71 {
72 return ((MidiPortProducer*) data)->GetData();
73 }
74
75
76 int32
GetData()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