Jack2  1.9.10
JackALSARawMidiPort.cpp
00001 /*
00002 Copyright (C) 2011 Devin Anderson
00003 
00004 This program is free software; you can redistribute it and/or modify
00005 it under the terms of the GNU General Public License as published by
00006 the Free Software Foundation; either version 2 of the License, or
00007 (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 
00018 */
00019 
00020 #include <cassert>
00021 #include <stdexcept>
00022 #include <string>
00023 
00024 #include "JackALSARawMidiPort.h"
00025 #include "JackALSARawMidiUtil.h"
00026 #include "JackError.h"
00027 
00028 using Jack::JackALSARawMidiPort;
00029 
00030 JackALSARawMidiPort::JackALSARawMidiPort(snd_rawmidi_info_t *info,
00031                                          size_t index, unsigned short io_mask)
00032 {
00033     int card = snd_rawmidi_info_get_card(info);
00034     unsigned int device = snd_rawmidi_info_get_device(info);
00035     unsigned int subdevice = snd_rawmidi_info_get_subdevice(info);
00036     char device_id[32];
00037     snprintf(device_id, sizeof(device_id), "hw:%d,%d,%d", card, device,
00038              subdevice);
00039     const char *alias_suffix;
00040     const char *error_message;
00041     snd_rawmidi_t **in;
00042     const char *name_prefix;
00043     snd_rawmidi_t **out;
00044     if (snd_rawmidi_info_get_stream(info) == SND_RAWMIDI_STREAM_OUTPUT) {
00045         alias_suffix = "out";
00046         in = 0;
00047         name_prefix = "system:midi_playback_";
00048         out = &rawmidi;
00049     } else {
00050         alias_suffix = "in";
00051         in = &rawmidi;
00052         name_prefix = "system:midi_capture_";
00053         out = 0;
00054     }
00055     const char *func;
00056     int code = snd_rawmidi_open(in, out, device_id, SND_RAWMIDI_NONBLOCK);
00057     if (code) {
00058         error_message = snd_strerror(code);
00059         func = "snd_rawmidi_open";
00060         goto handle_error;
00061     }
00062     snd_rawmidi_params_t *params;
00063     code = snd_rawmidi_params_malloc(&params);
00064     if (code) {
00065         error_message = snd_strerror(code);
00066         func = "snd_rawmidi_params_malloc";
00067         goto close;
00068     }
00069     code = snd_rawmidi_params_current(rawmidi, params);
00070     if (code) {
00071         error_message = snd_strerror(code);
00072         func = "snd_rawmidi_params_current";
00073         goto free_params;
00074     }
00075     code = snd_rawmidi_params_set_avail_min(rawmidi, params, 1);
00076     if (code) {
00077         error_message = snd_strerror(code);
00078         func = "snd_rawmidi_params_set_avail_min";
00079         goto free_params;
00080     }
00081 
00082     // Minimum buffer size allowed by ALSA
00083     code = snd_rawmidi_params_set_buffer_size(rawmidi, params, 32);
00084     if (code) {
00085         error_message = snd_strerror(code);
00086         func = "snd_rawmidi_params_set_buffer_size";
00087         goto free_params;
00088     }
00089 
00090     code = snd_rawmidi_params_set_no_active_sensing(rawmidi, params, 1);
00091     if (code) {
00092         error_message = snd_strerror(code);
00093         func = "snd_rawmidi_params_set_no_active_sensing";
00094         goto free_params;
00095     }
00096     code = snd_rawmidi_params(rawmidi, params);
00097     if (code) {
00098         error_message = snd_strerror(code);
00099         func = "snd_rawmidi_params";
00100         goto free_params;
00101     }
00102     snd_rawmidi_params_free(params);
00103     alsa_poll_fd_count = snd_rawmidi_poll_descriptors_count(rawmidi);
00104     if (! alsa_poll_fd_count) {
00105         error_message = "returned '0' count for poll descriptors";
00106         func = "snd_rawmidi_poll_descriptors_count";
00107         goto close;
00108     }
00109     try {
00110         CreateNonBlockingPipe(fds);
00111     } catch (std::exception e) {
00112         error_message = e.what();
00113         func = "CreateNonBlockingPipe";
00114         goto close;
00115     }
00116     snprintf(alias, sizeof(alias), "system:%d-%d %s %d %s", card + 1,
00117              device + 1, snd_rawmidi_info_get_name(info), subdevice + 1,
00118              alias_suffix);
00119     snprintf(name, sizeof(name), "%s%zu", name_prefix, index + 1);
00120     this->io_mask = io_mask;
00121     return;
00122  free_params:
00123     snd_rawmidi_params_free(params);
00124  close:
00125     snd_rawmidi_close(rawmidi);
00126  handle_error:
00127     throw std::runtime_error(std::string(func) + ": " + error_message);
00128 }
00129 
00130 JackALSARawMidiPort::~JackALSARawMidiPort()
00131 {
00132     DestroyNonBlockingPipe(fds);
00133     if (rawmidi) {
00134         int code = snd_rawmidi_close(rawmidi);
00135         if (code) {
00136             jack_error("JackALSARawMidiPort::~JackALSARawMidiPort - "
00137                        "snd_rawmidi_close: %s", snd_strerror(code));
00138         }
00139         rawmidi = 0;
00140     }
00141 }
00142 
00143 const char *
00144 JackALSARawMidiPort::GetAlias()
00145 {
00146     return alias;
00147 }
00148 
00149 int
00150 JackALSARawMidiPort::GetIOPollEvent()
00151 {
00152     unsigned short events;
00153     int code = snd_rawmidi_poll_descriptors_revents(rawmidi, alsa_poll_fds,
00154                                                     alsa_poll_fd_count,
00155                                                     &events);
00156     if (code) {
00157         jack_error("JackALSARawMidiPort::GetIOPollEvents - "
00158                    "snd_rawmidi_poll_descriptors_revents: %s",
00159                    snd_strerror(code));
00160         return -1;
00161     }
00162     if (events & POLLNVAL) {
00163         jack_error("JackALSARawMidiPort::GetIOPollEvents - the file "
00164                    "descriptor is invalid.");
00165         return -1;
00166     }
00167     if (events & POLLERR) {
00168         jack_error("JackALSARawMidiPort::GetIOPollEvents - an error has "
00169                    "occurred on the device or stream.");
00170         return -1;
00171     }
00172     return (events & io_mask) ? 1 : 0;
00173 }
00174 
00175 const char *
00176 JackALSARawMidiPort::GetName()
00177 {
00178     return name;
00179 }
00180 
00181 int
00182 JackALSARawMidiPort::GetPollDescriptorCount()
00183 {
00184     return alsa_poll_fd_count + 1;
00185 }
00186 
00187 int
00188 JackALSARawMidiPort::GetQueuePollEvent()
00189 {
00190     unsigned short events = queue_poll_fd->revents;
00191     if (events & POLLNVAL) {
00192         jack_error("JackALSARawMidiPort::GetQueuePollEvents - the file "
00193                    "descriptor is invalid.");
00194         return -1;
00195     }
00196     if (events & POLLERR) {
00197         jack_error("JackALSARawMidiPort::GetQueuePollEvents - an error has "
00198                    "occurred on the device or stream.");
00199         return -1;
00200     }
00201     int event = events & POLLIN ? 1 : 0;
00202     if (event) {
00203         char c;
00204         ssize_t result = read(fds[0], &c, 1);
00205         assert(result);
00206         if (result < 0) {
00207             jack_error("JackALSARawMidiPort::GetQueuePollEvents - error "
00208                        "reading a byte from the pipe file descriptor: %s",
00209                        strerror(errno));
00210             return -1;
00211         }
00212     }
00213     return event;
00214 }
00215 
00216 void
00217 JackALSARawMidiPort::PopulatePollDescriptors(struct pollfd *poll_fd)
00218 {
00219     alsa_poll_fds = poll_fd + 1;
00220     assert(snd_rawmidi_poll_descriptors(rawmidi, alsa_poll_fds,
00221                                         alsa_poll_fd_count) ==
00222            alsa_poll_fd_count);
00223     queue_poll_fd = poll_fd;
00224     queue_poll_fd->events = POLLERR | POLLIN | POLLNVAL;
00225     queue_poll_fd->fd = fds[0];
00226     SetIOEventsEnabled(true);
00227 }
00228 
00229 void
00230 JackALSARawMidiPort::SetIOEventsEnabled(bool enabled)
00231 {
00232     unsigned short mask = POLLNVAL | POLLERR | (enabled ? io_mask : 0);
00233     for (int i = 0; i < alsa_poll_fd_count; i++) {
00234         (alsa_poll_fds + i)->events = mask;
00235     }
00236 }
00237 
00238 bool
00239 JackALSARawMidiPort::TriggerQueueEvent()
00240 {
00241     char c;
00242     ssize_t result = write(fds[1], &c, 1);
00243     assert(result <= 1);
00244     switch (result) {
00245     case 1:
00246         return true;
00247     case 0:
00248         jack_error("JackALSARawMidiPort::TriggerQueueEvent - error writing a "
00249                    "byte to the pipe file descriptor: %s", strerror(errno));
00250         break;
00251     default:
00252         jack_error("JackALSARawMidiPort::TriggerQueueEvent - couldn't write a "
00253                    "byte to the pipe file descriptor.");
00254     }
00255     return false;
00256 }