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