Index: Makefile
===================================================================
RCS file: /cvs/ports/audio/portmidi/Makefile,v
retrieving revision 1.1.1.1
diff -u -p -u -p -r1.1.1.1 Makefile
--- Makefile 23 Mar 2019 13:30:08 -0000 1.1.1.1
+++ Makefile 4 Apr 2019 11:03:08 -0000
@@ -4,6 +4,7 @@ COMMENT = library for real time input an
DISTNAME = portmidi-src-217
PKGNAME = portmidi-217
+REVISION = 0
SHARED_LIBS = portmidi 0.0
Index: files/pm_sndio/pmsndio.c
===================================================================
RCS file: /cvs/ports/audio/portmidi/files/pm_sndio/pmsndio.c,v
retrieving revision 1.1.1.1
diff -u -p -u -p -r1.1.1.1 pmsndio.c
--- files/pm_sndio/pmsndio.c 23 Mar 2019 13:30:08 -0000 1.1.1.1
+++ files/pm_sndio/pmsndio.c 4 Apr 2019 11:03:08 -0000
@@ -3,9 +3,13 @@
#include <stdlib.h>
#include <stdio.h>
#include <sndio.h>
+#include <string.h>
+#include <poll.h>
+#include <pthread.h>
#include "portmidi.h"
#include "pmutil.h"
#include "pminternal.h"
+#include "porttime.h"
PmDeviceID pm_default_input_device_id = -1;
@@ -14,28 +18,70 @@ PmDeviceID pm_default_output_device_id =
extern pm_fns_node pm_sndio_in_dictionary;
extern pm_fns_node pm_sndio_out_dictionary;
+#define NDEVS 9
+#define SYSEX_MAXLEN 256
+
+struct mio_dev {
+ char name[16];
+ struct mio_hdl *hdl;
+ int mode;
+ char errmsg[PM_HOST_ERROR_MSG_LEN];
+ pthread_t thread;
+} devs[NDEVS];
+
+static void set_mode(struct mio_dev *, unsigned int);
+
void pm_init()
{
- // Add output devices
- pm_add_device("SNDIO",
- "default",
- FALSE,
- (void *)0,
- &pm_sndio_out_dictionary);
- pm_add_device("SNDIO",
- "midi/0",
- FALSE,
- (void *)1,
- &pm_sndio_out_dictionary);
+ int i, j = 0;
+
+ /* default */
+ strcpy(devs[j].name, MIO_PORTANY);
+ pm_add_device("SNDIO", devs[j].name, TRUE, (void *) &devs[j],
+ &pm_sndio_in_dictionary);
+ pm_add_device("SNDIO", devs[j].name, FALSE, (void *) &devs[j],
+ &pm_sndio_out_dictionary);
+ j++;
+
+ /* midithru */
+ for (i = 0; i < 4; i++) {
+ sprintf(devs[j].name, "midithru/%d", i);
+ pm_add_device("SNDIO", devs[j].name, TRUE, (void *) &devs[j],
+ &pm_sndio_in_dictionary);
+ pm_add_device("SNDIO", devs[j].name, FALSE, (void *) &devs[j],
+ &pm_sndio_out_dictionary);
+ j++;
+ }
+
+ /* rmidi */
+ for (i = 0; i < 4; i++) {
+ sprintf(devs[j].name, "rmidi/%d", i);
+ pm_add_device("SNDIO", devs[j].name, TRUE, (void *) &devs[j],
+ &pm_sndio_in_dictionary);
+ pm_add_device("SNDIO", devs[j].name, FALSE, (void *) &devs[j],
+ &pm_sndio_out_dictionary);
+ j++;
+ }
// this is set when we return to Pm_Initialize, but we need it
// now in order to (successfully) call Pm_CountDevices()
pm_initialized = TRUE;
- pm_default_output_device_id = 0;
+ pm_default_input_device_id = 0;
+ pm_default_output_device_id = 1;
}
void pm_term(void)
{
+ int i;
+ for(i = 0; i < NDEVS; i++) {
+ if (devs[i].mode != 0) {
+ set_mode(&devs[i], 0);
+ if (devs[i].thread) {
+ pthread_join(devs[i].thread, NULL);
+ devs[i].thread = NULL;
+ }
+ }
+ }
}
PmDeviceID Pm_GetDefaultInputDeviceID() {
@@ -68,74 +114,226 @@ static int midi_message_length(PmMessage
}
}
+void* input_thread(void *param)
+{
+ PmInternal *midi = (PmInternal*)param;
+ struct mio_dev *dev = (struct mio_dev *) midi->descriptor;
+ struct pollfd pfd[1];
+ nfds_t nfds;
+ unsigned char status, byte = 0;
+ int count = 0, msglen;
+ PmEvent pm_ev, pm_ev_rt;
+ unsigned char sysex_data[SYSEX_MAXLEN];
+
+ while(dev->mode & MIO_IN) {
+ nfds = mio_pollfd(dev->hdl, pfd, POLLIN);
+ if (poll(pfd, nfds, 100) < 0) {
+ fprintf(stderr, "poll error; aborting midi thread\n");
+ break;
+ }
+ if (!(mio_revents(dev->hdl, pfd) & POLLIN))
+ continue;
+
+ if (!mio_read(dev->hdl, &byte, 1)) {
+ fprintf(stderr, "read error; aborting midi thread\n");
+ break;
+ }
+
+ if (byte & 0x80) {
+ if (byte >= 0xf8) {
+ /* realtime message */
+ pm_ev_rt.message = byte;
+ pm_ev_rt.timestamp = Pt_Time();
+ pm_read_short(midi, &pm_ev_rt);
+ continue;
+ }
+ status = byte;
+ switch(byte) {
+ case 0xf0: /* sysex */
+ sysex_data[0] = byte;
+ msglen = 0;
+ count = 0;
+ break;
+ case 0xf7: /* sysex end */
+ sysex_data[count] = byte;
+ break;
+ default: /*status */
+ pm_ev.message = byte;
+ pm_ev.timestamp = Pt_Time();
+ msglen = midi_message_length(pm_ev.message);
+ count = 0;
+ break;
+ }
+ } else { /* data */
+ if (status == 0xf0) /* sysex data */
+ if (count < SYSEX_MAXLEN - 1) {
+ sysex_data[count] = byte;
+ } else {
+ fprintf(stderr, "the message is too long\n");
+ continue;
+ }
+ else if (count > 0) /* short messge */
+ pm_ev.message |= (byte << (8 * count));
+ }
+
+ count++;
+
+ if (status == 0xf7) /* sysex data received */
+ pm_read_bytes(midi, &sysex_data[0], count, Pt_Time());
+ else if (count == msglen) /* short message received */
+ pm_read_short(midi, &pm_ev);
+ }
+ pthread_exit(NULL);
+ return NULL;
+}
+
+static void set_mode(struct mio_dev *dev, unsigned int mode) {
+ if (dev->mode != 0)
+ mio_close(dev->hdl);
+ dev->mode = 0;
+ if (mode != 0)
+ dev->hdl = mio_open(dev->name, mode, 1);
+ if (dev->hdl)
+ dev->mode = mode;
+}
+
static PmError sndio_out_open(PmInternal *midi, void *driverInfo)
{
- const char *device = descriptors[midi->device_id].pub.name;
- struct mio_hdl *mio;
+ descriptor_type desc = &descriptors[midi->device_id];
+ struct mio_dev *dev = (struct mio_dev *) desc->descriptor;
- mio = mio_open(device, MIO_OUT, 0);
- if (!mio) {
- fprintf(stderr, "mio_open failed\n");
+ if (dev->mode & MIO_OUT)
+ return pmNoError;
+
+ set_mode(dev, dev->mode | MIO_OUT);
+ if (!(dev->mode & MIO_OUT)) {
+ snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN,
+ "mio_open (output) failed: %s\n", dev->name);
return pmHostError;
}
- midi->descriptor = mio;
+ midi->descriptor = (void *)dev;
return pmNoError;
}
+
+static PmError sndio_in_open(PmInternal *midi, void *driverInfo)
+{
+ descriptor_type desc = &descriptors[midi->device_id];
+ struct mio_dev *dev = (struct mio_dev *) desc->descriptor;
+
+ if (dev->mode & MIO_IN)
+ return pmNoError;
+
+ set_mode(dev, dev->mode | MIO_IN);
+ if (!(dev->mode & MIO_IN)) {
+ snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN,
+ "mio_open (input) failed: %s\n", dev->name);
+ return pmHostError;
+ }
+ midi->descriptor = (void *)dev;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_create(&dev->thread, &attr, input_thread, ( void* )midi);
+ return pmNoError;
+}
+
static PmError sndio_out_close(PmInternal *midi)
{
- mio_close(midi->descriptor);
+ struct mio_dev *dev = (struct mio_dev *) midi->descriptor;
+
+ if (dev->mode & MIO_OUT)
+ set_mode(dev, dev->mode & ~MIO_OUT);
+ return pmNoError;
+}
+
+static PmError sndio_in_close(PmInternal *midi)
+{
+ struct mio_dev *dev = (struct mio_dev *) midi->descriptor;
+
+ if (dev->mode & MIO_IN) {
+ set_mode(dev, dev->mode & ~MIO_IN);
+ pthread_join(dev->thread, NULL);
+ dev->thread = NULL;
+ }
return pmNoError;
}
+
static PmError sndio_abort(PmInternal *midi)
{
- mio_close(midi->descriptor);
return pmNoError;
}
+
static PmTimestamp sndio_synchronize(PmInternal *midi)
{
return 0;
}
-static PmError sndio_write_byte(PmInternal *midi, unsigned char byte,
- PmTimestamp timestamp)
+
+static PmError do_write(struct mio_dev *dev, const void *addr, size_t nbytes)
{
- size_t w = mio_write(midi->descriptor, &byte, 1);
- if (w != 1) {
- fprintf(stderr, "mio_write failed\n");
+ int nfds, revents;
+ struct pollfd pfds[1];
+
+ do {
+ nfds = mio_pollfd(dev->hdl, pfds, POLLOUT);
+ if (poll(pfds, nfds, -1) < 0) {
+ fprintf(stderr, "poll error; write failed\n");
+ break;
+ }
+ revents = mio_revents(dev->hdl, pfds);
+ } while (!(revents & POLLOUT));
+
+ size_t w = mio_write(dev->hdl, addr, nbytes);
+
+ if (w != nbytes) {
+ snprintf(dev->errmsg, PM_HOST_ERROR_MSG_LEN,
+ "mio_write failed, bytes written:%zu\n", w);
return pmHostError;
}
return pmNoError;
}
-static PmError sndio_write_short(PmInternal *midi, PmEvent *event)
+
+static PmError sndio_write_byte(PmInternal *midi, unsigned char byte,
+ PmTimestamp timestamp)
{
- int bytes = midi_message_length(event->message);
- PmMessage msg = event->message;
- int i;
- for (i = 0; i < bytes; i++) {
- unsigned char byte = msg;
- sndio_write_byte(midi, byte, event->timestamp);
- msg >>= 8;
- }
+ struct mio_dev *dev = (struct mio_dev *) midi->descriptor;
- return pmNoError;
+ return do_write(dev, &byte, 1);
}
-static PmError sndio_write_flush(PmInternal *midi, PmTimestamp timestamp)
+
+static PmError sndio_write_short(PmInternal *midi, PmEvent *event)
{
+ struct mio_dev *dev = (struct mio_dev *) midi->descriptor;
+ int nbytes = midi_message_length(event->message);
+
+ if (midi->latency > 0) {
+ /* XXX the event should be queued for later playback */
+ return do_write(dev, &event->message, nbytes);
+ } else {
+ return do_write(dev, &event->message, nbytes);
+ }
return pmNoError;
}
-PmError sndio_sysex(PmInternal *midi, PmTimestamp timestamp)
+
+static PmError sndio_write_flush(PmInternal *midi, PmTimestamp timestamp)
{
return pmNoError;
}
+
static unsigned int sndio_has_host_error(PmInternal *midi)
{
- return 0;
+ struct mio_dev *dev = (struct mio_dev *) midi->descriptor;
+
+ return (dev->errmsg[0] != '\0');
}
+
static void sndio_get_host_error(PmInternal *midi, char *msg, unsigned int len)
{
+ struct mio_dev *dev = (struct mio_dev *) midi->descriptor;
+
+ strlcpy(msg, dev->errmsg, len);
+ dev->errmsg[0] = '\0';
}
-/*
pm_fns_node pm_sndio_in_dictionary = {
none_write_short,
none_sysex,
@@ -147,16 +345,15 @@ pm_fns_node pm_sndio_in_dictionary = {
sndio_in_open,
sndio_abort,
sndio_in_close,
- sndio_poll,
+ success_poll,
sndio_has_host_error,
sndio_get_host_error
};
-*/
pm_fns_node pm_sndio_out_dictionary = {
sndio_write_short,
- sndio_sysex,
- sndio_sysex,
+ none_sysex,
+ none_sysex,
sndio_write_byte,
sndio_write_short,
sndio_write_flush,
The attached diff adds input support to portmidi.
For each open input device, a thread is started, waiting for input using
poll(2).
The following program can be used for testing input and ouput:
/usr/ports/pobj/portmidi-217/build-amd64/Release/test
(it is not installed by the port)
If I understand the API correctly, portmidi is not a good choice for realtime
midi-input. There is no callback functionality for reading, so busy-waiting is
needed, like this:
while (Pm_Poll(midi)) {
Pm_Read(midi, buffer, 1);
}
(see http://portmedia.sourceforge.net/portmidi/doxygen/group__grp__io.html)
Currently, audacity is the only user of portmidi, only using it for output.
.. so I'm not sure how useful this diff is ;)
Any comments or ok?
No comments:
Post a Comment