Jack2 1.9.6
|
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