Jack2: JackMidiRawInputWriteQueue.cpp Source File

Jack2 1.9.12

JackMidiRawInputWriteQueue.cpp
1 /*
2 Copyright (C) 2010 Devin Anderson
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18 */
19
20 #include <cassert>
21 #include <memory>
22 #include <new>
23
24 #include "JackMidiRawInputWriteQueue.h"
25 #include "JackError.h"
26
27 using Jack::JackMidiRawInputWriteQueue;
28
29 JackMidiRawInputWriteQueue::
30 JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue,
31 size_t max_packet_data, size_t max_packets)
32 {
33 packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets);
34 std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue);
35 input_buffer = new jack_midi_data_t[max_packet_data];
36 Clear();
37 expected_bytes = 0;
38 event_pending = false;
39 input_buffer_size = max_packet_data;
40 packet = 0;
41 status_byte = 0;
42 this->write_queue = write_queue;
43 packet_queue_ptr.release();
44 }
45
46 JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue()
47 {
48 delete[] input_buffer;
49 delete packet_queue;
50 }
51
52 void
53 JackMidiRawInputWriteQueue::Clear()
54 {
55 total_bytes = 0;
56 unbuffered_bytes = 0;
57 }
58
59 Jack::JackMidiWriteQueue::EnqueueResult
60 JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
61 jack_midi_data_t *buffer)
62 {
63 return packet_queue->EnqueueEvent(time, size, buffer);
64 }
65
66 size_t
67 JackMidiRawInputWriteQueue::GetAvailableSpace()
68 {
69 return packet_queue->GetAvailableSpace();
70 }
71
72 void
73 JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes,
74 size_t total_bytes)
75 {
76 jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI "
77 "byte(s) of a %d byte message could not be buffered. The "
78 "message has been dropped.", unbuffered_bytes, total_bytes);
79 }
80
81 void
82 JackMidiRawInputWriteQueue::HandleEventLoss(jack_midi_event_t *event)
83 {
84 jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI "
85 "event scheduled for frame '%d' could not be processed because "
86 "the write queue cannot accomodate an event of that size. The "
87 "event has been discarded.", event->size, event->time);
88 }
89
90 void
91 JackMidiRawInputWriteQueue::HandleIncompleteMessage(size_t total_bytes)
92 {
93 jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - "
94 "Discarding %d MIDI byte(s) of an incomplete message. The "
95 "MIDI cable may have been unplugged.", total_bytes);
96 }
97
98 void
99 JackMidiRawInputWriteQueue::HandleInvalidStatusByte(jack_midi_data_t byte)
100 {
101 jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - "
102 "Dropping invalid MIDI status byte '%x'.", (unsigned int) byte);
103 }
104
105 void
106 JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd(size_t total_bytes)
107 {
108 jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - "
109 "Received a sysex end byte without first receiving a sysex "
110 "start byte. Discarding %d MIDI byte(s). The cable may have "
111 "been unplugged.", total_bytes);
112 }
113
114 bool
115 JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time)
116 {
117 bool result = ! unbuffered_bytes;
118 if (! result) {
119 HandleBufferFailure(unbuffered_bytes, total_bytes);
120 } else {
121 PrepareEvent(time, total_bytes, input_buffer);
122 }
123 Clear();
124 if (status_byte >= 0xf0) {
125 expected_bytes = 0;
126 status_byte = 0;
127 }
128 return result;
129 }
130
131 bool
132 JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time,
133 jack_midi_data_t byte)
134 {
135 event_byte = byte;
136 PrepareEvent(time, 1, &event_byte);
137 return true;
138 }
139
140 void
141 JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size,
142 jack_midi_data_t *buffer)
143 {
144 event.buffer = buffer;
145 event.size = size;
146 event.time = time;
147 event_pending = true;
148 }
149
150 jack_nframes_t
151 JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame)
152 {
153 if (event_pending) {
154 if (! WriteEvent(boundary_frame)) {
155 return event.time;
156 }
157 }
158 if (! packet) {
159 packet = packet_queue->DequeueEvent();
160 }
161 for (; packet; packet = packet_queue->DequeueEvent()) {
162 for (; packet->size; (packet->buffer)++, (packet->size)--) {
163 if (ProcessByte(packet->time, *(packet->buffer))) {
164 if (! WriteEvent(boundary_frame)) {
165 (packet->buffer)++;
166 (packet->size)--;
167 return event.time;
168 }
169 }
170 }
171 }
172 return 0;
173 }
174
175 bool
176 JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time,
177 jack_midi_data_t byte)
178 {
179 if (byte >= 0xf8) {
180 // Realtime
181 if (byte == 0xfd) {
182 HandleInvalidStatusByte(byte);
183 return false;
184 }
185 return PrepareByteEvent(time, byte);
186 }
187 if (byte == 0xf7) {
188 // Sysex end
189 if (status_byte == 0xf0) {
190 RecordByte(byte);
191 return PrepareBufferedEvent(time);
192 }
193 HandleUnexpectedSysexEnd(total_bytes);
194 Clear();
195 expected_bytes = 0;
196 status_byte = 0;
197 return false;
198 }
199 if (byte >= 0x80) {
200 // Non-realtime status byte
201 if (total_bytes) {
202 HandleIncompleteMessage(total_bytes);
203 Clear();
204 }
205 status_byte = byte;
206 switch (byte & 0xf0) {
207 case 0x80:
208 case 0x90:
209 case 0xa0:
210 case 0xb0:
211 case 0xe0:
212 // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
213 expected_bytes = 3;
214 break;
215 case 0xc0:
216 case 0xd0:
217 // Program Change, Channel Pressure
218 expected_bytes = 2;
219 break;
220 case 0xf0:
221 switch (byte) {
222 case 0xf0:
223 // Sysex
224 expected_bytes = 0;
225 break;
226 case 0xf1:
227 case 0xf3:
228 // MTC Quarter Frame, Song Select
229 expected_bytes = 2;
230 break;
231 case 0xf2:
232 // Song Position
233 expected_bytes = 3;
234 break;
235 case 0xf4:
236 case 0xf5:
237 // Undefined
238 HandleInvalidStatusByte(byte);
239 expected_bytes = 0;
240 status_byte = 0;
241 return false;
242 case 0xf6:
243 // Tune Request
244 bool result = PrepareByteEvent(time, byte);
245 if (result) {
246 expected_bytes = 0;
247 status_byte = 0;
248 }
249 return result;
250 }
251 }
252 RecordByte(byte);
253 return false;
254 }
255 // Data byte
256 if (! status_byte) {
257 // Data bytes without a status will be discarded.
258 total_bytes++;
259 unbuffered_bytes++;
260 return false;
261 }
262 if (! total_bytes) {
263 // Apply running status.
264 RecordByte(status_byte);
265 }
266 RecordByte(byte);
267 return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) :
268 false;
269 }
270
271 void
272 JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte)
273 {
274 if (total_bytes < input_buffer_size) {
275 input_buffer[total_bytes] = byte;
276 } else {
277 unbuffered_bytes++;
278 }
279 total_bytes++;
280 }
281
282 bool
283 JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame)
284 {
285 if ((! boundary_frame) || (event.time < boundary_frame)) {
286 switch (write_queue->EnqueueEvent(&event)) {
287 case BUFFER_TOO_SMALL:
288 HandleEventLoss(&event);
289 // Fallthrough on purpose
290 case OK:
291 event_pending = false;
292 return true;
293 default:
294 // This is here to stop compilers from warning us about not
295 // handling enumeration values.
296 ;
297 }
298 }
299 return false;
300 }
jack_midi_event_t::buffer
jack_midi_data_t * buffer
Definition: JackMidiPort.h:37
Jack::JackMidiAsyncQueue
Definition: JackMidiAsyncQueue.h:40
Jack::JackMidiRawInputWriteQueue
Definition: JackMidiRawInputWriteQueue.h:36
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
Jack::JackMidiWriteQueue
Definition: JackMidiWriteQueue.h:32
jack_midi_event_t::size
size_t size
Definition: JackMidiPort.h:36
Jack::JackMidiRawInputWriteQueue::HandleIncompleteMessage
virtual void HandleIncompleteMessage(size_t total_bytes)
Definition: JackMidiRawInputWriteQueue.cpp:91
jack_midi_event_t
Definition: JackMidiPort.h:33
Jack::JackMidiAsyncQueue::GetAvailableSpace
size_t GetAvailableSpace()
Definition: JackMidiAsyncQueue.cpp:92
Jack::JackMidiWriteQueue::EnqueueEvent
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)=0
Jack::JackMidiRawInputWriteQueue::Process
jack_nframes_t Process(jack_nframes_t boundary_frame=0)
Definition: JackMidiRawInputWriteQueue.cpp:151
Jack::JackMidiRawInputWriteQueue::GetAvailableSpace
size_t GetAvailableSpace()
Definition: JackMidiRawInputWriteQueue.cpp:67
Jack::JackMidiRawInputWriteQueue::EnqueueEvent
EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
Definition: JackMidiRawInputWriteQueue.cpp:60
jack_midi_event_t::time
jack_nframes_t time
Definition: JackMidiPort.h:35
Jack::JackMidiRawInputWriteQueue::HandleBufferFailure
virtual void HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes)
Definition: JackMidiRawInputWriteQueue.cpp:73
Jack::JackMidiRawInputWriteQueue::HandleInvalidStatusByte
virtual void HandleInvalidStatusByte(jack_midi_data_t byte)
Definition: JackMidiRawInputWriteQueue.cpp:99
Jack::JackMidiAsyncQueue::DequeueEvent
virtual jack_midi_event_t * DequeueEvent()
Definition: JackMidiAsyncQueue.cpp:53
Jack::JackMidiRawInputWriteQueue::HandleEventLoss
virtual void HandleEventLoss(jack_midi_event_t *event)
Definition: JackMidiRawInputWriteQueue.cpp:82
Jack::JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd
virtual void HandleUnexpectedSysexEnd(size_t total_bytes)
Definition: JackMidiRawInputWriteQueue.cpp:106
Jack::JackMidiAsyncQueue::EnqueueEvent
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
Definition: JackMidiAsyncQueue.cpp:72