Jack2  1.9.8
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     mix_index = 0;
00038 }
00039 
00040 SERVER_EXPORT jack_shmsize_t JackMidiBuffer::MaxEventSize() const
00041 {
00042     assert (((jack_shmsize_t) - 1) < 0); // jack_shmsize_t should be signed
00043     jack_shmsize_t left = buffer_size - (sizeof(JackMidiBuffer) + sizeof(JackMidiEvent) * (event_count + 1) + write_pos);
00044     if (left < 0)
00045         return 0;
00046     if (left <= JackMidiEvent::INLINE_SIZE_MAX)
00047         return JackMidiEvent::INLINE_SIZE_MAX;
00048     return left;
00049 }
00050 
00051 SERVER_EXPORT jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time, jack_shmsize_t size)
00052 {
00053     jack_shmsize_t space = MaxEventSize();
00054     if (space == 0 || size > space) {
00055         jack_error("JackMidiBuffer::ReserveEvent - the buffer does not have "
00056                    "enough room to enqueue a %lu byte event", size);
00057         lost_events++;
00058         return 0;
00059     }
00060     JackMidiEvent* event = &events[event_count++];
00061     event->time = time;
00062     event->size = size;
00063     if (size <= JackMidiEvent::INLINE_SIZE_MAX)
00064         return event->data;
00065 
00066     write_pos += size;
00067     event->offset = buffer_size - write_pos;
00068     return (jack_midi_data_t*)this + event->offset;
00069 }
00070 
00071 static void MidiBufferInit(void* buffer, size_t buffer_size, jack_nframes_t nframes)
00072 {
00073     JackMidiBuffer* midi = (JackMidiBuffer*)buffer;
00074     midi->magic = JackMidiBuffer::MAGIC;
00075     /* Since port buffer has actually always BUFFER_SIZE_MAX frames, we can safely use all the size */
00076     midi->buffer_size = BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t);
00077     midi->Reset(nframes);
00078 }
00079 
00080 /*
00081  * The mixdown function below, is a simplest (read slowest) implementation possible.
00082  * But, since it is unlikely that it will mix many buffers with many events,
00083  * it should perform quite good.
00084  * More efficient (and possibly, fastest possible) implementation (it exists),
00085  * using calendar queue algorithm is about 3 times bigger, and uses alloca().
00086  * So, let's listen to D.Knuth about premature optimisation, a leave the current
00087  * implementation as is, until it is proved to be a bottleneck.
00088  * Dmitry Baikov.
00089  */
00090 static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count, jack_nframes_t nframes)
00091 {
00092     JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer);
00093     if (!mix->IsValid()) {
00094         jack_error("Jack::MidiBufferMixdown - invalid mix buffer");
00095         return;
00096     }
00097     mix->Reset(nframes);
00098 
00099     int event_count = 0;
00100     for (int i = 0; i < src_count; ++i) {
00101         JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
00102         if (!buf->IsValid()) {
00103             jack_error("Jack::MidiBufferMixdown - invalid source buffer");
00104             return;
00105         }
00106         buf->mix_index = 0;
00107         event_count += buf->event_count;
00108         mix->lost_events += buf->lost_events;
00109     }
00110 
00111     int events_done;
00112     for (events_done = 0; events_done < event_count; ++events_done) {
00113         JackMidiBuffer* next_buf = 0;
00114         JackMidiEvent* next_event = 0;
00115 
00116         // find the earliest event
00117         for (int i = 0; i < src_count; ++i) {
00118             JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
00119             if (buf->mix_index >= buf->event_count)
00120                 continue;
00121             JackMidiEvent* e = &buf->events[buf->mix_index];
00122             if (!next_event || e->time < next_event->time) {
00123                 next_event = e;
00124                 next_buf = buf;
00125             }
00126         }
00127         assert(next_event != 0);
00128 
00129         // write the event
00130         jack_midi_data_t* dest = mix->ReserveEvent(next_event->time, next_event->size);
00131         if (!dest)
00132             break;
00133         memcpy(dest, next_event->GetData(next_buf), next_event->size);
00134         next_buf->mix_index++;
00135     }
00136     mix->lost_events += event_count - events_done;
00137 }
00138 
00139 static size_t MidiBufferSize()
00140 {
00141     return BUFFER_SIZE_MAX * sizeof(jack_default_audio_sample_t);
00142 }
00143 
00144 const JackPortType gMidiPortType =
00145 {
00146     JACK_DEFAULT_MIDI_TYPE,
00147     MidiBufferSize,
00148     MidiBufferInit,
00149     MidiBufferMixdown
00150 };
00151 
00152 } // namespace Jack