Browse Source

Add 'alsarawmidi' driver, which is a slave MIDI driver that makes ALSA MIDI ports available to JACK. The driver uses the rawmidi devices, and uses the raw MIDI queues to optimize output.

tags/1.9.8
Devin Anderson 14 years ago
parent
commit
6e92bd8020
16 changed files with 1379 additions and 6 deletions
  1. +1
    -1
      common/JackMidiRawInputWriteQueue.h
  2. +1
    -1
      common/JackMidiRawOutputWriteQueue.h
  3. +0
    -4
      example-clients/midi_latency_test.c
  4. +586
    -0
      linux/alsarawmidi/JackALSARawMidiDriver.cpp
  5. +79
    -0
      linux/alsarawmidi/JackALSARawMidiDriver.h
  6. +121
    -0
      linux/alsarawmidi/JackALSARawMidiInputPort.cpp
  7. +43
    -0
      linux/alsarawmidi/JackALSARawMidiInputPort.h
  8. +146
    -0
      linux/alsarawmidi/JackALSARawMidiOutputPort.cpp
  9. +44
    -0
      linux/alsarawmidi/JackALSARawMidiOutputPort.h
  10. +158
    -0
      linux/alsarawmidi/JackALSARawMidiPort.cpp
  11. +51
    -0
      linux/alsarawmidi/JackALSARawMidiPort.h
  12. +35
    -0
      linux/alsarawmidi/JackALSARawMidiReceiveQueue.cpp
  13. +32
    -0
      linux/alsarawmidi/JackALSARawMidiReceiveQueue.h
  14. +40
    -0
      linux/alsarawmidi/JackALSARawMidiSendQueue.cpp
  15. +32
    -0
      linux/alsarawmidi/JackALSARawMidiSendQueue.h
  16. +10
    -0
      linux/wscript

+ 1
- 1
common/JackMidiRawInputWriteQueue.h View File

@@ -161,7 +161,7 @@ namespace Jack {
*/

jack_nframes_t
Process(jack_nframes_t boundary_frame);
Process(jack_nframes_t boundary_frame=0);

};



+ 1
- 1
common/JackMidiRawOutputWriteQueue.h View File

@@ -138,7 +138,7 @@ namespace Jack {
*/

jack_nframes_t
Process(jack_nframes_t boundary_frame);
Process(jack_nframes_t boundary_frame=0);

};



+ 0
- 4
example-clients/midi_latency_test.c View File

@@ -477,10 +477,6 @@ main(int argc, char **argv)
error_source = "jack_connect";
goto deactivate_client;
}
jack_port_get_latency_range(remote_in_port, JackCaptureLatency,
&in_latency_range);
jack_port_get_latency_range(remote_out_port, JackPlaybackLatency,
&out_latency_range);
code = pthread_mutex_unlock(&start_mutex);
if (code) {
error_message = strerror(code);


+ 586
- 0
linux/alsarawmidi/JackALSARawMidiDriver.cpp View File

@@ -0,0 +1,586 @@
#include <memory>
#include <new>
#include <stdexcept>

#include <alsa/asoundlib.h>

#include "JackALSARawMidiDriver.h"
#include "JackEngineControl.h"
#include "JackError.h"
#include "JackMidiUtil.h"

using Jack::JackALSARawMidiDriver;

JackALSARawMidiDriver::JackALSARawMidiDriver(const char *name,
const char *alias,
JackLockedEngine *engine,
JackSynchro *table):
JackMidiDriver(name, alias, engine, table)
{
thread = new JackThread(this);
fCaptureChannels = 0;
fds[0] = -1;
fds[1] = -1;
fPlaybackChannels = 0;
input_ports = 0;
output_ports = 0;
poll_fds = 0;
}

JackALSARawMidiDriver::~JackALSARawMidiDriver()
{
Stop();
delete thread;
Close();
}

int
JackALSARawMidiDriver::Attach()
{
jack_nframes_t buffer_size = fEngineControl->fBufferSize;
jack_port_id_t index;
const char *name;
JackPort *port;
for (int i = 0; i < fCaptureChannels; i++) {
JackALSARawMidiInputPort *input_port = input_ports[i];
name = input_port->GetName();
index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
JACK_DEFAULT_MIDI_TYPE,
CaptureDriverFlags, buffer_size);
if (index == NO_PORT) {
jack_error("JackALSARawMidiDriver::Attach - cannot register input "
"port with name '%s'.", name);
// X: Do we need to deallocate ports?
return -1;
}
port = fGraphManager->GetPort(index);
port->SetAlias(input_port->GetAlias());
port->SetLatency(buffer_size);
fCapturePortList[i] = index;
}
for (int i = 0; i < fPlaybackChannels; i++) {
JackALSARawMidiOutputPort *output_port = output_ports[i];
name = output_port->GetName();
index = fGraphManager->AllocatePort(fClientControl.fRefNum, name,
JACK_DEFAULT_MIDI_TYPE,
PlaybackDriverFlags, buffer_size);
if (index == NO_PORT) {
jack_error("JackALSARawMidiDriver::Attach - cannot register "
"output port with name '%s'.", name);
// X: Do we need to deallocate ports?
return -1;
}
port = fGraphManager->GetPort(index);
port->SetAlias(output_port->GetAlias());
port->SetLatency(buffer_size);
fPlaybackPortList[i] = index;
}
return 0;
}

int
JackALSARawMidiDriver::Close()
{
if (input_ports) {
for (int i = 0; i < fCaptureChannels; i++) {
delete input_ports[i];
}
delete[] input_ports;
input_ports = 0;
}
if (output_ports) {
for (int i = 0; i < fPlaybackChannels; i++) {
delete output_ports[i];
}
delete[] output_ports;
output_ports = 0;
}
return 0;
}

bool
JackALSARawMidiDriver::Execute()
{
jack_nframes_t timeout_frame = 0;
for (;;) {
jack_time_t wait_time;
unsigned short revents;
if (! timeout_frame) {
wait_time = 0;
} else {
jack_time_t next_time = GetTimeFromFrames(timeout_frame);
jack_time_t now = GetMicroSeconds();

if (next_time <= now) {
goto handle_ports;
}
wait_time = next_time - now;
}

jack_info("Calling 'Poll' with wait time '%d'.", wait_time);

if (Poll(wait_time) == -1) {
if (errno == EINTR) {
continue;
}
jack_error("JackALSARawMidiDriver::Execute - poll error: %s",
strerror(errno));
break;
}
revents = poll_fds[0].revents;
if (revents & POLLHUP) {
close(fds[0]);
fds[0] = -1;
break;
}
if (revents & (~(POLLHUP | POLLIN))) {
jack_error("JackALSARawMidiDriver::Execute - unexpected poll "
"event on pipe file descriptor.");
break;
}
handle_ports:
jack_nframes_t process_frame;
jack_nframes_t timeout_frame = 0;
for (int i = 0; i < fCaptureChannels; i++) {
process_frame = input_ports[i]->ProcessALSA();
if (process_frame && ((! timeout_frame) ||
(process_frame < timeout_frame))) {
timeout_frame = process_frame;
}
}
for (int i = 0; i < fPlaybackChannels; i++) {
process_frame = output_ports[i]->ProcessALSA(fds[0]);
if (process_frame && ((! timeout_frame) ||
(process_frame < timeout_frame))) {
timeout_frame = process_frame;
}
}
}

jack_info("ALSA thread is exiting.");

return false;
}

void
JackALSARawMidiDriver::
GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info,
std::vector<snd_rawmidi_info_t *> *info_list)
{
snd_rawmidi_info_set_subdevice(info, 0);
int code = snd_ctl_rawmidi_info(control, info);
if (code) {
if (code != -ENOENT) {
HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
}
return;
}
unsigned int count = snd_rawmidi_info_get_subdevices_count(info);
for (unsigned int i = 0; i < count; i++) {
snd_rawmidi_info_set_subdevice(info, i);
int code = snd_ctl_rawmidi_info(control, info);
if (code) {
HandleALSAError("GetDeviceInfo", "snd_ctl_rawmidi_info", code);
continue;
}
snd_rawmidi_info_t *info_copy;
code = snd_rawmidi_info_malloc(&info_copy);
if (code) {
HandleALSAError("GetDeviceInfo", "snd_rawmidi_info_malloc", code);
continue;
}
snd_rawmidi_info_copy(info_copy, info);
try {
info_list->push_back(info_copy);
} catch (std::bad_alloc &e) {
snd_rawmidi_info_free(info_copy);
jack_error("JackALSARawMidiDriver::GetDeviceInfo - "
"std::vector::push_back: %s", e.what());
}
}
}

void
JackALSARawMidiDriver::HandleALSAError(const char *driver_func,
const char *alsa_func, int code)
{
jack_error("JackALSARawMidiDriver::%s - %s: %s", driver_func, alsa_func,
snd_strerror(code));
}

bool
JackALSARawMidiDriver::Init()
{
set_threaded_log_function();
if (thread->AcquireSelfRealTime(fEngineControl->fServerPriority + 1)) {
jack_error("JackALSARawMidiDriver::Init - could not acquire realtime "
"scheduling. Continuing anyway.");
}
return true;
}

int
JackALSARawMidiDriver::Open(bool capturing, bool playing, int in_channels,
int out_channels, bool monitor,
const char *capture_driver_name,
const char *playback_driver_name,
jack_nframes_t capture_latency,
jack_nframes_t playback_latency)
{
snd_rawmidi_info_t *info;
int code = snd_rawmidi_info_malloc(&info);
if (code) {
HandleALSAError("Open", "snd_rawmidi_info_malloc", code);
return -1;
}
std::vector<snd_rawmidi_info_t *> in_info_list;
std::vector<snd_rawmidi_info_t *> out_info_list;
for (int card = -1;;) {
int code = snd_card_next(&card);
if (code) {
HandleALSAError("Open", "snd_card_next", code);
continue;
}
if (card == -1) {
break;
}
char name[32];
snprintf(name, sizeof(name), "hw:%d", card);
snd_ctl_t *control;
code = snd_ctl_open(&control, name, SND_CTL_NONBLOCK);
if (code) {
HandleALSAError("Open", "snd_ctl_open", code);
continue;
}
for (int device = -1;;) {
code = snd_ctl_rawmidi_next_device(control, &device);
if (code) {
HandleALSAError("Open", "snd_ctl_rawmidi_next_device", code);
continue;
}
if (device == -1) {
break;
}
snd_rawmidi_info_set_device(info, device);
snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_INPUT);
GetDeviceInfo(control, info, &in_info_list);
snd_rawmidi_info_set_stream(info, SND_RAWMIDI_STREAM_OUTPUT);
GetDeviceInfo(control, info, &out_info_list);
}
snd_ctl_close(control);
}
snd_rawmidi_info_free(info);
size_t potential_inputs = in_info_list.size();
size_t potential_outputs = out_info_list.size();
if (! (potential_inputs || potential_outputs)) {
jack_error("JackALSARawMidiDriver::Open - no ALSA raw MIDI input or "
"output ports found.");
return -1;
}

// XXX: Can't use auto_ptr here. These are arrays, and require the
// delete[] operator.
std::auto_ptr<JackALSARawMidiInputPort *> input_ptr;
if (potential_inputs) {
input_ports = new JackALSARawMidiInputPort *[potential_inputs];
input_ptr.reset(input_ports);
}
std::auto_ptr<JackALSARawMidiOutputPort *> output_ptr;
if (potential_outputs) {
output_ports = new JackALSARawMidiOutputPort *[potential_outputs];
output_ptr.reset(output_ports);
}

size_t num_inputs = 0;
size_t num_outputs = 0;
for (size_t i = 0; i < potential_inputs; i++) {
snd_rawmidi_info_t *info = in_info_list.at(i);
try {
input_ports[num_inputs] = new JackALSARawMidiInputPort(info, i);

jack_info("JackALSARawMidiDriver::Open - Input port: card=%d, "
"device=%d, subdevice=%d, id=%s, name=%s, subdevice "
"name=%s",
snd_rawmidi_info_get_card(info),
snd_rawmidi_info_get_device(info),
snd_rawmidi_info_get_subdevice(info),
snd_rawmidi_info_get_id(info),
snd_rawmidi_info_get_name(info),
snd_rawmidi_info_get_subdevice_name(info));

num_inputs++;
} catch (std::exception e) {
jack_error("JackALSARawMidiDriver::Open - while creating new "
"JackALSARawMidiInputPort: %s", e.what());
}
snd_rawmidi_info_free(info);
}
for (size_t i = 0; i < potential_outputs; i++) {
snd_rawmidi_info_t *info = out_info_list.at(i);
try {
output_ports[num_outputs] = new JackALSARawMidiOutputPort(info, i);

jack_info("JackALSARawMidiDriver::Open - Output port: card=%d, "
"device=%d, subdevice=%d, id=%s, name=%s, subdevice "
"name=%s",
snd_rawmidi_info_get_card(info),
snd_rawmidi_info_get_device(info),
snd_rawmidi_info_get_subdevice(info),
snd_rawmidi_info_get_id(info),
snd_rawmidi_info_get_name(info),
snd_rawmidi_info_get_subdevice_name(info));

num_outputs++;
} catch (std::exception e) {
jack_error("JackALSARawMidiDriver::Open - while creating new "
"JackALSARawMidiOutputPort: %s", e.what());
}
snd_rawmidi_info_free(info);
}
if (num_inputs || num_outputs) {
if (! JackMidiDriver::Open(capturing, playing, num_inputs, num_outputs,
monitor, capture_driver_name,
playback_driver_name, capture_latency,
playback_latency)) {
if (potential_inputs) {
input_ptr.release();
}
if (potential_outputs) {
output_ptr.release();
}
return 0;
}
jack_error("JackALSARawMidiDriver::Open - JackMidiDriver::Open error");
} else {
jack_error("JackALSARawMidiDriver::Open - none of the potential "
"inputs or outputs were successfully opened.");
}
Close();
return -1;
}

#ifdef HAVE_PPOLL

int
JackALSARawMidiDriver::Poll(jack_time_t wait_time)
{
struct timespec timeout;
struct timespec *timeout_ptr;
if (! wait_time) {
timeout_ptr = 0;
} else {
timeout.tv_sec = wait_time / 1000000;
timeout.tv_nsec = (wait_time % 1000000) * 1000;
timeout_ptr = &timeout;
}
return ppoll(poll_fds, poll_fd_count, timeout_ptr, 0);
}

#else

int
JackALSARawMidiDriver::Poll(jack_time_t wait_time)
{
int result = poll(poll_fds, poll_fd_count,
! wait_time ? -1 : (int) (wait_time / 1000));
if ((! result) && wait_time) {
wait_time %= 1000;
if (wait_time) {
// Cheap hack.
usleep(wait_time);
result = poll(poll_fds, poll_fd_count, 0);
}
}
return result;
}

#endif

int
JackALSARawMidiDriver::Read()
{
for (int i = 0; i < fCaptureChannels; i++) {
input_ports[i]->ProcessJack(GetInputBuffer(i),
fEngineControl->fBufferSize);
}
return 0;
}

int
JackALSARawMidiDriver::Start()
{

jack_info("JackALSARawMidiDriver::Start - Starting 'alsarawmidi' driver.");

// JackMidiDriver::Start();

poll_fd_count = 1;
for (int i = 0; i < fCaptureChannels; i++) {
poll_fd_count += input_ports[i]->GetPollDescriptorCount();
}
for (int i = 0; i < fPlaybackChannels; i++) {
poll_fd_count += output_ports[i]->GetPollDescriptorCount();
}
try {
poll_fds = new pollfd[poll_fd_count];
} catch (std::bad_alloc e) {
jack_error("JackALSARawMidiDriver::Start - creating poll descriptor "
"structures failed: %s", e.what());
return -1;
}
int flags;
struct pollfd *poll_fd_iter;
if (pipe(fds) == -1) {
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;
}
poll_fds[0].events = POLLERR | POLLIN | POLLNVAL;
poll_fds[0].fd = fds[0];
poll_fd_iter = poll_fds + 1;
for (int i = 0; i < fCaptureChannels; i++) {
JackALSARawMidiInputPort *input_port = input_ports[i];
input_port->PopulatePollDescriptors(poll_fd_iter);
poll_fd_iter += input_port->GetPollDescriptorCount();
}
for (int i = 0; i < fPlaybackChannels; i++) {
JackALSARawMidiOutputPort *output_port = output_ports[i];
output_port->PopulatePollDescriptors(poll_fd_iter);
poll_fd_iter += output_port->GetPollDescriptorCount();
}

jack_info("Starting ALSA thread ...");

if (! thread->StartSync()) {

jack_info("Started ALSA thread.");

return 0;
}
jack_error("JackALSARawMidiDriver::Start - failed to start MIDI "
"processing thread.");
close_fds:
close(fds[1]);
fds[1] = -1;
close(fds[0]);
fds[0] = -1;
free_poll_descriptors:
delete[] poll_fds;
poll_fds = 0;
return -1;
}

int
JackALSARawMidiDriver::Stop()
{

jack_info("Stopping 'alsarawmidi' driver.");

if (fds[1] != -1) {
close(fds[1]);
fds[1] = -1;
}
int result;
const char *verb;
switch (thread->GetStatus()) {
case JackThread::kIniting:
case JackThread::kStarting:
result = thread->Kill();
verb = "kill";
break;
case JackThread::kRunning:
result = thread->Stop();
verb = "stop";
break;
default:
result = 0;
verb = 0;
}
if (fds[0] != -1) {
close(fds[0]);
fds[0] = -1;
}
if (poll_fds) {
delete[] poll_fds;
poll_fds = 0;
}
if (result) {
jack_error("JackALSARawMidiDriver::Stop - could not %s MIDI "
"processing thread.", verb);
}
return result;
}

int
JackALSARawMidiDriver::Write()
{
jack_nframes_t buffer_size = fEngineControl->fBufferSize;
int write_fd = fds[1];
for (int i = 0; i < fPlaybackChannels; i++) {
output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size,
write_fd);
}
return 0;
}

#ifdef __cplusplus
extern "C" {
#endif

SERVER_EXPORT jack_driver_desc_t *
driver_get_descriptor()
{
jack_driver_desc_t *desc =
(jack_driver_desc_t *) malloc(sizeof(jack_driver_desc_t));
if (desc) {
strcpy(desc->desc, "Alternative ALSA raw MIDI backend.");
strcpy(desc->name, "alsarawmidi");

// X: There could be parameters here regarding setting I/O buffer
// sizes. I don't think MIDI drivers can accept parameters right
// now without being set as the main driver.
desc->nparams = 0;
desc->params = 0;
}
return desc;
}

SERVER_EXPORT Jack::JackDriverClientInterface *
driver_initialize(Jack::JackLockedEngine *engine, Jack::JackSynchro *table,
const JSList *params)
{
Jack::JackDriverClientInterface *driver =
new Jack::JackALSARawMidiDriver("system", "alsarawmidi", engine,
table);
if (driver->Open(1, 1, 0, 0, false, "midi in", "midi out", 0, 0)) {
delete driver;
driver = 0;
}
return driver;
}

#ifdef __cplusplus
}
#endif

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

@@ -0,0 +1,79 @@
#ifndef __JackALSARawMidiDriver__
#define __JackALSARawMidiDriver__

#include <vector>

#include <alsa/asoundlib.h>
#include <poll.h>

#include "JackALSARawMidiInputPort.h"
#include "JackALSARawMidiOutputPort.h"
#include "JackMidiDriver.h"
#include "JackThread.h"

namespace Jack {

class JackALSARawMidiDriver:
public JackMidiDriver, public JackRunnableInterface {

private:

int fds[2];
JackALSARawMidiInputPort **input_ports;
JackALSARawMidiOutputPort **output_ports;
nfds_t poll_fd_count;
struct pollfd *poll_fds;
JackThread *thread;

void
GetDeviceInfo(snd_ctl_t *control, snd_rawmidi_info_t *info,
std::vector<snd_rawmidi_info_t *> *info_list);

void
HandleALSAError(const char *driver_func, const char *alsa_func,
int code);

int
Poll(jack_time_t wait_time);

public:

JackALSARawMidiDriver(const char *name, const char *alias,
JackLockedEngine *engine, JackSynchro *table);
~JackALSARawMidiDriver();

int
Attach();

int
Close();

bool
Execute();

bool
Init();

int
Open(bool capturing, bool playing, int in_channels, int out_channels,
bool monitoring, const char *capture_driver_name,
const char *playback_driver_name, jack_nframes_t capture_latency,
jack_nframes_t playback_latency);

int
Read();

int
Start();

int
Stop();

int
Write();

};

}

#endif

+ 121
- 0
linux/alsarawmidi/JackALSARawMidiInputPort.cpp View File

@@ -0,0 +1,121 @@
#include <memory>

#include "JackALSARawMidiInputPort.h"
#include "JackMidiUtil.h"

using Jack::JackALSARawMidiInputPort;

JackALSARawMidiInputPort::JackALSARawMidiInputPort(snd_rawmidi_info_t *info,
size_t index,
size_t max_bytes,
size_t max_messages):
JackALSARawMidiPort(info, index)
{
alsa_event = 0;
jack_event = 0;
receive_queue = new JackALSARawMidiReceiveQueue(rawmidi, max_bytes);
std::auto_ptr<JackALSARawMidiReceiveQueue> receive_ptr(receive_queue);
thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue);
write_queue = new JackMidiBufferWriteQueue();
std::auto_ptr<JackMidiBufferWriteQueue> write_ptr(write_queue);
raw_queue = new JackMidiRawInputWriteQueue(thread_queue, max_bytes,
max_messages);
write_ptr.release();
thread_ptr.release();
receive_ptr.release();
}

JackALSARawMidiInputPort::~JackALSARawMidiInputPort()
{
delete raw_queue;
delete receive_queue;
delete thread_queue;
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;
}

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

void
JackALSARawMidiInputPort::ProcessJack(JackMidiBuffer *port_buffer,
jack_nframes_t frames)
{
write_queue->ResetMidiBuffer(port_buffer, frames);
if (! jack_event) {
jack_event = thread_queue->DequeueEvent();
}
for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
// We add `frames` so that MIDI events align with audio as closely as
// possible.
switch (write_queue->EnqueueEvent(jack_event->time + frames,
jack_event->size,
jack_event->buffer)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackALSARawMidiInputPort::Process - The write queue "
"couldn't enqueue a %d-byte event. Dropping event.",
jack_event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
;
}
break;
}
}

+ 43
- 0
linux/alsarawmidi/JackALSARawMidiInputPort.h View File

@@ -0,0 +1,43 @@
#ifndef __JackALSARawMidiInputPort__
#define __JackALSARawMidiInputPort__

#include "JackALSARawMidiPort.h"
#include "JackALSARawMidiReceiveQueue.h"
#include "JackMidiAsyncQueue.h"
#include "JackMidiBufferWriteQueue.h"
#include "JackMidiRawInputWriteQueue.h"

namespace Jack {

class JackALSARawMidiInputPort: public JackALSARawMidiPort {

private:

jack_midi_event_t *alsa_event;
jack_midi_event_t *jack_event;
JackMidiRawInputWriteQueue *raw_queue;
JackALSARawMidiReceiveQueue *receive_queue;
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();

jack_nframes_t
ProcessALSA();

void
ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames);

};

}

#endif

+ 146
- 0
linux/alsarawmidi/JackALSARawMidiOutputPort.cpp View File

@@ -0,0 +1,146 @@
#include <memory>

#include "JackALSARawMidiOutputPort.h"

using Jack::JackALSARawMidiOutputPort;

JackALSARawMidiOutputPort::JackALSARawMidiOutputPort(snd_rawmidi_info_t *info,
size_t index,
size_t max_bytes,
size_t max_messages):
JackALSARawMidiPort(info, index)
{
alsa_event = 0;
blocked = false;
read_queue = new JackMidiBufferReadQueue();
std::auto_ptr<JackMidiBufferReadQueue> read_ptr(read_queue);
send_queue = new JackALSARawMidiSendQueue(rawmidi);
std::auto_ptr<JackALSARawMidiSendQueue> send_ptr(send_queue);
thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
std::auto_ptr<JackMidiAsyncQueue> thread_ptr(thread_queue);
raw_queue = new JackMidiRawOutputWriteQueue(send_queue, max_bytes,
max_messages, max_messages);
thread_ptr.release();
send_ptr.release();
read_ptr.release();
}

JackALSARawMidiOutputPort::~JackALSARawMidiOutputPort()
{
delete raw_queue;
delete read_queue;
delete send_queue;
delete thread_queue;
}

jack_midi_event_t *
JackALSARawMidiOutputPort::DequeueALSAEvent(int read_fd)
{
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));
}
}
return event;
}

jack_nframes_t
JackALSARawMidiOutputPort::ProcessALSA(int read_fd)
{
unsigned short revents = ProcessPollEvents();
if (blocked) {
if (! (revents & POLLOUT)) {
return 0;
}
blocked = false;
}
if (! alsa_event) {
alsa_event = DequeueALSAEvent(read_fd);
}
for (; alsa_event; alsa_event = DequeueALSAEvent(read_fd)) {
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 "
"output queue couldn't enqueue a %d-byte event. "
"Dropping event.", alsa_event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
;
}
break;
}
process_events:
jack_nframes_t next_frame = raw_queue->Process();
blocked = send_queue->IsBlocked();
if (blocked) {
SetPollEventMask(POLLERR | POLLNVAL | POLLOUT);
return 0;
}
SetPollEventMask(POLLERR | POLLNVAL);
return next_frame;
}

void
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;

jack_info("Attempting to write to file descriptor '%d'", write_fd);

ssize_t result = write(write_fd, &c, 1);
assert(result <= 1);
if (! result) {
jack_error("JackALSARawMidiOutputPort::ProcessJack - Couldn't "
"write a byte to the pipe file descriptor. Dropping "
"event.");
} else if (result < 0) {
jack_error("JackALSARawMidiOutputPort::ProcessJack - error "
"writing a byte to the pipe file descriptor: %s",
strerror(errno));
} else if (thread_queue->EnqueueEvent(event->time + frames,
event->size, event->buffer) !=
JackMidiWriteQueue::OK) {
jack_error("JackALSARawMidiOutputPort::ProcessJack - **BUG** The "
"thread queue said it had enough space to enqueue a "
"%d-byte event, but failed to enqueue the event.");
}
}
}

+ 44
- 0
linux/alsarawmidi/JackALSARawMidiOutputPort.h View File

@@ -0,0 +1,44 @@
#ifndef __JackALSARawMidiOutputPort__
#define __JackALSARawMidiOutputPort__

#include "JackALSARawMidiPort.h"
#include "JackALSARawMidiSendQueue.h"
#include "JackMidiAsyncQueue.h"
#include "JackMidiBufferReadQueue.h"
#include "JackMidiRawOutputWriteQueue.h"

namespace Jack {

class JackALSARawMidiOutputPort: public JackALSARawMidiPort {

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=4096,
size_t max_messages=1024);
~JackALSARawMidiOutputPort();

jack_nframes_t
ProcessALSA(int read_fd);

void
ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames,
int write_fd);

};

}

#endif

+ 158
- 0
linux/alsarawmidi/JackALSARawMidiPort.cpp View File

@@ -0,0 +1,158 @@
#include <stdexcept>
#include <string>

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

using Jack::JackALSARawMidiPort;

JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
size_t index)
{
char device_id[32];
snprintf(device_id, sizeof(device_id), "hw:%d,%d,%d",
snd_rawmidi_info_get_card(info),
snd_rawmidi_info_get_device(info),
snd_rawmidi_info_get_subdevice(info));
const char *alias_prefix;
const char *error_message;
snd_rawmidi_t **in;
snd_rawmidi_t **out;
const char *name_suffix;
if (snd_rawmidi_info_get_stream(info) == SND_RAWMIDI_STREAM_OUTPUT) {
alias_prefix = "system:midi_playback_";
in = 0;
name_suffix = "out";
out = &rawmidi;
} else {
alias_prefix = "system:midi_capture_";
in = &rawmidi;
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) {
error_message = snd_strerror(code);
func = "snd_rawmidi_open";
goto handle_error;
}
snd_rawmidi_params_t *params;
code = snd_rawmidi_params_malloc(&params);
if (code) {
error_message = snd_strerror(code);
func = "snd_rawmidi_params_malloc";
goto close;
}
code = snd_rawmidi_params_current(rawmidi, params);
if (code) {
error_message = snd_strerror(code);
func = "snd_rawmidi_params_current";
goto close;
}
code = snd_rawmidi_params_set_avail_min(rawmidi, params, 1);
if (code) {
error_message = snd_strerror(code);
func = "snd_rawmidi_params_set_avail_min";
goto free_params;
}
code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1);
if (code) {
error_message = snd_strerror(code);
func = "snd_rawmidi_params_set_no_active_sensing";
goto free_params;
}
snd_rawmidi_params_free(params);
num_fds = snd_rawmidi_poll_descriptors_count(rawmidi);
if (! num_fds) {
error_message = "returned '0' count for poll descriptors";
func = "snd_rawmidi_poll_descriptors_count";
goto close;
}
snprintf(alias, sizeof(alias), "%s%d", alias_prefix, index);
device_name = snd_rawmidi_info_get_subdevice_name(info);
if (! strlen(device_name)) {
device_name = snd_rawmidi_info_get_name(info);
}
snprintf(name, sizeof(name), "system:%s %s", device_name, name_suffix);
return;
free_params:
snd_rawmidi_params_free(params);
close:
snd_rawmidi_close(rawmidi);
handle_error:
throw std::runtime_error(std::string(func) + ": " + error_message);
}

JackALSARawMidiPort::~JackALSARawMidiPort()
{
if (rawmidi) {
int code = snd_rawmidi_close(rawmidi);
if (code) {
jack_error("JackALSARawMidiPort::~JackALSARawMidiPort - "
"snd_rawmidi_close: %s", snd_strerror(code));
}
rawmidi = 0;
}
}

const char *
JackALSARawMidiPort::GetAlias()
{
return alias;
}

const char *
JackALSARawMidiPort::GetName()
{
return name;
}

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;
}

int
JackALSARawMidiPort::ProcessPollEvents()
{
unsigned short revents;
int code = snd_rawmidi_poll_descriptors_revents(rawmidi, poll_fds, num_fds,
&revents);
if (code) {
jack_error("JackALSARawMidiInputPort::ProcessPollEvents - "
"snd_rawmidi_poll_descriptors_revents: %s",
snd_strerror(code));
return 0;
}
if (revents & POLLNVAL) {
jack_error("JackALSARawMidiInputPort::ProcessPollEvents - the file "
"descriptor is invalid.");
}
if (revents & POLLERR) {
jack_error("JackALSARawMidiInputPort::ProcessPollEvents - an error "
"has occurred on the device or stream.");
}
return revents;
}

void
JackALSARawMidiPort::SetPollEventMask(unsigned short events)
{
for (int i = 0; i < num_fds; i++) {
(poll_fds + i)->events = events;
}
}

+ 51
- 0
linux/alsarawmidi/JackALSARawMidiPort.h View File

@@ -0,0 +1,51 @@
#ifndef __JackALSARawMidiPort__
#define __JackALSARawMidiPort__

#include <alsa/asoundlib.h>
#include <poll.h>

#include "JackConstants.h"

namespace Jack {

class JackALSARawMidiPort {

private:

char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
int num_fds;
struct pollfd *poll_fds;

protected:

snd_rawmidi_t *rawmidi;

int
ProcessPollEvents();

void
SetPollEventMask(unsigned short events);

public:

JackALSARawMidiPort(snd_rawmidi_info_t *info, size_t index);
virtual ~JackALSARawMidiPort();

const char *
GetAlias();

const char *
GetName();

int
GetPollDescriptorCount();

bool
PopulatePollDescriptors(struct pollfd *poll_fd);

};

}

#endif

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

@@ -0,0 +1,35 @@
#include "JackALSARawMidiReceiveQueue.h"
#include "JackError.h"
#include "JackMidiUtil.h"

using Jack::JackALSARawMidiReceiveQueue;

JackALSARawMidiReceiveQueue::
JackALSARawMidiReceiveQueue(snd_rawmidi_t *rawmidi, size_t buffer_size)
{
buffer = new jack_midi_data_t[buffer_size];
this->buffer_size = buffer_size;
this->rawmidi = rawmidi;
}

JackALSARawMidiReceiveQueue::~JackALSARawMidiReceiveQueue()
{
delete[] buffer;
}

jack_midi_event_t *
JackALSARawMidiReceiveQueue::DequeueEvent()
{
ssize_t result = snd_rawmidi_read(rawmidi, buffer, buffer_size);
if (result > 0) {
event.buffer = buffer;
event.size = (size_t) result;
event.time = GetCurrentFrame();
return &event;
}
if (result && (result != -EWOULDBLOCK)) {
jack_error("JackALSARawMidiReceiveQueue::DequeueEvent - "
"snd_rawmidi_read: %s", snd_strerror(result));
}
return 0;
}

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

@@ -0,0 +1,32 @@
#ifndef __JackALSARawMidiReceiveQueue__
#define __JackALSARawMidiReceiveQueue__

#include <alsa/asoundlib.h>

#include "JackMidiReceiveQueue.h"

namespace Jack {

class JackALSARawMidiReceiveQueue: public JackMidiReceiveQueue {

private:

jack_midi_data_t *buffer;
size_t buffer_size;
jack_midi_event_t event;
snd_rawmidi_t *rawmidi;

public:

JackALSARawMidiReceiveQueue(snd_rawmidi_t *rawmidi,
size_t buffer_size=4096);
~JackALSARawMidiReceiveQueue();

jack_midi_event_t *
DequeueEvent();

};

}

#endif

+ 40
- 0
linux/alsarawmidi/JackALSARawMidiSendQueue.cpp View File

@@ -0,0 +1,40 @@
#include <cassert>

#include "JackALSARawMidiSendQueue.h"
#include "JackMidiUtil.h"

using Jack::JackALSARawMidiSendQueue;

JackALSARawMidiSendQueue::JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi)
{
this->rawmidi = rawmidi;
blocked = false;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackALSARawMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
assert(size == 1);
if (time > GetCurrentFrame()) {
return EVENT_EARLY;
}
ssize_t result = snd_rawmidi_write(rawmidi, buffer, 1);
switch (result) {
case 1:
blocked = false;
return OK;
case -EWOULDBLOCK:
blocked = true;
return BUFFER_FULL;
}
jack_error("JackALSARawMidiSendQueue::EnqueueEvent - snd_rawmidi_write: "
"%s", snd_strerror(result));
return ERROR;
}

bool
JackALSARawMidiSendQueue::IsBlocked()
{
return blocked;
}

+ 32
- 0
linux/alsarawmidi/JackALSARawMidiSendQueue.h View File

@@ -0,0 +1,32 @@
#ifndef __JackALSARawMidiSendQueue__
#define __JackALSARawMidiSendQueue__

#include <alsa/asoundlib.h>

#include "JackMidiSendQueue.h"

namespace Jack {

class JackALSARawMidiSendQueue: public JackMidiSendQueue {

private:

bool blocked;
snd_rawmidi_t *rawmidi;

public:

JackALSARawMidiSendQueue(snd_rawmidi_t *rawmidi);

JackMidiWriteQueue::EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

bool
IsBlocked();

};

}

#endif

+ 10
- 0
linux/wscript View File

@@ -57,6 +57,14 @@ def build(bld):
'alsa/ice1712.c'
]

alsarawmidi_driver_src = ['alsarawmidi/JackALSARawMidiDriver.cpp',
'alsarawmidi/JackALSARawMidiInputPort.cpp',
'alsarawmidi/JackALSARawMidiOutputPort.cpp',
'alsarawmidi/JackALSARawMidiPort.cpp',
'alsarawmidi/JackALSARawMidiReceiveQueue.cpp',
'alsarawmidi/JackALSARawMidiSendQueue.cpp'
]

ffado_driver_src = ['firewire/JackFFADODriver.cpp',
'firewire/JackFFADOMidiInputPort.cpp',
'firewire/JackFFADOMidiOutputPort.cpp',
@@ -66,6 +74,8 @@ def build(bld):

if bld.env['BUILD_DRIVER_ALSA'] == True:
create_jack_driver_obj(bld, 'alsa', alsa_driver_src, "ALSA")
create_jack_driver_obj(bld, 'alsarawmidi', alsarawmidi_driver_src,
"ALSA")

if bld.env['BUILD_DRIVER_FREEBOB'] == True:
create_jack_driver_obj(bld, 'freebob', 'freebob/JackFreebobDriver.cpp', "LIBFREEBOB")


Loading…
Cancel
Save