1 /*
2 Copyright (C) 2010 Devin Anderson
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU Lesser General Public License for more details.
13 
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 
18 */
19 
20 #include <memory>
21 #include <new>
22 
23 #include "JackError.h"
24 #include "JackMidiRawOutputWriteQueue.h"
25 #include "JackMidiUtil.h"
26 
27 using Jack::JackMidiRawOutputWriteQueue;
28 
29 #define STILL_TIME(c, b) ((! (b)) || ((c) < (b)))
30 
31 JackMidiRawOutputWriteQueue::
JackMidiRawOutputWriteQueue(JackMidiSendQueue * send_queue,size_t non_rt_size,size_t max_non_rt_messages,size_t max_rt_messages)32 JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size,
33                             size_t max_non_rt_messages, size_t max_rt_messages)
34 {
35     non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages);
36     std::unique_ptr<JackMidiAsyncQueue> non_rt_ptr(non_rt_queue);
37     rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages);
38     std::unique_ptr<JackMidiAsyncQueue> rt_ptr(rt_queue);
39     non_rt_event = 0;
40     rt_event = 0;
41     running_status = 0;
42     this->send_queue = send_queue;
43     rt_ptr.release();
44     non_rt_ptr.release();
45 }
46 
~JackMidiRawOutputWriteQueue()47 JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue()
48 {
49     delete non_rt_queue;
50     delete rt_queue;
51 }
52 
53 void
DequeueNonRealtimeEvent()54 JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent()
55 {
56     non_rt_event = non_rt_queue->DequeueEvent();
57     if (non_rt_event) {
58         non_rt_event_time = non_rt_event->time;
59         running_status = ApplyRunningStatus(non_rt_event, running_status);
60     }
61 }
62 
63 void
DequeueRealtimeEvent()64 JackMidiRawOutputWriteQueue::DequeueRealtimeEvent()
65 {
66     rt_event = rt_queue->DequeueEvent();
67     if (rt_event) {
68         rt_event_time = rt_event->time;
69     }
70 }
71 
72 Jack::JackMidiWriteQueue::EnqueueResult
EnqueueEvent(jack_nframes_t time,size_t size,jack_midi_data_t * buffer)73 JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
74                                           jack_midi_data_t *buffer)
75 {
76     JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue :
77         non_rt_queue;
78     return queue->EnqueueEvent(time, size, buffer);
79 }
80 
81 void
HandleWriteQueueBug(jack_nframes_t time,jack_midi_data_t byte)82 JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time,
83                                                  jack_midi_data_t byte)
84 {
85     jack_error("JackMidiRawOutputWriteQueue::HandleWriteQueueBug - **BUG** "
86                "The write queue told us that it couldn't enqueue a 1-byte "
87                "MIDI event scheduled for frame '%d'.  This is probably a bug "
88                "in the write queue implementation.", time);
89 }
90 
91 jack_nframes_t
Process(jack_nframes_t boundary_frame)92 JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame)
93 {
94     if (! non_rt_event) {
95         DequeueNonRealtimeEvent();
96     }
97     if (! rt_event) {
98         DequeueRealtimeEvent();
99     }
100     while (rt_event) {
101         jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
102         if ((rt_event_time > current_frame) && non_rt_event &&
103             (non_rt_event_time < rt_event_time)) {
104             if (! SendNonRTBytes(rt_event_time < boundary_frame ?
105                                  rt_event_time : boundary_frame)) {
106                 return non_rt_event_time;
107             }
108             current_frame = send_queue->GetNextScheduleFrame();
109         }
110         if (! STILL_TIME(current_frame, boundary_frame)) {
111             return (! non_rt_event) ? rt_event_time :
112                 non_rt_event_time < rt_event_time ? non_rt_event_time :
113                 rt_event_time;
114         }
115         if (! SendByte(rt_event_time, *(rt_event->buffer))) {
116             return rt_event_time;
117         }
118         DequeueRealtimeEvent();
119     }
120     SendNonRTBytes(boundary_frame);
121     return non_rt_event ? non_rt_event_time : 0;
122 }
123 
124 bool
SendByte(jack_nframes_t time,jack_midi_data_t byte)125 JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time,
126                                       jack_midi_data_t byte)
127 {
128     switch (send_queue->EnqueueEvent(time, 1, &byte)) {
129     case BUFFER_TOO_SMALL:
130         HandleWriteQueueBug(time, byte);
131     case OK:
132         return true;
133     default:
134         // This is here to stop compilers from warning us about not handling
135         // enumeration values.
136         ;
137     }
138     return false;
139 }
140 
141 bool
SendNonRTBytes(jack_nframes_t boundary_frame)142 JackMidiRawOutputWriteQueue::SendNonRTBytes(jack_nframes_t boundary_frame)
143 {
144     while (non_rt_event) {
145         for (; non_rt_event->size;
146              (non_rt_event->size)--, (non_rt_event->buffer)++) {
147             jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
148             if (! STILL_TIME(current_frame, boundary_frame)) {
149                 return true;
150             }
151             if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) {
152                 return false;
153             }
154         }
155         DequeueNonRealtimeEvent();
156     }
157     return true;
158 }
159