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