Jack2  1.9.13
JackMidiPort.cpp
1 /*
2 Copyright (C) 2007 Dmitry Baikov
3 Copyright (C) 2018 Filipe Coelho
4 Original JACK MIDI implementation Copyright (C) 2004 Ian Esten
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15 
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 
20 */
21 
22 #include "JackError.h"
23 #include "JackPortType.h"
24 #include "JackMidiPort.h"
25 #include <assert.h>
26 #include <string.h>
27 
28 namespace Jack
29 {
30 
31 SERVER_EXPORT void JackMidiBuffer::Reset(jack_nframes_t nframes)
32 {
33  /* This line ate 1 hour of my life... dsbaikov */
34  this->nframes = nframes;
35  write_pos = 0;
36  event_count = 0;
37  lost_events = 0;
38 }
39 
40 SERVER_EXPORT jack_shmsize_t JackMidiBuffer::MaxEventSize() const
41 {
42  assert (((jack_shmsize_t) - 1) < 0); // jack_shmsize_t should be signed
43  jack_shmsize_t left = buffer_size - (sizeof(JackMidiBuffer) + sizeof(JackMidiEvent) * (event_count + 1) + write_pos);
44  if (left < 0) {
45  return 0;
46  }
47  if (left <= JackMidiEvent::INLINE_SIZE_MAX) {
48  return JackMidiEvent::INLINE_SIZE_MAX;
49  }
50  return left;
51 }
52 
53 SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time, jack_shmsize_t size)
54 {
55  jack_shmsize_t space = MaxEventSize();
56  if (space == 0 || size > space) {
57  jack_error("JackMidiBuffer::ReserveEvent - the buffer does not have "
58  "enough room to enqueue a %lu byte event", size);
59  lost_events++;
60  return 0;
61  }
62  JackMidiEvent* event = &events[event_count++];
63  event->time = time;
64  event->size = size;
65 
66  if (size <= JackMidiEvent::INLINE_SIZE_MAX) {
67  return event->data;
68  }
69 
70  write_pos += size;
71  event->offset = buffer_size - write_pos;
72  return (jack_midi_data_t*)this + event->offset;
73 }
74 
75 void MidiBufferInit(void* buffer, size_t buffer_size, jack_nframes_t nframes)
76 {
77  JackMidiBuffer* midi = (JackMidiBuffer*)buffer;
78  midi->magic = JackMidiBuffer::MAGIC;
79  /* Since port buffer has actually always BUFFER_SIZE_MAX frames, we can safely use all the size */
80  midi->buffer_size = BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t);
81  midi->Reset(nframes);
82 }
83 
84 /*
85  * The mixdown function below, is a simplest (read slowest) implementation possible.
86  * But, since it is unlikely that it will mix many buffers with many events,
87  * it should perform quite good.
88  * More efficient (and possibly, fastest possible) implementation (it exists),
89  * using calendar queue algorithm is about 3 times bigger, and uses alloca().
90  * So, let's listen to D.Knuth about premature optimisation, a leave the current
91  * implementation as is, until it is proved to be a bottleneck.
92  * Dmitry Baikov.
93  */
94 static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count, jack_nframes_t nframes)
95 {
96  JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer);
97  if (!mix->IsValid()) {
98  jack_error("Jack::MidiBufferMixdown - invalid mix buffer");
99  return;
100  }
101  mix->Reset(nframes);
102 
103  uint32_t mix_index[src_count];
104  int event_count = 0;
105  for (int i = 0; i < src_count; ++i) {
106  JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
107  if (!buf->IsValid()) {
108  jack_error("Jack::MidiBufferMixdown - invalid source buffer");
109  return;
110  }
111  mix_index[i] = 0;
112  event_count += buf->event_count;
113  mix->lost_events += buf->lost_events;
114  }
115 
116  int events_done;
117  for (events_done = 0; events_done < event_count; ++events_done) {
118  JackMidiBuffer* next_buf = 0;
119  JackMidiEvent* next_event = 0;
120  uint32_t next_buf_index = 0;
121 
122  // find the earliest event
123  for (int i = 0; i < src_count; ++i) {
124  JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
125  if (mix_index[i] >= buf->event_count)
126  continue;
127  JackMidiEvent* e = &buf->events[mix_index[i]];
128  if (!next_event || e->time < next_event->time) {
129  next_event = e;
130  next_buf = buf;
131  next_buf_index = i;
132  }
133  }
134  if (next_event == 0) {
135  jack_error("Jack::MidiBufferMixdown - got invalid next event");
136  break;
137  }
138 
139  // write the event
140  jack_midi_data_t* dest = mix->ReserveEvent(next_event->time, next_event->size);
141  if (!dest) break;
142 
143  memcpy(dest, next_event->GetData(next_buf), next_event->size);
144  mix_index[next_buf_index]++;
145  }
146  mix->lost_events += event_count - events_done;
147 }
148 
149 static size_t MidiBufferSize()
150 {
151  return BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t);
152 }
153 
154 const JackPortType gMidiPortType =
155 {
156  JACK_DEFAULT_MIDI_TYPE,
157  MidiBufferSize,
158  MidiBufferInit,
159  MidiBufferMixdown
160 };
161 
162 } // namespace Jack
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
jack_shmsize_t write_pos
data write position from the end of the buffer.
Definition: JackMidiPort.h:82