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 General Public License as published by
00007   the Free Software Foundation; either version 2 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 General Public License for more details.
00014 
00015   You should have received a copy of the GNU General Public License
00016   along with this program; if not, write to the Free Software
00017   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018 
00019 */
00020 #include "JackError.h"
00021 #include "JackPortType.h"
00022 #include "JackMidiPort.h"
00023 #include <assert.h>
00024 #include <string.h>
00025 
00026 namespace Jack
00027 {
00028 
00029 void JackMidiBuffer::Reset(jack_nframes_t nframes)
00030 {
00031     /* This line ate 1 hour of my life... dsbaikov */
00032     this->nframes = nframes;
00033     write_pos = 0;
00034     event_count = 0;
00035     lost_events = 0;
00036     mix_index = 0;
00037 }
00038 
00039 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     if (left <= JackMidiEvent::INLINE_SIZE_MAX)
00046         return JackMidiEvent::INLINE_SIZE_MAX;
00047     return left;
00048 }
00049 
00050 jack_midi_data_t* JackMidiBuffer::ReserveEvent(jack_nframes_t time, jack_shmsize_t size)
00051 {
00052     jack_shmsize_t space = MaxEventSize();
00053     if (space == 0 || size > space) {
00054         lost_events++;
00055         return 0;
00056     }
00057 
00058     JackMidiEvent* event = &events[event_count++];
00059     event->time = time;
00060     event->size = size;
00061     if (size <= JackMidiEvent::INLINE_SIZE_MAX)
00062         return event->data;
00063 
00064     write_pos += size;
00065     event->offset = buffer_size - write_pos;
00066     return (jack_midi_data_t*)this + event->offset;
00067 }
00068 
00069 static void MidiBufferInit(void* buffer, size_t buffer_size, jack_nframes_t nframes)
00070 {
00071     JackMidiBuffer* midi = (JackMidiBuffer*)buffer;
00072     midi->magic = JackMidiBuffer::MAGIC;
00073     midi->buffer_size = buffer_size;
00074     midi->Reset(nframes);
00075 }
00076 
00077 /*
00078  * The mixdown function below, is a simplest (read slowest) implementation possible.
00079  * But, since it is unlikely that it will mix many buffers with many events,
00080  * it should perform quite good.
00081  * More efficient (and possibly, fastest possible) implementation (it exists),
00082  * using calendar queue algorithm is about 3 times bigger, and uses alloca().
00083  * So, let's listen to D.Knuth about premature optimisation, a leave the current
00084  * implementation as is, until it is proved to be a bottleneck.
00085  * Dmitry Baikov.
00086  */
00087 static void MidiBufferMixdown(void* mixbuffer, void** src_buffers, int src_count, jack_nframes_t nframes)
00088 {
00089     JackMidiBuffer* mix = static_cast<JackMidiBuffer*>(mixbuffer);
00090     if (!mix->IsValid()) {
00091         jack_error("MIDI: invalid mix buffer");
00092         return;
00093     }
00094     mix->Reset(nframes);
00095 
00096     int event_count = 0;
00097     for (int i = 0; i < src_count; ++i) {
00098         JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
00099         if (!buf->IsValid())
00100             return;
00101         buf->mix_index = 0;
00102         event_count += buf->event_count;
00103         mix->lost_events += buf->lost_events;
00104     }
00105 
00106     int events_done;
00107     for (events_done = 0; events_done < event_count; ++events_done) {
00108         JackMidiBuffer* next_buf = 0;
00109         JackMidiEvent* next_event = 0;
00110 
00111         // find the earliest event
00112         for (int i = 0; i < src_count; ++i) {
00113             JackMidiBuffer* buf = static_cast<JackMidiBuffer*>(src_buffers[i]);
00114             if (buf->mix_index >= buf->event_count)
00115                 continue;
00116             JackMidiEvent* e = &buf->events[buf->mix_index];
00117             if (!next_event || e->time < next_event->time) {
00118                 next_event = e;
00119                 next_buf = buf;
00120             }
00121         }
00122         assert(next_event != 0);
00123 
00124         // write the event
00125         jack_midi_data_t* dest = mix->ReserveEvent(next_event->time, next_event->size);
00126         if (!dest)
00127             break;
00128         memcpy(dest, next_event->GetData(next_buf), next_event->size);
00129         next_buf->mix_index++;
00130     }
00131     mix->lost_events += event_count - events_done;
00132 }
00133 
00134 const JackPortType gMidiPortType = {
00135         JACK_DEFAULT_MIDI_TYPE,
00136         MidiBufferInit,
00137         MidiBufferMixdown
00138 };
00139 
00140 } // namespace Jack

Generated on Thu Feb 14 11:16:02 2008 for Jackdmp by  doxygen 1.5.1