Jack2 1.9.6

JackPhysicalMidiOutput.cpp

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 }