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 #include "JackError.h"
31 using Jack::JackCoreMidiOutputPort
;
33 JackCoreMidiOutputPort::JackCoreMidiOutputPort(double time_ratio
,
36 JackCoreMidiPort(time_ratio
)
38 read_queue
= new JackMidiBufferReadQueue();
39 std::auto_ptr
<JackMidiBufferReadQueue
> read_queue_ptr(read_queue
);
40 thread_queue
= new JackMidiAsyncQueue(max_bytes
, max_messages
);
41 std::auto_ptr
<JackMidiAsyncQueue
> thread_queue_ptr(thread_queue
);
42 thread
= new JackThread(this);
43 std::auto_ptr
<JackThread
> thread_ptr(thread
);
44 snprintf(semaphore_name
, sizeof(semaphore_name
), "coremidi_%p", this);
45 thread_queue_semaphore
= sem_open(semaphore_name
, O_CREAT
, 0777, 0);
46 if (thread_queue_semaphore
== (sem_t
*) SEM_FAILED
) {
47 throw std::runtime_error(strerror(errno
));
49 advance_schedule_time
= 0;
51 thread_queue_ptr
.release();
52 read_queue_ptr
.release();
55 JackCoreMidiOutputPort::~JackCoreMidiOutputPort()
58 sem_close(thread_queue_semaphore
);
59 sem_unlink(semaphore_name
);
65 JackCoreMidiOutputPort::Execute()
67 jack_midi_event_t
*event
= 0;
68 MIDIPacketList
*packet_list
= (MIDIPacketList
*) packet_buffer
;
70 MIDIPacket
*packet
= MIDIPacketListInit(packet_list
);
73 event
= GetCoreMidiEvent(true);
75 jack_midi_data_t
*data
= event
->buffer
;
76 jack_nframes_t send_frame
= event
->time
;
77 jack_time_t send_time
=
78 GetTimeFromFrames(send_frame
) - advance_schedule_time
;
79 size_t size
= event
->size
;
80 MIDITimeStamp timestamp
= GetTimeStampFromFrames(send_frame
);
81 packet
= MIDIPacketListAdd(packet_list
, PACKET_BUFFER_SIZE
, packet
,
82 timestamp
, size
, data
);
85 if (GetMicroSeconds() >= send_time
) {
89 event
= GetCoreMidiEvent(false);
93 packet
= MIDIPacketListAdd(packet_list
, sizeof(packet_buffer
),
95 GetTimeStampFromFrames(event
->time
),
96 event
->size
, event
->buffer
);
98 SendPacketList(packet_list
);
101 // We have a large system exclusive event. We'll have to send it
102 // out in multiple packets.
103 size_t bytes_sent
= 0;
105 packet
= MIDIPacketListInit(packet_list
);
107 size_t num_bytes
= 0;
108 for (; bytes_sent
< size
; bytes_sent
+= num_bytes
) {
109 size_t num_bytes
= size
- bytes_sent
;
111 // We use 256 because the MIDIPacket struct defines the
112 // size of the 'data' member to be 256 bytes. I believe
113 // this prevents packets from being dynamically allocated
114 // by 'MIDIPacketListAdd', but I might be wrong.
115 if (num_bytes
> 256) {
118 packet
= MIDIPacketListAdd(packet_list
,
119 sizeof(packet_buffer
), packet
,
120 timestamp
, num_bytes
,
126 if (! SendPacketList(packet_list
)) {
127 // An error occurred. The error message has already been
128 // output. We lick our wounds and move along.
131 } while (bytes_sent
< size
);
139 JackCoreMidiOutputPort::GetCoreMidiEvent(bool block
)
142 if (sem_trywait(thread_queue_semaphore
)) {
143 if (errno
!= EAGAIN
) {
144 jack_error("JackCoreMidiOutputPort::Execute - sem_trywait: %s",
150 while (sem_wait(thread_queue_semaphore
)) {
151 if (errno
!= EINTR
) {
152 jack_error("JackCoreMidiOutputPort::Execute - sem_wait: %s",
158 return thread_queue
->DequeueEvent();
162 JackCoreMidiOutputPort::GetTimeStampFromFrames(jack_nframes_t frames
)
164 return GetTimeFromFrames(frames
) / time_ratio
;
168 JackCoreMidiOutputPort::Init()
170 set_threaded_log_function();
172 // OSX only, values read in RT CoreMIDI thread
174 UInt64 computation
= 250 * 1000;
175 UInt64 constraint
= 500 * 1000;
176 thread
->SetParams(period
, computation
, constraint
);
178 if (thread
->AcquireSelfRealTime()) {
179 jack_error("JackCoreMidiOutputPort::Init - could not acquire realtime "
180 "scheduling. Continuing anyway.");
186 JackCoreMidiOutputPort::Initialize(const char *alias_name
,
187 const char *client_name
,
188 const char *driver_name
, int index
,
189 MIDIEndpointRef endpoint
,
190 SInt32 advance_schedule_time
)
192 JackCoreMidiPort::Initialize(alias_name
, client_name
, driver_name
, index
,
194 assert(advance_schedule_time
>= 0);
195 this->advance_schedule_time
= advance_schedule_time
;
199 JackCoreMidiOutputPort::ProcessJack(JackMidiBuffer
*port_buffer
,
200 jack_nframes_t frames
)
202 read_queue
->ResetMidiBuffer(port_buffer
);
203 for (jack_midi_event_t
*event
= read_queue
->DequeueEvent(); event
;
204 event
= read_queue
->DequeueEvent()) {
205 switch (thread_queue
->EnqueueEvent(event
, frames
)) {
206 case JackMidiWriteQueue::BUFFER_FULL
:
207 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
208 "queue buffer is full. Dropping event.");
210 case JackMidiWriteQueue::BUFFER_TOO_SMALL
:
211 jack_error("JackCoreMidiOutputPort::ProcessJack - The thread "
212 "queue couldn't enqueue a %d-byte event. Dropping "
213 "event.", event
->size
);
216 if (sem_post(thread_queue_semaphore
)) {
217 jack_error("JackCoreMidiOutputPort::ProcessJack - unexpected "
218 "error while posting to thread queue semaphore: %s",
226 JackCoreMidiOutputPort::Start()
228 bool result
= thread
->GetStatus() != JackThread::kIdle
;
230 result
= ! thread
->StartSync();
232 jack_error("JackCoreMidiOutputPort::Start - failed to start MIDI "
233 "processing thread.");
240 JackCoreMidiOutputPort::Stop()
242 bool result
= thread
->GetStatus() == JackThread::kIdle
;
244 result
= ! thread
->Kill();
246 jack_error("JackCoreMidiOutputPort::Stop - failed to stop MIDI "
247 "processing thread.");