//dmx-waveform.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2014 * * This file is part of roard a part of RoarAudio, * a cross-platform sound system for both, home and professional use. * See README for details. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * RoarAudio 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 this software; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include #define SAMPLES_PER_TICK 2 struct state { size_t startaddr; size_t len; int stream; struct roar_stream_server * ss; }; static struct state * g_state; static struct state g_state_init = { .startaddr = 0, .len = 4, .stream = -1, .ss = NULL }; static int16_t __get_chanval(struct state * self, size_t c, int pos) { int16_t ret = 0; if ( pos ) { ret = light_dmxchannel_get(self->startaddr + c); } else { if ( self->len & 1 ) { ret = light_dmxchannel_get(self->startaddr + self->len - 1); } else { ret = light_dmxchannel_get(self->startaddr + self->len/2 + c); } } if ( !pos ) ret *= -1; ret *= 127; ROAR_DBG("__get_chanval(self=%p, c=%lu, pos=%i) = %i", self, (long unsigned int)c, pos, (int)ret); return ret; } static ssize_t _vio_read (struct roar_vio_calls * vio, void *buf, size_t count) { struct state * self = vio->inst; ssize_t samplesize = roar_info2samplesize(&(ROAR_STREAM(self->ss)->info)) / 8; ssize_t framesize = roar_info2framesize(&(ROAR_STREAM(self->ss)->info)) / 8; size_t i, c; int16_t * samp = buf; size_t zeros = 0; ROAR_DBG("_vio_read(vio=%p, buf=%p, count=%lu) = ?", vio, buf, (long unsigned int)count); if ( count % framesize ) { roar_err_set(ROAR_ERROR_RANGE); return -1; } framesize *= 2; if ( count % framesize ) { zeros = count % framesize; count -= zeros; memset(buf+count, 0, zeros); } ROAR_DBG("_vio_read(vio=%p, buf=%p, count=%lu) = ?", vio, buf, (long unsigned int)count); for (i = 0; i < count/samplesize;) { for (c = 0; c < ROAR_STREAM(self->ss)->info.channels; c++, i++) samp[i] = __get_chanval(self, c, 1); for (c = 0; c < ROAR_STREAM(self->ss)->info.channels; c++, i++) samp[i] = __get_chanval(self, c, 0); } ROAR_DBG("_vio_read(vio=%p, buf=%p, count=%lu) = %lu", vio, buf, (long unsigned int)count, (long unsigned int)count); return count + zeros; } static int _vio_ctl (struct roar_vio_calls * vio, roar_vio_ctl_t cmd, void * data) { (void)vio, (void)data; if ( cmd == ROAR_VIO_CTL_NONBLOCK ) return 0; roar_err_set(ROAR_ERROR_BADRQC); return -1; } static int _vio_close (struct roar_vio_calls * vio) { struct state * self = vio->inst; self->stream = -1; self->ss = NULL; return 0; } static int _init (struct roar_dl_librarypara * para) { struct roar_keyval * p; p = roar_keyval_lookup(para->argv, "startaddr", para->argc, 1); if ( p != NULL && p->value != NULL ) g_state->startaddr = atoi(p->value); p = roar_keyval_lookup(para->argv, "len", para->argc, 1); if ( p != NULL && p->value != NULL ) g_state->len = atoi(p->value); if ((g_state->stream = streams_new()) == -1 ) return -1; if ( streams_get(g_state->stream, &(g_state->ss)) == -1 ) { streams_delete(g_state->stream); return -1; } if ( streams_set_dir(g_state->stream, ROAR_DIR_PLAY, 1) == -1 ) { streams_delete(g_state->stream); return -1; } if ( streams_set_name(g_state->stream, "DMX to Waveform bridge") == -1 ) { streams_delete(g_state->stream); return -1; } ROAR_STREAM(g_state->ss)->info = *g_sa; if ( g_state->len & 1 ) { ROAR_STREAM(g_state->ss)->info.channels = g_state->len - 1; } else { ROAR_STREAM(g_state->ss)->info.channels = g_state->len / 2; } ROAR_STREAM(g_state->ss)->info.bits = 16; roar_vio_clear_calls(&(g_state->ss->vio)); g_state->ss->vio.inst = g_state; g_state->ss->vio.read = _vio_read; g_state->ss->vio.ctl = _vio_ctl; g_state->ss->vio.close = _vio_close; streams_set_fh(g_state->stream, -2); client_stream_add(g_self_client, g_state->stream); return 0; } static int _free (struct roar_dl_librarypara * para) { (void)para; if ( g_state->stream == -1 ) return 0; if ( streams_reset_flag(g_state->stream, ROAR_FLAG_IMMUTABLE) == -1 ) return -1; if ( streams_delete(g_state->stream) == -1 ) return -1; g_state->stream = -1; return 0; } static struct roar_dl_appsched sched = { .init = _init, .free = _free, .update = NULL, .tick = NULL, .wait = NULL }; ROAR_DL_PLUGIN_START(dmx_waveform) { ROARD_DL_CHECK_VERSIONS(); ROAR_DL_PLUGIN_META_PRODUCT_NIV("dmx-waveform", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO); ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING); ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0); ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org"); ROAR_DL_PLUGIN_META_DESC("This renders DMX channels as waveform signals. This is helpful to drive LEDs with cheap PWM based sound devices."); ROAR_DL_PLUGIN_REG_GLOBAL_DATA(g_state, g_state_init); ROAR_DL_PLUGIN_REG_APPSCHED(&sched); } ROAR_DL_PLUGIN_END //ll