Jack2  1.9.8
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 
27 
28 JackMidiRawInputWriteQueue::
29 JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue,
30  size_t max_packet_data, size_t max_packets)
31 {
32  packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets);
33  std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue);
34  input_buffer = new jack_midi_data_t[max_packet_data];
35  Clear();
36  expected_bytes = 0;
37  event_pending = false;
38  input_buffer_size = max_packet_data;
39  packet = 0;
40  status_byte = 0;
41  this->write_queue = write_queue;
42  packet_queue_ptr.release();
43 }
44 
45 JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue()
46 {
47  delete[] input_buffer;
48  delete packet_queue;
49 }
50 
51 void
52 JackMidiRawInputWriteQueue::Clear()
53 {
54  total_bytes = 0;
55  unbuffered_bytes = 0;
56 }
57 
58 Jack::JackMidiWriteQueue::EnqueueResult
59 JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
60  jack_midi_data_t *buffer)
61 {
62  return packet_queue->EnqueueEvent(time, size, buffer);
63 }
64 
65 size_t
67 {
68  return packet_queue->GetAvailableSpace();
69 }
70 
71 void
73  size_t total_bytes)
74 {
75  jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI "
76  "byte(s) of a %d byte message could not be buffered. The "
77  "message has been dropped.", unbuffered_bytes, total_bytes);
78 }
79 
80 void
82 {
83  jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI "
84  "event scheduled for frame '%d' could not be processed because "
85  "the write queue cannot accomodate an event of that size. The "
86  "event has been discarded.", event->size, event->time);
87 }
88 
89 void
91 {
92  jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - "
93  "Discarding %d MIDI byte(s) of an incomplete message. The "
94  "MIDI cable may have been unplugged.", total_bytes);
95 }
96 
97 void
99 {
100  jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - "
101  "Dropping invalid MIDI status byte '%x'.", (unsigned int) byte);
102 }
103 
104 void
106 {
107  jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - "
108  "Received a sysex end byte without first receiving a sysex "
109  "start byte. Discarding %d MIDI byte(s). The cable may have "
110  "been unplugged.", total_bytes);
111 }
112 
113 bool
114 JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time)
115 {
116  bool result = ! unbuffered_bytes;
117  if (! result) {
118  HandleBufferFailure(unbuffered_bytes, total_bytes);
119  } else {
120  PrepareEvent(time, total_bytes, input_buffer);
121  }
122  Clear();
123  if (status_byte >= 0xf0) {
124  expected_bytes = 0;
125  status_byte = 0;
126  }
127  return result;
128 }
129 
130 bool
131 JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time,
132  jack_midi_data_t byte)
133 {
134  event_byte = byte;
135  PrepareEvent(time, 1, &event_byte);
136  return true;
137 }
138 
139 void
140 JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size,
141  jack_midi_data_t *buffer)
142 {
143  event.buffer = buffer;
144  event.size = size;
145  event.time = time;
146  event_pending = true;
147 }
148 
149 jack_nframes_t
150 JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame)
151 {
152  if (event_pending) {
153  if (! WriteEvent(boundary_frame)) {
154  return event.time;
155  }
156  }
157  if (! packet) {
158  packet = packet_queue->DequeueEvent();
159  }
160  for (; packet; packet = packet_queue->DequeueEvent()) {
161  for (; packet->size; (packet->buffer)++, (packet->size)--) {
162  if (ProcessByte(packet->time, *(packet->buffer))) {
163  if (! WriteEvent(boundary_frame)) {
164  (packet->buffer)++;
165  (packet->size)--;
166  return event.time;
167  }
168  }
169  }
170  }
171  return 0;
172 }
173 
174 bool
175 JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time,
176  jack_midi_data_t byte)
177 {
178  if (byte >= 0xf8) {
179  // Realtime
180  if (byte == 0xfd) {
182  return false;
183  }
184  return PrepareByteEvent(time, byte);
185  }
186  if (byte == 0xf7) {
187  // Sysex end
188  if (status_byte == 0xf0) {
189  RecordByte(byte);
190  return PrepareBufferedEvent(time);
191  }
192  HandleUnexpectedSysexEnd(total_bytes);
193  Clear();
194  expected_bytes = 0;
195  status_byte = 0;
196  return false;
197  }
198  if (byte >= 0x80) {
199  // Non-realtime status byte
200  if (total_bytes) {
201  HandleIncompleteMessage(total_bytes);
202  Clear();
203  }
204  status_byte = byte;
205  switch (byte & 0xf0) {
206  case 0x80:
207  case 0x90:
208  case 0xa0:
209  case 0xb0:
210  case 0xe0:
211  // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
212  expected_bytes = 3;
213  break;
214  case 0xc0:
215  case 0xd0:
216  // Program Change, Channel Pressure
217  expected_bytes = 2;
218  break;
219  case 0xf0:
220  switch (byte) {
221  case 0xf0:
222  // Sysex
223  expected_bytes = 0;
224  break;
225  case 0xf1:
226  case 0xf3:
227  // MTC Quarter Frame, Song Select
228  expected_bytes = 2;
229  break;
230  case 0xf2:
231  // Song Position
232  expected_bytes = 3;
233  break;
234  case 0xf4:
235  case 0xf5:
236  // Undefined
238  expected_bytes = 0;
239  status_byte = 0;
240  return false;
241  case 0xf6:
242  // Tune Request
243  bool result = PrepareByteEvent(time, byte);
244  if (result) {
245  expected_bytes = 0;
246  status_byte = 0;
247  }
248  return result;
249  }
250  }
251  RecordByte(byte);
252  return false;
253  }
254  // Data byte
255  if (! status_byte) {
256  // Data bytes without a status will be discarded.
257  total_bytes++;
258  unbuffered_bytes++;
259  return false;
260  }
261  if (! total_bytes) {
262  // Apply running status.
263  RecordByte(status_byte);
264  }
265  RecordByte(byte);
266  return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) :
267  false;
268 }
269 
270 void
271 JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte)
272 {
273  if (total_bytes < input_buffer_size) {
274  input_buffer[total_bytes] = byte;
275  } else {
276  unbuffered_bytes++;
277  }
278  total_bytes++;
279 }
280 
281 bool
282 JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame)
283 {
284  if ((! boundary_frame) || (event.time < boundary_frame)) {
285  switch (write_queue->EnqueueEvent(&event)) {
286  case BUFFER_TOO_SMALL:
287  HandleEventLoss(&event);
288  // Fallthrough on purpose
289  case OK:
290  event_pending = false;
291  return true;
292  default:
293  // This is here to stop compilers from warning us about not
294  // handling enumeration values.
295  ;
296  }
297  }
298  return false;
299 }