From 90877724d03c71111c2b47fd6ea6eb3ebce88dd4 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 25 Jan 2010 22:19:44 +0100 Subject: multiple fixes in streamer, alsa and oss --- plugins/oss/oss.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'plugins/oss') diff --git a/plugins/oss/oss.c b/plugins/oss/oss.c index 06eb5a78..13fd8ec7 100644 --- a/plugins/oss/oss.c +++ b/plugins/oss/oss.c @@ -48,7 +48,7 @@ static int state; static int fd; static uintptr_t mutex; -#define BLOCKSIZE 4096 +#define BLOCKSIZE 8192 static void oss_thread (void *context); @@ -241,7 +241,7 @@ oss_thread (void *context) { if (oss_terminate) { break; } - if (state != OUTPUT_STATE_PLAYING) { + if (state != OUTPUT_STATE_PLAYING || !deadbeef->streamer_ok_to_read (-1)) { usleep (10000); continue; } @@ -252,6 +252,7 @@ oss_thread (void *context) { int res = write (fd, buf, sizeof (buf)); deadbeef->mutex_unlock (mutex); if (res != sizeof (buf)) { + perror ("oss write"); fprintf (stderr, "oss: failed to write buffer\n"); } usleep (1000); // this must be here to prevent mutex deadlock @@ -260,10 +261,6 @@ oss_thread (void *context) { static void oss_callback (char *stream, int len) { - if (!deadbeef->streamer_ok_to_read (len)) { - memset (stream, 0, len); - return; - } int bytesread = deadbeef->streamer_read (stream, len); int16_t ivolume = deadbeef->volume_get_amp () * 1000; for (int i = 0; i < bytesread/2; i++) { -- cgit v1.2.3 From 7262368b6897b41188f0cf6844ca875027d425a4 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 26 Jan 2010 21:21:26 +0100 Subject: fixed samplerate switching bugs in streamer better sound configuration multiple fixes to ALSA and streamer --- playback.h | 5 +++-- plugins.c | 11 ++++++++-- plugins/alsa/alsa.c | 49 +++++++++++++++++++++++++++----------------- plugins/gtkui/callbacks.c | 10 ++++++--- plugins/gtkui/callbacks.h | 4 ++++ plugins/gtkui/deadbeef.glade | 8 ++++---- plugins/gtkui/interface.c | 16 +++++++-------- plugins/oss/oss.c | 3 +-- streamer.c | 48 ++++++++++++++++++++++++++++++++----------- 9 files changed, 102 insertions(+), 52 deletions(-) (limited to 'plugins/oss') diff --git a/playback.h b/playback.h index 5728d63c..21ac74f1 100644 --- a/playback.h +++ b/playback.h @@ -27,7 +27,8 @@ #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) +#define p_state() (plug_get_output ()->state ()) +#define p_isstopped() (p_state () == OUTPUT_STATE_STOPPED) +#define p_ispaused() (p_state () == OUTPUT_STATE_PAUSED) #endif // __PLAYBACK_H diff --git a/plugins.c b/plugins.c index d7a0dd45..f37aec32 100644 --- a/plugins.c +++ b/plugins.c @@ -740,9 +740,16 @@ plug_reinit_sound (void) { fprintf (stderr, "failed to select output plugin %s\nreverted to %s\n", outplugname, prev->plugin.name); output_plugin = prev; } - p_init (); + streamer_reset (1); + if (p_init () < 0) { + streamer_set_nextsong (-2, 0); + return; + } if (state != OUTPUT_STATE_PAUSED && state != OUTPUT_STATE_STOPPED) { - p_play (); + if (p_play () < 0) { + fprintf (stderr, "failed to reinit sound output\n"); + streamer_set_nextsong (-2, 0); + } } } diff --git a/plugins/alsa/alsa.c b/plugins/alsa/alsa.c index 157814ce..ca6b4e4d 100644 --- a/plugins/alsa/alsa.c +++ b/plugins/alsa/alsa.c @@ -34,6 +34,7 @@ DB_functions_t *deadbeef; static snd_pcm_t *audio; //static int bufsize = -1; static int alsa_terminate; +static int requested_rate = -1; static int alsa_rate = 44100; static int state; // one of output_state_t static uintptr_t mutex; @@ -232,6 +233,10 @@ palsa_init (void) { mutex = deadbeef->mutex_create (); + if (requested_rate != -1) { + alsa_rate = requested_rate; + } + if (palsa_set_hw_params (alsa_rate) < 0) { goto open_error; } @@ -308,26 +313,25 @@ open_error: int palsa_change_rate (int rate) { - return 0; + trace ("palsa_change_rate: %d\n", rate); + requested_rate = rate; if (!audio) { - return 0; + return alsa_rate; } if (rate == alsa_rate) { - trace ("palsa_change_rate: same rate (%d), ignored\n", rate); + trace ("palsa_change_rate: ignored\n", rate); return rate; } - trace ("trying to change samplerate to: %d\n", rate); - deadbeef->mutex_lock (mutex); + state = OUTPUT_STATE_STOPPED; snd_pcm_drop (audio); + deadbeef->mutex_lock (mutex); int ret = palsa_set_hw_params (rate); - if (state != OUTPUT_STATE_STOPPED) { - snd_pcm_start (audio); - } deadbeef->mutex_unlock (mutex); if (ret < 0) { - return -1; + trace ("palsa_change_rate: impossible to set samplerate to %d\n", rate); + return alsa_rate; } - trace ("chosen samplerate: %d Hz\n", alsa_rate); + trace ("chosen samplerate: %d\n", alsa_rate); return alsa_rate; } @@ -386,9 +390,8 @@ palsa_play (void) { } else { if ((err = snd_pcm_prepare (audio)) < 0) { - perror ("snd_pcm_prepare"); - trace ("cannot prepare audio interface for use (%s)\n", - snd_strerror (err)); + trace ("cannot prepare audio interface for use (%d, %s)\n", + err, snd_strerror (err)); return -1; } } @@ -499,23 +502,25 @@ palsa_thread (void *context) { deadbeef->mutex_lock (mutex); if ((err = snd_pcm_wait (audio, 1000)) < 0) { - perror ("snd_pcm_wait"); if (err == -ESTRPIPE) { - perror ("snd_pcm_writei"); - trace ("alsa: trying to recover from suspend...\n"); + trace ("alsa: trying to recover from suspend... (error=%d, %s)\n", err, snd_strerror (err)); deadbeef->sendmessage (M_REINIT_SOUND, 0, 0, 0); deadbeef->mutex_unlock (mutex); break; } - else { + else if (err == -EPIPE) { // this pretty frequent condition, no spam here - perror ("snd_pcm_wait"); -// trace ("alsa: trying to recover from xrun...\n"); +// trace ("alsa: snd_pcm_wait error=%d, %s\n", err, snd_strerror (err)); snd_pcm_prepare (audio); snd_pcm_start (audio); deadbeef->mutex_unlock (mutex); continue; } + else { + trace ("alsa: snd_pcm_wait error=%d, %s\n", err, snd_strerror (err)); + deadbeef->mutex_unlock (mutex); + continue; + } } /* find out how much space is available for playback data */ int written = 0; @@ -583,6 +588,7 @@ palsa_callback (char *stream, int len) { static int palsa_configchanged (DB_event_t *ev, uintptr_t data) { + trace ("alsa: config option changed, restarting\n"); 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 @@ -644,6 +650,10 @@ alsa_load (DB_functions_t *api) { return DB_PLUGIN (&plugin); } +static const char settings_dlg[] = + "property \"Enable software resampling\" checkbox alsa.resample 0;\n" +; + // define plugin interface static DB_output_t plugin = { DB_PLUGIN_SET_API_VERSION @@ -658,6 +668,7 @@ static DB_output_t plugin = { .plugin.website = "http://deadbeef.sf.net", .plugin.start = alsa_start, .plugin.stop = alsa_stop, + .plugin.configdialog = settings_dlg, .init = palsa_init, .free = palsa_free, .change_rate = palsa_change_rate, diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index 603c5267..108d0f29 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -1562,7 +1562,7 @@ on_preferences_activate (GtkMenuItem *menuitem, // alsa resampling - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_alsa_resampling")), deadbeef->conf_get_int ("alsa.resample", 0)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_dynsamplerate")), deadbeef->conf_get_int ("playback.dynsamplerate", 0)); // alsa freeonstop gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "pref_alsa_freewhenstopped")), deadbeef->conf_get_int ("alsa.freeonstop", 0)); @@ -1691,11 +1691,11 @@ on_pref_output_plugin_changed (GtkComboBox *combobox, } void -on_pref_alsa_resampling_clicked (GtkButton *button, +on_pref_dynsamplerate_clicked (GtkButton *button, gpointer user_data) { int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)); - deadbeef->conf_set_int ("alsa.resample", active); + deadbeef->conf_set_int ("playback.dynsamplerate", active); deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); } @@ -2035,6 +2035,7 @@ on_prop_entry_changed(GtkEditable *editable, gpointer user_data) { const char *key = g_object_get_data (G_OBJECT (editable), "key"); if (key) { deadbeef->conf_set_str (key, gtk_entry_get_text (GTK_ENTRY (editable))); + deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); } } @@ -2043,6 +2044,7 @@ on_prop_checkbox_clicked (GtkButton *button, gpointer user_data) { const char *key = g_object_get_data (G_OBJECT (button), "key"); if (key) { deadbeef->conf_set_int (key, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))); + deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); } } @@ -2059,6 +2061,7 @@ on_prop_browse_file (GtkButton *button, gpointer user_data) { if (folder) { deadbeef->conf_set_str ("filechooser.lastdir", folder); g_free (folder); + deadbeef->sendmessage (M_CONFIGCHANGED, 0, 0, 0); } if (response == GTK_RESPONSE_OK) { gchar *file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg)); @@ -2451,3 +2454,4 @@ on_trackproperties_delete_event (GtkWidget *widget, } + diff --git a/plugins/gtkui/callbacks.h b/plugins/gtkui/callbacks.h index cee7b241..c30fc05d 100644 --- a/plugins/gtkui/callbacks.h +++ b/plugins/gtkui/callbacks.h @@ -803,3 +803,7 @@ on_gpl1_activate (GtkMenuItem *menuitem, void on_lgpl1_activate (GtkMenuItem *menuitem, gpointer user_data); + +void +on_pref_dynsamplerate_clicked (GtkButton *button, + gpointer user_data); diff --git a/plugins/gtkui/deadbeef.glade b/plugins/gtkui/deadbeef.glade index 022a1515..e8d8de9d 100644 --- a/plugins/gtkui/deadbeef.glade +++ b/plugins/gtkui/deadbeef.glade @@ -1591,7 +1591,7 @@ True - Software ALSA resampling + Allow dynamic samplerate switching False False GTK_JUSTIFY_LEFT @@ -1689,17 +1689,17 @@ - + True True - + (WARNING: turning this on this might break gapless playback) True GTK_RELIEF_NORMAL True False False True - + 1 diff --git a/plugins/gtkui/interface.c b/plugins/gtkui/interface.c index ca824c85..9b7c6161 100644 --- a/plugins/gtkui/interface.c +++ b/plugins/gtkui/interface.c @@ -1271,7 +1271,7 @@ create_prefwin (void) GtkWidget *label4; GtkWidget *label23; GtkWidget *pref_soundcard; - GtkWidget *pref_alsa_resampling; + GtkWidget *pref_dynsamplerate; GtkWidget *pref_replaygain_scale; GtkWidget *pref_alsa_freewhenstopped; GtkWidget *pref_src_quality; @@ -1353,7 +1353,7 @@ create_prefwin (void) (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label6), 0, 0.5); - label5 = gtk_label_new ("Software ALSA resampling"); + label5 = gtk_label_new ("Allow dynamic samplerate switching"); gtk_widget_show (label5); gtk_table_attach (GTK_TABLE (table3), label5, 0, 1, 2, 3, (GtkAttachOptions) (GTK_FILL), @@ -1380,9 +1380,9 @@ create_prefwin (void) (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); - pref_alsa_resampling = gtk_check_button_new_with_mnemonic (""); - gtk_widget_show (pref_alsa_resampling); - gtk_table_attach (GTK_TABLE (table3), pref_alsa_resampling, 1, 2, 2, 3, + pref_dynsamplerate = gtk_check_button_new_with_mnemonic ("(WARNING: turning this on this might break gapless playback)"); + gtk_widget_show (pref_dynsamplerate); + gtk_table_attach (GTK_TABLE (table3), pref_dynsamplerate, 1, 2, 2, 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); @@ -1624,8 +1624,8 @@ create_prefwin (void) g_signal_connect ((gpointer) pref_soundcard, "changed", G_CALLBACK (on_pref_soundcard_changed), NULL); - g_signal_connect ((gpointer) pref_alsa_resampling, "clicked", - G_CALLBACK (on_pref_alsa_resampling_clicked), + g_signal_connect ((gpointer) pref_dynsamplerate, "clicked", + G_CALLBACK (on_pref_dynsamplerate_clicked), NULL); g_signal_connect ((gpointer) pref_replaygain_scale, "clicked", G_CALLBACK (on_pref_replaygain_scale_clicked), @@ -1676,7 +1676,7 @@ create_prefwin (void) GLADE_HOOKUP_OBJECT (prefwin, label4, "label4"); GLADE_HOOKUP_OBJECT (prefwin, label23, "label23"); GLADE_HOOKUP_OBJECT (prefwin, pref_soundcard, "pref_soundcard"); - GLADE_HOOKUP_OBJECT (prefwin, pref_alsa_resampling, "pref_alsa_resampling"); + GLADE_HOOKUP_OBJECT (prefwin, pref_dynsamplerate, "pref_dynsamplerate"); GLADE_HOOKUP_OBJECT (prefwin, pref_replaygain_scale, "pref_replaygain_scale"); GLADE_HOOKUP_OBJECT (prefwin, pref_alsa_freewhenstopped, "pref_alsa_freewhenstopped"); GLADE_HOOKUP_OBJECT (prefwin, pref_src_quality, "pref_src_quality"); diff --git a/plugins/oss/oss.c b/plugins/oss/oss.c index 13fd8ec7..a7d716da 100644 --- a/plugins/oss/oss.c +++ b/plugins/oss/oss.c @@ -130,11 +130,10 @@ oss_init (void) { static int oss_change_rate (int rate) { if (!fd) { - oss_rate = rate; return oss_rate; } if (rate == oss_rate) { - trace ("oss_change_rate: same rate (%d), ignored\n", rate); + trace ("oss_change_rate: ignored\n", rate); return rate; } deadbeef->mutex_lock (mutex); diff --git a/streamer.c b/streamer.c index 2a477fbb..8a8b35f3 100644 --- a/streamer.c +++ b/streamer.c @@ -38,8 +38,8 @@ #include "volume.h" #include "vfs.h" -//#define trace(...) { fprintf(stderr, __VA_ARGS__); } -#define trace(fmt,...) +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) static intptr_t streamer_tid; static int src_quality; @@ -82,8 +82,6 @@ static float playpos = 0; // play position of current song static int avg_bitrate = -1; // avg bitrate of current song static int last_bitrate = -1; // last bitrate of current song -static int prevtrack_samplerate = -1; - playItem_t str_playing_song; playItem_t str_streaming_song; // remember pointers to original instances of playitems @@ -312,9 +310,11 @@ streamer_thread (void *ctx) { else if (pstate == 1) { last_bitrate = -1; avg_bitrate = -1; - if (p_play () < 0) { - fprintf (stderr, "streamer: failed to start playback; output plugin doesn't work\n"); - streamer_set_nextsong (-2, 0); + if (p_state () != OUTPUT_STATE_PLAYING) { + if (p_play () < 0) { + fprintf (stderr, "streamer: failed to start playback; output plugin doesn't work\n"); + streamer_set_nextsong (-2, 0); + } } } else if (pstate == 2) { @@ -377,13 +377,37 @@ streamer_thread (void *ctx) { playlist_current_ptr = orig_playing_song; // that is needed for playlist drawing // plugin will get pointer to new str_playing_song - trace ("sending songstarted to plugins\n"); + trace ("sending songstarted to plugins\ncurrent playtrack: %s\n", str_playing_song.fname); plug_trigger_event (DB_EV_SONGSTARTED, 0); playpos = 0; - // change samplerate - if (prevtrack_samplerate != 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; + + // try to switch samplerate to the closest supported by output plugin + if (conf_get_int ("playback.dynsamplerate", 0)) { + + // don't switch if unchanged + int prevtrack_samplerate = p_get_rate (); + if (prevtrack_samplerate != str_playing_song.decoder->info.samplerate) { + int newrate = plug_get_output ()->change_rate (str_playing_song.decoder->info.samplerate); + if (newrate != prevtrack_samplerate) { + // restart streaming of current track + trace ("streamer: output samplerate changed from %d to %d; restarting track\n", prevtrack_samplerate, newrate); + str_streaming_song.decoder->free (); + str_streaming_song.decoder->init (DB_PLAYITEM (orig_streaming_song)); + bytes_until_next_song = -1; + streamer_buffering = 1; + streamer_reset (1); + prevtrack_samplerate = str_playing_song.decoder->info.samplerate; + } + } + + // output plugin may stop playback before switching samplerate + if (p_state () != OUTPUT_STATE_PLAYING) { + if (p_play () < 0) { + fprintf (stderr, "streamer: failed to start playback after samplerate change; output plugin doesn't work\n"); + streamer_set_nextsong (-2, 0); + continue; + } + } } } -- cgit v1.2.3