| @@ -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") | |||