summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-09-14 21:45:43 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-09-14 21:45:43 +0200
commit54deede40a49d2947029608a6dd942302569ee1c (patch)
tree168e4acb36b92b044281c1c3e7cbb281ca349052
parentebae6eba48b75ed9fdcbf8ed2b13dfa7e022ab93 (diff)
gapless playback WIP
-rw-r--r--callbacks.c26
-rw-r--r--main.c12
-rw-r--r--playlist.c41
-rw-r--r--playlist.h8
-rw-r--r--plugins.c10
-rw-r--r--streamer.c171
-rw-r--r--streamer.h5
7 files changed, 187 insertions, 86 deletions
diff --git a/callbacks.c b/callbacks.c
index 26264582..2b300e19 100644
--- a/callbacks.c
+++ b/callbacks.c
@@ -642,8 +642,8 @@ on_voice1_clicked (GtkButton *button,
gpointer user_data)
{
codec_lock ();
- if (playlist_current.decoder && playlist_current.decoder->mutevoice) {
- playlist_current.decoder->mutevoice (0, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
+ if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
+ str_playing_song.decoder->mutevoice (0, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
}
codec_unlock ();
}
@@ -654,8 +654,8 @@ on_voice2_clicked (GtkButton *button,
gpointer user_data)
{
codec_lock ();
- if (playlist_current.decoder && playlist_current.decoder->mutevoice) {
- playlist_current.decoder->mutevoice (1, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
+ if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
+ str_playing_song.decoder->mutevoice (1, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
}
codec_unlock ();
}
@@ -666,8 +666,8 @@ on_voice3_clicked (GtkButton *button,
gpointer user_data)
{
codec_lock ();
- if (playlist_current.decoder && playlist_current.decoder->mutevoice) {
- playlist_current.decoder->mutevoice (2, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
+ if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
+ str_playing_song.decoder->mutevoice (2, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
}
codec_unlock ();
}
@@ -678,8 +678,8 @@ on_voice4_clicked (GtkButton *button,
gpointer user_data)
{
codec_lock ();
- if (playlist_current.decoder && playlist_current.decoder->mutevoice) {
- playlist_current.decoder->mutevoice (3, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
+ if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
+ str_playing_song.decoder->mutevoice (3, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
}
codec_unlock ();
}
@@ -690,8 +690,8 @@ on_voice5_clicked (GtkButton *button,
gpointer user_data)
{
codec_lock ();
- if (playlist_current.decoder && playlist_current.decoder->mutevoice) {
- playlist_current.decoder->mutevoice (4, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
+ if (str_playing_song.decoder && str_playing_song.decoder->mutevoice) {
+ str_playing_song.decoder->mutevoice (4, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)) ? 0 : 1);
}
codec_unlock ();
}
@@ -936,8 +936,8 @@ seekbar_draw (GtkWidget *widget) {
pos = x;
}
else {
- if (playlist_current.decoder && playlist_current.duration > 0) {
- pos = streamer_get_playpos () / playlist_current.duration;
+ if (str_playing_song.decoder && str_playing_song.duration > 0) {
+ pos = streamer_get_playpos () / str_playing_song.duration;
pos *= widget->allocation.width;
}
}
@@ -1024,7 +1024,7 @@ on_seekbar_button_release_event (GtkWidget *widget,
seekbar_moving = 0;
seekbar_draw (widget);
seekbar_expose (widget, 0, 0, widget->allocation.width, widget->allocation.height);
- float time = event->x * playlist_current.duration / (widget->allocation.width);
+ float time = event->x * str_playing_song.duration / (widget->allocation.width);
if (time < 0) {
time = 0;
}
diff --git a/main.c b/main.c
index b569bbba..7c817fa4 100644
--- a/main.c
+++ b/main.c
@@ -95,21 +95,21 @@ update_songinfo (void) {
else if (p_isstopped ()) {
strcpy (sbtext_new, "Stopped");
}
- else if (playlist_current.decoder) {
+ else if (str_playing_song.decoder) {
codec_lock ();
- DB_decoder_t *c = playlist_current.decoder;
+ DB_decoder_t *c = str_playing_song.decoder;
float playpos = streamer_get_playpos ();
int minpos = playpos / 60;
int secpos = playpos - minpos * 60;
- int mindur = playlist_current.duration / 60;
- int secdur = playlist_current.duration - mindur * 60;
+ int mindur = str_playing_song.duration / 60;
+ int secdur = str_playing_song.duration - mindur * 60;
const char *mode = c->info.channels == 1 ? "Mono" : "Stereo";
int samplerate = c->info.samplerate;
int bitspersample = c->info.bps;
songpos = playpos;
codec_unlock ();
- snprintf (sbtext_new, 512, "[%s] %dHz | %d bit | %s | %d:%02d / %d:%02d | %d songs total", playlist_current.filetype ? playlist_current.filetype:"-", samplerate, bitspersample, mode, minpos, secpos, mindur, secdur, pl_getcount ());
+ snprintf (sbtext_new, 512, "[%s] %dHz | %d bit | %s | %d:%02d / %d:%02d | %d songs total", str_playing_song.filetype ? str_playing_song.filetype:"-", samplerate, bitspersample, mode, minpos, secpos, mindur, secdur, pl_getcount ());
}
if (strcmp (sbtext_new, sb_text)) {
@@ -135,7 +135,7 @@ update_songinfo (void) {
if (mainwin) {
GtkWidget *widget = lookup_widget (mainwin, "seekbar");
// translate volume to seekbar pixels
- songpos /= playlist_current.duration;
+ songpos /= str_playing_song.duration;
songpos *= widget->allocation.width;
if ((int)(songpos*2) != (int)(last_songpos*2)) {
GDK_THREADS_ENTER();
diff --git a/playlist.c b/playlist.c
index 151f8409..14ccd2cd 100644
--- a/playlist.c
+++ b/playlist.c
@@ -46,7 +46,6 @@
playItem_t *playlist_head[PL_MAX_ITERATORS];
playItem_t *playlist_tail[PL_MAX_ITERATORS];
-playItem_t playlist_current;
playItem_t *playlist_current_ptr;
int pl_count = 0;
static int pl_order = 0; // 0 = linear, 1 = shuffle, 2 = random
@@ -645,46 +644,6 @@ pl_item_free (playItem_t *it) {
}
int
-pl_set_current (playItem_t *it) {
- int ret = 0;
- int from = pl_get_idx_of (playlist_current_ptr);
- int to = it ? pl_get_idx_of (it) : -1;
- if (playlist_current.decoder) {
- plug_trigger_event (DB_EV_SONGFINISHED);
- }
- codec_lock ();
- if (playlist_current.decoder) {
- playlist_current.decoder->free ();
- }
- pl_item_free (&playlist_current);
- playlist_current_ptr = it;
- if (it && it->decoder) {
- // don't do anything on fail, streamer will take care
- ret = it->decoder->init (DB_PLAYITEM (it));
- if (ret < 0) {
-// pl_item_free (&playlist_current);
-// playlist_current_ptr = NULL;
-// return ret;
-//// it->decoder->info.samplesPerSecond = -1;
- }
- }
- if (playlist_current_ptr) {
- streamer_reset (0);
- }
- if (it) {
- it->played = 1;
- it->started_timestamp = time (NULL);
- pl_item_copy (&playlist_current, it);
- }
- codec_unlock ();
- if (it) {
- plug_trigger_event (DB_EV_SONGSTARTED);
- }
- messagepump_push (M_SONGCHANGED, 0, from, to);
- return ret;
-}
-
-int
pl_prevsong (void) {
if (!playlist_head[PL_MAIN]) {
streamer_set_nextsong (-2, 1);
diff --git a/playlist.h b/playlist.h
index 51bf7124..40fdc7db 100644
--- a/playlist.h
+++ b/playlist.h
@@ -53,8 +53,8 @@ typedef struct playItem_s {
extern playItem_t *playlist_head[PL_MAX_ITERATORS]; // head of linked list
extern playItem_t *playlist_tail[PL_MAX_ITERATORS]; // tail of linked list
-extern playItem_t *playlist_current_ptr; // pointer to a real current playlist item
-extern playItem_t playlist_current; // copy of playlist item being played (stays in memory even if removed from playlist)
+extern playItem_t *playlist_current_ptr; // pointer to a real current playlist item (or NULL)
+
extern int pl_count;
int
@@ -108,8 +108,8 @@ pl_insert_cue_from_buffer (playItem_t *after, const char *fname, const uint8_t *
playItem_t *
pl_insert_cue (playItem_t *after, const char *cuename, struct DB_decoder_s *decoder, const char *ftype, float duration);
-int
-pl_set_current (playItem_t *it);
+//int
+//pl_set_current (playItem_t *it);
// returns -1 if theres no next song, or playlist finished
// reason 0 means "song finished", 1 means "user clicked next"
diff --git a/plugins.c b/plugins.c
index f6ac266b..4a91641b 100644
--- a/plugins.c
+++ b/plugins.c
@@ -215,18 +215,18 @@ plug_playback_random (void) {
float
plug_playback_get_pos (void) {
- if (playlist_current.duration <= 0) {
+ if (str_playing_song.duration <= 0) {
return 0;
}
- return streamer_get_playpos () * 100 / playlist_current.duration;
+ return streamer_get_playpos () * 100 / str_playing_song.duration;
}
void
plug_playback_set_pos (float pos) {
- if (playlist_current.duration <= 0) {
+ if (str_playing_song.duration <= 0) {
return;
}
- float t = pos * playlist_current.duration / 100.f;
+ float t = pos * str_playing_song.duration / 100.f;
streamer_set_seek (t);
}
@@ -246,7 +246,7 @@ plug_trigger_event (int ev) {
case DB_EV_SONGFINISHED:
{
DB_event_song_t *pev = malloc (sizeof (DB_event_song_t));
- pev->song = DB_PLAYITEM (&playlist_current);
+ pev->song = DB_PLAYITEM (&str_playing_song);
event = DB_EVENT (pev);
}
break;
diff --git a/streamer.c b/streamer.c
index e351e41d..c6d85b00 100644
--- a/streamer.c
+++ b/streamer.c
@@ -44,7 +44,7 @@ static float g_fbuffer[200000]; // hack!
static float g_srcbuffer[200000]; // hack!
static int streaming_terminate;
-#define STREAM_BUFFER_SIZE 200000
+#define STREAM_BUFFER_SIZE (200000*5)
static int streambuffer_fill;
static int bytes_until_next_song = 0;
static char streambuffer[STREAM_BUFFER_SIZE];
@@ -57,6 +57,100 @@ static float seekpos = -1;
static float playpos = 0; // play position of current song
+playItem_t str_playing_song;
+playItem_t str_streaming_song;
+// remember pointers to original instances of playitems
+static playItem_t *orig_playing_song;
+static playItem_t *orig_streaming_song;
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+// playlist must call that whenever item was removed
+void
+streamer_song_removed_notify (playItem_t *it) {
+ if (it == orig_playing_song) {
+ orig_playing_song = NULL;
+ }
+ if (it == orig_streaming_song) {
+ orig_streaming_song = NULL;
+ // FIXME: must queue new next song for streaming
+ }
+}
+
+// that must be called after last sample from str_playing_song was done reading
+static int
+streamer_set_current (playItem_t *it) {
+ trace ("streamer_set_current %p, buns=%d\n", it);
+ if (str_streaming_song.decoder) {
+ str_streaming_song.decoder->free ();
+ }
+ pl_item_free (&str_streaming_song);
+ orig_streaming_song = it;
+ if (!it) {
+ return 0;
+ }
+ if (it->decoder) {
+ pl_item_copy (&str_streaming_song, it);
+ int ret = it->decoder->init (DB_PLAYITEM (it));
+ if (ret < 0) {
+ trace ("decoder->init returned %d\n", ret);
+ return ret;
+ }
+ else {
+ trace ("bps=%d, channels=%d, samplerate=%d\n", it->decoder->info.bps, it->decoder->info.channels, it->decoder->info.samplerate);
+ }
+ streamer_reset (0); // reset SRC
+ }
+ else {
+ trace ("no decoder in playitem!\n");
+ orig_streaming_song = NULL;
+ return -1;
+ }
+ if (bytes_until_next_song == -1) {
+ bytes_until_next_song = 0;
+ }
+ return 0;
+}
+
+#if 0
+static int
+str_set_current (playItem_t *it) {
+ int ret = 0;
+ int from = pl_get_idx_of (playlist_current_ptr);
+ int to = it ? pl_get_idx_of (it) : -1;
+ if (str_playing_song.decoder) {
+ plug_trigger_event (DB_EV_SONGFINISHED);
+ }
+ codec_lock ();
+ if (str_playing_song.decoder) {
+ str_playing_song.decoder->free ();
+ }
+ pl_item_free (&str_playing_song);
+ playlist_current_ptr = it;
+ if (it && it->decoder) {
+ // don't do anything on fail, streamer will take care
+ ret = it->decoder->init (DB_PLAYITEM (it));
+// if (ret < 0) {
+// }
+ }
+ if (playlist_current_ptr) {
+ streamer_reset (0);
+ }
+ if (it) {
+ it->played = 1;
+ it->started_timestamp = time (NULL);
+ pl_item_copy (&str_playing_song, it);
+ }
+ codec_unlock ();
+ if (it) {
+ plug_trigger_event (DB_EV_SONGSTARTED);
+ }
+ messagepump_push (M_SONGCHANGED, 0, from, to);
+ return ret;
+}
+#endif
+
float
streamer_get_playpos (void) {
return playpos;
@@ -64,10 +158,12 @@ streamer_get_playpos (void) {
void
streamer_set_nextsong (int song, int pstate) {
+ trace ("streamer_set_nextsong %d %d\n", song, pstate);
nextsong = song;
nextsong_pstate = pstate;
if (p_isstopped ()) {
// no sense to wait until end of previous song, reset buffer
+ trace ("fuck!\n");
bytes_until_next_song = 0;
}
}
@@ -88,7 +184,8 @@ streamer_thread (uintptr_t ctx) {
while (!streaming_terminate) {
struct timeval tm1;
gettimeofday (&tm1, NULL);
- if (nextsong >= 0 && bytes_until_next_song == 0) {
+ if (nextsong >= 0) { // start streaming next song
+ trace ("nextsong=%d\n", nextsong);
int sng = nextsong;
int pstate = nextsong_pstate;
nextsong = -1;
@@ -96,14 +193,14 @@ streamer_thread (uintptr_t ctx) {
codecleft = 0;
codec_unlock ();
if (badsong == sng) {
- //printf ("looped to bad file. stopping...\n");
+ trace ("looped to bad file. stopping...\n");
streamer_set_nextsong (-2, 1);
badsong = -1;
continue;
}
- int ret = pl_set_current (pl_get_for_idx (sng));
+ int ret = streamer_set_current (pl_get_for_idx (sng));
if (ret < 0) {
- //printf ("bad file in playlist, skipping...\n");
+ trace ("bad file in playlist, skipping...\n");
// remember bad song number in case of looping
if (badsong == -1) {
badsong = sng;
@@ -114,7 +211,6 @@ streamer_thread (uintptr_t ctx) {
continue;
}
badsong = -1;
- playpos = 0;
if (pstate == 0) {
p_stop ();
}
@@ -126,10 +222,11 @@ streamer_thread (uintptr_t ctx) {
}
}
else if (nextsong == -2) {
+ trace ("nextsong=-2\n");
nextsong = -1;
p_stop ();
messagepump_push (M_SONGCHANGED, 0, pl_get_idx_of (playlist_current_ptr), -1);
- pl_set_current (NULL);
+ streamer_set_current (NULL);
continue;
}
else if (p_isstopped ()) {
@@ -137,12 +234,46 @@ streamer_thread (uintptr_t ctx) {
continue;
}
+ if (bytes_until_next_song == 0) {
+ trace ("bytes_until_next_song=0, starting playback of new song\n");
+ bytes_until_next_song = -1;
+ // plugin will get pointer to str_playing_song
+ trace ("sending songfinished to plugins\n");
+ plug_trigger_event (DB_EV_SONGFINISHED);
+ // copy streaming into playing
+ pl_item_copy (&str_playing_song, &str_streaming_song);
+ int from = orig_playing_song ? pl_get_idx_of (orig_playing_song) : -1;
+ int to = orig_streaming_song ? pl_get_idx_of (orig_streaming_song) : -1;
+ trace ("from=%d, to=%d\n", from, to);
+ orig_playing_song = orig_streaming_song;
+ str_playing_song.played = 1;
+ str_playing_song.started_timestamp = time (NULL);
+ playlist_current_ptr = orig_playing_song;
+ // that is needed for playlist drawing
+ trace ("sending songchanged\n");
+ messagepump_push (M_SONGCHANGED, 0, from, to);
+ // plugin will get pointer to new str_playing_song
+ trace ("sending songstarted to plugins\n");
+ plug_trigger_event (DB_EV_SONGSTARTED);
+ playpos = 0;
+ }
+
if (seekpos >= 0) {
+ trace ("seeking to %f\n", seekpos);
float pos = seekpos;
seekpos = -1;
- if (playlist_current.decoder && playlist_current.decoder->seek (pos) >= 0) {
+
+ if (orig_playing_song != orig_streaming_song) {
+ // restart playing from new position
+ pl_item_free (&str_streaming_song);
+ orig_streaming_song = orig_playing_song;
+ pl_item_copy (&str_streaming_song, orig_streaming_song);
+ bytes_until_next_song = -1;
+ }
+
+ if (str_playing_song.decoder && str_playing_song.decoder->seek (pos) >= 0) {
streamer_lock ();
- playpos = playlist_current.decoder->info.readpos;
+ playpos = str_playing_song.decoder->info.readpos;
streambuffer_fill = 0;
streamer_unlock ();
codec_lock ();
@@ -156,7 +287,7 @@ streamer_thread (uintptr_t ctx) {
int alloc_time = 1000 / (96000 * 4 / 4096);
streamer_lock ();
- if (streambuffer_fill < (STREAM_BUFFER_SIZE-4096) && bytes_until_next_song == 0) {
+ if (streambuffer_fill < (STREAM_BUFFER_SIZE-4096)/* && bytes_until_next_song == 0*/) {
int sz = STREAM_BUFFER_SIZE - streambuffer_fill;
int minsize = 4096;
if (streambuffer_fill < 16384) {
@@ -178,6 +309,7 @@ streamer_thread (uintptr_t ctx) {
if (alloc_time > 0) {
usleep (alloc_time * 1000);
}
+// trace ("fill: %d/%d\n", streambuffer_fill, STREAM_BUFFER_SIZE);
}
if (src) {
@@ -222,8 +354,9 @@ streamer_read_async (char *bytes, int size) {
for (;;) {
int bytesread = 0;
codec_lock ();
- DB_decoder_t *decoder = playlist_current.decoder;
+ DB_decoder_t *decoder = str_streaming_song.decoder;
if (!decoder) {
+ trace ("no decoder!\n");
codec_unlock ();
break;
}
@@ -356,7 +489,8 @@ streamer_read_async (char *bytes, int size) {
}
else {
// that means EOF
- if (bytes_until_next_song == 0) {
+ if (bytes_until_next_song < 0) {
+ trace ("finished streaming song, queueing next\n");
bytes_until_next_song = streambuffer_fill;
pl_nextsong (0);
}
@@ -388,13 +522,16 @@ streamer_read (char *bytes, int size) {
}
streambuffer_fill -= sz;
playpos += (float)sz/p_get_rate ()/4.f;
- playlist_current.playtime += (float)sz/p_get_rate ()/4.f;
+ str_streaming_song.playtime += (float)sz/p_get_rate ()/4.f;
if (playlist_current_ptr) {
- playlist_current_ptr->playtime = playlist_current.playtime;
+ playlist_current_ptr->playtime = str_streaming_song.playtime;
}
- bytes_until_next_song -= sz;
- if (bytes_until_next_song < 0) {
- bytes_until_next_song = 0;
+ if (bytes_until_next_song > 0) {
+ bytes_until_next_song -= sz;
+ if (bytes_until_next_song < 0) {
+ bytes_until_next_song = 0;
+ }
+ trace ("buns: %d\n", bytes_until_next_song);
}
}
streamer_unlock ();
diff --git a/streamer.h b/streamer.h
index 7c0a987a..80668f7d 100644
--- a/streamer.h
+++ b/streamer.h
@@ -18,6 +18,11 @@
#ifndef __STREAMER_H
#define __STREAMER_H
+#include "playlist.h"
+
+extern playItem_t str_playing_song;
+extern playItem_t str_streaming_song;
+
int
streamer_init (void);