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