diff options
author | Christian Pointner <equinox@spreadspace.org> | 2015-10-12 06:54:55 +0200 |
---|---|---|
committer | Christian Pointner <equinox@spreadspace.org> | 2015-10-12 06:54:55 +0200 |
commit | 24714dcba8a4b4717251f320570653e1cebf029a (patch) | |
tree | c23e643005b951e0c714e045312099cc34879a9d /server/mixer.c | |
parent | added command line parser (diff) |
moved apps to server
Diffstat (limited to 'server/mixer.c')
-rw-r--r-- | server/mixer.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/server/mixer.c b/server/mixer.c new file mode 100644 index 0000000..54df1b4 --- /dev/null +++ b/server/mixer.c @@ -0,0 +1,284 @@ +/* + * 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 <string.h> +#include <error.h> +#include <poll.h> +#include <assert.h> +#include <sys/types.h> +#include <dirent.h> + +#include "mixer.h" + +static void free_lang_entry(void* ptr) +{ + lang_t* l = ptr; + assert(l); + free(l->name_); + slist_clear(&(l->cmds_)); + free(l); +} + +static void free_task_entry(void* ptr) +{ + task_t* t = ptr; + assert(t); + free(t->buf_); + free(t); +} + +static int mixer_read_config_file(mixer_t* x, int dirfd, const char* filename) +{ + assert(dirfd > 0); + + int fd = openat(dirfd, filename, O_RDONLY); + if(fd < 0) { + error(0, errno, "erorr open('%s')", filename); + return -1; + } + FILE* conf = fdopen(fd, "r"); + if(conf == NULL) { + error(0, errno, "erorr fdopen('%s')", filename); + return -1; + } + + lang_t* lang = malloc(sizeof(lang_t)); + assert(lang); + assert((lang->name_ = strdup(filename))); + slist_init(&(lang->cmds_), free); + assert(slist_add(&(x->langs_), lang)); + + char buf[256]; + while(fgets(buf, sizeof(buf), conf) != NULL) { + midi_cmd_t* cmd = malloc(sizeof(midi_cmd_t)); + if(!cmd) { + error(0, ENOMEM, "can't create midi command stucture"); + break; + } + if(sscanf(buf, "%02hhx %02hhx %02hhx", &((*cmd)[0]), &((*cmd)[1]), &((*cmd)[2])) != 3) { + continue; + } + assert(slist_add(&(lang->cmds_), cmd)); + } + + fclose(conf); + return 0; +} + +static int mixer_read_config(mixer_t* x) +{ + char path[1024]; + int ret = snprintf(path, sizeof(path), "%s/%s", ETCDIR, x->name_); + assert(ret < sizeof(path)); + + DIR* d = opendir(path); + if(d == NULL) { + error(0, errno, "MIXER: cannot open config directory '%s'", path); + return -1; + } + + struct dirent* entry; + while((entry = readdir(d))) { + if(entry->d_type == DT_REG) { + if((ret = mixer_read_config_file(x, dirfd(d), entry->d_name))) + break; + } + } + + if(closedir(d) < 0) { + error(0, errno, "MIXER: cannot closeconfig directory '%s'", path); + return -1; + } + + return ret; +} + +int mixer_init(mixer_t* x, const char* name, const char* device) +{ + assert(x != NULL); + + x->name_ = name; + x->output_ = NULL; + slist_init(&(x->langs_), free_lang_entry); + slist_init(&(x->tasks_), free_task_entry); + int ret = snd_rawmidi_open(NULL, &(x->output_), device, SND_RAWMIDI_NONBLOCK); + if(ret < 0) { + error(0, 0, "MIXER: cannot open midi port '%s': %s", device, snd_strerror(ret)); + return ret; + } + + return mixer_read_config(x);; +} + +static void mixer_print_midi_cmds(slist_t cmds) +{ + if(!slist_length(&(cmds))) + printf("<no commands>\n"); + else { + slist_element_t* tmp; + for(tmp = cmds.first_; tmp; tmp = tmp->next_) { + midi_cmd_t* c = (midi_cmd_t*)(tmp->data_); + printf(" 0x%02X 0x%02X 0x%02X\n", (*c)[0], (*c)[1], (*c)[2]); + } + } +} + +void mixer_print_langs(mixer_t* x) +{ + assert(x); + + printf("languages:\n"); + if(!slist_length(&(x->langs_))) + printf(" <no languages>\n"); + else { + slist_element_t* tmp; + for(tmp = x->langs_.first_; tmp; tmp = tmp->next_) { + lang_t* l = (lang_t*)(tmp->data_); + printf(" %s:\n", l->name_); + mixer_print_midi_cmds(l->cmds_); + } + } +} + +static u_int8_t* mixer_create_task_buf(slist_t cmds, int len) +{ + u_int8_t* buf = malloc(len); + slist_element_t* tmp; + int i; + for(tmp = cmds.first_, i=0; tmp; tmp = tmp->next_, i+=sizeof(midi_cmd_t)) { + midi_cmd_t* c = (midi_cmd_t*)(tmp->data_); + memcpy(&(buf[i]), (*c), sizeof(midi_cmd_t)); + } + return buf; +} + +int mixer_switch_lang(mixer_t* x, const char* lang, void (*done_cb)(void*), void* done_data) +{ + slist_element_t* tmp; + for(tmp = x->langs_.first_; tmp; tmp = tmp->next_) { + lang_t* l = (lang_t*)(tmp->data_); + if(!strcmp(lang, l->name_)) { + int len = slist_length(&(l->cmds_)); + if(len > 0) { + task_t* task = malloc(sizeof(task_t)); + assert(task); + task->len_ = sizeof(midi_cmd_t) * len; + assert((task->buf_ = mixer_create_task_buf(l->cmds_, task->len_))); + task->write_idx_ = 0; + task->done_cb_ = done_cb; + task->done_data_ = done_data; + assert(slist_add(&(x->tasks_), task)); + } + return 0; + } + } + error(0, 0, "requested language '%s' not found ... ignoring request", lang); + return 0; +} + +void mixer_print_tasks(mixer_t* x) +{ + assert(x); + + printf("tasks:\n"); + if(!slist_length(&(x->tasks_))) + printf(" <no tasks>\n"); + else { + slist_element_t* tmp; + for(tmp = x->tasks_.first_; tmp; tmp = tmp->next_) { + task_t* t = (task_t*)(tmp->data_); + int i; + printf(" buf(%d) = ", t->len_); + for(i = 0; i < t->len_; ++i) { + printf("0x%02X%s", t->buf_[i], (i && !((i+1) % 3)) ? ", " : " "); + } + printf("\n write_idx = %d\n done = %016lX ( %016lX )\n\n", t->write_idx_, (int64_t)(t->done_cb_), (int64_t)(t->done_data_)); + } + } +} + +int mixer_get_poll_fd_count(mixer_t* x) +{ + assert(x); + + return snd_rawmidi_poll_descriptors_count(x->output_); +} + +int mixer_get_poll_fds(mixer_t* x, struct pollfd *pfds, int npfds) +{ + assert(x && pfds && npfds); + + int ret = snd_rawmidi_poll_descriptors(x->output_, pfds, npfds); + if(!slist_length(&(x->tasks_))) { + int i; + for(i = 0; i < ret; ++i) + pfds[i].events = 0; + } + return ret; +} + +int mixer_handle_revents(mixer_t* x, struct pollfd *pfds, int npfds) +{ + assert(x && pfds && npfds); + + int err; + unsigned short revents; + if((err = snd_rawmidi_poll_descriptors_revents(x->output_, pfds, npfds, &revents)) < 0) { + error(0, 0, "MIXER: cannot get poll events: %s", snd_strerror(errno)); + return -1; + } + if(pfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + error(0, 0, "MIXER: got POLLERR, POLLHUP or POLLNVAL"); + return -1; + } + if(!(revents & POLLOUT)) + return 0; + + assert(x->tasks_.first_); + task_t* task = (task_t*)(x->tasks_.first_->data_); + assert(task); + assert(task->buf_); + + int ret = snd_rawmidi_write(x->output_, &(task->buf_[task->write_idx_]), task->len_ - task->write_idx_); + if(ret == -EAGAIN) + return 0; + if(ret < 0) { + error(0, 0, "MIXER: cannot write to midi port: %s", snd_strerror(ret)); + return -1; + } + task->write_idx_ += ret; + if(task->write_idx_ >= task->len_) { + if((err = snd_rawmidi_drain(x->output_)) < 0) { + error(0, 0, "MIXER: cannot drain output: %s", snd_strerror(err)); + return -1; + } + if(task->done_cb_) + task->done_cb_(task->done_data_); + + slist_remove(&(x->tasks_), task); + } + + return 0; +} |