@@ -161,7 +161,7 @@ namespace Jack { | |||
*/ | |||
jack_nframes_t | |||
Process(jack_nframes_t boundary_frame); | |||
Process(jack_nframes_t boundary_frame=0); | |||
}; | |||
@@ -138,7 +138,7 @@ namespace Jack { | |||
*/ | |||
jack_nframes_t | |||
Process(jack_nframes_t boundary_frame); | |||
Process(jack_nframes_t boundary_frame=0); | |||
}; | |||
@@ -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); | |||
@@ -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 |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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."); | |||
} | |||
} | |||
} |
@@ -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 |
@@ -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(¶ms); | |||
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; | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} |
@@ -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 |
@@ -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; | |||
} |
@@ -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 |
@@ -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") | |||