diff options
author | Christian Pointner <equinox@spreadspace.org> | 2015-10-12 07:25:04 +0200 |
---|---|---|
committer | Christian Pointner <equinox@spreadspace.org> | 2015-10-12 07:25:04 +0200 |
commit | dfcd1d94f82bebd11d09b86c339d98bcda542a73 (patch) | |
tree | c42fc375960787c35923271c9e575678c71ad355 | |
parent | improved error checking (diff) |
added support for midi
-rw-r--r-- | client/Makefile | 4 | ||||
-rw-r--r-- | client/dolmetschctl-client.c | 29 | ||||
-rw-r--r-- | client/midi.c | 240 | ||||
-rw-r--r-- | client/midi.h | 55 | ||||
-rw-r--r-- | client/slist.c | 123 | ||||
-rw-r--r-- | client/slist.h | 46 |
6 files changed, 482 insertions, 15 deletions
diff --git a/client/Makefile b/client/Makefile index a353d9d..d9cab9a 100644 --- a/client/Makefile +++ b/client/Makefile @@ -26,7 +26,9 @@ endif EXECUTABLE := dolmetschctl-client -C_OBJS := dolmetschctl-client.o +C_OBJS := slist.o \ + midi.o \ + dolmetschctl-client.o C_SRCS := $(C_OBJS:%.o=%.c) diff --git a/client/dolmetschctl-client.c b/client/dolmetschctl-client.c index e001db9..1f97475 100644 --- a/client/dolmetschctl-client.c +++ b/client/dolmetschctl-client.c @@ -30,7 +30,7 @@ #include <lo/lo.h> #include <alsa/asoundlib.h> -/* #include "midi.h" */ +#include "midi.h" /* #include "osc.h" */ @@ -62,21 +62,21 @@ void print_usage() } /* int main_loop(midi_t* m, osc_t* o) */ -int main_loop() +int main_loop(midi_t* m) { int ret = 0; printf("main_loop just started\n"); - /* int midi_npfds_offset = 0; */ - /* int midi_npfds = midi_get_poll_fd_count(m); */ - /* assert(midi_npfds > 0); */ + int midi_npfds_offset = 0; + int midi_npfds = midi_get_poll_fd_count(m); + assert(midi_npfds > 0); /* int osc_npfds_offset = midi_npfds_offset + midi_npfds; */ /* int osc_npfds = osc_get_poll_fd_count(o); */ /* assert(osc_npfds > 0); */ - int npfds = 0; //midi_npfds + osc_npfds; + int npfds = midi_npfds; // + osc_npfds; struct pollfd *pfds = alloca(npfds * sizeof(struct pollfd)); if(!pfds) { error(0, 0, "error while allocating poll fds - stack corrupted??"); @@ -85,7 +85,7 @@ int main_loop() printf("main_loop running with %d pollfds...\n", npfds); for (;;) { - /* midi_get_poll_fds(m, &(pfds[midi_npfds_offset]), midi_npfds); */ + midi_get_poll_fds(m, &(pfds[midi_npfds_offset]), midi_npfds); /* osc_get_poll_fds(o, &(pfds[osc_npfds_offset]), osc_npfds); */ int err = poll(pfds, npfds, 200); @@ -98,9 +98,10 @@ int main_loop() continue; } - /* ret = midi_handle_revents(m, &(pfds[midi_npfds_offset]), midi_npfds, x); */ - /* if(ret) */ - /* break; */ +// ret = midi_handle_revents(m, &(pfds[midi_npfds_offset]), midi_npfds, o); + ret = midi_handle_revents(m, &(pfds[midi_npfds_offset]), midi_npfds); + if(ret) + break; /* ret = osc_handle_revents(o, &(pfds[osc_npfds_offset]), osc_npfds, x); */ /* if(ret) */ @@ -168,16 +169,16 @@ int main(int argc, char* argv[]) return -1; } - /* midi_t m; */ - /* if(midi_init(&m, midi_dev)) */ - /* return -1; */ + midi_t m; + if(midi_init(&m, midi_dev)) + return -1; /* osc_t o; */ /* if(osc_init(&o, osc_target)) */ /* return -1; */ /* int ret = main_loop(&m, &o); */ - int ret = main_loop(); + int ret = main_loop(&m); return ret; } diff --git a/client/midi.c b/client/midi.c new file mode 100644 index 0000000..cd4885a --- /dev/null +++ b/client/midi.c @@ -0,0 +1,240 @@ +/* + * dolmetschctl + * + * + * Copyright (C) 2015 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of dolmetschctl. + * + * dolmetschctl 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 3 of the License, or + * any later version. + * + * dolmetschctl 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 dolmetschctl. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <stdio.h> +#include <error.h> +#include <poll.h> +#include <assert.h> + +#include "midi.h" + +#define NOTE_EN 0x00 +#define NOTE_DE 0x01 +u_int8_t done_data_en[] = { 0xB0, NOTE_EN, 0x01, 0xB0, NOTE_DE, 0x00 }; +u_int8_t done_data_de[] = { 0xB0, NOTE_EN, 0x00, 0xB0, NOTE_DE, 0x01 }; + +int midi_init(midi_t* m, const char* device) +{ + assert(m != NULL); + + m->input_ = NULL; + m->output_ = NULL; + memset(m->buf_, 0, sizeof(m->buf_)); + m->read_idx_ = 0; + + slist_init(&(m->done_data_), free); + + + int ret = snd_rawmidi_open(&(m->input_), &(m->output_), device, SND_RAWMIDI_NONBLOCK); + if(ret < 0) { + error(0, 0, "MIDI: cannot open port '%s': %s", device, snd_strerror(ret)); + return ret; + } + + return 0; +} + +int midi_get_poll_fd_count(midi_t* m) +{ + assert(m); + + m->in_pfds_cnt_ = snd_rawmidi_poll_descriptors_count(m->input_); + assert(m->in_pfds_cnt_ > 0); + m->out_pfds_cnt_ = snd_rawmidi_poll_descriptors_count(m->output_); + assert(m->out_pfds_cnt_ > 0); + + return (m->in_pfds_cnt_ + m->out_pfds_cnt_); +} + +int midi_get_poll_fds(midi_t* m, struct pollfd *pfds, int npfds) +{ + assert(m && pfds && npfds); + + snd_rawmidi_poll_descriptors(m->input_, pfds, m->in_pfds_cnt_); + snd_rawmidi_poll_descriptors(m->output_, &(pfds[m->in_pfds_cnt_]), npfds - m->in_pfds_cnt_); + + int pending_data = 0; + if(m->done_data_.first_) { + midi_done_data_t* d = (midi_done_data_t*)(m->done_data_.first_->data_); + if(d->active_) + pending_data = 1; + } + if(!pending_data) { + int i; + for(i = m->in_pfds_cnt_; i < npfds; ++i) + pfds[i].events = 0; + } + return (m->in_pfds_cnt_ + m->out_pfds_cnt_); +} + +void midi_lang_switch_done(void* data) +{ + assert(data); + midi_done_data_t* d = data; + d->active_ = 1; +} + +//static int midi_enqueue_lang_switch(midi_t* m, osc_t* o, const char* lang, const u_int8_t* buf, int len) +static int midi_enqueue_lang_switch(midi_t* m, const char* lang, const u_int8_t* buf, int len) +{ + midi_done_data_t* done_data = malloc(sizeof(midi_done_data_t)); + assert(done_data); + done_data->self_ = m; + done_data->active_ = 0; + done_data->buf_ = buf; + done_data->len_ = len; + done_data->write_idx_ = 0; + assert(slist_add(&(m->done_data_), done_data)); + + return 0; // mixer_switch_lang(x, lang, &midi_lang_switch_done, done_data); +} + +//static int midi_handle_note_on(midi_t* m, osc_t* o) +static int midi_handle_note_on(midi_t* m) +{ + int ret = 0; + switch(m->buf_[1]) { + case NOTE_EN: ret = midi_enqueue_lang_switch(m/*, o*/,"en", done_data_en, sizeof(done_data_en)); break; + case NOTE_DE: ret = midi_enqueue_lang_switch(m/*, o*/, "de", done_data_de, sizeof(done_data_de)); break; + default: printf("ignoring unknown note\n"); break; + } + return ret; +} + +//static int midi_handle_note_off(midi_t* m, osc_t* o) +static int midi_handle_note_off(midi_t* m) +{ + return 0; +} + +//static int midi_handle_message(midi_t* m, osc_t* o) +static int midi_handle_message(midi_t* m) +{ + int i; + printf("MIDI: "); + for (i = 0; i < sizeof(m->buf_); ++i) + printf("%02X%c", m->buf_[i], (i >= (sizeof(m->buf_)-1)) ? '\n' : ' '); + + int ret = 0; + switch(m->buf_[0]) { +// case 0x90: ret = midi_handle_note_on(m, o); break; +// case 0x80: ret = midi_handle_note_off(m, o); break; + case 0x90: ret = midi_handle_note_on(m); break; + case 0x80: ret = midi_handle_note_off(m); break; + default: printf("ignoring unknown midi command\n"); break; + } + + return ret; +} + +//static int midi_handle_in_revents(midi_t* m, struct pollfd *pfds, int npfds, osc_t* o) +static int midi_handle_in_revents(midi_t* m, struct pollfd *pfds, int npfds) +{ + int err; + unsigned short revents; + if((err = snd_rawmidi_poll_descriptors_revents(m->input_, pfds, npfds, &revents)) < 0) { + error(0, 0, "MIDI: cannot get poll events: %s", snd_strerror(errno)); + return -1; + } + if(pfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + error(0, 0, "MIDI: got POLLERR, POLLHUP or POLLNVAL"); + return -1; + } + if(!(revents & POLLIN)) + return 0; + + int ret = snd_rawmidi_read(m->input_, &(m->buf_[m->read_idx_]), sizeof(m->buf_) - m->read_idx_); + if(ret == -EAGAIN) + return 0; + if(ret < 0) { + error(0, 0, "MIDI: cannot read from midi port: %s", snd_strerror(ret)); + return -1; + } + m->read_idx_ += ret; + if(m->read_idx_ >= sizeof(m->buf_)) { +// ret = midi_handle_message(m, o); + ret = midi_handle_message(m); + memset(m->buf_, 0, sizeof(m->buf_)); + m->read_idx_ = 0; + return ret; + } + + return ret; +} + +static int midi_handle_out_revents(midi_t* m, struct pollfd *pfds, int npfds) +{ + int err; + unsigned short revents; + if((err = snd_rawmidi_poll_descriptors_revents(m->output_, pfds, npfds, &revents)) < 0) { + error(0, 0, "MIDI: cannot get poll events: %s", snd_strerror(errno)); + return -1; + } + if(pfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + error(0, 0, "MIDI: got POLLERR, POLLHUP or POLLNVAL"); + return -1; + } + if(!(revents & POLLOUT)) + return 0; + + assert(m->done_data_.first_); + midi_done_data_t* done_data = (midi_done_data_t*)(m->done_data_.first_->data_); + assert(done_data); + assert(done_data->active_); + assert(done_data->buf_); + + int ret = snd_rawmidi_write(m->output_, &(done_data->buf_[done_data->write_idx_]), done_data->len_ - done_data->write_idx_); + if(ret == -EAGAIN) + return 0; + if(ret < 0) { + error(0, 0, "MIDI: cannot write to port: %s", snd_strerror(ret)); + return -1; + } + done_data->write_idx_ += ret; + if(done_data->write_idx_ >= done_data->len_) { + if((err = snd_rawmidi_drain(m->output_)) < 0) { + error(0, 0, "MIDI: cannot drain output: %s", snd_strerror(err)); + return -1; + } + slist_remove(&(m->done_data_), done_data); + } + + return 0; +} + +//int midi_handle_revents(midi_t* m, struct pollfd *pfds, int npfds, osc_t* o) +int midi_handle_revents(midi_t* m, struct pollfd *pfds, int npfds) +{ + assert(m); + + if(!m->input_ || !m->output_) + return 0; + +// int ret = midi_handle_in_revents(m, pfds, m->in_pfds_cnt_, o); + int ret = midi_handle_in_revents(m, pfds, m->in_pfds_cnt_); + if(ret) + return ret; + + return midi_handle_out_revents(m, &(pfds[m->in_pfds_cnt_]), m->out_pfds_cnt_); +} diff --git a/client/midi.h b/client/midi.h new file mode 100644 index 0000000..2446103 --- /dev/null +++ b/client/midi.h @@ -0,0 +1,55 @@ +/* + * dolmetschctl + * + * + * Copyright (C) 2015 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of dolmetschctl. + * + * dolmetschctl 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 3 of the License, or + * any later version. + * + * dolmetschctl 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 dolmetschctl. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef DOLMETSCHCTL_midi_h_INCLUDED +#define DOLMETSCHCTL_midi_h_INCLUDED + +#include <alsa/asoundlib.h> +#include <poll.h> + +#include "slist.h" + +typedef struct { + snd_rawmidi_t* input_; + int in_pfds_cnt_; + snd_rawmidi_t* output_; + int out_pfds_cnt_; + u_int8_t buf_[3]; + int read_idx_; + slist_t done_data_; +} midi_t; + +typedef struct { + midi_t* self_; + int active_; + const u_int8_t* buf_; + int len_; + int write_idx_; +} midi_done_data_t; + +int midi_init(midi_t* m, const char* device); +int midi_get_poll_fd_count(midi_t* m); +int midi_get_poll_fds(midi_t* m, struct pollfd *pfds, int npfds); +//int midi_handle_revents(midi_t* m, struct pollfd *pfds, int npfds, osc_t* o); +int midi_handle_revents(midi_t* m, struct pollfd *pfds, int npfds); + +#endif diff --git a/client/slist.c b/client/slist.c new file mode 100644 index 0000000..b10aca7 --- /dev/null +++ b/client/slist.c @@ -0,0 +1,123 @@ +/* + * dolmetschctl + * + * + * Copyright (C) 2015 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of dolmetschctl. + * + * dolmetschctl 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 3 of the License, or + * any later version. + * + * dolmetschctl 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 dolmetschctl. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <unistd.h> +#include <stdlib.h> +#include <assert.h> + +#include "slist.h" + +slist_element_t* slist_get_last(slist_element_t* first) +{ + if(!first) + return NULL; + + while(first->next_) + first = first->next_; + + return first; +} + +void slist_init(slist_t* lst, void (*delete_element)(void*)) +{ + assert(lst && delete_element); + + lst->delete_element = delete_element; + lst->first_ = NULL; +} + +slist_element_t* slist_add(slist_t* lst, void* data) +{ + if(!lst || !data) + return NULL; + + slist_element_t* new_element = malloc(sizeof(slist_element_t)); + if(!new_element) + return NULL; + + new_element->data_ = data; + new_element->next_ = NULL; + + if(!lst->first_) + lst->first_ = new_element; + else + slist_get_last(lst->first_)->next_ = new_element; + + return new_element; +} + +void slist_remove(slist_t* lst, void* data) +{ + if(!lst || !lst->first_ || !data) + return; + + slist_element_t* tmp = lst->first_->next_; + slist_element_t* prev = lst->first_; + if(lst->first_->data_ == data) { + lst->first_ = tmp; + lst->delete_element(prev->data_); + free(prev); + } + else { + while(tmp) { + if(tmp->data_ == data) { + prev->next_ = tmp->next_; + lst->delete_element(tmp->data_); + free(tmp); + return; + } + prev = tmp; + tmp = tmp->next_; + } + } +} + +void slist_clear(slist_t* lst) +{ + if(!lst || !lst->first_) + return; + + do { + slist_element_t* deletee = lst->first_; + lst->first_ = lst->first_->next_; + lst->delete_element(deletee->data_); + free(deletee); + } + while(lst->first_); + + lst->first_ = NULL; +} + +int slist_length(slist_t* lst) +{ + if(!lst || !lst->first_) + return 0; + + int len = 0; + slist_element_t* tmp; + for(tmp = lst->first_; tmp; tmp = tmp->next_) + len++; + + return len; +} diff --git a/client/slist.h b/client/slist.h new file mode 100644 index 0000000..70a81b5 --- /dev/null +++ b/client/slist.h @@ -0,0 +1,46 @@ +/* + * dolmetschctl + * + * + * Copyright (C) 2015 Christian Pointner <equinox@spreadspace.org> + * + * This file is part of dolmetschctl. + * + * dolmetschctl 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 3 of the License, or + * any later version. + * + * dolmetschctl 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 dolmetschctl. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef DOLMETSCHCTL_slist_h_INCLUDED +#define DOLMETSCHCTL_slist_h_INCLUDED + +struct slist_element_struct { + void* data_; + struct slist_element_struct* next_; +}; +typedef struct slist_element_struct slist_element_t; + +slist_element_t* slist_get_last(slist_element_t* first); + +struct slist_struct { + void (*delete_element)(void* element); + slist_element_t* first_; +}; +typedef struct slist_struct slist_t; + +void slist_init(slist_t* lst, void (*delete_element)(void*)); +slist_element_t* slist_add(slist_t* lst, void* data); +void slist_remove(slist_t* lst, void* data); +void slist_clear(slist_t* lst); +int slist_length(slist_t* lst); + +#endif |