diff options
Diffstat (limited to 'midialsa.cpp')
-rw-r--r-- | midialsa.cpp | 181 |
1 files changed, 122 insertions, 59 deletions
diff --git a/midialsa.cpp b/midialsa.cpp index d9c36ed..ff2c41f 100644 --- a/midialsa.cpp +++ b/midialsa.cpp @@ -16,56 +16,23 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define LOG_ENABLED 1 -#define LOG_ENABLE_FLOW 1 +//#define LOG_ENABLED 1 +//#define LOG_ENABLE_FLOW 1 #define LOG_GROUP LOG_GROUP_DEV_SB16 +#include <iprt/assert.h> +#include <iprt/poll.h> #include <VBox/err.h> #include <VBox/log.h> #include <alsa/asoundlib.h> +#include <sys/eventfd.h> #include "midialsa.h" #define MAX_POLL_FDS 4 -static ssize_t rawmidi_avail(snd_rawmidi_t *rmidi) -{ - struct pollfd pfds[MAX_POLL_FDS]; - - int nfds = snd_rawmidi_poll_descriptors(rmidi, pfds, MAX_POLL_FDS); - if (nfds <= 0) { - LogWarn(("ALSA rawmidi avail: no descriptors to poll!\n")); - return VERR_AUDIO_ENUMERATION_FAILED; - } - - int ready = poll(pfds, nfds, 0); - if (ready < 0) { - if (errno != EAGAIN && errno != EINTR) { - LogWarnFunc(("Cannot poll, errno=%d\n", errno)); - return VERR_AUDIO_STREAM_NOT_READY; - } - return 0; - } else if (ready == 0) { - return 0; - } else /* ready > 0 */ { - unsigned short revents; - int err = snd_rawmidi_poll_descriptors_revents(rmidi, pfds, nfds, &revents); - if (err != 0) { - LogWarnFunc(("Cannot call revents, err=%d\n", err)); - return VERR_AUDIO_STREAM_NOT_READY; - } - if (revents & POLLNVAL) { - LogWarnFunc(("POLLNVAL\n")); - } - if (revents & POLLERR) { - LogWarnFunc(("POLLERR\n")); - } - return revents & (POLLIN | POLLOUT); - } -} - -MIDIAlsa::MIDIAlsa() : _out(NULL) +MIDIAlsa::MIDIAlsa() : _out(NULL), _eventfd(-1) { } @@ -78,6 +45,12 @@ int MIDIAlsa::open(const char *dev) { int err; + _eventfd = ::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (_eventfd == -1) { + LogWarn(("eventfd error: %s\n", strerror(errno))); + return RTErrConvertFromErrno(errno); + } + if ((err = snd_rawmidi_open(&_in, &_out, "virtual", SND_RAWMIDI_NONBLOCK))) { LogWarn(("ALSA rawmidi open error: %s\n", snd_strerror(err))); return VERR_AUDIO_STREAM_COULD_NOT_CREATE; @@ -99,47 +72,137 @@ int MIDIAlsa::close() snd_rawmidi_close(_out); _out = NULL; } + if (_eventfd) { + ::close(_eventfd); + } return VINF_SUCCESS; } -ssize_t MIDIAlsa::writeAvail() +int MIDIAlsa::reset() { - return _out ? rawmidi_avail(_out) : 0; + if (_in) { + snd_rawmidi_drop(_in); + } + if (_out) { + snd_rawmidi_drop(_out); + } + return VINF_SUCCESS; } +int MIDIAlsa::poll(uint32_t events, uint32_t *revents, RTMSINTERVAL millies) +{ + struct pollfd pfds[MAX_POLL_FDS]; + int i_pipe = -1, i_in = -1, i_out = -1; + int n_in = 0, n_out = 0; + int nfds = 0; + + LogFlowFuncEnter(); + + *revents = 0; + + if (millies > 0 && _eventfd != -1) { + LogFlowFunc(("including eventfd\n")); + i_pipe = nfds; + pfds[nfds].fd = _eventfd; + pfds[nfds].events = POLLIN; + nfds++; + } + + if (_in && events & RTPOLL_EVT_READ) { + LogFlowFunc(("including in\n")); + i_in = nfds; + n_in = snd_rawmidi_poll_descriptors(_in, &pfds[i_in], MAX_POLL_FDS - nfds); + AssertLogRelReturn(n_in > 0, VERR_IO_NOT_READY); + nfds+=n_in; + } + if (_out && events & RTPOLL_EVT_WRITE) { + LogFlowFunc(("including out\n")); + i_out = nfds; + n_out = snd_rawmidi_poll_descriptors(_out, &pfds[i_out], MAX_POLL_FDS - nfds); + AssertLogRelReturn(n_out > 0, VERR_IO_NOT_READY); + nfds+=n_out; + } + + AssertReturn(millies != RT_INDEFINITE_WAIT || nfds > 0, VERR_DEADLOCK); + + int rc; + int ready = ::poll(pfds, nfds, millies = RT_INDEFINITE_WAIT ? -1 : millies); + LogFlowFunc(("poll ready=%d\n", ready)); + if (ready < 0) { + if (errno != EAGAIN && errno != EINTR) { + LogWarnFunc(("Cannot poll, errno=%d\n", errno)); + rc = VERR_IO_NOT_READY; + } else { + rc = VINF_TRY_AGAIN; + } + } else if (ready == 0) { + // Timeout + rc = VINF_TIMEOUT; + } else /* ready > 0 */ { + rc = VINF_SUCCESS; + if (i_pipe != -1) { + if (pfds[i_pipe].revents) { + uint64_t val; + ssize_t r = ::read(_eventfd, &val, sizeof(val)); + Assert(r == sizeof(val)); + NOREF(r); + rc = VINF_INTERRUPTED; + } + } + if (i_in != -1) { + Assert(n_in > 0); + unsigned short rev; + + int err = snd_rawmidi_poll_descriptors_revents(_in, &pfds[i_in], n_in, &rev); + AssertLogRelReturn(err >= 0, VERR_IO_GEN_FAILURE); + + if (rev & POLLIN) *revents |= RTPOLL_EVT_READ; + if (rev & (POLLNVAL|POLLERR)) *revents |= RTPOLL_EVT_ERROR; + } + if (i_out != -1) { + Assert(n_out > 0); + unsigned short rev; + int err = snd_rawmidi_poll_descriptors_revents(_out, &pfds[i_out], n_out, &rev); + AssertLogRelReturn(err >= 0, VERR_IO_GEN_FAILURE); + + if (rev & POLLOUT) *revents |= RTPOLL_EVT_WRITE; + if (rev & (POLLNVAL|POLLERR)) *revents |= RTPOLL_EVT_ERROR; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int MIDIAlsa::pollInterrupt() +{ + if (_eventfd) { + uint64_t val = 1; + ssize_t r = ::write(_eventfd, &val, sizeof(val)); + Assert(r == sizeof(uint64_t)); + NOREF(r); + return VINF_SUCCESS; + } else { + return VERR_INVALID_STATE; + } +} ssize_t MIDIAlsa::write(uint8_t *data, size_t len) { ssize_t result = snd_rawmidi_write(_out, data, len); if (result < 0) { LogWarn(("ALSA midi write error: %s\n", snd_strerror(result))); - return VERR_AUDIO_STREAM_NOT_READY; + return VERR_BROKEN_PIPE; } return result; } -ssize_t MIDIAlsa::readAvail() -{ - return _in ? rawmidi_avail(_in) : 0; -} - ssize_t MIDIAlsa::read(uint8_t *buf, size_t len) { ssize_t result = snd_rawmidi_read(_in, buf, len); if (result < 0) { LogWarn(("ALSA midi read error: %s\n", snd_strerror(result))); - return VERR_AUDIO_STREAM_NOT_READY; + return VERR_BROKEN_PIPE; } return result; } - -int MIDIAlsa::reset() -{ - if (_in) { - snd_rawmidi_drop(_in); - } - if (_out) { - snd_rawmidi_drop(_out); - } - return VINF_SUCCESS; -} |