diff options
Diffstat (limited to 'streamer.c')
-rw-r--r-- | streamer.c | 398 |
1 files changed, 289 insertions, 109 deletions
@@ -36,7 +36,6 @@ #include "volume.h" #include "vfs.h" #include "premix.h" -#include "plugins/dsp_libsrc/src.h" #include "ringbuf.h" //#define trace(...) { fprintf(stderr, __VA_ARGS__); } @@ -49,10 +48,9 @@ FILE *out; #endif static intptr_t streamer_tid; -static DB_dsp_t *srcplug; -static DB_dsp_t *eqplug; static ddb_dsp_context_t *dsp_chain; -static ddb_dsp_context_t *src; + +static DB_dsp_t *eqplug; static ddb_dsp_context_t *eq; static int conf_replaygain_mode = 0; @@ -83,7 +81,9 @@ static playlist_t *streamer_playlist; static playItem_t *playing_track; static playItem_t *streaming_track; static playItem_t *playlist_track; -static ddb_waveformat_t prevformat; + +static ddb_waveformat_t output_format; +static int formatchanged; static DB_fileinfo_t *fileinfo; @@ -655,8 +655,6 @@ streamer_set_current (playItem_t *it) { mutex_unlock (decodemutex); trace ("bps=%d, channels=%d, samplerate=%d\n", fileinfo->fmt.bps, fileinfo->fmt.channels, fileinfo->fmt.samplerate); } -// FIXME: that might break streaming at boundaries between 2 different samplerates -// streamer_reset (0); // reset SRC } else { trace ("no decoder in playitem!\n"); @@ -809,11 +807,11 @@ streamer_start_new_song (void) { avg_bitrate = -1; if (output->state () != OUTPUT_STATE_PLAYING) { streamer_reset (1); - if (fileinfo && memcmp (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { - memcpy (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t)); - plug_get_output ()->setformat (&fileinfo->fmt); + if (fileinfo && memcmp (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { + memcpy (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t)); + plug_get_output ()->setformat (&output_format); } - if (output->play () < 0) { + if (0 != output->play ()) { fprintf (stderr, "streamer: failed to start playback; output plugin doesn't work\n"); streamer_set_nextsong (-2, 0); } @@ -824,14 +822,9 @@ streamer_start_new_song (void) { last_bitrate = -1; avg_bitrate = -1; streamer_reset (1); - if (fileinfo && memcmp (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { - memcpy (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t)); - plug_get_output ()->setformat (&fileinfo->fmt); - } - if (output->play () < 0) { - fprintf (stderr, "streamer: failed to start playback; output plugin doesn't work\n"); - streamer_set_nextsong (-2, 0); - return; + if (fileinfo && memcmp (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { + memcpy (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t)); + formatchanged = 1; } } output->pause (); @@ -920,53 +913,45 @@ streamer_thread (void *ctx) { playpos = 0; seekpos = -1; - // try to switch samplerate to the closest supported by output plugin - if (conf_get_int ("playback.dynsamplerate", 0)) { - - // don't switch if unchanged - ddb_waveformat_t prevfmt; - memcpy (&prevfmt, &output->fmt, sizeof (ddb_waveformat_t)); - if (memcmp (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { - memcpy (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t)); - output->setformat (&fileinfo->fmt); - // check if the format actually changed - if (memcmp (&output->fmt, &prevfmt, sizeof (ddb_waveformat_t))) { - // restart streaming of current track - trace ("streamer: output samplerate changed from %d to %d; restarting track\n", prevfmt.samplerate, output->fmt.samplerate); - mutex_lock (decodemutex); - fileinfo->plugin->free (fileinfo); - fileinfo = NULL; - DB_decoder_t *dec = NULL; - dec = plug_get_decoder_for_id (streaming_track->decoder_id); - if (dec) { - fileinfo = dec->open (0); - if (fileinfo && dec->init (fileinfo, DB_PLAYITEM (streaming_track)) < 0) { - dec->free (fileinfo); - fileinfo = NULL; - } - } - if (!dec || !fileinfo) { - // FIXME: handle error +#if 0 + // don't switch if unchanged + ddb_waveformat_t prevfmt; + memcpy (&prevfmt, &output->fmt, sizeof (ddb_waveformat_t)); + if (memcmp (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { + memcpy (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t)); + output->setformat (&fileinfo->fmt); + // check if the format actually changed + if (memcmp (&output->fmt, &prevfmt, sizeof (ddb_waveformat_t))) { + // restart streaming of current track + trace ("streamer: output samplerate changed from %d to %d; restarting track\n", prevfmt.samplerate, output->fmt.samplerate); + mutex_lock (decodemutex); + fileinfo->plugin->free (fileinfo); + fileinfo = NULL; + DB_decoder_t *dec = NULL; + dec = plug_get_decoder_for_id (streaming_track->decoder_id); + if (dec) { + fileinfo = dec->open (0); + if (fileinfo && dec->init (fileinfo, DB_PLAYITEM (streaming_track)) < 0) { + dec->free (fileinfo); + fileinfo = NULL; } - mutex_unlock (decodemutex); - bytes_until_next_song = -1; - streamer_buffering = 1; - streamer_reset (1); } + if (!dec || !fileinfo) { + // FIXME: handle error + } + mutex_unlock (decodemutex); + bytes_until_next_song = -1; + streamer_buffering = 1; + streamer_reset (1); } + } +#endif - // output plugin may stop playback before switching samplerate - if (output->state () != OUTPUT_STATE_PLAYING) { - if (fileinfo && memcmp (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { - memcpy (&prevformat, &fileinfo->fmt, sizeof (ddb_waveformat_t)); - plug_get_output ()->setformat (&fileinfo->fmt); - } - if (output->play () < 0) { - fprintf (stderr, "streamer: failed to start playback after samplerate change; output plugin doesn't work\n"); - streamer_set_nextsong (-2, 0); - streamer_unlock (); - continue; - } + // output plugin may stop playback before switching samplerate + if (output->state () != OUTPUT_STATE_PLAYING) { + if (fileinfo && memcmp (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t))) { + memcpy (&output_format, &fileinfo->fmt, sizeof (ddb_waveformat_t)); + formatchanged = 1; } } streamer_unlock (); @@ -1128,44 +1113,185 @@ streamer_thread (void *ctx) { mutex_unlock (decodemutex); } -int -streamer_init (void) { -#if WRITE_DUMP - out = fopen ("out.raw", "w+b"); -#endif - mutex = mutex_create (); - decodemutex = mutex_create (); +void +streamer_dsp_chain_free (ddb_dsp_context_t *dsp_chain) { + while (dsp_chain) { + ddb_dsp_context_t *next = dsp_chain->next; + dsp_chain->plugin->close (dsp_chain); + dsp_chain = next; + } +} - ringbuf_init (&streamer_ringbuf, streambuffer, STREAM_BUFFER_SIZE); +ddb_dsp_context_t * +streamer_dsp_chain_load (const char *fname) { + int err = 1; + FILE *fp = fopen (fname, "rt"); + if (!fp) { + return NULL; + } + + char temp[100]; + ddb_dsp_context_t *chain = NULL; + ddb_dsp_context_t *tail = NULL; + for (;;) { + // plugin enabled { + int enabled = 0; + int err = fscanf (fp, "%100s %d {\n", temp, &enabled); + if (err == EOF) { + break; + } + else if (2 != err) { + fprintf (stderr, "error plugin name\n"); + goto error; + } - pl_set_order (conf_get_int ("playback.order", 0)); + DB_dsp_t *plug = (DB_dsp_t *)deadbeef->plug_get_for_id (temp); + if (!plug) { + fprintf (stderr, "streamer_dsp_chain_load: plugin %s not found. preset will not be loaded\n", temp); + goto error; + } + ddb_dsp_context_t *ctx = plug->open (); + if (!ctx) { + fprintf (stderr, "streamer_dsp_chain_load: failed to open ctxance of plugin %s\n", temp); + goto error; + } + + if (tail) { + tail->next = ctx; + tail = ctx; + } + else { + tail = chain = ctx; + } + + int n = 0; + for (;;) { + char value[1000]; + if (!fgets (temp, sizeof (temp), fp)) { + fprintf (stderr, "streamer_dsp_chain_load: unexpected eof while reading plugin params\n"); + goto error; + } + if (!strcmp (temp, "}\n")) { + break; + } + else if (1 != sscanf (temp, "\t%1000[^\n]\n", value)) { + fprintf (stderr, "streamer_dsp_chain_load: error loading param %d\n", n); + goto error; + } + if (plug->num_params) { + plug->set_param (ctx, n, value); + } + n++; + } + ctx->enabled = enabled; + } - // find src plugin, and use it if found - srcplug = (DB_dsp_t*)plug_get_for_id ("SRC"); - if (srcplug) { - src = srcplug->open (); - srcplug->set_param (src, SRC_PARAM_QUALITY, conf_get_str ("src_quality", "2")); - src->next = dsp_chain; - dsp_chain = src; + err = 0; +error: + if (err) { + fprintf (stderr, "streamer_dsp_chain_load: error loading %s\n", fname); } + if (fp) { + fclose (fp); + } + if (err && chain) { + streamer_dsp_chain_free (chain); + chain = NULL; + } + return chain; +} + +int +streamer_dsp_chain_save (const char *fname, ddb_dsp_context_t *chain) { + FILE *fp = fopen (fname, "w+t"); + if (!fp) { + return -1; + } + + ddb_dsp_context_t *ctx = chain; + while (ctx) { + fprintf (fp, "%s %d {\n", ctx->plugin->plugin.id, (int)ctx->enabled); + if (ctx->plugin->num_params) { + int n = ctx->plugin->num_params (); + int i; + for (i = 0; i < n; i++) { + char v[1000]; + ctx->plugin->get_param (ctx, i, v, sizeof (v)); + fprintf (fp, "\t%s\n", v); + } + } + fprintf (fp, "}\n"); + ctx = ctx->next; + } + + fclose (fp); + return 0; +} + +void +streamer_dsp_postinit (void) { + // note about EQ hack: + // we 1st check if there's an EQ in dsp chain, and just use it + // if not -- we add our own // eq plug - eqplug = (DB_dsp_t *)plug_get_for_id ("supereq"); if (eqplug) { - eq = eqplug->open (); + ddb_dsp_context_t *p; + + for (p = dsp_chain; p; p = p->next) { + if (!strcmp (p->plugin->plugin.id, "supereq")) { + break; + } + } + if (p) { + eq = p; + } + else { + eq = eqplug->open (); + eq->next = dsp_chain; + dsp_chain = eq; + } + + } +} + +void +streamer_dsp_init (void) { + // load dsp chain from file + char fname[PATH_MAX]; + snprintf (fname, sizeof (fname), "%s/dspconfig", plug_get_config_dir ()); + dsp_chain = streamer_dsp_chain_load (fname); + + eqplug = (DB_dsp_t *)plug_get_for_id ("supereq"); + streamer_dsp_postinit (); - // load settings - eqplug->enable (eq, deadbeef->conf_get_int ("eq.enable", 0)); - eqplug->set_param (eq, 0, conf_get_str ("eq.preamp", "1")); + // load legacy eq settings from pre-0.5 + if (conf_find ("eq.", NULL)) { + eq->enabled = deadbeef->conf_get_int ("eq.enable", 0); + eqplug->set_param (eq, 0, conf_get_str ("eq.preamp", "0")); for (int i = 0; i < 18; i++) { char key[100]; snprintf (key, sizeof (key), "eq.band%d", i); - eqplug->set_param (eq, 1+i, conf_get_str (key, "1")); + eqplug->set_param (eq, 1+i, conf_get_str (key, "0")); } - - eq->next = dsp_chain; - dsp_chain = eq; + // delete obsolete settings + conf_remove_items ("eq."); } +} + +int +streamer_init (void) { +#if WRITE_DUMP + out = fopen ("out.raw", "w+b"); +#endif + mutex = mutex_create (); + decodemutex = mutex_create (); + + ringbuf_init (&streamer_ringbuf, streambuffer, STREAM_BUFFER_SIZE); + + pl_set_order (conf_get_int ("playback.order", 0)); + + streamer_dsp_init (); conf_replaygain_mode = conf_get_int ("replaygain_mode", 0); conf_replaygain_scale = conf_get_int ("replaygain_scale", 1); @@ -1198,16 +1324,16 @@ streamer_free (void) { decodemutex = 0; mutex_free (mutex); mutex = 0; - if (srcplug && src) { - srcplug->close (src); - srcplug = NULL; - src = NULL; - } - if (eqplug && eq) { - eqplug->close (eq); - eqplug = NULL; - eq = NULL; - } + + char fname[PATH_MAX]; + snprintf (fname, sizeof (fname), "%s/dspconfig", plug_get_config_dir ()); + streamer_dsp_chain_save (fname, dsp_chain); + + streamer_dsp_chain_free (dsp_chain); + dsp_chain = NULL; + + eqplug = NULL; + eq = NULL; } void @@ -1385,14 +1511,6 @@ streamer_read_async (char *bytes, int size) { // convert to float int tempsize = pcm_convert (&fileinfo->fmt, input, &dspfmt, tempbuf, inputsize); - if (srcplug) { - // FIXME: update on change only, conversion from float to - // str and back is slow - char s[100]; - snprintf (s, sizeof (s), "%d", dspfmt.samplerate); - srcplug->set_param (src, SRC_PARAM_SAMPLERATE, s); - } - int nframes = inputsize / inputsamplesize; ddb_dsp_context_t *dsp = dsp_chain; dspfmt.samplerate = fileinfo->fmt.samplerate; @@ -1405,6 +1523,17 @@ streamer_read_async (char *bytes, int size) { int n = pcm_convert (&dspfmt, tempbuf, &output->fmt, bytes, nframes * dspsamplesize); bytesread = n; + + ddb_waveformat_t outfmt; + outfmt.bps = output->fmt.bps; + outfmt.is_float = output->fmt.is_float; + outfmt.channels = dspfmt.channels; + outfmt.samplerate = dspfmt.samplerate; + outfmt.channelmask = dspfmt.channelmask; + if (memcmp (&output_format, &outfmt, sizeof (ddb_waveformat_t))) { + memcpy (&output_format, &outfmt, sizeof (ddb_waveformat_t)); + formatchanged = 1; + } } } else { @@ -1468,6 +1597,15 @@ streamer_read (char *bytes, int size) { return -1; } DB_output_t *output = plug_get_output (); + if (formatchanged && bytes_until_next_song <= 0) { + formatchanged = 0; + plug_get_output ()->setformat (&output_format); + if (0 != output->play ()) { + fprintf (stderr, "streamer: failed to start playback; output plugin doesn't work\n"); + streamer_set_nextsong (-2, 0); + return -1; + } + } streamer_lock (); int sz = min (size, streamer_ringbuf.remaining); if (sz) { @@ -1593,10 +1731,6 @@ streamer_configchanged (void) { conf_replaygain_mode = conf_get_int ("replaygain_mode", 0); conf_replaygain_scale = conf_get_int ("replaygain_scale", 1); pl_set_order (conf_get_int ("playback.order", 0)); - - if (srcplug) { - srcplug->set_param (src, SRC_PARAM_QUALITY, conf_get_str ("src_quality", "2")); - } } void @@ -1662,3 +1796,49 @@ ddb_dsp_context_t * streamer_get_dsp_chain (void) { return dsp_chain; } + +static ddb_dsp_context_t * +dsp_clone (ddb_dsp_context_t *from) { + ddb_dsp_context_t *dsp = from->plugin->open (); + char param[2000]; + if (from->plugin->num_params) { + int n = from->plugin->num_params (); + for (int i = 0; i < n; i++) { + from->plugin->get_param (from, i, param, sizeof (param)); + dsp->plugin->set_param (dsp, i, param); + } + } + dsp->enabled = from->enabled; + return dsp; +} + +void +streamer_set_dsp_chain (ddb_dsp_context_t *chain) { + mutex_lock (decodemutex); + streamer_dsp_chain_free (dsp_chain); + + dsp_chain = NULL; + eq = NULL; + + ddb_dsp_context_t *tail = NULL; + while (chain) { + printf ("copy %s\n", chain->plugin->plugin.id); + ddb_dsp_context_t *new = dsp_clone (chain); + if (tail) { + tail->next = new; + tail = new; + } + else { + dsp_chain = tail = new; + } + chain = chain->next; + } + + streamer_dsp_postinit (); + + char fname[PATH_MAX]; + snprintf (fname, sizeof (fname), "%s/dspconfig", plug_get_config_dir ()); + streamer_dsp_chain_save (fname, dsp_chain); + + mutex_unlock (decodemutex); +} |