Jack2 1.9.6
|
00001 /* 00002 Copyright (C) 2009 Devin Anderson 00003 00004 This program is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU Lesser General Public License as published by 00006 the Free Software Foundation; either version 2.1 of the License, or 00007 (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU Lesser General Public License for more details. 00013 00014 You should have received a copy of the GNU Lesser General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00017 00018 */ 00019 00020 #include <cassert> 00021 00022 #include "JackError.h" 00023 #include "JackPhysicalMidiOutput.h" 00024 00025 namespace Jack { 00026 00027 JackPhysicalMidiOutput::JackPhysicalMidiOutput(size_t non_rt_buffer_size, 00028 size_t rt_buffer_size) 00029 { 00030 size_t datum_size = sizeof(jack_midi_data_t); 00031 assert(non_rt_buffer_size > 0); 00032 assert(rt_buffer_size > 0); 00033 output_ring = jack_ringbuffer_create((non_rt_buffer_size + 1) * 00034 datum_size); 00035 if (! output_ring) { 00036 throw std::bad_alloc(); 00037 } 00038 rt_output_ring = jack_ringbuffer_create((rt_buffer_size + 1) * 00039 datum_size); 00040 if (! rt_output_ring) { 00041 jack_ringbuffer_free(output_ring); 00042 throw std::bad_alloc(); 00043 } 00044 jack_ringbuffer_mlock(output_ring); 00045 jack_ringbuffer_mlock(rt_output_ring); 00046 running_status = 0; 00047 } 00048 00049 JackPhysicalMidiOutput::~JackPhysicalMidiOutput() 00050 { 00051 jack_ringbuffer_free(output_ring); 00052 jack_ringbuffer_free(rt_output_ring); 00053 } 00054 00055 jack_nframes_t 00056 JackPhysicalMidiOutput::Advance(jack_nframes_t frame) 00057 { 00058 return frame; 00059 } 00060 00061 inline jack_midi_data_t 00062 JackPhysicalMidiOutput::ApplyRunningStatus(jack_midi_data_t **buffer, 00063 size_t *size) 00064 { 00065 00066 // Stolen and modified from alsa/midi_pack.h 00067 00068 jack_midi_data_t status = (*buffer)[0]; 00069 if ((status >= 0x80) && (status < 0xf0)) { 00070 if (status == running_status) { 00071 (*buffer)++; 00072 (*size)--; 00073 } else { 00074 running_status = status; 00075 } 00076 } else if (status < 0xf8) { 00077 running_status = 0; 00078 } 00079 return status; 00080 } 00081 00082 void 00083 JackPhysicalMidiOutput::HandleEventLoss(JackMidiEvent *event) 00084 { 00085 jack_error("%d byte MIDI event lost", event->size); 00086 } 00087 00088 void 00089 JackPhysicalMidiOutput::Process(jack_nframes_t frames) 00090 { 00091 assert(port_buffer); 00092 jack_nframes_t current_frame = Advance(0); 00093 jack_nframes_t current_midi_event = 0; 00094 jack_midi_data_t datum; 00095 size_t datum_size = sizeof(jack_midi_data_t); 00096 JackMidiEvent *midi_event; 00097 jack_midi_data_t *midi_event_buffer; 00098 size_t midi_event_size; 00099 jack_nframes_t midi_events = port_buffer->event_count; 00100 00101 // First, send any realtime MIDI data that's left from last cycle. 00102 00103 if ((current_frame < frames) && 00104 jack_ringbuffer_read_space(rt_output_ring)) { 00105 00106 jack_log("JackPhysicalMidiOutput::Process (%d) - Sending buffered " 00107 "realtime data from last period.", current_frame); 00108 00109 current_frame = SendBufferedData(rt_output_ring, current_frame, 00110 frames); 00111 00112 jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", current_frame); 00113 00114 } 00115 00116 // Iterate through the events in this cycle. 00117 00118 for (; (current_midi_event < midi_events) && (current_frame < frames); 00119 current_midi_event++) { 00120 00121 // Once we're inside this loop, we know that the realtime buffer 00122 // is empty. As long as we don't find a realtime message, we can 00123 // concentrate on sending non-realtime data. 00124 00125 midi_event = &(port_buffer->events[current_midi_event]); 00126 jack_nframes_t midi_event_time = midi_event->time; 00127 midi_event_buffer = midi_event->GetData(port_buffer); 00128 midi_event_size = midi_event->size; 00129 datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); 00130 if (current_frame < midi_event_time) { 00131 00132 // We have time before this event is scheduled to be sent. 00133 // Send data in the non-realtime buffer. 00134 00135 if (jack_ringbuffer_read_space(output_ring)) { 00136 00137 jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " 00138 "buffered non-realtime data from last period.", 00139 current_frame); 00140 00141 current_frame = SendBufferedData(output_ring, current_frame, 00142 midi_event_time); 00143 00144 jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", 00145 current_frame); 00146 00147 } 00148 if (current_frame < midi_event_time) { 00149 00150 // We _still_ have time before this event is scheduled to 00151 // be sent. Let's send as much of this event as we can 00152 // (save for one byte, which will need to be sent at or 00153 // after its scheduled time). First though, we need to 00154 // make sure that we can buffer this data if we need to. 00155 // Otherwise, we might start sending a message that we 00156 // can't finish. 00157 00158 if (midi_event_size > 1) { 00159 if (jack_ringbuffer_write_space(output_ring) < 00160 ((midi_event_size - 1) * datum_size)) { 00161 HandleEventLoss(midi_event); 00162 continue; 00163 } 00164 00165 // Send as much of the event as possible (save for one 00166 // byte). 00167 00168 do { 00169 00170 jack_log("JackPhysicalMidiOutput::Process (%d) - " 00171 "Sending unbuffered event byte early.", 00172 current_frame); 00173 00174 current_frame = Send(current_frame, 00175 *midi_event_buffer); 00176 00177 jack_log("JackPhysicalMidiOutput::Process (%d) - " 00178 "Sent.", current_frame); 00179 00180 midi_event_buffer++; 00181 midi_event_size--; 00182 if (current_frame >= midi_event_time) { 00183 00184 // The event we're processing must be a 00185 // non-realtime event. It has more than one 00186 // byte. 00187 00188 goto buffer_non_realtime_data; 00189 } 00190 } while (midi_event_size > 1); 00191 } 00192 00193 jack_log("JackPhysicalMidiOutput::Process (%d) - Advancing to " 00194 ">= %d", current_frame, midi_event_time); 00195 00196 current_frame = Advance(midi_event_time); 00197 00198 jack_log("JackPhysicalMidiOutput::Process (%d) - Advanced.", 00199 current_frame); 00200 00201 } 00202 } 00203 00204 // If the event is realtime, then we'll send the event now. 00205 // Otherwise, we attempt to put the rest of the event bytes in the 00206 // non-realtime buffer. 00207 00208 if (datum >= 0xf8) { 00209 00210 jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " 00211 "unbuffered realtime event.", current_frame); 00212 00213 current_frame = Send(current_frame, datum); 00214 00215 jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", 00216 current_frame); 00217 00218 } else if (jack_ringbuffer_write_space(output_ring) >= 00219 (midi_event_size * datum_size)) { 00220 buffer_non_realtime_data: 00221 00222 jack_log("JackPhysicalMidiOutput::Process (%d) - Buffering %d " 00223 "byte(s) of non-realtime data.", current_frame, 00224 midi_event_size); 00225 00226 jack_ringbuffer_write(output_ring, 00227 (const char *) midi_event_buffer, 00228 midi_event_size); 00229 } else { 00230 HandleEventLoss(midi_event); 00231 } 00232 } 00233 00234 if (current_frame < frames) { 00235 00236 // If we have time left to send data, then we know that all of the 00237 // data in the realtime buffer has been sent, and that all of the 00238 // non-realtime messages have either been sent, or buffered. We 00239 // use whatever time is left to send data in the non-realtime 00240 // buffer. 00241 00242 if (jack_ringbuffer_read_space(output_ring)) { 00243 00244 jack_log("JackPhysicalMidiOutput::Process (%d) - All events " 00245 "processed. Sending buffered non-realtime data.", 00246 current_frame); 00247 00248 current_frame = SendBufferedData(output_ring, current_frame, 00249 frames); 00250 00251 jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", 00252 current_frame); 00253 00254 } 00255 } else { 00256 00257 // Since we have no time left, we need to put all remaining midi 00258 // events in their appropriate buffers, and send them next period. 00259 00260 for (; current_midi_event < midi_events; current_midi_event++) { 00261 midi_event = &(port_buffer->events[current_midi_event]); 00262 midi_event_buffer = midi_event->GetData(port_buffer); 00263 midi_event_size = midi_event->size; 00264 datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); 00265 if (datum >= 0xf8) { 00266 00267 // Realtime. 00268 00269 if (jack_ringbuffer_write_space(rt_output_ring) >= 00270 datum_size) { 00271 00272 jack_log("JackPhysicalMidiOutput::Process - Buffering " 00273 "realtime event for next period."); 00274 00275 jack_ringbuffer_write(rt_output_ring, 00276 (const char *) &datum, datum_size); 00277 continue; 00278 } 00279 } else { 00280 00281 // Non-realtime. 00282 00283 if (jack_ringbuffer_write_space(output_ring) >= 00284 (midi_event_size * datum_size)) { 00285 00286 jack_log("JackPhysicalMidiOutput::Process - Buffering " 00287 "non-realtime event for next period."); 00288 00289 jack_ringbuffer_write(output_ring, 00290 (const char *) midi_event_buffer, 00291 midi_event_size * datum_size); 00292 continue; 00293 } 00294 } 00295 HandleEventLoss(midi_event); 00296 } 00297 } 00298 } 00299 00300 jack_nframes_t 00301 JackPhysicalMidiOutput::SendBufferedData(jack_ringbuffer_t *buffer, 00302 jack_nframes_t current_frame, 00303 jack_nframes_t boundary) 00304 { 00305 assert(buffer); 00306 assert(current_frame < boundary); 00307 size_t datum_size = sizeof(jack_midi_data_t); 00308 size_t data_length = jack_ringbuffer_read_space(buffer) / datum_size; 00309 for (size_t i = 0; i < data_length; i++) { 00310 jack_midi_data_t datum; 00311 jack_ringbuffer_read(buffer, (char *) &datum, datum_size); 00312 current_frame = Send(current_frame, datum); 00313 if (current_frame >= boundary) { 00314 break; 00315 } 00316 } 00317 return current_frame; 00318 } 00319 00320 }