xref: /haiku/src/servers/midi/PortDrivers.cpp (revision a4f52ae465cfd01939231dacd31c994d07c948f9)
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