1 /*
2 Copyright (C) 2011 Devin Anderson
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 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 General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include <cassert>
21 #include <memory>
22 
23 #include "JackALSARawMidiOutputPort.h"
24 #include "JackError.h"
25 
26 using Jack::JackALSARawMidiOutputPort;
27 
JackALSARawMidiOutputPort(const char * client_name,snd_rawmidi_info_t * info,size_t index,size_t max_bytes_per_poll,size_t max_bytes,size_t max_messages)28 JackALSARawMidiOutputPort::JackALSARawMidiOutputPort(const char* client_name,
29                                                      snd_rawmidi_info_t *info,
30                                                      size_t index,
31                                                      size_t max_bytes_per_poll,
32                                                      size_t max_bytes,
33                                                      size_t max_messages):
34     JackALSARawMidiPort(client_name, info, index, POLLOUT)
35 {
36     alsa_event = 0;
37     read_queue = new JackMidiBufferReadQueue();
38     std::unique_ptr<JackMidiBufferReadQueue> read_ptr(read_queue);
39     send_queue = new JackALSARawMidiSendQueue(rawmidi, max_bytes_per_poll);
40     std::unique_ptr<JackALSARawMidiSendQueue> send_ptr(send_queue);
41     thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
42     std::unique_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue);
43     raw_queue = new JackMidiRawOutputWriteQueue(send_queue, max_bytes,
44                                                 max_messages, max_messages);
45     thread_ptr.release();
46     send_ptr.release();
47     read_ptr.release();
48 }
49 
~JackALSARawMidiOutputPort()50 JackALSARawMidiOutputPort::~JackALSARawMidiOutputPort()
51 {
52     delete raw_queue;
53     delete read_queue;
54     delete send_queue;
55     delete thread_queue;
56 }
57 
58 bool
ProcessJack(JackMidiBuffer * port_buffer,jack_nframes_t frames)59 JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
60                                        jack_nframes_t frames)
61 {
62     read_queue->ResetMidiBuffer(port_buffer);
63     bool enqueued = false;
64     for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
65          event = read_queue->DequeueEvent()) {
66         switch (thread_queue->EnqueueEvent(event, frames)) {
67         case JackMidiWriteQueue::BUFFER_FULL:
68             jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread "
69                        "queue doesn't have enough room to enqueue a %d-byte "
70                        "event.  Dropping event.", event->size);
71             continue;
72         case JackMidiWriteQueue::BUFFER_TOO_SMALL:
73             jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread "
74                        "queue is too small to enqueue a %d-byte event.  "
75                        "Dropping event.", event->size);
76             continue;
77         default:
78             enqueued = true;
79         }
80     }
81     return enqueued ? TriggerQueueEvent() : true;
82 }
83 
84 bool
ProcessPollEvents(bool handle_output,bool timeout,jack_nframes_t * frame)85 JackALSARawMidiOutputPort::ProcessPollEvents(bool handle_output, bool timeout,
86                                              jack_nframes_t *frame)
87 {
88     int io_event;
89     int queue_event;
90     send_queue->ResetPollByteCount();
91     if (! handle_output) {
92         assert(timeout);
93         goto process_raw_queue;
94     }
95     io_event = GetIOPollEvent();
96     if (io_event == -1) {
97         return false;
98     }
99     queue_event = GetQueuePollEvent();
100     if (queue_event == -1) {
101         return false;
102     }
103     if (io_event || timeout) {
104     process_raw_queue:
105         // We call the 'Process' event early because there are events waiting
106         // to be processed that either need to be sent now, or before now.
107         raw_queue->Process();
108     } else if (! queue_event) {
109         return true;
110     }
111     if (! alsa_event) {
112         alsa_event = thread_queue->DequeueEvent();
113     }
114     for (; alsa_event; alsa_event = thread_queue->DequeueEvent()) {
115         switch (raw_queue->EnqueueEvent(alsa_event)) {
116         case JackMidiWriteQueue::BUFFER_TOO_SMALL:
117             jack_error("JackALSARawMidiOutputPort::ProcessQueues - The raw "
118                        "output queue couldn't enqueue a %d-byte event.  "
119                        "Dropping event.", alsa_event->size);
120             // Fallthrough on purpose.
121         case JackMidiWriteQueue::OK:
122             continue;
123         default:
124             ;
125         }
126 
127         // Try to free up some space by processing events early.
128         *frame = raw_queue->Process();
129 
130         switch (raw_queue->EnqueueEvent(alsa_event)) {
131         case JackMidiWriteQueue::BUFFER_FULL:
132             goto set_io_events;
133         case JackMidiWriteQueue::BUFFER_TOO_SMALL:
134             // This shouldn't happen.
135             assert(false);
136         default:
137             ;
138         }
139     }
140     *frame = raw_queue->Process();
141  set_io_events:
142     bool blocked = send_queue->IsBlocked();
143     SetIOEventsEnabled(blocked);
144     if (blocked) {
145         *frame = 0;
146     }
147     return true;
148 }
149