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