Jack2  1.9.10
JackMidiPort.cpp
00001 /*
00002 Copyright (C) 2007 Dmitry Baikov
00003 Original JACK MIDI implementation Copyright (C) 2004 Ian Esten
00004 
00005 This program is free software; you can redistribute it and/or modify
00006 it under the terms of the GNU Lesser General Public License as published by
00007 the Free Software Foundation; either version 2.1 of the License, or
00008 (at your option) any later version.
00009 
00010 This program is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 GNU Lesser General Public License for more details.
00014 
00015 You should have received a copy of the GNU Lesser General Public License
00016 along with this program; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 
00019 */
00020 
00021 #include "JackError.h"
00022 #include "JackPortType.h"
00023 #include "JackMidiPort.h"
00024 #include <assert.h>
00025 #include <string.h>
00026 
00027 namespace Jack
00028 {
00029 
00030 SERVER_EXPORT void JackMidiBuffer::Reset(jack_nframes_t nframes)
00031 {
00032     /* This line ate 1 hour of my life... dsbaikov */
00033     this->nframes = nframes;
00034     write_pos = 0;
00035     event_count = 0;
00036     lost_events = 0;
00037 }
00038 
00039 SERVER_EXPORT jack_shmsize_t JackMidiBuffer::MaxEventSize() const
00040 {
00041     assert (((jack_shmsize_t) - 1) < 0); // jack_shmsize_t should be signed
00042     jack_shmsize_t left = buffer_size - (sizeof(JackMidiBuffer) + sizeof(JackMidiEvent) * (event_count + 1) + write_pos);
00043     if (left < 0) {
00044         return 0;
00045     }
00046     if (left <= JackMidiEvent::INLINE_SIZE_MAX) {
00047         return JackMidiEvent::INLINE_SIZE_MAX;
00048     }
00049     return left;
00050 }
00051 
00052 SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time, jack_shmsize_t size)
00053 {
00054     jack_shmsize_t space = MaxEventSize();
00055     if (space == 0 || size > space) {
00056         jack_error("JackMidiBuffer::ReserveEvent - the buffer does not have "
00057                    "enough room to enqueue a %lu byte event", size);
00058         lost_events++;
00059         return 0;
00060     }
00061     JackMidiEvent* event = &events[event_count++];
00062     event->time = time;
00063     event->size = size;
00064     
00065     if (size <= JackMidiEvent::INLINE_SIZE_MAX) {
00066         return event->data;
00067     }
00068    
00069     write_pos += size;
00070     event->offset = buffer_size - write_pos;
00071     return (jack_midi_data_t*)this + event->offset;
00072 }
00073 
00074 void MidiBufferInit(void* buffer, size_t buffer_size, jack_nframes_t nframes)
00075 {
00076     JackMidiBuffer* midi = (JackMidiBuffer*)buffer;
00077     midi->magic = JackMidiBuffer::MAGIC;
00078     /* Since port buffer has actually always BUFFER_SIZE_MAX frames, we can safely use all the size */
00079     midi->buffer_size = BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t);
00080     midi->Reset(nframes);
00081 }
00082 
00083 /*
00084  * The mixdown function below, is a simplest (read slowest) implementation possible.
00085  * But, since it is unlikely that it will mix many buffers with many events,
00086  * it should perform quite good.
00087  * More efficient (and possibly, fastest possible) implementation (it exists),
00088  * using calendar queue algorithm is about 3 times bigger, and uses alloca().
00089  * So, let's listen to D.Knuth about premature optimisation, a leave the current
00090  * implementation as is, until it is proved to be a bottleneck.
00091  * Dmitry Baikov.
00092  */
00093 static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count, jack_nframes_t nframes)
00094 {
00095     JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer);
00096     if (!mix->IsValid()) {
00097         jack_error("Jack::MidiBufferMixdown - invalid mix buffer");
00098         return;
00099     }
00100     mix->Reset(nframes);
00101 
00102     uint32_t mix_index[src_count];
00103     int event_count = 0;
00104     for (int i = 0; i < src_count; ++i) {
00105         JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
00106         if (!buf->IsValid()) {
00107             jack_error("Jack::MidiBufferMixdown - invalid source buffer");
00108             return;
00109         }
00110         mix_index[i] = 0;
00111         event_count += buf->event_count;
00112         mix->lost_events += buf->lost_events;
00113     }
00114 
00115     int events_done;
00116     for (events_done = 0; events_done < event_count; ++events_done) {
00117         JackMidiBuffer* next_buf = 0;
00118         JackMidiEvent* next_event = 0;
00119         uint32_t next_buf_index = 0;
00120 
00121         // find the earliest event
00122         for (int i = 0; i < src_count; ++i) {
00123             JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
00124             if (mix_index[i] >= buf->event_count)
00125                 continue;
00126             JackMidiEvent* e = &buf->events[mix_index[i]];
00127             if (!next_event || e->time < next_event->time) {
00128                 next_event = e;
00129                 next_buf = buf;
00130                 next_buf_index = i;
00131             }
00132         }
00133         assert(next_event != 0);
00134 
00135         // write the event
00136         jack_midi_data_t* dest = mix->ReserveEvent(next_event->time, next_event->size);
00137         if (!dest) break;
00138 
00139         memcpy(dest, next_event->GetData(next_buf), next_event->size);
00140         mix_index[next_buf_index]++;
00141     }
00142     mix->lost_events += event_count - events_done;
00143 }
00144 
00145 static size_t MidiBufferSize()
00146 {
00147     return BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t);
00148 }
00149 
00150 const JackPortType gMidiPortType =
00151 {
00152     JACK_DEFAULT_MIDI_TYPE,
00153     MidiBufferSize,
00154     MidiBufferInit,
00155     MidiBufferMixdown
00156 };
00157 
00158 } // namespace Jack