@@ -161,7 +161,7 @@ namespace Jack { | |||||
*/ | */ | ||||
jack_nframes_t | 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 | 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"; | error_source = "jack_connect"; | ||||
goto deactivate_client; | 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); | code = pthread_mutex_unlock(&start_mutex); | ||||
if (code) { | if (code) { | ||||
error_message = strerror(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' | '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', | ffado_driver_src = ['firewire/JackFFADODriver.cpp', | ||||
'firewire/JackFFADOMidiInputPort.cpp', | 'firewire/JackFFADOMidiInputPort.cpp', | ||||
'firewire/JackFFADOMidiOutputPort.cpp', | 'firewire/JackFFADOMidiOutputPort.cpp', | ||||
@@ -66,6 +74,8 @@ def build(bld): | |||||
if bld.env['BUILD_DRIVER_ALSA'] == True: | if bld.env['BUILD_DRIVER_ALSA'] == True: | ||||
create_jack_driver_obj(bld, 'alsa', alsa_driver_src, "ALSA") | 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: | if bld.env['BUILD_DRIVER_FREEBOB'] == True: | ||||
create_jack_driver_obj(bld, 'freebob', 'freebob/JackFreebobDriver.cpp', "LIBFREEBOB") | create_jack_driver_obj(bld, 'freebob', 'freebob/JackFreebobDriver.cpp', "LIBFREEBOB") | ||||