2 Copyright (C) 2011 Devin Anderson
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.
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.
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.
26 #include "JackCoreMidiOutputPort.h"
27 #include "JackMidiUtil.h"
29 using Jack::JackCoreMidiOutputPort
;
31 JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio
,
34 JackCoreMidiPort(time_ratio
)
36 read_queue
= new JackMidiBufferReadQueue();
37 std::auto_ptr
<JackMidiBufferReadQueue
> read_queue_ptr(read_queue
);
38 thread_queue
= new JackMidiAsyncQueue(max_bytes
, max_messages
);
39 std::auto_ptr
<JackMidiAsyncQueue
> thread_queue_ptr(thread_queue
);
40 thread
= new JackThread(this);
41 std::auto_ptr
<JackThread
> thread_ptr(thread
);
42 sprintf(semaphore_name
, "coremidi_%p", this);
43 thread_queue_semaphore
= sem_open(semaphore_name
, O_CREAT
, 0777, 0);
44 if (thread_queue_semaphore
== (sem_t
*) SEM_FAILED
) {
45 throw std::runtime_error(strerror(errno
));
48 thread_queue_ptr
.release();
49 read_queue_ptr
.release();
52 JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
56 sem_destroy(thread_queue_semaphore
);
57 sem_unlink(semaphore_name
);
63 JackCoreMidiOutputPort::Execute()
65 jack_midi_event_t
*event
= 0;
66 MIDIPacketList
*packet_list
= (MIDIPacketList
*) packet_buffer
;
68 MIDIPacket
*packet
= MIDIPacketListInit(packet_list
);
72 event
= GetCoreMidiEvent(true);
74 jack_midi_data_t
*data
= event
->buffer
;
76 // This is the latest time that the packet list can be sent out. We
77 // may want to consider subtracting some frames to leave room for the
78 // CoreMIDI driver/client to handle all of the events. There's a
79 // property called 'kMIDIPropertyAdvanceScheduleTimeMuSec' that might
80 // be useful in this case.
81 jack_nframes_t send_time
= event
->time
;
83 size_t size
= event
->size
;
84 MIDITimeStamp timestamp
= GetTimeStampFromFrames(send_time
);
85 packet
= MIDIPacketListAdd(packet_list
, PACKET_BUFFER_SIZE
, packet
,
86 timestamp
, size
, data
);
88 while (GetCurrentFrame() < send_time
) {
89 event
= GetCoreMidiEvent(false);
93 packet
= MIDIPacketListAdd(packet_list
, sizeof(packet_buffer
),
95 GetTimeStampFromFrames(event
->time
),
96 event
->size
, event
->buffer
);
101 SendPacketList(packet_list
);
104 // We have a large system exclusive event. We'll have to send it
105 // out in multiple packets.
106 size_t bytes_sent
= 0;
108 packet
= MIDIPacketListInit(packet_list
);
110 size_t num_bytes
= 0;
111 for (; bytes_sent
< size
; bytes_sent
+= num_bytes
) {
112 size_t num_bytes
= size
- bytes_sent
;
114 // We use 256 because the MIDIPacket struct defines the
115 // size of the 'data' member to be 256 bytes. I believe
116 // this prevents packets from being dynamically allocated
117 // by 'MIDIPacketListAdd', but I might be wrong.
118 if (num_bytes
> 256) {
121 packet
= MIDIPacketListAdd(packet_list
,
122 sizeof(packet_buffer
), packet
,
123 timestamp
, num_bytes
,
129 if (! SendPacketList(packet_list
)) {
130 // An error occurred. The error message has already been
131 // output. We lick our wounds and move along.
134 } while (bytes_sent
< size
);
142 JackCoreMidiOutputPort::GetCoreMidiEvent(bool block
)
145 if (sem_trywait(thread_queue_semaphore
)) {
146 if (errno
!= ETIMEDOUT
) {
147 jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s",
153 while (sem_wait(thread_queue_semaphore
)) {
154 if (errno
!= EINTR
) {
155 jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s",
161 return thread_queue
->DequeueEvent();
165 JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames
)
167 return GetTimeFromFrames(frames
) / time_ratio
;
171 JackCoreMidiOutputPort::Init()
173 set_threaded_log_function();
177 UInt64 computation
= 500 * 1000;
178 UInt64 constraint
= 500 * 1000;
179 thread
->SetParams(period
, computation
, constraint
);
181 // Use the server priority : y
182 if (thread
->AcquireSelfRealTime()) {
183 jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
184 "scheduling. Continuing anyway.");
190 JackCoreMidiOutputPort::Initialize(const char *alias_name
,
191 const char *client_name
,
192 const char *driver_name
, int index
,
193 MIDIEndpointRef endpoint
)
195 JackCoreMidiPort::Initialize(alias_name
, client_name
, driver_name
, index
,
200 JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer
*port_buffer
,
201 jack_nframes_t frames
)
203 read_queue
->ResetMidiBuffer(port_buffer
);
204 for (jack_midi_event_t
*event
= read_queue
->DequeueEvent(); event
;
205 event
= read_queue
->DequeueEvent()) {
206 switch (thread_queue
->EnqueueEvent(event
, frames
)) {
207 case JackMidiWriteQueue::BUFFER_FULL
:
208 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
209 "queue buffer is full. Dropping event.");
211 case JackMidiWriteQueue::BUFFER_TOO_SMALL
:
212 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
213 "queue couldn't enqueue a %d-byte event. Dropping "
214 "event.", event
->size
);
217 if (sem_post(thread_queue_semaphore
)) {
218 jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected "
219 "error while posting to thread queue semaphore: %s",
227 JackCoreMidiOutputPort::Start()
229 bool result
= thread
->GetStatus() != JackThread::kIdle
;
231 result
= ! thread
->StartSync();
233 jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI "
234 "processing thread.");
241 JackCoreMidiOutputPort::Stop()
243 bool result
= thread
->GetStatus() == JackThread::kIdle
;
245 result
= ! thread
->Kill();
247 jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI "
248 "processing thread.");