Jack2  1.9.9
JackCoreMidiInputPort.cpp
1 /*
2 Copyright (C) 2011 Devin Anderson
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 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 General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include <cassert>
21 #include <memory>
22 
23 #include "JackCoreMidiInputPort.h"
24 #include "JackMidiUtil.h"
25 #include "JackError.h"
26 
28 
29 JackCoreMidiInputPort::JackCoreMidiInputPort(double time_ratio,
30  size_t max_bytes,
31  size_t max_messages):
32  JackCoreMidiPort(time_ratio)
33 {
34  thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
35  std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
36  write_queue = new JackMidiBufferWriteQueue();
37  std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
38  sysex_buffer = new jack_midi_data_t[max_bytes];
39  write_queue_ptr.release();
40  thread_queue_ptr.release();
41  jack_event = 0;
42 }
43 
44 JackCoreMidiInputPort::~JackCoreMidiInputPort()
45 {
46  delete thread_queue;
47  delete write_queue;
48  delete[] sysex_buffer;
49 }
50 
51 jack_nframes_t
52 JackCoreMidiInputPort::GetFramesFromTimeStamp(MIDITimeStamp timestamp)
53 {
54  return GetFramesFromTime((jack_time_t) (timestamp * time_ratio));
55 }
56 
57 void
58 JackCoreMidiInputPort::Initialize(const char *alias_name,
59  const char *client_name,
60  const char *driver_name, int index,
61  MIDIEndpointRef endpoint)
62 {
63  JackCoreMidiPort::Initialize(alias_name, client_name, driver_name, index, endpoint, false);
64 }
65 
66 void
67 JackCoreMidiInputPort::ProcessCoreMidi(const MIDIPacketList *packet_list)
68 {
69  set_threaded_log_function();
70 
71  unsigned int packet_count = packet_list->numPackets;
72  assert(packet_count);
73  MIDIPacket *packet = (MIDIPacket *) packet_list->packet;
74  for (unsigned int i = 0; i < packet_count; i++) {
75  jack_midi_data_t *data = packet->data;
76  size_t size = packet->length;
77  assert(size);
78  jack_midi_event_t event;
79 
80  // XX: There might be dragons in my spaghetti. This code is begging
81  // for a rewrite.
82 
83  if (sysex_bytes_sent) {
84  if (data[0] & 0x80) {
85  jack_error("JackCoreMidiInputPort::ProcessCoreMidi - System "
86  "exclusive message aborted.");
87  sysex_bytes_sent = 0;
88  goto parse_event;
89  }
90  buffer_sysex_bytes:
91  if ((sysex_bytes_sent + size) <= sizeof(sysex_buffer)) {
92  memcpy(sysex_buffer + sysex_bytes_sent, packet,
93  size * sizeof(jack_midi_data_t));
94  }
95  sysex_bytes_sent += size;
96  if (data[size - 1] == 0xf7) {
97  if (sysex_bytes_sent > sizeof(sysex_buffer)) {
98  jack_error("JackCoreMidiInputPort::ProcessCoreMidi - "
99  "Could not buffer a %d-byte system exclusive "
100  "message. Discarding message.",
101  sysex_bytes_sent);
102  sysex_bytes_sent = 0;
103  goto get_next_packet;
104  }
105  event.buffer = sysex_buffer;
106  event.size = sysex_bytes_sent;
107  sysex_bytes_sent = 0;
108  goto send_event;
109  }
110  goto get_next_packet;
111  }
112 
113  parse_event:
114  if (data[0] == 0xf0) {
115  if (data[size - 1] != 0xf7) {
116  goto buffer_sysex_bytes;
117  }
118  }
119  event.buffer = data;
120  event.size = size;
121 
122  send_event:
123  event.time = GetFramesFromTimeStamp(packet->timeStamp);
124  switch (thread_queue->EnqueueEvent(&event)) {
125  case JackMidiWriteQueue::BUFFER_FULL:
126  jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
127  "queue buffer is full. Dropping event.");
128  break;
129  case JackMidiWriteQueue::BUFFER_TOO_SMALL:
130  jack_error("JackCoreMidiInputPort::ProcessCoreMidi - The thread "
131  "queue couldn't enqueue a %d-byte packet. Dropping "
132  "event.", event.size);
133  break;
134  default:
135  ;
136  }
137 
138  get_next_packet:
139  packet = MIDIPacketNext(packet);
140  assert(packet);
141  }
142 }
143 
144 void
145 JackCoreMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer,
146  jack_nframes_t frames)
147 {
148  write_queue->ResetMidiBuffer(port_buffer, frames);
149  if (! jack_event) {
150  jack_event = thread_queue->DequeueEvent();
151  }
152 
153  for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
154  // Add 'frames' to MIDI events to align with audio.
155  switch (write_queue->EnqueueEvent(jack_event, frames)) {
156  case JackMidiWriteQueue::BUFFER_TOO_SMALL:
157  jack_error("JackCoreMidiInputPort::ProcessJack - The write queue "
158  "couldn't enqueue a %d-byte event. Dropping event.",
159  jack_event->size);
160  // Fallthrough on purpose
161  case JackMidiWriteQueue::OK:
162  continue;
163  default:
164  ;
165  }
166  break;
167  }
168 }
169 
170 bool
171 JackCoreMidiInputPort::Start()
172 {
173  // Hack: Get rid of any messages that might have come in before starting
174  // the engine.
175  while (thread_queue->DequeueEvent());
176  sysex_bytes_sent = 0;
177  return true;
178 }
179 
180 bool
181 JackCoreMidiInputPort::Stop()
182 {
183  return true;
184 }
EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:91
void ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames)
virtual jack_midi_event_t * DequeueEvent()
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)