Browse Source

Optimize the 'alsarawmidi' driver for I/O over multiple ports. Make 'GetCurrentFrame' call 'GetMicroSeconds' earlier in the function call.

tags/1.9.8
Devin Anderson 14 years ago
parent
commit
52d0665023
17 changed files with 462 additions and 300 deletions
  1. +0
    -4
      common/JackMidiRawOutputWriteQueue.cpp
  2. +2
    -1
      common/JackMidiUtil.cpp
  3. +107
    -66
      linux/alsarawmidi/JackALSARawMidiDriver.cpp
  4. +1
    -0
      linux/alsarawmidi/JackALSARawMidiDriver.h
  5. +50
    -71
      linux/alsarawmidi/JackALSARawMidiInputPort.cpp
  6. +3
    -5
      linux/alsarawmidi/JackALSARawMidiInputPort.h
  7. +67
    -89
      linux/alsarawmidi/JackALSARawMidiOutputPort.cpp
  8. +5
    -7
      linux/alsarawmidi/JackALSARawMidiOutputPort.h
  9. +100
    -45
      linux/alsarawmidi/JackALSARawMidiPort.cpp
  10. +20
    -7
      linux/alsarawmidi/JackALSARawMidiPort.h
  11. +0
    -0
      linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp
  12. +0
    -0
      linux/alsarawmidi/JackALSARawMidiReceiveQueue.h
  13. +15
    -1
      linux/alsarawmidi/JackALSARawMidiSendQueue.cpp
  14. +7
    -1
      linux/alsarawmidi/JackALSARawMidiSendQueue.h
  15. +43
    -0
      linux/alsarawmidi/JackALSARawMidiUtil.cpp
  16. +36
    -0
      linux/alsarawmidi/JackALSARawMidiUtil.h
  17. +6
    -3
      linux/wscript

+ 0
- 4
common/JackMidiRawOutputWriteQueue.cpp View File

@@ -129,10 +129,6 @@ JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time,
case BUFFER_TOO_SMALL:
HandleWriteQueueBug(time, byte);
case OK:

jack_info("JackMidiRawOutputWriteQueue::SendByte - '%d', '%d'", time,
GetCurrentFrame());

return true;
default:
// This is here to stop compilers from warning us about not handling


+ 2
- 1
common/JackMidiUtil.cpp View File

@@ -55,10 +55,11 @@ Jack::ApplyRunningStatus(jack_midi_event_t *event,
jack_nframes_t
Jack::GetCurrentFrame()
{
jack_time_t time = GetMicroSeconds();
JackEngineControl *control = GetEngineControl();
JackTimer timer;
control->ReadFrameTime(&timer);
return timer.Time2Frames(GetMicroSeconds(), control->fBufferSize);
return timer.Time2Frames(time, control->fBufferSize);
}

jack_nframes_t


+ 107
- 66
linux/alsarawmidi/JackALSARawMidiDriver.cpp View File

@@ -24,6 +24,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include <alsa/asoundlib.h>

#include "JackALSARawMidiDriver.h"
#include "JackALSARawMidiUtil.h"
#include "JackEngineControl.h"
#include "JackError.h"
#include "JackMidiUtil.h"
@@ -43,6 +44,7 @@ JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name,
fPlaybackChannels = 0;
input_ports = 0;
output_ports = 0;
output_port_timeouts = 0;
poll_fds = 0;
}

@@ -72,7 +74,7 @@ JackALSARawMidiDriver::Attach()
if (index == NO_PORT) {
jack_error("JackALSARawMidiDriver::Attach - cannot register input "
"port with name '%s'.", name);
// X: Do we need to deallocate ports?
// XX: Do we need to deallocate ports?
return -1;
}
alias = input_port->GetAlias();
@@ -99,7 +101,7 @@ JackALSARawMidiDriver::Attach()
if (index == NO_PORT) {
jack_error("JackALSARawMidiDriver::Attach - cannot register "
"output port with name '%s'.", name);
// X: Do we need to deallocate ports?
// XX: Do we need to deallocate ports?
return -1;
}
alias = output_port->GetAlias();
@@ -143,21 +145,31 @@ JackALSARawMidiDriver::Execute()
{
jack_nframes_t timeout_frame = 0;
for (;;) {
jack_nframes_t process_frame;
unsigned short revents;
struct timespec timeout;
struct timespec *timeout_ptr;
if (! timeout_frame) {
timeout_ptr = 0;
} else {

// We use a relative timeout here. By the time ppoll is called,
// the wait time is larger than it needs to be. Maybe we should
// use a timerfd instead.
// The timeout value is relative to the time that
// 'GetMicroSeconds()' is called, not the time that 'poll()' is
// called. This means that the amount of time that passes between
// 'GetMicroSeconds()' and 'ppoll()' is time that will be lost
// while waiting for 'poll() to timeout.
//
// I tried to replace the timeout with a 'timerfd' with absolute
// times, but, strangely, it actually slowed things down, and made
// the code a lot more complicated.
//
// Also, ppoll is inefficient in certain cases. We might want to
// consider replacing it with epoll, though I'm uncertain as to
// whether or not it would be more efficient in this case.
// Another alternative would be to use 'epoll' interface. The
// problem with the 'epoll' interface is that the timeout
// resolution of 'epoll_wait()' is set in milliseconds. We need
// microsecond resolution. Without microsecond resolution, we
// impose the same jitter as USB MIDI.
//
// Of course, a bigger problem is that 'ppoll()' returns later than
// the wait time. The problem can be minimized with high precision
// timers.

timeout_ptr = &timeout;
jack_time_t next_time = GetTimeFromFrames(timeout_frame);
@@ -171,7 +183,14 @@ JackALSARawMidiDriver::Execute()
timeout.tv_nsec = (wait_time % 1000000) * 1000;
}
}
if (ppoll(poll_fds, poll_fd_count, timeout_ptr, 0) == -1) {
int poll_result = ppoll(poll_fds, poll_fd_count, timeout_ptr, 0);

// Getting the current frame value here allows us to use it for
// incoming MIDI bytes. This makes sense, as the data has already
// arrived at this point.
jack_nframes_t current_frame = GetCurrentFrame();

if (poll_result == -1) {
if (errno == EINTR) {
continue;
}
@@ -179,44 +198,73 @@ JackALSARawMidiDriver::Execute()
strerror(errno));
break;
}

if (timeout_ptr) {
jack_info("JackALSARawMidiDriver::Execute - '%d', '%d'",
timeout_frame, GetCurrentFrame());
jack_nframes_t port_timeout;
timeout_frame = 0;
if (! poll_result) {

// No I/O events occurred. So, only handle timeout events on
// output ports.

for (int i = 0; i < fPlaybackChannels; i++) {
port_timeout = output_port_timeouts[i];
if (port_timeout && (port_timeout <= current_frame)) {
if (! output_ports[i]->ProcessPollEvents(false, true,
&port_timeout)) {
jack_error("JackALSARawMidiDriver::Execute - a fatal "
"error occurred while processing ALSA "
"output events.");
goto cleanup;
}
output_port_timeouts[i] = port_timeout;
}
if (port_timeout && ((! timeout_frame) ||
(port_timeout < timeout_frame))) {
timeout_frame = port_timeout;
}
}
continue;
}

revents = poll_fds[0].revents;
if (revents & POLLHUP) {
// Driver is being stopped.
break;
}
if (revents & (~ POLLIN)) {
jack_error("JackALSARawMidiDriver::Execute - unexpected poll "
"event on pipe file descriptor.");
// See if it's time to shutdown.
unsigned short revents = poll_fds[0].revents;
if (revents) {
if (revents & (~ POLLHUP)) {
jack_error("JackALSARawMidiDriver::Execute - unexpected poll "
"event on pipe file descriptor.");
}
break;
}
timeout_frame = 0;

// Handle I/O events *and* timeout events on output ports.

for (int i = 0; i < fPlaybackChannels; i++) {
if (! output_ports[i]->ProcessALSA(fds[0], &process_frame)) {
port_timeout = output_port_timeouts[i];
bool timeout = port_timeout && (port_timeout <= current_frame);
if (! output_ports[i]->ProcessPollEvents(true, timeout,
&port_timeout)) {
jack_error("JackALSARawMidiDriver::Execute - a fatal error "
"occurred while processing ALSA output events.");
goto cleanup;
}
if (process_frame && ((! timeout_frame) ||
(process_frame < timeout_frame))) {
timeout_frame = process_frame;
output_port_timeouts[i] = port_timeout;
if (port_timeout && ((! timeout_frame) ||
(port_timeout < timeout_frame))) {
timeout_frame = port_timeout;
}
}

// Handle I/O events on input ports. We handle these last because we
// already computed the arrival time above, and will impose a delay on
// the events by 'period-size' frames anyway, which gives us a bit of
// borrowed time.

for (int i = 0; i < fCaptureChannels; i++) {
if (! input_ports[i]->ProcessALSA(&process_frame)) {
if (! input_ports[i]->ProcessPollEvents(current_frame)) {
jack_error("JackALSARawMidiDriver::Execute - a fatal error "
"occurred while processing ALSA input events.");
goto cleanup;
}
if (process_frame && ((! timeout_frame) ||
(process_frame < timeout_frame))) {
timeout_frame = process_frame;
}
}
}
cleanup:
@@ -460,39 +508,27 @@ JackALSARawMidiDriver::Start()
}
try {
poll_fds = new pollfd[poll_fd_count];
} catch (std::bad_alloc e) {
} catch (std::exception e) {
jack_error("JackALSARawMidiDriver::Start - creating poll descriptor "
"structures failed: %s", e.what());
return -1;
}
int flags;
if (fPlaybackChannels) {
try {
output_port_timeouts = new jack_nframes_t[fPlaybackChannels];
} catch (std::exception e) {
jack_error("JackALSARawMidiDriver::Start - creating array for "
"output port timeout values failed: %s", e.what());
goto free_poll_descriptors;
}
}
struct pollfd *poll_fd_iter;
if (pipe(fds) == -1) {
try {
CreateNonBlockingPipe(fds);
} catch (std::exception e) {
jack_error("JackALSARawMidiDriver::Start - while creating wake pipe: "
"%s", strerror(errno));
goto free_poll_descriptors;
}
flags = fcntl(fds[0], F_GETFL);
if (flags == -1) {
jack_error("JackALSARawMidiDriver::Start = while getting flags for "
"read file descriptor: %s", strerror(errno));
goto close_fds;
}
if (fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) == -1) {
jack_error("JackALSARawMidiDriver::Start - while setting non-blocking "
"mode for read file descriptor: %s", strerror(errno));
goto close_fds;
}
flags = fcntl(fds[1], F_GETFL);
if (flags == -1) {
jack_error("JackALSARawMidiDriver::Start = while getting flags for "
"write file descriptor: %s", strerror(errno));
goto close_fds;
}
if (fcntl(fds[1], F_SETFL, flags | O_NONBLOCK) == -1) {
jack_error("JackALSARawMidiDriver::Start - while setting non-blocking "
"mode for write file descriptor: %s", strerror(errno));
goto close_fds;
"%s", e.what());
goto free_output_port_timeouts;
}
poll_fds[0].events = POLLERR | POLLIN | POLLNVAL;
poll_fds[0].fd = fds[0];
@@ -506,6 +542,7 @@ JackALSARawMidiDriver::Start()
JackALSARawMidiOutputPort *output_port = output_ports[i];
output_port->PopulatePollDescriptors(poll_fd_iter);
poll_fd_iter += output_port->GetPollDescriptorCount();
output_port_timeouts[i] = 0;
}

jack_info("JackALSARawMidiDriver::Start - starting ALSA thread ...");
@@ -518,11 +555,13 @@ JackALSARawMidiDriver::Start()
}
jack_error("JackALSARawMidiDriver::Start - failed to start MIDI "
"processing thread.");
close_fds:
close(fds[1]);
DestroyNonBlockingPipe(fds);
fds[1] = -1;
close(fds[0]);
fds[0] = -1;
free_output_port_timeouts:
delete[] output_port_timeouts;
output_port_timeouts = 0;
free_poll_descriptors:
delete[] poll_fds;
poll_fds = 0;
@@ -558,6 +597,10 @@ JackALSARawMidiDriver::Stop()
close(fds[0]);
fds[0] = -1;
}
if (output_port_timeouts) {
delete[] output_port_timeouts;
output_port_timeouts = 0;
}
if (poll_fds) {
delete[] poll_fds;
poll_fds = 0;
@@ -573,10 +616,8 @@ int
JackALSARawMidiDriver::Write()
{
jack_nframes_t buffer_size = fEngineControl->fBufferSize;
int write_fd = fds[1];
for (int i = 0; i < fPlaybackChannels; i++) {
if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size,
write_fd)) {
if (! output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size)) {
return -1;
}
}


+ 1
- 0
linux/alsarawmidi/JackALSARawMidiDriver.h View File

@@ -40,6 +40,7 @@ namespace Jack {
int fds[2];
JackALSARawMidiInputPort **input_ports;
JackALSARawMidiOutputPort **output_ports;
jack_nframes_t *output_port_timeouts;
nfds_t poll_fd_count;
struct pollfd *poll_fds;
JackThread *thread;


+ 50
- 71
linux/alsarawmidi/JackALSARawMidiInputPort.cpp View File

@@ -17,6 +17,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <cassert>
#include <memory>

#include "JackALSARawMidiInputPort.h"
@@ -28,7 +29,7 @@ JackALSARawMidiInputPort::JackALSARawMidiInputPort(snd_rawmidi_info_t *info,
size_t index,
size_t max_bytes,
size_t max_messages):
JackALSARawMidiPort(info, index)
JackALSARawMidiPort(info, index, POLLIN)
{
alsa_event = 0;
jack_event = 0;
@@ -53,91 +54,69 @@ JackALSARawMidiInputPort::~JackALSARawMidiInputPort()
delete write_queue;
}

jack_nframes_t
JackALSARawMidiInputPort::EnqueueALSAEvent()
{
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_FULL:
// Processing events early might free up some space in the raw queue.
raw_queue->Process();
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiInputPort::Process - **BUG** "
"JackMidiRawInputWriteQueue::EnqueueEvent returned "
"`BUFFER_FULL` and then returned `BUFFER_TOO_SMALL` "
"after a `Process()` call.");
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
return 0;
default:
;
}
break;
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiInputPort::Execute - The thread queue "
"couldn't enqueue a %d-byte packet. Dropping event.",
alsa_event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
return 0;
default:
;
}
jack_nframes_t alsa_time = alsa_event->time;
jack_nframes_t next_time = raw_queue->Process();
return (next_time < alsa_time) ? next_time : alsa_time;
}

bool
JackALSARawMidiInputPort::ProcessALSA(jack_nframes_t *frame)
{
unsigned short revents;
if (! ProcessPollEvents(&revents)) {
return false;
}
if (alsa_event) {
*frame = EnqueueALSAEvent();
if (*frame) {
return true;
}
}
if (revents & POLLIN) {
for (alsa_event = receive_queue->DequeueEvent(); alsa_event;
alsa_event = receive_queue->DequeueEvent()) {
*frame = EnqueueALSAEvent();
if (*frame) {
return true;
}
}
}
*frame = raw_queue->Process();
return true;
}

bool
JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer,
jack_nframes_t frames)
{
write_queue->ResetMidiBuffer(port_buffer, frames);
bool dequeued = false;
if (! jack_event) {
jack_event = thread_queue->DequeueEvent();
goto dequeue_event;
}
for (; jack_event; jack_event = thread_queue->DequeueEvent()) {

// We add `frames` so that MIDI events align with audio as closely as
// possible.
for (;;) {
switch (write_queue->EnqueueEvent(jack_event, frames)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiInputPort::ProcessJack - The write "
"queue couldn't enqueue a %d-byte event. Dropping "
"event.", jack_event->size);
// Fallthrough on purpose
// Fallthrough on purpose.
case JackMidiWriteQueue::OK:
continue;
break;
default:
;
goto trigger_queue_event;
}
dequeue_event:
jack_event = thread_queue->DequeueEvent();
if (! jack_event) {
break;
}
dequeued = true;
}
trigger_queue_event:
return dequeued ? TriggerQueueEvent() : true;
}

bool
JackALSARawMidiInputPort::ProcessPollEvents(jack_nframes_t current_frame)
{
if (GetQueuePollEvent() == -1) {
return false;
}
int io_event = GetIOPollEvent();
switch (io_event) {
case -1:
return false;
case 1:
alsa_event = receive_queue->DequeueEvent();
}
if (alsa_event) {
size_t size = alsa_event->size;
size_t space = raw_queue->GetAvailableSpace();
bool enough_room = space >= size;
if (enough_room) {
assert(raw_queue->EnqueueEvent(current_frame, size,
alsa_event->buffer) ==
JackMidiWriteQueue::OK);
alsa_event = 0;
} else if (space) {
assert(raw_queue->EnqueueEvent(current_frame, space,
alsa_event->buffer) ==
JackMidiWriteQueue::OK);
alsa_event->buffer += space;
alsa_event->size -= space;
}
break;
SetIOEventsEnabled(enough_room);
}
raw_queue->Process();
return true;
}

+ 3
- 5
linux/alsarawmidi/JackALSARawMidiInputPort.h View File

@@ -39,21 +39,19 @@ namespace Jack {
JackMidiAsyncQueue *thread_queue;
JackMidiBufferWriteQueue *write_queue;

jack_nframes_t
EnqueueALSAEvent();

public:

JackALSARawMidiInputPort(snd_rawmidi_info_t *info, size_t index,
size_t max_bytes=4096,
size_t max_messages=1024);

~JackALSARawMidiInputPort();

bool
ProcessALSA(jack_nframes_t *frame);
ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames);

bool
ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames);
ProcessPollEvents(jack_nframes_t current_frame);

};



+ 67
- 89
linux/alsarawmidi/JackALSARawMidiOutputPort.cpp View File

@@ -17,6 +17,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <cassert>
#include <memory>

#include "JackALSARawMidiOutputPort.h"
@@ -25,15 +26,15 @@ using Jack::JackALSARawMidiOutputPort;

JackALSARawMidiOutputPort::JackALSARawMidiOutputPort(snd_rawmidi_info_t *info,
size_t index,
size_t max_bytes_per_poll,
size_t max_bytes,
size_t max_messages):
JackALSARawMidiPort(info, index)
JackALSARawMidiPort(info, index, POLLOUT)
{
alsa_event = 0;
blocked = false;
read_queue = new JackMidiBufferReadQueue();
std::auto_ptr<JackMidiBufferReadQueue> read_ptr(read_queue);
send_queue = new JackALSARawMidiSendQueue(rawmidi);
send_queue = new JackALSARawMidiSendQueue(rawmidi, max_bytes_per_poll);
std::auto_ptr<JackALSARawMidiSendQueue> send_ptr(send_queue);
thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue);
@@ -52,117 +53,94 @@ JackALSARawMidiOutputPort::~JackALSARawMidiOutputPort()
delete thread_queue;
}

jack_midi_event_t *
JackALSARawMidiOutputPort::DequeueALSAEvent(int read_fd)
bool
JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
jack_nframes_t frames)
{
jack_midi_event_t *event = thread_queue->DequeueEvent();
if (event) {
char c;
ssize_t result = read(read_fd, &c, 1);
if (! result) {
jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - **BUG** "
"An event was dequeued from the thread queue, but no "
"byte was available for reading from the pipe file "
"descriptor.");
} else if (result < 0) {
jack_error("JackALSARawMidiOutputPort::DequeueALSAEvent - error "
"reading a byte from the pipe file descriptor: %s",
strerror(errno));
read_queue->ResetMidiBuffer(port_buffer);
bool enqueued = false;
for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
event = read_queue->DequeueEvent()) {
switch (thread_queue->EnqueueEvent(event, frames)) {
case JackMidiWriteQueue::BUFFER_FULL:
jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread "
"queue doesn't have enough room to enqueue a %d-byte "
"event. Dropping event.", event->size);
continue;
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread "
"queue is too small to enqueue a %d-byte event. "
"Dropping event.", event->size);
continue;
default:
enqueued = true;
}
}
return event;
return enqueued ? TriggerQueueEvent() : true;
}

bool
JackALSARawMidiOutputPort::ProcessALSA(int read_fd, jack_nframes_t *frame)
JackALSARawMidiOutputPort::ProcessPollEvents(bool handle_output, bool timeout,
jack_nframes_t *frame)
{
unsigned short revents;
if (! ProcessPollEvents(&revents)) {
int io_event;
int queue_event;
send_queue->ResetPollByteCount();
if (! handle_output) {
assert(timeout);
goto process_raw_queue;
}
io_event = GetIOPollEvent();
if (io_event == -1) {
return false;
}
if (blocked) {
if (! (revents & POLLOUT)) {
*frame = 0;
return true;
}
blocked = false;
queue_event = GetQueuePollEvent();
if (queue_event == -1) {
return false;
}
if (io_event || timeout) {
process_raw_queue:
// We call the 'Process' event early because there are events waiting
// to be processed that either need to be sent now, or before now.
raw_queue->Process();
} else if (! queue_event) {
return true;
}
if (! alsa_event) {
alsa_event = DequeueALSAEvent(read_fd);
alsa_event = thread_queue->DequeueEvent();
}
for (; alsa_event; alsa_event = DequeueALSAEvent(read_fd)) {
for (; alsa_event; alsa_event = thread_queue->DequeueEvent()) {
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_FULL:
// Try to free up some space by processing events early.
raw_queue->Process();
switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiOutputPort::ProcessALSA - **BUG** "
"JackMidiRawOutputWriteQueue::EnqueueEvent "
"returned `BUFFER_FULL`, and then returned "
"`BUFFER_TOO_SMALL` after a Process() call.");
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
;
}
goto process_events;
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiOutputPort::ProcessALSA - The raw "
jack_error("JackALSARawMidiOutputPort::ProcessQueues - The raw "
"output queue couldn't enqueue a %d-byte event. "
"Dropping event.", alsa_event->size);
// Fallthrough on purpose
// Fallthrough on purpose.
case JackMidiWriteQueue::OK:
continue;
default:
;
}
break;

// Try to free up some space by processing events early.
*frame = raw_queue->Process();

switch (raw_queue->EnqueueEvent(alsa_event)) {
case JackMidiWriteQueue::BUFFER_FULL:
goto set_io_events;
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
// This shouldn't happen.
assert(false);
default:
;
}
}
process_events:
*frame = raw_queue->Process();
blocked = send_queue->IsBlocked();
set_io_events:
bool blocked = send_queue->IsBlocked();
SetIOEventsEnabled(blocked);
if (blocked) {
SetPollEventMask(POLLERR | POLLNVAL | POLLOUT);
*frame = 0;
} else {
SetPollEventMask(POLLERR | POLLNVAL);
}
return true;
}

bool
JackALSARawMidiOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
jack_nframes_t frames, int write_fd)
{
read_queue->ResetMidiBuffer(port_buffer);
for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
event = read_queue->DequeueEvent()) {
if (event->size > thread_queue->GetAvailableSpace()) {
jack_error("JackALSARawMidiOutputPort::ProcessJack - The thread "
"queue doesn't have enough room to enqueue a %d-byte "
"event. Dropping event.", event->size);
continue;
}
char c = 1;
ssize_t result = write(write_fd, &c, 1);
assert(result <= 1);
if (result < 0) {
jack_error("JackALSARawMidiOutputPort::ProcessJack - error "
"writing a byte to the pipe file descriptor: %s",
strerror(errno));
return false;
}
if (! result) {
// Recoverable.
jack_error("JackALSARawMidiOutputPort::ProcessJack - Couldn't "
"write a byte to the pipe file descriptor. Dropping "
"event.");
} else {
assert(thread_queue->EnqueueEvent(event, frames) ==
JackMidiWriteQueue::OK);
}
}
return true;
}

+ 5
- 7
linux/alsarawmidi/JackALSARawMidiOutputPort.h View File

@@ -33,28 +33,26 @@ namespace Jack {
private:

jack_midi_event_t *alsa_event;
bool blocked;
JackMidiRawOutputWriteQueue *raw_queue;
JackMidiBufferReadQueue *read_queue;
JackALSARawMidiSendQueue *send_queue;
JackMidiAsyncQueue *thread_queue;

jack_midi_event_t *
DequeueALSAEvent(int read_fd);

public:

JackALSARawMidiOutputPort(snd_rawmidi_info_t *info, size_t index,
size_t max_bytes_per_poll=3,
size_t max_bytes=4096,
size_t max_messages=1024);

~JackALSARawMidiOutputPort();

bool
ProcessALSA(int read_fd, jack_nframes_t *frame);
ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames);

bool
ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames,
int write_fd);
ProcessPollEvents(bool handle_output, bool timeout,
jack_nframes_t *frame);

};



+ 100
- 45
linux/alsarawmidi/JackALSARawMidiPort.cpp View File

@@ -17,16 +17,18 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#include <cassert>
#include <stdexcept>
#include <string>

#include "JackALSARawMidiPort.h"
#include "JackALSARawMidiUtil.h"
#include "JackError.h"

using Jack::JackALSARawMidiPort;

JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
size_t index)
size_t index, unsigned short io_mask)
{
int card = snd_rawmidi_info_get_card(info);
unsigned int device = snd_rawmidi_info_get_device(info);
@@ -50,7 +52,6 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
name_suffix = "in";
out = 0;
}
const char *device_name;
const char *func;
int code = snd_rawmidi_open(in, out, device_id, SND_RAWMIDI_NONBLOCK);
if (code) {
@@ -77,15 +78,6 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
func = "snd_rawmidi_params_set_avail_min";
goto free_params;
}

// Smallest valid buffer size.
code = snd_rawmidi_params_set_buffer_size(rawmidi, params, 32);
if (code) {
error_message = snd_strerror(code);
func = "snd_rawmidi_params_set_buffer_size";
goto free_params;
}

code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1);
if (code) {
error_message = snd_strerror(code);
@@ -99,15 +91,23 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
goto free_params;
}
snd_rawmidi_params_free(params);
num_fds = snd_rawmidi_poll_descriptors_count(rawmidi);
if (! num_fds) {
alsa_poll_fd_count = snd_rawmidi_poll_descriptors_count(rawmidi);
if (! alsa_poll_fd_count) {
error_message = "returned '0' count for poll descriptors";
func = "snd_rawmidi_poll_descriptors_count";
goto close;
}
try {
CreateNonBlockingPipe(fds);
} catch (std::exception e) {
error_message = e.what();
func = "CreateNonBlockingPipe";
goto close;
}
snprintf(alias, sizeof(alias), "%s%d", alias_prefix, index + 1);
snprintf(name, sizeof(name), "system:%d-%d %s %d %s", card + 1, device + 1,
snd_rawmidi_info_get_name(info), subdevice + 1, name_suffix);
this->io_mask = io_mask;
return;
free_params:
snd_rawmidi_params_free(params);
@@ -119,6 +119,7 @@ JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,

JackALSARawMidiPort::~JackALSARawMidiPort()
{
DestroyNonBlockingPipe(fds);
if (rawmidi) {
int code = snd_rawmidi_close(rawmidi);
if (code) {
@@ -135,6 +136,32 @@ JackALSARawMidiPort::GetAlias()
return alias;
}

int
JackALSARawMidiPort::GetIOPollEvent()
{
unsigned short events;
int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds,
alsa_poll_fd_count,
&events);
if (code) {
jack_error("JackALSARawMidiPort::GetIOPollEvents - "
"snd_rawmidi_poll_descriptors_revents: %s",
snd_strerror(code));
return -1;
}
if (events & POLLNVAL) {
jack_error("JackALSARawMidiPort::GetIOPollEvents - the file "
"descriptor is invalid.");
return -1;
}
if (events & POLLERR) {
jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has "
"occurred on the device or stream.");
return -1;
}
return (events & io_mask) ? 1 : 0;
}

const char *
JackALSARawMidiPort::GetName()
{
@@ -144,48 +171,76 @@ JackALSARawMidiPort::GetName()
int
JackALSARawMidiPort::GetPollDescriptorCount()
{
return num_fds;
}

bool
JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd)
{
bool result = snd_rawmidi_poll_descriptors(rawmidi, poll_fd, num_fds) ==
num_fds;
if (result) {
poll_fds = poll_fd;
}
return result;
return alsa_poll_fd_count + 1;
}

bool
JackALSARawMidiPort::ProcessPollEvents(unsigned short *revents)
int
JackALSARawMidiPort::GetQueuePollEvent()
{
int code = snd_rawmidi_poll_descriptors_revents(rawmidi, poll_fds, num_fds,
revents);
if (code) {
jack_error("JackALSARawMidiPort::ProcessPollEvents - "
"snd_rawmidi_poll_descriptors_revents: %s",
snd_strerror(code));
return false;
}
if ((*revents) & POLLNVAL) {
jack_error("JackALSARawMidiPort::ProcessPollEvents - the file "
unsigned short events = queue_poll_fd->revents;
if (events & POLLNVAL) {
jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file "
"descriptor is invalid.");
return false;
return -1;
}
if ((*revents) & POLLERR) {
jack_error("JackALSARawMidiPort::ProcessPollEvents - an error has "
if (events & POLLERR) {
jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has "
"occurred on the device or stream.");
return false;
return -1;
}
int event = events & POLLIN ? 1 : 0;
if (event) {
char c;
ssize_t result = read(fds[0], &c, 1);
assert(result);
if (result < 0) {
jack_error("JackALSARawMidiPort::GetQueuePollEvents - error "
"reading a byte from the pipe file descriptor: %s",
strerror(errno));
return -1;
}
}
return true;
return event;
}

void
JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd)
{
alsa_poll_fds = poll_fd + 1;
assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds,
alsa_poll_fd_count) ==
alsa_poll_fd_count);
queue_poll_fd = poll_fd;
queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL;
queue_poll_fd->fd = fds[0];
SetIOEventsEnabled(true);
}

void
JackALSARawMidiPort::SetPollEventMask(unsigned short events)
JackALSARawMidiPort::SetIOEventsEnabled(bool enabled)
{
unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0);
for (int i = 0; i < alsa_poll_fd_count; i++) {
(alsa_poll_fds + i)->events = mask;
}
}

bool
JackALSARawMidiPort::TriggerQueueEvent()
{
for (int i = 0; i < num_fds; i++) {
(poll_fds + i)->events = events;
char c;
ssize_t result = write(fds[1], &c, 1);
assert(result <= 1);
switch (result) {
case 1:
return true;
case 0:
jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a "
"byte to the pipe file descriptor: %s", strerror(errno));
break;
default:
jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a "
"byte to the pipe file descriptor.");
}
return false;
}

+ 20
- 7
linux/alsarawmidi/JackALSARawMidiPort.h View File

@@ -32,23 +32,36 @@ namespace Jack {
private:

char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
struct pollfd *alsa_poll_fds;
int alsa_poll_fd_count;
int fds[2];
unsigned short io_mask;
char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
int num_fds;
struct pollfd *poll_fds;
struct pollfd *queue_poll_fd;

protected:

snd_rawmidi_t *rawmidi;

bool
ProcessPollEvents(unsigned short *revents);
int
GetIOPollEvent();

int
GetQueuePollEvent();

void
SetIOEventsEnabled(bool enabled);

void
SetPollEventMask(unsigned short events);
SetQueueEventsEnabled(bool enabled);

bool
TriggerQueueEvent();

public:

JackALSARawMidiPort(snd_rawmidi_info_t *info, size_t index);
JackALSARawMidiPort(snd_rawmidi_info_t *info, size_t index,
unsigned short io_mask);

virtual
~JackALSARawMidiPort();
@@ -62,7 +75,7 @@ namespace Jack {
int
GetPollDescriptorCount();

bool
void
PopulatePollDescriptors(struct pollfd *poll_fd);

};


+ 0
- 0
linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp View File


+ 0
- 0
linux/alsarawmidi/JackALSARawMidiReceiveQueue.h View File


+ 15
- 1
linux/alsarawmidi/JackALSARawMidiSendQueue.cpp View File

@@ -24,10 +24,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

using Jack::JackALSARawMidiSendQueue;

JackALSARawMidiSendQueue::JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi)
JackALSARawMidiSendQueue::JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi,
size_t bytes_per_poll)
{
assert(bytes_per_poll > 0);
this->bytes_per_poll = bytes_per_poll;
this->rawmidi = rawmidi;
blocked = false;
bytes_available = bytes_per_poll;
}

Jack::JackMidiWriteQueue::EnqueueResult
@@ -38,10 +42,14 @@ JackALSARawMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size,
if (time > GetCurrentFrame()) {
return EVENT_EARLY;
}
if (! bytes_available) {
return BUFFER_FULL;
}
ssize_t result = snd_rawmidi_write(rawmidi, buffer, 1);
switch (result) {
case 1:
blocked = false;
bytes_available--;
return OK;
case -EWOULDBLOCK:
blocked = true;
@@ -57,3 +65,9 @@ JackALSARawMidiSendQueue::IsBlocked()
{
return blocked;
}

void
JackALSARawMidiSendQueue::ResetPollByteCount()
{
bytes_available = bytes_per_poll;
}

+ 7
- 1
linux/alsarawmidi/JackALSARawMidiSendQueue.h View File

@@ -31,11 +31,14 @@ namespace Jack {
private:

bool blocked;
size_t bytes_available;
size_t bytes_per_poll;
snd_rawmidi_t *rawmidi;

public:

JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi);
JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi,
size_t bytes_per_poll=0);

JackMidiWriteQueue::EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
@@ -44,6 +47,9 @@ namespace Jack {
bool
IsBlocked();

void
ResetPollByteCount();

};

}


+ 43
- 0
linux/alsarawmidi/JackALSARawMidiUtil.cpp View File

@@ -0,0 +1,43 @@
#include <cerrno>
#include <cstring>
#include <stdexcept>

#include <fcntl.h>
#include <unistd.h>

#include "JackALSARawMidiUtil.h"

void
Jack::CreateNonBlockingPipe(int *fds)
{
if (pipe(fds) == -1) {
throw std::runtime_error(strerror(errno));
}
try {
SetNonBlocking(fds[0]);
SetNonBlocking(fds[1]);
} catch (...) {
close(fds[1]);
close(fds[0]);
throw;
}
}

void
Jack::DestroyNonBlockingPipe(int *fds)
{
close(fds[1]);
close(fds[0]);
}

void
Jack::SetNonBlocking(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
throw std::runtime_error(strerror(errno));
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
throw std::runtime_error(strerror(errno));
}
}

+ 36
- 0
linux/alsarawmidi/JackALSARawMidiUtil.h View File

@@ -0,0 +1,36 @@
/*
Copyright (C) 2011 Devin Anderson

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

#ifndef __JackALSARawMidiUtil__
#define __JackALSARawMidiUtil__

namespace Jack {

void
CreateNonBlockingPipe(int *fds);

void
DestroyNonBlockingPipe(int *fds);

void
SetNonBlocking(int fd);

}

#endif

+ 6
- 3
linux/wscript View File

@@ -13,12 +13,14 @@ def configure(conf):

conf.define('HAVE_PPOLL', 1 )


def create_jack_driver_obj(bld, target, sources, uselib = None):
driver = bld.new_task_gen('cxx', 'shlib')
driver.features.append('cc')
driver.env['shlib_PATTERN'] = 'jack_%s.so'
driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL']

#driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL']
driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD']

driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus']
driver.target = target
driver.source = sources
@@ -62,7 +64,8 @@ def build(bld):
'alsarawmidi/JackALSARawMidiOutputPort.cpp',
'alsarawmidi/JackALSARawMidiPort.cpp',
'alsarawmidi/JackALSARawMidiReceiveQueue.cpp',
'alsarawmidi/JackALSARawMidiSendQueue.cpp'
'alsarawmidi/JackALSARawMidiSendQueue.cpp',
'alsarawmidi/JackALSARawMidiUtil.cpp'
]

ffado_driver_src = ['firewire/JackFFADODriver.cpp',


Loading…
Cancel
Save