Jack2 1.9.6

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