/* Copyright (C) 2011 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "JackALSARawMidiPort.h" #include "JackALSARawMidiUtil.h" #include "JackError.h" using Jack::JackALSARawMidiPort; JackALSARawMidiPort::JackALSARawMidiPort(const char *client_name, snd_rawmidi_info_t *info, size_t index, unsigned short io_mask) { int card = snd_rawmidi_info_get_card(info); unsigned int device = snd_rawmidi_info_get_device(info); unsigned int subdevice = snd_rawmidi_info_get_subdevice(info); char device_id[32]; snprintf(device_id, sizeof(device_id), "hw:%d,%d,%d", card, device, subdevice); const char* driver_name = snd_rawmidi_info_get_name(info); const char *alias_suffix; const char *error_message; snd_rawmidi_t **in; const char *port_name; snd_rawmidi_t **out; if (snd_rawmidi_info_get_stream(info) == SND_RAWMIDI_STREAM_OUTPUT) { alias_suffix = "out"; in = 0; port_name = "playback_"; out = &rawmidi; } else { alias_suffix = "in"; in = &rawmidi; port_name = "capture_"; out = 0; } 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 free_params; } 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; } // Minimum buffer size allowed by ALSA code = snd_rawmidi_params_set_buffer_size(rawmidi, params, 32); if (code) { error_message = snd_strerror(code); func = "snd_rawmidi_params_set_buffer_size"; goto free_params; } code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1); if (code) { error_message = snd_strerror(code); func = "snd_rawmidi_params_set_no_active_sensing"; goto free_params; } code = snd_rawmidi_params(rawmidi, params); if (code) { error_message = snd_strerror(code); func = "snd_rawmidi_params"; goto free_params; } snd_rawmidi_params_free(params); alsa_poll_fd_count = snd_rawmidi_poll_descriptors_count(rawmidi); if (! alsa_poll_fd_count) { error_message = "returned '0' count for poll descriptors"; func = "snd_rawmidi_poll_descriptors_count"; goto close; } try { CreateNonBlockingPipe(fds); } catch (std::exception& e) { error_message = e.what(); func = "CreateNonBlockingPipe"; goto close; } snprintf(alias, sizeof(alias), "system:%d-%d %s %d %s", card + 1, device + 1, driver_name, subdevice + 1, alias_suffix); snprintf(name, sizeof(name), "%s:%s%zu", client_name, port_name, index + 1); strncpy(device_name, driver_name, sizeof(device_name) - 1); this->io_mask = io_mask; 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() { DestroyNonBlockingPipe(fds); 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; } int JackALSARawMidiPort::GetIOPollEvent() { unsigned short events; int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds, alsa_poll_fd_count, &events); if (code) { jack_error("JackALSARawMidiPort::GetIOPollEvents - " "snd_rawmidi_poll_descriptors_revents: %s", snd_strerror(code)); return -1; } if (events & POLLNVAL) { jack_error("JackALSARawMidiPort::GetIOPollEvents - the file " "descriptor is invalid."); return -1; } if (events & POLLERR) { jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has " "occurred on the device or stream."); return -1; } return (events & io_mask) ? 1 : 0; } const char * JackALSARawMidiPort::GetName() { return name; } const char * JackALSARawMidiPort::GetDeviceName() { return device_name; } int JackALSARawMidiPort::GetPollDescriptorCount() { return alsa_poll_fd_count + 1; } int JackALSARawMidiPort::GetQueuePollEvent() { unsigned short events = queue_poll_fd->revents; if (events & POLLNVAL) { jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file " "descriptor is invalid."); return -1; } if (events & POLLERR) { jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has " "occurred on the device or stream."); return -1; } int event = events & POLLIN ? 1 : 0; if (event) { char c; ssize_t result = read(fds[0], &c, 1); assert(result); if (result < 0) { jack_error("JackALSARawMidiPort::GetQueuePollEvents - error " "reading a byte from the pipe file descriptor: %s", strerror(errno)); return -1; } } return event; } void JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd) { alsa_poll_fds = poll_fd + 1; assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds, alsa_poll_fd_count) == alsa_poll_fd_count); queue_poll_fd = poll_fd; queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL; queue_poll_fd->fd = fds[0]; SetIOEventsEnabled(true); } void JackALSARawMidiPort::SetIOEventsEnabled(bool enabled) { unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0); for (int i = 0; i < alsa_poll_fd_count; i++) { (alsa_poll_fds + i)->events = mask; } } bool JackALSARawMidiPort::TriggerQueueEvent() { char c; ssize_t result = write(fds[1], &c, 1); assert(result <= 1); switch (result) { case 1: return true; case 0: jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a " "byte to the pipe file descriptor: %s", strerror(errno)); break; default: jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a " "byte to the pipe file descriptor."); } return false; }