//output.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2011 * * 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 "roard.h" int output_buffer_init (struct roar_audio_info * info) { size_t size; void * buf; g_output_buffer = NULL; g_input_buffer = NULL; g_output_buffer_len = 0; size = ROAR_OUTPUT_CALC_OUTBUFSIZE(info); ROAR_DBG("output_buffer_init(*): output buffer size is %i", size); if ( (buf = roar_mm_malloc(size)) == NULL ) return -1; g_output_buffer = buf; g_output_buffer_len = size; if ( (buf = roar_mm_malloc(size)) == NULL ) { g_output_buffer = NULL; g_output_buffer_len = 0; roar_mm_free(g_output_buffer); return -1; } g_input_buffer = buf; ROAR_DBG("output_buffer_init(*): output buffer is at %p", buf); memlock_register(MEMLOCK_LOW, g_output_buffer, size); memlock_register(MEMLOCK_LOW, g_input_buffer, size); output_buffer_reinit(); return 0; } int output_buffer_reinit (void) { if ( g_output_buffer != NULL ) memset(g_output_buffer, 0, g_output_buffer_len); return 0; } int output_buffer_free (void) { ROAR_DBG("output_buffer_init(*): freeing output buffer at %p", g_output_buffer); if ( g_output_buffer != NULL ) roar_mm_free(g_output_buffer); if ( g_input_buffer != NULL ) roar_mm_free(g_input_buffer); g_output_buffer = NULL; g_input_buffer = NULL; g_output_buffer_len = 0; return 0; } #ifdef ROAR_DRIVER_DEFAULT int output_add_default (char * drv, char * dev, char * opts, int prim, int count) { return output_add(drv, dev, opts, prim, count); } #else int output_add_default (char * drv, char * dev, char * opts, int prim, int count) { char * drvs[] = { // operating system depended things: #ifdef __OpenBSD__ /* OpenBSD use sndio natively, this check is discusses with upstream (See ML archive August 2010) */ "sndio", #endif // native and pseudo-native interfaces: "oss", "alsa", "sndio", "wmm", // sound libs: "ao", "portaudio", // other sound systems: "esd", "rsound", "artsc", "pulsesimple", "roar", // libroareio's drivers: "cdriver", // specal buildins: "sysclock", "null", // terminator: NULL }; int i; int ret; int _alive; char * _opts; if ( drv != NULL ) return output_add(drv, dev, opts, prim, count); if ( dev != NULL ) { ROAR_WARN("add_default_output(drv=(none), dev='%s', opts='%s', prim=%i, count=%i): It's not recommended to use device name without driver name.", dev, opts, prim, count); } for (i = 0; drvs[i] != NULL; i++) { ROAR_INFO("add_default_output(*): trying driver %s", ROAR_DBG_INFO_INFO, drvs[i]); _alive = alive; // save global alive setting if ( opts == NULL ) { _opts = NULL; } else { _opts = roar_mm_strdup(opts); } ret = output_add(drvs[i], dev, _opts, prim, count); if ( _opts != NULL ) roar_mm_free(_opts); if ( ret != -1 ) return ret; alive = _alive; // restore global alive setting ROAR_INFO("add_default_output(*): Driver %s faild to load", ROAR_DBG_INFO_VERBOSE, drvs[i]); } return -1; } #endif #define _CKPARAM(n) if ( n (v == NULL) ) { \ ROAR_WARN("add_output(drv='%s', dev='%s', opts=..., prim=%i, count=%i): " \ "Parameter '%s' expects %s parameter.", \ drv, dev, prim, count, k, (n 1) ? "a" : "no"); \ error++; \ } else int output_add (char * drv, char * dev, char * opts, int prim, int count) { int stream; int default_flags = 0; struct roar_stream * s; struct roar_stream_server * ss; char * k, * v; #ifdef ROAR_DRIVER_CODEC char * to_free = NULL; #endif int sync = 0, f_mmap = 0; int32_t blocks = -1, blocksize = -1; int dir = ROAR_DIR_OUTPUT; int error = 0; // DMX: int32_t channel = -1; int32_t universe = -1; uint16_t tu16; float q = -32e6; ROAR_INFO("add_output(drv='%s', dev='%s', opts='%s', prim=%i, count=%i): trying to add output driver", ROAR_DBG_INFO_INFO, drv, dev, opts, prim, count); if ( drv == NULL && count == 0 ) { #ifdef ROAR_DRIVER_DEFAULT drv = ROAR_DRIVER_DEFAULT; prim = 1; sync = 1; #ifdef ROAR_DRIVER_CODEC if ( opts == NULL ) { opts = to_free = strdup("codec=" ROAR_DRIVER_CODEC); } #endif #else ROAR_ERR("add_output(*): Can not find default driver"); return -1; #endif } if ( opts == NULL && count == 0 ) { ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts); default_flags = ROAR_FLAG_AUTOCONF|ROAR_FLAG_RECSOURCE; sync = 1; prim = 1; // if ( prim == 0 ) prim = 1; -> prim allways = 1 } ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts); if ( (stream = streams_new()) == -1 ) { ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = -1", drv, dev, opts); if ( prim ) alive = 0; return -1; } ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts); streams_get(stream, &ss); s = ROAR_STREAM(ss); ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts); memset(&(s->info), 0xFF, sizeof(struct roar_audio_info)); // set everything to -1 s->pos_rel_id = -1; // s->info.codec = codec; // seting default flags: streams_set_flag(stream, default_flags); ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts); if ( opts == NULL ) { k = NULL; } else { k = strtok(opts, ","); } ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s'): initial k='%s'(%p)", drv, dev, opts, k, k); while (k != NULL) { // ROAR_WARN("add_output(*): opts: %s", k); if ( (v = strstr(k, "=")) != NULL ) { *v++ = 0; } ROAR_DBG("add_output(*): opts: k='%s', v='%s'", k, v); if ( strcmp(k, "rate") == 0 ) { _CKPARAM() s->info.rate = atoi(v); } else if ( strcmp(k, "channels") == 0 ) { _CKPARAM() s->info.channels = atoi(v); } else if ( strcmp(k, "bits") == 0 ) { _CKPARAM() s->info.bits = atoi(v); } else if ( strcmp(k, "codec") == 0 ) { _CKPARAM() if ( (s->info.codec = roar_str2codec(v)) == -1 ) { ROAR_ERR("add_output(*): unknown codec '%s'", v); error++; } } else if ( strcmp(k, "q") == 0 ) { _CKPARAM() q = atof(v); } else if ( strcmp(k, "blocks") == 0 ) { _CKPARAM() blocks = atoi(v); } else if ( strcmp(k, "blocksize") == 0 ) { _CKPARAM() blocksize = atoi(v); } else if ( strcmp(k, "mmap") == 0 ) { _CKPARAM(!) f_mmap = 1; } else if ( strcmp(k, "subsystem") == 0 ) { _CKPARAM() { if ( !strcasecmp(v, "wave") || !strcasecmp(v, "waveform") ) { dir = ROAR_DIR_OUTPUT; #ifndef ROAR_WITHOUT_DCOMP_MIDI } else if ( !strcasecmp(v, "midi") ) { dir = ROAR_DIR_MIDI_OUT; #endif #ifndef ROAR_WITHOUT_DCOMP_LIGHT } else if ( !strcasecmp(v, "light") ) { dir = ROAR_DIR_LIGHT_OUT; #endif #ifndef ROAR_WITHOUT_DCOMP_RAW } else if ( !strcasecmp(v, "raw") ) { dir = ROAR_DIR_RAW_OUT; #endif } else if ( !strcasecmp(v, "complex") ) { dir = ROAR_DIR_COMPLEX_OUT; } else { ROAR_ERR("add_output(*): unknown/unsupported subsystem '%s'", k); error++; } } // DMX: } else if ( strcmp(k, "channel") == 0 ) { _CKPARAM() { channel = atoi(v); if ( channel < 0 || channel > 65535 ) { ROAR_ERR("add_output(*): Invalide channel (not within 0..65535): %i", channel); channel = -1; error++; } } } else if ( strcmp(k, "universe") == 0 ) { _CKPARAM() { universe = atoi(v); if ( universe < 0 || universe > 255 ) { ROAR_ERR("add_output(*): Invalide universe (not within 0..255): %i", universe); universe = -1; error++; } } } else if ( strcmp(k, "name") == 0 ) { _CKPARAM() { if ( streams_set_name(stream, v) == -1 ) { ROAR_ERR("add_output(*): Can not set Stream name"); error++; } } } else if ( strcmp(k, "meta") == 0 ) { _CKPARAM(!) streams_set_flag(stream, ROAR_FLAG_META); } else if ( strcmp(k, "sync") == 0 ) { _CKPARAM(!) sync = 1; } else if ( strcmp(k, "primary") == 0 ) { _CKPARAM(!) prim = 1; } else if ( strcmp(k, "cleanmeta") == 0 ) { _CKPARAM(!) streams_set_flag(stream, ROAR_FLAG_CLEANMETA); } else if ( strcmp(k, "autoconf") == 0 ) { _CKPARAM(!) streams_set_flag(stream, ROAR_FLAG_AUTOCONF); } else if ( strcmp(k, "recsource") == 0 ) { _CKPARAM(!) streams_set_flag(stream, ROAR_FLAG_RECSOURCE); } else if ( strcmp(k, "passmixer") == 0 ) { _CKPARAM(!) streams_set_flag(stream, ROAR_FLAG_PASSMIXER); } else if ( strcmp(k, "recsource") == 0 ) { _CKPARAM(!) streams_set_flag(stream, ROAR_FLAG_RECSOURCE); } else { ROAR_ERR("add_output(*): unknown option '%s'", k); error++; } if ( error ) { streams_delete(stream); if ( prim ) alive = 0; #ifdef ROAR_DRIVER_CODEC if ( to_free != NULL ) free(to_free); #endif return -1; } k = strtok(NULL, ","); } ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = ?", drv, dev, opts); // set audio info... switch (dir) { case ROAR_DIR_LIGHT_OUT: switch (s->info.codec) { case ROAR_CODEC_DMX512: case -1: if ( s->info.rate == -1 ) s->info.rate = ROAR_OUTPUT_CFREQ; s->info.channels = 0; s->info.bits = 8; s->info.codec = ROAR_CODEC_DMX512; // in case codec == -1 break; } break; case ROAR_DIR_MIDI_OUT: switch (s->info.codec) { case ROAR_CODEC_MIDI: case -1: if ( s->info.rate == -1 ) s->info.rate = ROAR_MIDI_TICKS_PER_BEAT; s->info.channels = ROAR_MIDI_CHANNELS_DEFAULT; s->info.bits = ROAR_MIDI_BITS; s->info.codec = ROAR_CODEC_MIDI; // in case codec == -1 break; } break; case ROAR_DIR_RAW_OUT: if ( s->info.rate == -1 ) s->info.rate = 0; if ( s->info.bits == -1 ) s->info.bits = 0; if ( s->info.channels == -1 ) s->info.channels = 0; if ( s->info.codec == -1 ) s->info.codec = 0; break; } if ( s->info.rate == -1 ) s->info.rate = g_sa->rate; if ( s->info.bits == -1 ) s->info.bits = g_sa->bits; if ( s->info.channels == -1 ) s->info.channels = g_sa->channels; if ( s->info.codec == -1 ) s->info.codec = g_sa->codec; ROAR_DBG("add_output(*): s->info = {.rate=%i, .bits=%i, .channels=%i, .codec=%i}", s->info.rate, s->info.bits, s->info.channels, s->info.codec); if ( streams_set_dir(stream, dir, 1) == -1 ) { streams_delete(stream); return -1; } #ifdef ROAR_DRIVER_CODEC if ( to_free != NULL ) free(to_free); #endif if ( s->info.codec == ROAR_CODEC_ALAW || s->info.codec == ROAR_CODEC_MULAW ) s->info.bits = 8; // needed to open OSS driver, will be overriden by codecfilter ROAR_STREAM_SERVER(s)->codec_orgi = s->info.codec; if ( driver_openvio(&(ss->vio), &(ss->driver_id), drv, dev, &(s->info), -1, ss) == -1 ) { ss->driver_id = -1; // don't close a driver not opened... memset(&(ss->vio), 0, sizeof(struct roar_vio_calls)); streams_delete(stream); if ( prim ) alive = 0; ROAR_ERR("add_output(drv='%s', dev='%s', opts='%s'): can not open output driver.", drv, dev, opts); ROAR_DBG("add_output(drv='%s', dev='%s', opts='%s') = -1", drv, dev, opts); return -1; } roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_SSTREAMID, &stream); // ignore errors here roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_SSTREAM, s); // ignore errors here if ( blocks != -1 ) roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DBLOCKS, &blocks); if ( blocksize != -1 ) roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DBLKSIZE, &blocksize); // TODO: we shoudld *really* check for errors here... if ( channel != -1 ) { tu16 = channel; roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DMXSCHAN, &tu16); } if ( universe != -1 ) { tu16 = universe; roar_vio_ctl(&(ss->vio), ROAR_VIO_CTL_SET_DMXUNIV, &tu16); } ROAR_DBG("add_output(*): ss->driver_id=%i", ss->driver_id); streams_set_fh(stream, -1); // update some internal structures if ( q > -1e6 ) { ROAR_DBG("add_output(*): setting q=%f", q); streams_ctl(stream, ROAR_CODECFILTER_CTL_SET_Q|ROAR_STREAM_CTL_TYPE_FLOAT, &q); } client_stream_add(g_self_client, stream); if ( prim ) { streams_mark_primary(stream); s->pos_rel_id = stream; } if ( sync ) { streams_set_flag(stream, ROAR_FLAG_SYNC); } else { streams_reset_flag(stream, ROAR_FLAG_SYNC); } if ( f_mmap ) streams_set_flag(stream, ROAR_FLAG_MMAP); ROAR_DBG("add_output(*): s->info = {.rate=%i, .bits=%i, .channels=%i, .codec=%i}", s->info.rate, s->info.bits, s->info.channels, s->info.codec); return 0; } //ll