From b5196b51f6858347bc1af2e243cbc1cd742110ee Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Fri, 11 Dec 2009 21:40:13 +0100 Subject: alsa code converted into output plugin --- Makefile.am | 3 +- cdumb.c | 2 +- cgme.c | 5 +- configure.ac | 14 +- csid.cpp | 4 +- deadbeef.h | 24 +- main.c | 20 +- palsa.c | 524 ------------------------------------ palsa.h | 58 ---- playback.h | 33 +-- plugins.c | 48 +++- plugins.h | 3 + plugins/alsa/alsa.c | 632 ++++++++++++++++++++++++++++++++++++++++++++ plugins/gtkui/callbacks.c | 2 +- plugins/gtkui/gtkplaylist.c | 2 +- plugins/gtkui/gtkui.c | 4 +- streamer.c | 2 +- 17 files changed, 740 insertions(+), 640 deletions(-) delete mode 100644 palsa.c delete mode 100644 palsa.h create mode 100644 plugins/alsa/alsa.c diff --git a/Makefile.am b/Makefile.am index 3cded06c..ba9ef329 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,7 @@ SUBDIRS = gme/Game_Music_Emu-0.5.2\ icons\ plugins/hotkeys\ plugins/ffap\ + ${ALSA_DIR}\ ${LFM_DIR}\ ${MPGMAD_DIR}\ ${VORBIS_DIR}\ @@ -34,7 +35,7 @@ deadbeef_SOURCES =\ messagepump.c messagepump.h\ conf.c conf.h\ cgme.c cdumb.c csid.cpp\ - palsa.c palsa.h playback.h\ + playback.h\ threading_pthread.c threading.h\ md5/md5.c md5/md5.h md5/md5_loc.h\ volume.c volume.h\ diff --git a/cdumb.c b/cdumb.c index 0aaaec71..2799d6d8 100644 --- a/cdumb.c +++ b/cdumb.c @@ -100,7 +100,7 @@ cdumb_init (DB_playItem_t *it) { plugin.info.bps = 16; plugin.info.channels = 2; - plugin.info.samplerate = deadbeef->playback_get_samplerate (); + plugin.info.samplerate = deadbeef->get_output ()->samplerate (); plugin.info.readpos = 0; if (cdumb_startrenderer () < 0) { diff --git a/cgme.c b/cgme.c index 21aeb292..f4e805ce 100644 --- a/cgme.c +++ b/cgme.c @@ -32,7 +32,8 @@ static float duration; // of current song static int cgme_init (DB_playItem_t *it) { - if (gme_open_file (it->fname, &emu, deadbeef->playback_get_samplerate ())) { + int samplerate = deadbeef->get_output ()->samplerate (); + if (gme_open_file (it->fname, &emu, samplerate)) { return -1; } gme_mute_voices (emu, cgme_voicemask); @@ -41,7 +42,7 @@ cgme_init (DB_playItem_t *it) { gme_track_info (emu, &inf, it->tracknum); plugin.info.bps = 16; plugin.info.channels = 2; - plugin.info.samplerate = deadbeef->playback_get_samplerate (); + plugin.info.samplerate = samplerate; duration = deadbeef->pl_get_item_duration (it); reallength = inf.length; nzerosamples = 0; diff --git a/configure.ac b/configure.ac index d782f7eb..ed726b13 100644 --- a/configure.ac +++ b/configure.ac @@ -39,8 +39,11 @@ test "x$prefix" = xNONE && prefix=$ac_default_prefix CFLAGS="$CFLAGS -D_GNU_SOURCE -DLIBDIR=\\\"$libdir\\\" -DPREFIX=\\\"$prefix\\\"" CPPFLAGS="$CFLAGS" -PKG_CHECK_MODULES(DEPS, samplerate alsa) +PKG_CHECK_MODULES(DEPS, samplerate) PKG_CHECK_MODULES(GTKUI_DEPS, gtk+-2.0 >= 2.12 gthread-2.0 glib-2.0, HAVE_GTK=1) +PKG_CHECK_MODULES(ALSA_DEPS, alsa, HAVE_ALSA=1) +AC_CHECK_LIB([pthread], [main]) +AC_CHECK_LIB([dl], [main]) AX_CHECK_COMPILER_FLAGS(-msse2, SIMD_FLAGS="$SIMD_FLAGS -msse2";HAVE_SSE2=1, []) if test ${HAVE_SSE2}; then @@ -153,6 +156,13 @@ if test ${HAVE_GTK}; then AC_SUBST(GTKUI_DIR) fi +AM_CONDITIONAL(HAVE_ALSA, test $HAVE_ALSA) +if test ${HAVE_ALSA}; then + ALSA_DIR="plugins/alsa" + AC_SUBST(ALSA_DEPS_CFLAGS) + AC_SUBST(ALSA_DEPS_LIBS) + AC_SUBST(ALSA_DIR) +fi dnl print summary echo @@ -170,6 +180,7 @@ AC_DEFUN([PRINT_PLUGIN_INFO], ) PRINT_PLUGIN_INFO([stdio],[Standard IO plugin],[true]) +PRINT_PLUGIN_INFO([alsa],[alsa output],[test $HAVE_ALSA]) PRINT_PLUGIN_INFO([sid],[SID player based on libsidplay2],[true]) PRINT_PLUGIN_INFO([gme],[chiptune music player based on GME],[true]) PRINT_PLUGIN_INFO([dumb],[module player based on DUMB library],[true]) @@ -194,6 +205,7 @@ gme/Game_Music_Emu-0.5.2/Makefile gme/Game_Music_Emu-0.5.2/gme/Makefile sid/sidplay-libs-2.1.0/Makefile dumb/Makefile +plugins/alsa/Makefile plugins/hotkeys/Makefile plugins/lastfm/Makefile plugins/ffap/Makefile diff --git a/csid.cpp b/csid.cpp index fc742cc9..9051a1df 100644 --- a/csid.cpp +++ b/csid.cpp @@ -343,7 +343,7 @@ csid_init (DB_playItem_t *it) { resid->create (sidplay->info ().maxsids); // resid->create (1); resid->filter (true); - resid->sampling (deadbeef->playback_get_samplerate ()); + resid->sampling (deadbeef->get_output ()->samplerate ()); duration = deadbeef->pl_get_item_duration (it); tune = new SidTune (it->fname); @@ -351,7 +351,7 @@ csid_init (DB_playItem_t *it) { plugin.info.channels = tune->isStereo () ? 2 : 1; sid2_config_t conf; conf = sidplay->config (); - conf.frequency = deadbeef->playback_get_samplerate (); + conf.frequency = deadbeef->get_output ()->samplerate (); conf.precision = 16; conf.playback = plugin.info.channels == 2 ? sid2_stereo : sid2_mono; conf.sidEmulation = resid; diff --git a/deadbeef.h b/deadbeef.h index 5bbe4660..f6a3c0b5 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -100,6 +100,13 @@ enum { DB_PLUGIN_VFS = 5, }; +// output plugin states +enum output_state_t { + OUTPUT_STATE_STOPPED = 0, + OUTPUT_STATE_PLAYING = 1, + OUTPUT_STATE_PAUSED = 2, +}; + typedef struct { int event; time_t time; @@ -202,6 +209,7 @@ typedef struct { void (*md5) (uint8_t sig[16], const char *in, int len); void (*md5_to_str) (char *str, const uint8_t sig[16]); // playback control + struct DB_output_s* (*get_output) (void); void (*playback_next) (void); void (*playback_prev) (void); void (*playback_pause) (void); @@ -210,12 +218,8 @@ typedef struct { void (*playback_random) (void); float (*playback_get_pos) (void); // [0..100] void (*playback_set_pos) (float pos); // [0..100] - int (*playback_get_samplerate) (void); // output samplerate void (*playback_update_bitrate) (float bitrate); void (*playback_enum_soundcards) (void (*callback)(const char *name, const char *desc, void*), void *userdata); - // playback status - int (*playback_isstopped) (void); - int (*playback_ispaused) (void); // streamer access // FIXME: needs to be thread-safe DB_playItem_t *(*streamer_get_playing_track) (void); @@ -223,6 +227,8 @@ typedef struct { float (*streamer_get_playpos) (void); void (*streamer_seek) (float time); int (*streamer_ok_to_read) (int len); + void (*streamer_reset) (int full); + int (*streamer_read) (char *bytes, int size); // process control const char *(*get_config_dir) (void); void (*quit) (void); @@ -438,21 +444,21 @@ typedef struct DB_decoder_s { } DB_decoder_t; // output plugin -typedef struct { +typedef struct DB_output_s { DB_plugin_t plugin; // init is called once at plugin activation - int (*init) (void (*callback)(char *stream, int len)); + int (*init) (void); // free is called if output plugin was changed to another, or unload is about to happen int (*free) (void); // reconfigure output to another samplerate, if supported int (*change_rate) (int rate); // play, stop, pause, unpause are called by deadbeef in response to user // events, or as part of streaming process - // state must be 0 for stopped, 1 for playing and 2 for paused int (*play) (void); int (*stop) (void); int (*pause) (void); int (*unpause) (void); + // one of output_state_t enum values int (*state) (void); // following functions must return output sampling rate, bits per sample and number // of channels @@ -460,7 +466,9 @@ typedef struct { int (*bitspersample) (void); int (*channels) (void); // must return 0 for little endian output, or 1 for big endian - int (*endianess) (void); + int (*endianness) (void); + // soundcard enumeration (can be NULL) + void (*enum_soundcards) (void (*callback)(const char *name, const char *desc, void*), void *userdata); } DB_output_t; // dsp plugin diff --git a/main.c b/main.c index 6df2c6c6..1d512df9 100644 --- a/main.c +++ b/main.c @@ -235,15 +235,13 @@ player_thread (uintptr_t ctx) { switch (msg) { case M_REINIT_SOUND: { - int play = 0; - if (!palsa_ispaused () && !palsa_isstopped ()) { - play = 1; - } - - palsa_free (); - palsa_init (); - if (play) { - palsa_play (); + int state = p_get_state (); + + p_free (); + p_init (); + + if (state != OUTPUT_STATE_PAUSED && state != OUTPUT_STATE_STOPPED) { + p_play (); } } break; @@ -274,7 +272,7 @@ player_thread (uintptr_t ctx) { pl_prevsong (); break; case M_PAUSESONG: - if (p_ispaused ()) { + if (p_get_state () == OUTPUT_STATE_PAUSED) { p_unpause (); plug_trigger_event_paused (0); } @@ -291,7 +289,7 @@ player_thread (uintptr_t ctx) { plug_trigger_event_playlistchanged (); break; case M_CONFIGCHANGED: - palsa_configchanged (); + //plug_get_output ()->configchanged (); streamer_configchanged (); plug_trigger_event (DB_EV_CONFIGCHANGED, 0); break; diff --git a/palsa.c b/palsa.c deleted file mode 100644 index 284755c5..00000000 --- a/palsa.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009 Alexey Yakovenko - - This program 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 2 of the License, or - (at your option) any later version. - - This program 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 program. If not, see . -*/ -#include -#include -#include -#include -#include -#ifdef HAVE_CONFIG_H -# include -#endif -#include "palsa.h" -#include "threading.h" -#include "streamer.h" -#include "conf.h" -#include "volume.h" -#include "messagepump.h" -#include "deadbeef.h" - -//#define trace(...) { fprintf(stderr, __VA_ARGS__); } -#define trace(fmt,...) - -static snd_pcm_t *audio; -static int bufsize = -1; -static int alsa_terminate; -static int alsa_rate = 44100; -static int state; // 0 = stopped, 1 = playing, 2 = pause -static uintptr_t mutex; -static intptr_t alsa_tid; - -static int conf_alsa_resample = 0; -static char conf_alsa_soundcard[100] = "default"; - -static void -palsa_callback (char *stream, int len); - -static void -palsa_thread (void *context); - -static int -palsa_set_hw_params (int samplerate) { - snd_pcm_hw_params_t *hw_params = NULL; -// int alsa_resample = conf_get_int ("alsa.resample", 0); - int err = 0; - - if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { - trace ("cannot allocate hardware parameter structure (%s)\n", - snd_strerror (err)); - goto error; - } - - if ((err = snd_pcm_hw_params_any (audio, hw_params)) < 0) { - trace ("cannot initialize hardware parameter structure (%s)\n", - snd_strerror (err)); - goto error; - } - - if ((err = snd_pcm_hw_params_set_access (audio, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { - trace ("cannot set access type (%s)\n", - snd_strerror (err)); - goto error; - } - - snd_pcm_format_t fmt; -#if WORDS_BIGENDIAN - fmt = SND_PCM_FORMAT_S16_BE; -#else - fmt = SND_PCM_FORMAT_S16_LE; -#endif - if ((err = snd_pcm_hw_params_set_format (audio, hw_params, fmt)) < 0) { - trace ("cannot set sample format (%s)\n", - snd_strerror (err)); - goto error; - } - - snd_pcm_hw_params_get_format (hw_params, &fmt); - trace ("chosen sample format: %04Xh\n", (int)fmt); - - int val = samplerate; - int ret = 0; - - if ((err = snd_pcm_hw_params_set_rate_resample (audio, hw_params, conf_alsa_resample)) < 0) { - trace ("cannot setup resampling (%s)\n", - snd_strerror (err)); - goto error; - } - - if ((err = snd_pcm_hw_params_set_rate_near (audio, hw_params, &val, &ret)) < 0) { - trace ("cannot set sample rate (%s)\n", - snd_strerror (err)); - goto error; - } - alsa_rate = val; - trace ("chosen samplerate: %d Hz\n", alsa_rate); - - if ((err = snd_pcm_hw_params_set_channels (audio, hw_params, 2)) < 0) { - trace ("cannot set channel count (%s)\n", - snd_strerror (err)); - goto error; - } - - int nchan; - snd_pcm_hw_params_get_channels (hw_params, &nchan); - trace ("alsa channels: %d\n", nchan); - - unsigned int buffer_time = 500000; - int dir; - if ((err = snd_pcm_hw_params_set_buffer_time_near (audio, hw_params, &buffer_time, &dir)) < 0) { - trace ("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); - goto error; - } - trace ("alsa buffer time: %d ms\n", buffer_time); - snd_pcm_uframes_t size; - if ((err = snd_pcm_hw_params_get_buffer_size (hw_params, &size)) < 0) { - trace ("Unable to get buffer size for playback: %s\n", snd_strerror(err)); - goto error; - } - trace ("alsa buffer size: %d frames\n", size); - bufsize = size; - - if ((err = snd_pcm_hw_params (audio, hw_params)) < 0) { - trace ("cannot set parameters (%s)\n", - snd_strerror (err)); - goto error; - } -error: - if (hw_params) { - snd_pcm_hw_params_free (hw_params); - } - return err; -} - -int -palsa_init (void) { - int err; - - // get and cache conf variables - strcpy (conf_alsa_soundcard, conf_get_str ("alsa_soundcard", "default")); - conf_alsa_resample = conf_get_int ("alsa.resample", 0); - trace ("alsa_soundcard: %s\n", conf_alsa_soundcard); - trace ("alsa.resample: %d\n", conf_alsa_resample); - - snd_pcm_sw_params_t *sw_params = NULL; - state = 0; - //const char *conf_alsa_soundcard = conf_get_str ("alsa_soundcard", "default"); - if ((err = snd_pcm_open (&audio, conf_alsa_soundcard, SND_PCM_STREAM_PLAYBACK, 0))) { - trace ("could not open audio device (%s)\n", - snd_strerror (err)); - return -1; - } - - mutex = mutex_create (); - - if (palsa_set_hw_params (alsa_rate) < 0) { - goto open_error; - } - - if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) { - trace ("cannot allocate software parameters structure (%s)\n", - snd_strerror (err)); - goto open_error; - } - if ((err = snd_pcm_sw_params_current (audio, sw_params)) < 0) { - trace ("cannot initialize software parameters structure (%s)\n", - snd_strerror (err)); - goto open_error; - } - - if ((err = snd_pcm_sw_params_set_avail_min (audio, sw_params, bufsize/2)) < 0) { - trace ("cannot set minimum available count (%s)\n", - snd_strerror (err)); - goto open_error; - } - snd_pcm_uframes_t av; - if ((err = snd_pcm_sw_params_get_avail_min (sw_params, &av)) < 0) { - trace ("snd_pcm_sw_params_get_avail_min failed (%s)\n", - snd_strerror (err)); - goto open_error; - } - trace ("alsa period size: %d frames\n", av); - - if ((err = snd_pcm_sw_params_set_start_threshold (audio, sw_params, 0U)) < 0) { - trace ("cannot set start mode (%s)\n", - snd_strerror (err)); - goto open_error; - } - if ((err = snd_pcm_sw_params (audio, sw_params)) < 0) { - trace ("cannot set software parameters (%s)\n", - snd_strerror (err)); - goto open_error; - } - snd_pcm_sw_params_free (sw_params); - sw_params = NULL; - - /* the interface will interrupt the kernel every N frames, and ALSA - will wake up this program very soon after that. - */ - - if ((err = snd_pcm_prepare (audio)) < 0) { - trace ("cannot prepare audio interface for use (%s)\n", - snd_strerror (err)); - goto open_error; - } - - snd_pcm_start (audio); - - alsa_terminate = 0; - alsa_tid = thread_start (palsa_thread, NULL); - - return 0; - -open_error: - if (sw_params) { - snd_pcm_sw_params_free (sw_params); - } - if (audio != NULL) { - palsa_free (); - } - - return -1; -} - -int -palsa_change_rate (int rate) { - if (!audio) { - return 0; - } - if (rate == alsa_rate) { - trace ("palsa_change_rate: same rate (%d), ignored\n", rate); - return rate; - } - trace ("trying to change samplerate to: %d\n", rate); - mutex_lock (mutex); - snd_pcm_drop (audio); - int ret = palsa_set_hw_params (rate); - if (state != 0) { - snd_pcm_start (audio); - } - mutex_unlock (mutex); - if (ret < 0) { - return -1; - } - trace ("chosen samplerate: %d Hz\n", alsa_rate); - return alsa_rate; -} - -void -palsa_free (void) { - trace ("palsa_free\n"); - if (audio && !alsa_terminate) { - alsa_terminate = 1; - thread_join (alsa_tid); - alsa_tid = 0; - snd_pcm_close(audio); - audio = NULL; - mutex_free (mutex); - state = 0; - alsa_terminate = 0; - } -} - -static void -palsa_hw_pause (int pause) { - if (!audio) { - return; - } - if (state == 0) { - return; - } - if (pause == 1) { - snd_pcm_drop (audio); - } - else { - snd_pcm_prepare (audio); - snd_pcm_start (audio); - } -} - -int -palsa_play (void) { - int err; - if (state == 0) { - if (!audio) { - if (palsa_init () < 0) { - state = 0; - return -1; - } - } - else { - if ((err = snd_pcm_prepare (audio)) < 0) { - trace ("cannot prepare audio interface for use (%s)\n", - snd_strerror (err)); - return -1; - } - } - } - if (state != 1) { - state = 1; - snd_pcm_start (audio); - } - return 0; -} - - -int -palsa_stop (void) { - if (!audio) { - return 0; - } - state = 0; - if (conf_get_int ("alsa.freeonstop", 0)) { - palsa_free (); - } - else { - mutex_lock (mutex); - snd_pcm_drop (audio); - mutex_unlock (mutex); - } - streamer_reset (1); - return 0; -} - -int -palsa_isstopped (void) { - return (state == 0); -} - -int -palsa_ispaused (void) { - // return pause state - return (state == 2); -} - -int -palsa_pause (void) { - if (state == 0 || !audio) { - return -1; - } - // set pause state - palsa_hw_pause (1); - state = 2; - return 0; -} - -int -palsa_unpause (void) { - // unset pause state - if (state == 2) { - state = 1; - palsa_hw_pause (0); - } - return 0; -} - -int -palsa_get_rate (void) { - return alsa_rate; -} - -static void -palsa_thread (void *context) { - prctl (PR_SET_NAME, "deadbeef-alsa", 0, 0, 0, 0); - int err; - for (;;) { - if (alsa_terminate) { - break; - } - if (state != 1) { - usleep (10000); - continue; - } - - mutex_lock (mutex); - /* wait till the interface is ready for data, or 1 second - has elapsed. - */ - if ((err = snd_pcm_wait (audio, 500)) < 0 && state == 1) { - if (err == -ESTRPIPE) { - trace ("alsa: trying to recover from suspend...\n"); - messagepump_push (M_REINIT_SOUND, 0, 0, 0); - mutex_unlock (mutex); - break; - } - else { - trace ("alsa: trying to recover from xrun...\n"); - snd_pcm_prepare (audio); - mutex_unlock (mutex); - continue; - } - } - - /* find out how much space is available for playback data */ - - snd_pcm_sframes_t frames_to_deliver; - if ((frames_to_deliver = snd_pcm_avail_update (audio)) < 0) { - if (frames_to_deliver == -EPIPE) { - mutex_unlock (mutex); - trace ("an xrun occured\n"); - continue; - } else { - mutex_unlock (mutex); - trace ("unknown ALSA avail update return value (%d)\n", - (int)frames_to_deliver); - continue; - } - } - - /* deliver the data */ - // FIXME: under some conditions, frames_to_deliver may become huge - // like 20M. this case is not handled here. - char buf[frames_to_deliver*4]; - palsa_callback (buf, frames_to_deliver*4); - if ((err = snd_pcm_writei (audio, buf, frames_to_deliver)) < 0) { - trace ("write %d frames failed (%s)\n", frames_to_deliver, snd_strerror (err)); - snd_pcm_prepare (audio); - snd_pcm_start (audio); - } - mutex_unlock (mutex); - usleep (1000); // this must be here to prevent mutex deadlock - } -} - -static void -palsa_callback (char *stream, int len) { - if (!streamer_ok_to_read (len)) { - memset (stream, 0, len); - return; - } - int bytesread = streamer_read (stream, len); - -// FIXME: move volume control to streamer_read for copy optimization -#if 0 - int16_t vol[4]; - vol[0] = volume_get_amp () * 255; // that will be extra 8 bits - // pack 4 times - vol[1] = vol[2] = vol[3] = vol[0]; - - // apply volume with mmx - __asm__ volatile( - " mov %0, %%ecx\n\t" - " shr $4, %%ecx\n\t" - " mov %1, %%eax\n\t" - " movq %2, %mm1\n\t" - "1:\n\t" - " movq [%%eax], %mm0\n\t" - " movq %mm0, %mm2\n\t" - " movq %mm0, %mm3\n\t" - " pmullw %mm1, %mm2\n\t" - " pmulhw %mm1, %mm3\n\t" - " psrlw $8, %mm2\n\t" // discard lowest 8 bits - " psllw $8, %mm3\n\t" // shift left 8 lsbs of hiwords - " por %mm3, %mm2\n\t" // OR them together - " movq %mm3, [%%eax]\n\t" // load back to memory - " add $8, %%eax\n\t" - " dec %%ecx\n\t" - " jnz 1b\n\t" - : - : "r"(len), "r"(stream), "r"(vol) - : "%ecx", "%eax" - ); - -#else - int16_t ivolume = volume_get_amp () * 1000; - for (int i = 0; i < bytesread/2; i++) { - ((int16_t*)stream)[i] = (int16_t)(((int32_t)(((int16_t*)stream)[i])) * ivolume / 1000); - } -#endif - if (bytesread < len) { - memset (stream + bytesread, 0, len-bytesread); - } -} - -void -palsa_configchanged (void) { - int alsa_resample = conf_get_int ("alsa.resample", 0); - const char *alsa_soundcard = conf_get_str ("alsa_soundcard", "default"); - if (alsa_resample != conf_alsa_resample - || strcmp (alsa_soundcard, conf_alsa_soundcard)) { - messagepump_push (M_REINIT_SOUND, 0, 0, 0); - } -} - -// derived from alsa-utils/aplay.c -void -palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void *), void *userdata) { - void **hints, **n; - char *name, *descr, *descr1, *io; - const char *filter = "Output"; - if (snd_device_name_hint(-1, "pcm", &hints) < 0) - return; - n = hints; - while (*n != NULL) { - name = snd_device_name_get_hint(*n, "NAME"); - descr = snd_device_name_get_hint(*n, "DESC"); - io = snd_device_name_get_hint(*n, "IOID"); - if (io == NULL || !strcmp(io, filter)) { - if (name && descr && callback) { - callback (name, descr, userdata); - } - } - if (name != NULL) - free(name); - if (descr != NULL) - free(descr); - if (io != NULL) - free(io); - n++; - } - snd_device_name_free_hint(hints); -} diff --git a/palsa.h b/palsa.h deleted file mode 100644 index 2896702a..00000000 --- a/palsa.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009 Alexey Yakovenko - - This program 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 2 of the License, or - (at your option) any later version. - - This program 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 program. If not, see . -*/ -#ifndef __PALSA_H -#define __PALSA_H - -int -palsa_init (void); - -void -palsa_free (void); - -int -palsa_change_rate (int rate); - -int -palsa_play (void); - -int -palsa_stop (void); - -int -palsa_isstopped (void); - -int -palsa_ispaused (void); - -int -palsa_pause (void); - -int -palsa_unpause (void); - -int -palsa_get_rate (void); - -void -palsa_configchanged (void); - -void -palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void*), void *userdata); - -#endif // __PALSA_H - diff --git a/playback.h b/playback.h index 2a4ce722..da12504f 100644 --- a/playback.h +++ b/playback.h @@ -18,27 +18,16 @@ #ifndef __PLAYBACK_H #define __PLAYBACK_H -#if USE_SDL -#include "psdl.h" -#define p_init psdl_init -#define p_free psdl_free -#define p_play psdl_play -#define p_stop psdl_stop -#define p_ispaused psdl_ispaused -#define p_pause psdl_pause -#define p_unpause psdl_unpause -#define p_get_rate psdl_get_rate -#else -#include "palsa.h" -#define p_init palsa_init -#define p_free palsa_free -#define p_play palsa_play -#define p_stop palsa_stop -#define p_ispaused palsa_ispaused -#define p_pause palsa_pause -#define p_unpause palsa_unpause -#define p_get_rate palsa_get_rate -#define p_isstopped palsa_isstopped -#endif +#define p_init plug_get_output ()->init +#define p_free plug_get_output ()->free +#define p_play plug_get_output ()->play +#define p_stop plug_get_output ()->stop +#define p_pause plug_get_output ()->pause +#define p_unpause plug_get_output ()->unpause +#define p_get_rate plug_get_output ()->samplerate +#define p_get_state plug_get_output ()->state + +#define p_isstopped() (plug_get_output ()->state () == OUTPUT_STATE_STOPPED) +#define p_ispaused() (plug_get_output ()->state () == OUTPUT_STATE_PAUSED) #endif // __PLAYBACK_H diff --git a/plugins.c b/plugins.c index b982cc82..d8a1534f 100644 --- a/plugins.c +++ b/plugins.c @@ -51,6 +51,7 @@ static DB_functions_t deadbeef_api = { .ev_unsubscribe = plug_ev_unsubscribe, .md5 = plug_md5, .md5_to_str = plug_md5_to_str, + .get_output = plug_get_output, .playback_next = plug_playback_next, .playback_prev = plug_playback_prev, .playback_pause = plug_playback_pause, @@ -59,18 +60,15 @@ static DB_functions_t deadbeef_api = { .playback_random = plug_playback_random, .playback_get_pos = plug_playback_get_pos, .playback_set_pos = plug_playback_set_pos, - .playback_get_samplerate = p_get_rate, .playback_update_bitrate = streamer_update_bitrate, - .playback_enum_soundcards = palsa_enum_soundcards, - // playback status - .playback_isstopped = p_isstopped, - .playback_ispaused = p_ispaused, // streamer access .streamer_get_playing_track = (DB_playItem_t *(*) (void))streamer_get_playing_track, .streamer_get_streaming_track = (DB_playItem_t *(*) (void))streamer_get_streaming_track, .streamer_get_playpos = streamer_get_playpos, .streamer_seek = streamer_set_seek, .streamer_ok_to_read = streamer_ok_to_read, + .streamer_reset = streamer_reset, + .streamer_read = streamer_read, // process control .get_config_dir = plug_get_config_dir, .quit = plug_quit, @@ -197,6 +195,10 @@ DB_decoder_t *g_decoder_plugins[MAX_DECODER_PLUGINS+1]; #define MAX_VFS_PLUGINS 10 DB_vfs_t *g_vfs_plugins[MAX_VFS_PLUGINS+1]; +#define MAX_OUTPUT_PLUGINS 10 +DB_output_t *g_output_plugins[MAX_OUTPUT_PLUGINS+1]; +DB_output_t *output_plugin = NULL; + void plug_md5 (uint8_t sig[16], const char *in, int len) { md5_buffer (in, len, sig); @@ -558,6 +560,7 @@ plug_load_all (void) { int numplugins = 0; int numdecoders = 0; int numvfs = 0; + int numoutput = 0; for (plugin_t *plug = plugins; plug; plug = plug->next) { g_plugins[numplugins++] = plug->plugin; if (plug->plugin->type == DB_PLUGIN_DECODER) { @@ -574,11 +577,41 @@ plug_load_all (void) { } g_vfs_plugins[numvfs++] = (DB_vfs_t *)plug->plugin; } + else if (plug->plugin->type == DB_PLUGIN_OUTPUT) { + fprintf (stderr, "found output plugin %s\n", plug->plugin->name); + if (numvfs >= MAX_OUTPUT_PLUGINS) { + break; + } + g_output_plugins[numoutput++] = (DB_output_t *)plug->plugin; + } } // fprintf (stderr, "numplugins: %d, numdecoders: %d, numvfs: %d\n", numplugins, numdecoders, numvfs); g_plugins[numplugins] = NULL; g_decoder_plugins[numdecoders] = NULL; g_vfs_plugins[numvfs] = NULL; + g_output_plugins[numoutput] = NULL; + + // select output plugin + const char *outplugname = conf_get_str ("output_plugin", "alsa"); + for (int i = 0; g_output_plugins[i]; i++) { + DB_output_t *p = g_output_plugins[i]; + if (!strcmp (p->plugin.name, outplugname)) { + fprintf (stderr, "selected output plugin: %s\n", outplugname); + output_plugin = p; + break; + } + } + if (!output_plugin) { + output_plugin = g_output_plugins[0]; + } + if (output_plugin) { + fprintf (stderr, "selected output plugin: %s\n", output_plugin->plugin.name); + conf_set_str ("output_plugin", output_plugin->plugin.name); + } + else { + fprintf (stderr, "failed to find output plugin!\n"); + exit (-1); + } } void @@ -650,3 +683,8 @@ plug_activate (DB_plugin_t *plug, int activate) { } } } + +DB_output_t * +plug_get_output (void) { + return output_plugin; +} diff --git a/plugins.h b/plugins.h index 7a1ce845..cfa7df11 100644 --- a/plugins.h +++ b/plugins.h @@ -106,4 +106,7 @@ plug_get_config_dir (void); int plug_activate (DB_plugin_t *plug, int activate); +DB_output_t * +plug_get_output (void); + #endif // __PLUGINS_H diff --git a/plugins/alsa/alsa.c b/plugins/alsa/alsa.c new file mode 100644 index 00000000..186a5f9d --- /dev/null +++ b/plugins/alsa/alsa.c @@ -0,0 +1,632 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009 Alexey Yakovenko + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 program. If not, see . +*/ +#include +#include +#include +#include +#include +#include "deadbeef.h" + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + +static DB_output_t plugin; +DB_functions_t *deadbeef; + +static snd_pcm_t *audio; +static int bufsize = -1; +static int alsa_terminate; +static int alsa_rate = 44100; +static int state; // 0 = stopped, 1 = playing, 2 = pause +static uintptr_t mutex; +static intptr_t alsa_tid; + +static int conf_alsa_resample = 0; +static char conf_alsa_soundcard[100] = "default"; + +static void +palsa_callback (char *stream, int len); + +static void +palsa_thread (void *context); + +static int +palsa_init (void); + +static int +palsa_free (void); + +static int +palsa_change_rate (int rate); + +static int +palsa_play (void); + +static int +palsa_stop (void); + +static int +palsa_isstopped (void); + +static int +palsa_ispaused (void); + +static int +palsa_pause (void); + +static int +palsa_unpause (void); + +static int +palsa_get_rate (void); + +static int +palsa_get_bps (void); + +static int +palsa_get_channels (void); + +static int +palsa_get_endianness (void); + +static void +palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void*), void *userdata); + +static int +palsa_set_hw_params (int samplerate) { + snd_pcm_hw_params_t *hw_params = NULL; +// int alsa_resample = conf_get_int ("alsa.resample", 0); + int err = 0; + + if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { + trace ("cannot allocate hardware parameter structure (%s)\n", + snd_strerror (err)); + goto error; + } + + if ((err = snd_pcm_hw_params_any (audio, hw_params)) < 0) { + trace ("cannot initialize hardware parameter structure (%s)\n", + snd_strerror (err)); + goto error; + } + + if ((err = snd_pcm_hw_params_set_access (audio, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + trace ("cannot set access type (%s)\n", + snd_strerror (err)); + goto error; + } + + snd_pcm_format_t fmt; +#if WORDS_BIGENDIAN + fmt = SND_PCM_FORMAT_S16_BE; +#else + fmt = SND_PCM_FORMAT_S16_LE; +#endif + if ((err = snd_pcm_hw_params_set_format (audio, hw_params, fmt)) < 0) { + trace ("cannot set sample format (%s)\n", + snd_strerror (err)); + goto error; + } + + snd_pcm_hw_params_get_format (hw_params, &fmt); + trace ("chosen sample format: %04Xh\n", (int)fmt); + + int val = samplerate; + int ret = 0; + + if ((err = snd_pcm_hw_params_set_rate_resample (audio, hw_params, conf_alsa_resample)) < 0) { + trace ("cannot setup resampling (%s)\n", + snd_strerror (err)); + goto error; + } + + if ((err = snd_pcm_hw_params_set_rate_near (audio, hw_params, &val, &ret)) < 0) { + trace ("cannot set sample rate (%s)\n", + snd_strerror (err)); + goto error; + } + alsa_rate = val; + trace ("chosen samplerate: %d Hz\n", alsa_rate); + + if ((err = snd_pcm_hw_params_set_channels (audio, hw_params, 2)) < 0) { + trace ("cannot set channel count (%s)\n", + snd_strerror (err)); + goto error; + } + + int nchan; + snd_pcm_hw_params_get_channels (hw_params, &nchan); + trace ("alsa channels: %d\n", nchan); + + unsigned int buffer_time = 500000; + int dir; + if ((err = snd_pcm_hw_params_set_buffer_time_near (audio, hw_params, &buffer_time, &dir)) < 0) { + trace ("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); + goto error; + } + trace ("alsa buffer time: %d ms\n", buffer_time); + snd_pcm_uframes_t size; + if ((err = snd_pcm_hw_params_get_buffer_size (hw_params, &size)) < 0) { + trace ("Unable to get buffer size for playback: %s\n", snd_strerror(err)); + goto error; + } + trace ("alsa buffer size: %d frames\n", size); + bufsize = size; + + if ((err = snd_pcm_hw_params (audio, hw_params)) < 0) { + trace ("cannot set parameters (%s)\n", + snd_strerror (err)); + goto error; + } +error: + if (hw_params) { + snd_pcm_hw_params_free (hw_params); + } + return err; +} + +int +palsa_init (void) { + int err; + + // get and cache conf variables + strcpy (conf_alsa_soundcard, deadbeef->conf_get_str ("alsa_soundcard", "default")); + conf_alsa_resample = deadbeef->conf_get_int ("alsa.resample", 0); + trace ("alsa_soundcard: %s\n", conf_alsa_soundcard); + trace ("alsa.resample: %d\n", conf_alsa_resample); + + snd_pcm_sw_params_t *sw_params = NULL; + state = 0; + //const char *conf_alsa_soundcard = conf_get_str ("alsa_soundcard", "default"); + if ((err = snd_pcm_open (&audio, conf_alsa_soundcard, SND_PCM_STREAM_PLAYBACK, 0))) { + trace ("could not open audio device (%s)\n", + snd_strerror (err)); + return -1; + } + + mutex = deadbeef->mutex_create (); + + if (palsa_set_hw_params (alsa_rate) < 0) { + goto open_error; + } + + if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) { + trace ("cannot allocate software parameters structure (%s)\n", + snd_strerror (err)); + goto open_error; + } + if ((err = snd_pcm_sw_params_current (audio, sw_params)) < 0) { + trace ("cannot initialize software parameters structure (%s)\n", + snd_strerror (err)); + goto open_error; + } + + if ((err = snd_pcm_sw_params_set_avail_min (audio, sw_params, bufsize/2)) < 0) { + trace ("cannot set minimum available count (%s)\n", + snd_strerror (err)); + goto open_error; + } + snd_pcm_uframes_t av; + if ((err = snd_pcm_sw_params_get_avail_min (sw_params, &av)) < 0) { + trace ("snd_pcm_sw_params_get_avail_min failed (%s)\n", + snd_strerror (err)); + goto open_error; + } + trace ("alsa period size: %d frames\n", av); + + if ((err = snd_pcm_sw_params_set_start_threshold (audio, sw_params, 0U)) < 0) { + trace ("cannot set start mode (%s)\n", + snd_strerror (err)); + goto open_error; + } + if ((err = snd_pcm_sw_params (audio, sw_params)) < 0) { + trace ("cannot set software parameters (%s)\n", + snd_strerror (err)); + goto open_error; + } + snd_pcm_sw_params_free (sw_params); + sw_params = NULL; + + /* the interface will interrupt the kernel every N frames, and ALSA + will wake up this program very soon after that. + */ + + if ((err = snd_pcm_prepare (audio)) < 0) { + trace ("cannot prepare audio interface for use (%s)\n", + snd_strerror (err)); + goto open_error; + } + + snd_pcm_start (audio); + + alsa_terminate = 0; + alsa_tid = deadbeef->thread_start (palsa_thread, NULL); + + return 0; + +open_error: + if (sw_params) { + snd_pcm_sw_params_free (sw_params); + } + if (audio != NULL) { + palsa_free (); + } + + return -1; +} + +int +palsa_change_rate (int rate) { + if (!audio) { + return 0; + } + if (rate == alsa_rate) { + trace ("palsa_change_rate: same rate (%d), ignored\n", rate); + return rate; + } + trace ("trying to change samplerate to: %d\n", rate); + deadbeef->mutex_lock (mutex); + snd_pcm_drop (audio); + int ret = palsa_set_hw_params (rate); + if (state != 0) { + snd_pcm_start (audio); + } + deadbeef->mutex_unlock (mutex); + if (ret < 0) { + return -1; + } + trace ("chosen samplerate: %d Hz\n", alsa_rate); + return alsa_rate; +} + +int +palsa_free (void) { + trace ("palsa_free\n"); + if (audio && !alsa_terminate) { + alsa_terminate = 1; + deadbeef->thread_join (alsa_tid); + alsa_tid = 0; + snd_pcm_close(audio); + audio = NULL; + deadbeef->mutex_free (mutex); + state = 0; + alsa_terminate = 0; + } + return 0; +} + +static void +palsa_hw_pause (int pause) { + if (!audio) { + return; + } + if (state == 0) { + return; + } + if (pause == 1) { + snd_pcm_drop (audio); + } + else { + snd_pcm_prepare (audio); + snd_pcm_start (audio); + } +} + +int +palsa_play (void) { + int err; + if (state == 0) { + if (!audio) { + if (palsa_init () < 0) { + state = 0; + return -1; + } + } + else { + if ((err = snd_pcm_prepare (audio)) < 0) { + trace ("cannot prepare audio interface for use (%s)\n", + snd_strerror (err)); + return -1; + } + } + } + if (state != 1) { + state = 1; + snd_pcm_start (audio); + } + return 0; +} + + +int +palsa_stop (void) { + if (!audio) { + return 0; + } + state = 0; + if (deadbeef->conf_get_int ("alsa.freeonstop", 0)) { + palsa_free (); + } + else { + deadbeef->mutex_lock (mutex); + snd_pcm_drop (audio); + deadbeef->mutex_unlock (mutex); + } + deadbeef->streamer_reset (1); + return 0; +} + +int +palsa_isstopped (void) { + return (state == 0); +} + +int +palsa_ispaused (void) { + // return pause state + return (state == 2); +} + +int +palsa_pause (void) { + if (state == 0 || !audio) { + return -1; + } + // set pause state + palsa_hw_pause (1); + state = 2; + return 0; +} + +int +palsa_unpause (void) { + // unset pause state + if (state == 2) { + state = 1; + palsa_hw_pause (0); + } + return 0; +} + +int +palsa_get_rate (void) { + return alsa_rate; +} + +int +palsa_get_bps (void) { + return 16; +} + +int +palsa_get_channels (void) { + return 2; +} + +static int +palsa_get_endianness (void) { +#if WORDS_BIGENDIAN + return 1; +#else + return 0; +#endif +} + +static void +palsa_thread (void *context) { + prctl (PR_SET_NAME, "deadbeef-alsa", 0, 0, 0, 0); + int err; + for (;;) { + if (alsa_terminate) { + break; + } + if (state != 1) { + usleep (10000); + continue; + } + + deadbeef->mutex_lock (mutex); + /* wait till the interface is ready for data, or 1 second + has elapsed. + */ + if ((err = snd_pcm_wait (audio, 500)) < 0 && state == 1) { + if (err == -ESTRPIPE) { + trace ("alsa: trying to recover from suspend...\n"); + deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0); + deadbeef->mutex_unlock (mutex); + break; + } + else { + trace ("alsa: trying to recover from xrun...\n"); + snd_pcm_prepare (audio); + deadbeef->mutex_unlock (mutex); + continue; + } + } + + /* find out how much space is available for playback data */ + + snd_pcm_sframes_t frames_to_deliver; + if ((frames_to_deliver = snd_pcm_avail_update (audio)) < 0) { + if (frames_to_deliver == -EPIPE) { + deadbeef->mutex_unlock (mutex); + trace ("an xrun occured\n"); + continue; + } else { + deadbeef->mutex_unlock (mutex); + trace ("unknown ALSA avail update return value (%d)\n", + (int)frames_to_deliver); + continue; + } + } + + /* deliver the data */ + // FIXME: under some conditions, frames_to_deliver may become huge + // like 20M. this case is not handled here. + char buf[frames_to_deliver*4]; + palsa_callback (buf, frames_to_deliver*4); + if ((err = snd_pcm_writei (audio, buf, frames_to_deliver)) < 0) { + trace ("write %d frames failed (%s)\n", frames_to_deliver, snd_strerror (err)); + snd_pcm_prepare (audio); + snd_pcm_start (audio); + } + deadbeef->mutex_unlock (mutex); + usleep (1000); // this must be here to prevent mutex deadlock + } +} + +static void +palsa_callback (char *stream, int len) { + if (!deadbeef->streamer_ok_to_read (len)) { + memset (stream, 0, len); + return; + } + int bytesread = deadbeef->streamer_read (stream, len); + +// FIXME: move volume control to streamer_read for copy optimization +#if 0 + int16_t vol[4]; + vol[0] = volume_get_amp () * 255; // that will be extra 8 bits + // pack 4 times + vol[1] = vol[2] = vol[3] = vol[0]; + + // apply volume with mmx + __asm__ volatile( + " mov %0, %%ecx\n\t" + " shr $4, %%ecx\n\t" + " mov %1, %%eax\n\t" + " movq %2, %mm1\n\t" + "1:\n\t" + " movq [%%eax], %mm0\n\t" + " movq %mm0, %mm2\n\t" + " movq %mm0, %mm3\n\t" + " pmullw %mm1, %mm2\n\t" + " pmulhw %mm1, %mm3\n\t" + " psrlw $8, %mm2\n\t" // discard lowest 8 bits + " psllw $8, %mm3\n\t" // shift left 8 lsbs of hiwords + " por %mm3, %mm2\n\t" // OR them together + " movq %mm3, [%%eax]\n\t" // load back to memory + " add $8, %%eax\n\t" + " dec %%ecx\n\t" + " jnz 1b\n\t" + : + : "r"(len), "r"(stream), "r"(vol) + : "%ecx", "%eax" + ); + +#else + int16_t ivolume = deadbeef->volume_get_amp () * 1000; + for (int i = 0; i < bytesread/2; i++) { + ((int16_t*)stream)[i] = (int16_t)(((int32_t)(((int16_t*)stream)[i])) * ivolume / 1000); + } +#endif + if (bytesread < len) { + memset (stream + bytesread, 0, len-bytesread); + } +} + +static int +palsa_configchanged (DB_event_t *ev, uintptr_t data) { + int alsa_resample = deadbeef->conf_get_int ("alsa.resample", 0); + const char *alsa_soundcard = deadbeef->conf_get_str ("alsa_soundcard", "default"); + if (alsa_resample != conf_alsa_resample + || strcmp (alsa_soundcard, conf_alsa_soundcard)) { + deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0); + } +} + +// derived from alsa-utils/aplay.c +void +palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void *), void *userdata) { + void **hints, **n; + char *name, *descr, *descr1, *io; + const char *filter = "Output"; + if (snd_device_name_hint(-1, "pcm", &hints) < 0) + return; + n = hints; + while (*n != NULL) { + name = snd_device_name_get_hint(*n, "NAME"); + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + if (io == NULL || !strcmp(io, filter)) { + if (name && descr && callback) { + callback (name, descr, userdata); + } + } + if (name != NULL) + free(name); + if (descr != NULL) + free(descr); + if (io != NULL) + free(io); + n++; + } + snd_device_name_free_hint(hints); +} + +int +palsa_get_state (void) { + return state; +} + +int +alsa_start (void) { + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (palsa_configchanged), 0); + return 0; +} + +int +alsa_stop (void) { + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (palsa_configchanged), 0); + return 0; +} + +DB_plugin_t * +alsa_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} + +// define plugin interface +static DB_output_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.nostop = 1, + .plugin.type = DB_PLUGIN_OUTPUT, + .plugin.name = "alsa output plugin", + .plugin.descr = "plays sound through linux standard alsa library", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = alsa_start, + .plugin.stop = alsa_stop, + .init = palsa_init, + .free = palsa_free, + .change_rate = palsa_change_rate, + .play = palsa_play, + .stop = palsa_stop, + .pause = palsa_pause, + .unpause = palsa_unpause, + .state = palsa_get_state, + .samplerate = palsa_get_rate, + .bitspersample = palsa_get_bps, + .channels = palsa_get_channels, + .endianness = palsa_get_endianness, + .enum_soundcards = palsa_enum_soundcards, +}; diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index d83fa534..be2e7d12 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -975,7 +975,7 @@ on_seekbar_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { - if (deadbeef->playback_isstopped ()) { + if (deadbeef->get_output ()->state () == OUTPUT_STATE_STOPPED) { return FALSE; } seekbar_moving = 1; diff --git a/plugins/gtkui/gtkplaylist.c b/plugins/gtkui/gtkplaylist.c index a01d2889..a9a0d44b 100644 --- a/plugins/gtkui/gtkplaylist.c +++ b/plugins/gtkui/gtkplaylist.c @@ -387,7 +387,7 @@ gtkpl_draw_pl_row (gtkplaylist_t *ps, int row, DB_playItem_t *it) { gtkpl_column_t *c; for (c = ps->columns; c; c = c->next) { if (it == deadbeef->pl_getcurrent () && c->id == DB_COLUMN_PLAYING/* && !p_isstopped ()*/) { - int paused = deadbeef->playback_ispaused (); + int paused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED; int buffering = !deadbeef->streamer_ok_to_read (-1); uintptr_t pixbuf; if (paused) { diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index 1eba2b02..248b38b8 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -73,7 +73,7 @@ update_songinfo (gpointer ctx) { DB_playItem_t *track = deadbeef->streamer_get_playing_track (); float duration = deadbeef->pl_get_item_duration (track); - if (deadbeef->playback_isstopped ()) { + if (deadbeef->get_output ()->state () == OUTPUT_STATE_STOPPED) { snprintf (sbtext_new, sizeof (sbtext_new), "Stopped | %d tracks | %s total playtime", deadbeef->pl_getcount (), totaltime_str); songpos = 0; } @@ -107,7 +107,7 @@ update_songinfo (gpointer ctx) { snprintf (sbitrate, sizeof (sbitrate), "%d kbps ", bitrate); } #endif - const char *spaused = deadbeef->playback_ispaused () ? "Paused | " : ""; + const char *spaused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED ? "Paused | " : ""; snprintf (sbtext_new, sizeof (sbtext_new), "%s%s %s| %dHz | %d bit | %s | %d:%02d / %s | %d tracks | %s total playtime", spaused, track->filetype ? track->filetype:"-", sbitrate, samplerate, bitspersample, mode, minpos, secpos, t, deadbeef->pl_getcount (), totaltime_str); } diff --git a/streamer.c b/streamer.c index f13ecf94..da07cd84 100644 --- a/streamer.c +++ b/streamer.c @@ -369,7 +369,7 @@ streamer_thread (void *ctx) { avg_bitrate = -1; // change samplerate if (prevtrack_samplerate != str_playing_song.decoder->info.samplerate) { - palsa_change_rate (str_playing_song.decoder->info.samplerate); + plug_get_output ()->change_rate (str_playing_song.decoder->info.samplerate); prevtrack_samplerate = str_playing_song.decoder->info.samplerate; } } -- cgit v1.2.3