/* * dolmetschctl * * * Copyright (C) 2015 Christian Pointner * * 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 . */ #include "config.h" #include #include #include #include #include #include #include #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; x->pfd_count_ = 0; 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("\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(" \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 switch", lang); return -1; } void mixer_print_tasks(mixer_t* x) { assert(x); printf("tasks:\n"); if(!slist_length(&(x->tasks_))) printf(" \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) { return (x->pfd_count_ = snd_rawmidi_poll_descriptors_count(x->output_)); } int mixer_get_poll_fds(mixer_t* x, struct pollfd *pfds, int npfds) { if(slist_length(&(x->tasks_))) return snd_rawmidi_poll_descriptors(x->output_, pfds, npfds); else { int i; for(i = 0; i < x->pfd_count_; ++i) pfds[i].fd = -1; } return x->pfd_count_; } int mixer_handle_revents(mixer_t* x, struct pollfd *pfds, int npfds) { assert(x); 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; }