xref: /haiku/src/servers/midi/PortDrivers.cpp (revision 81f5654c124bf46fba0fd251f208e2d88d81e1ce)
1 /*
2  * Copyright (c) 2003-2004 Matthijs Hollemans
3  * Copyright (c) 2004 Christian Packmann
4  * Copyright (c) 2003 Jerome Leveque
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 
29 #include "PortDrivers.h"
30 
31 //------------------------------------------------------------------------------
32 
33 MidiPortConsumer::MidiPortConsumer(int fd_, const char* name = NULL)
34 	: BMidiLocalConsumer(name)
35 {
36 	fd = fd_;
37 }
38 
39 //------------------------------------------------------------------------------
40 
41 void MidiPortConsumer::Data(
42 	uchar* data, size_t length, bool atomic, bigtime_t time)
43 {
44 	snooze_until(time - Latency(), B_SYSTEM_TIMEBASE);
45 
46 	if (write(fd, data, length) == -1)
47 	{
48 		perror("Error sending data to driver");
49 	}
50 }
51 
52 //------------------------------------------------------------------------------
53 
54 MidiPortProducer::MidiPortProducer(int fd_, const char *name = NULL)
55 	: BMidiLocalProducer(name)
56 {
57 	fd = fd_;
58 	keepRunning = true;
59 
60 	thread_id thread = spawn_thread(
61 		SpawnThread, "MidiPortProducer", B_URGENT_PRIORITY, this);
62 
63 	resume_thread(thread);
64 }
65 
66 //------------------------------------------------------------------------------
67 
68 MidiPortProducer::~MidiPortProducer()
69 {
70 	keepRunning = false;
71 }
72 
73 //------------------------------------------------------------------------------
74 
75 int32 MidiPortProducer::SpawnThread(void* data)
76 {
77 	return ((MidiPortProducer*) data)->GetData();
78 }
79 
80 //------------------------------------------------------------------------------
81 
82 int32 MidiPortProducer::GetData()
83 {
84 	uint8 msgBuf[3];
85 	uint8* sysexBuf;
86 
87 	uint8* msgPtr;
88 	size_t msgSize;
89 	size_t needed = 0;
90 	uint8 runningStatus = 0;
91 
92 	bool haveSysEx = false;
93 	size_t sysexAlloc;
94 	size_t sysexSize;
95 
96 	uint8 next;
97 
98 	while (keepRunning)
99 	{
100 		if (read(fd, &next, 1) != 1)
101 		{
102 			perror("Error reading data from driver");
103 			return B_ERROR;
104 		}
105 
106 		if (haveSysEx)  // System Exclusive
107 		{
108 			if (next < 0x80)  // data byte
109 			{
110 				sysexBuf[sysexSize++] = next;
111 				if (sysexSize == sysexAlloc)
112 				{
113 					sysexAlloc *= 2;
114 					sysexBuf = (uint8*) realloc(sysexBuf, sysexAlloc);
115 				}
116 				continue;
117 			}
118 			else if (next < 0xF8)  // end of sysex
119 			{
120 				SpraySystemExclusive(sysexBuf, sysexSize);
121 				haveSysEx = false;
122 			}
123 		}
124 
125 		if ((next & 0xF8) == 0xF8)  // System Realtime
126 		{
127 			SpraySystemRealTime(next);
128 		}
129 		else if ((next & 0xF0) == 0xF0)  // System Common
130 		{
131 			runningStatus = 0;
132 			msgBuf[0] = next;
133 			msgPtr = msgBuf + 1;
134 			switch (next)
135 			{
136 				case B_SYS_EX_START:
137 					sysexAlloc = 4096;
138 					sysexBuf = (uint8*) malloc(sysexAlloc);
139 					sysexBuf[0] = next;
140 					sysexSize = 1;
141 					haveSysEx = true;
142 					break;
143 
144 				case B_SONG_POSITION:
145 					needed = 2;
146 					msgSize = 3;
147 					break;
148 
149 				case B_MIDI_TIME_CODE:
150 				case B_SONG_SELECT:
151 				case B_CABLE_MESSAGE:
152 					needed = 1;
153 					msgSize = 2;
154 					break;
155 
156 				case B_TUNE_REQUEST:
157 				case B_SYS_EX_END:
158 					SpraySystemCommon(next, 0, 0);
159 					break;
160 			}
161 		}
162 		else if ((next & 0x80) == 0x80)  // Voice message
163 		{
164 			runningStatus = next;
165 			msgBuf[0] = next;
166 			msgPtr = msgBuf + 1;
167 			switch (next & 0xF0)
168 			{
169 				case B_NOTE_OFF:
170 				case B_NOTE_ON:
171 				case B_KEY_PRESSURE:
172 				case B_CONTROL_CHANGE:
173 				case B_PITCH_BEND:
174 					needed = 2;
175 					msgSize = 3;
176 					break;
177 
178 				case B_PROGRAM_CHANGE:
179 				case B_CHANNEL_PRESSURE:
180 					needed = 1;
181 					msgSize = 2;
182 					break;
183 			}
184 		}
185 		else if (needed > 0)  // Data bytes to complete message
186 		{
187 			*msgPtr++ = next;
188 			if (--needed == 0)
189 			{
190 				switch (msgBuf[0] & 0xF0)
191 				{
192 					case B_NOTE_OFF:
193 						SprayNoteOff(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2]);
194 						break;
195 
196 					case B_NOTE_ON:
197 						SprayNoteOn(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2]);
198 						break;
199 
200 					case B_KEY_PRESSURE:
201 						SprayKeyPressure(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2]);
202 						break;
203 
204 					case B_CONTROL_CHANGE:
205 						SprayControlChange(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2]);
206 						break;
207 
208 					case B_PROGRAM_CHANGE:
209 						SprayProgramChange(msgBuf[0] & 0x0F, msgBuf[1]);
210 						break;
211 
212 					case B_CHANNEL_PRESSURE:
213 						SprayChannelPressure(msgBuf[0] & 0x0F, msgBuf[1]);
214 						break;
215 
216 					case B_PITCH_BEND:
217 						SprayPitchBend(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2]);
218 						break;
219 				}
220 
221 				switch (msgBuf[0])
222 				{
223 					case B_SONG_POSITION:
224 						SpraySystemCommon(msgBuf[0], msgBuf[1], msgBuf[2]);
225 						break;
226 
227 					case B_MIDI_TIME_CODE:
228 					case B_SONG_SELECT:
229 					case B_CABLE_MESSAGE:
230 						SpraySystemCommon(msgBuf[0], msgBuf[1], 0);
231 						break;
232 				}
233 			}
234 		}
235 		else if (runningStatus != 0)  // Repeated voice command
236 		{
237 			msgBuf[0] = runningStatus;
238 			msgBuf[1] = next;
239 			msgPtr = msgBuf + 2;
240 			needed = msgSize - 2;
241 		}
242 	}
243 
244 	if (haveSysEx)
245 	{
246 		free(sysexBuf);
247 	}
248 
249 	return B_OK;
250 }
251 
252 //------------------------------------------------------------------------------
253