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