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