Jack2  1.9.9
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 
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
68 {
69  return packet_queue->GetAvailableSpace();
70 }
71 
72 void
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
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
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
100 {
101  jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - "
102  "Dropping invalid MIDI status byte '%x'.", (unsigned int) byte);
103 }
104 
105 void
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) {
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
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_data_t * buffer
Definition: JackMidiPort.h:37
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:91
virtual void HandleIncompleteMessage(size_t total_bytes)
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)=0
jack_nframes_t Process(jack_nframes_t boundary_frame=0)
EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
jack_nframes_t time
Definition: JackMidiPort.h:35
virtual void HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes)
virtual void HandleInvalidStatusByte(jack_midi_data_t byte)
virtual jack_midi_event_t * DequeueEvent()
virtual void HandleEventLoss(jack_midi_event_t *event)
virtual void HandleUnexpectedSysexEnd(size_t total_bytes)
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)