summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-07-08 21:30:31 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2009-07-08 22:50:46 +0200
commit425f2d8d2b252e027593808a8e297193e2c9f7de (patch)
treee848ed02ac5f68839124ffb4144dbfad31bd4be6
parentbb6da46236137f12be28147f93ea9654c5ccc125 (diff)
[WIP] seamless playback
-rw-r--r--cmp3.c35
-rw-r--r--cvorbis.c21
-rw-r--r--gtkplaylist.c55
-rw-r--r--main.c1
-rw-r--r--playlist.c57
-rw-r--r--playlist.h9
-rw-r--r--psdl.c34
-rw-r--r--psdl.h7
-rw-r--r--streamer.c156
-rw-r--r--streamer.h3
10 files changed, 209 insertions, 169 deletions
diff --git a/cmp3.c b/cmp3.c
index 0d2ceb21..c9a5fd07 100644
--- a/cmp3.c
+++ b/cmp3.c
@@ -32,7 +32,6 @@ static struct mad_stream stream;
static struct mad_frame frame;
static struct mad_synth synth;
static mad_timer_t timer;
-static int endoffile;
static int
cmp3_decode (void);
@@ -54,7 +53,6 @@ cmp3_init (const char *fname, int track, float start, float end) {
buffer.output = NULL;
buffer.readsize = 0;
buffer.cachefill = 0;
- endoffile = 0;
cmp3.info.position = 0;
mad_stream_init(&stream);
mad_frame_init(&frame);
@@ -313,7 +311,11 @@ cmp3_skip2 (float seconds) {
static int
cmp3_decode (void) {
int nread = 0;
+ int eof = 0;
for (;;) {
+ if (eof) {
+ break;
+ }
// read more MPEG data if needed
if(stream.buffer==NULL || stream.error==MAD_ERROR_BUFLEN) {
// copy part of last frame to beginning
@@ -324,22 +326,20 @@ cmp3_decode (void) {
int size = READBUFFER - buffer.remaining;
int bytesread = 0;
char *bytes = buffer.input + buffer.remaining;
- if (!endoffile) {
- bytesread = fread (bytes, 1, size, buffer.file);
- if (bytesread < size) {
- // end of file
- endoffile = 1;
- size -= bytesread;
- bytes += bytesread;
- }
+ bytesread = fread (bytes, 1, size, buffer.file);
+ if (!bytesread) {
+ // add guard
+ eof = 1;
+ memset (bytes, 0, 8);
+ bytesread = 8;
}
- bytesread += buffer.remaining;
- if (bytesread) {
- mad_stream_buffer(&stream,buffer.input,bytesread);
- }
- else {
- return -1;
+ if (bytesread < size) {
+ // end of file
+ size -= bytesread;
+ bytes += bytesread;
}
+ bytesread += buffer.remaining;
+ mad_stream_buffer(&stream,buffer.input,bytesread);
stream.error=0;
}
// decode next frame
@@ -408,7 +408,7 @@ cmp3_decode (void) {
break;
}
}
- if (buffer.readsize == 0) {
+ if (buffer.readsize == 0 || eof) {
break;
}
// if (buffer.readsize > 0 && endoffile) {
@@ -467,7 +467,6 @@ cmp3_seek (float time) {
mad_synth_finish (&synth);
mad_frame_finish (&frame);
mad_stream_finish (&stream);
- endoffile = 0;
fseek(buffer.file, 0, SEEK_SET);
mad_stream_init(&stream);
mad_frame_init(&frame);
diff --git a/cvorbis.c b/cvorbis.c
index f8b4e94e..8b3acb6e 100644
--- a/cvorbis.c
+++ b/cvorbis.c
@@ -39,7 +39,7 @@ void
cvorbis_free (void) {
if (file) {
ov_clear (&vorbis_file);
- //fclose (file); -- ov_clear closes it
+ //fclose (file); //-- ov_clear closes it
file = NULL;
vi = NULL;
}
@@ -49,37 +49,32 @@ int
cvorbis_read (char *bytes, int size)
{
if (!file)
- return -1;
+ return 0;
+ int initsize = size;
for (;;)
{
// read ogg
long ret=ov_read (&vorbis_file, bytes, size, 0, 2, 1, &cur_bit_stream);
if (ret < 0)
{
- printf ("WARNING: ogg vorbis decoder tells error %x\n", ret);
- memset (bytes, 0, size);
- return -1;
+ break;
}
else if (ret == 0) {
- if (size > 0) {
- memset (bytes, 0, size);
- }
- return -1;
+ break;
}
else if (ret < size)
{
size -= ret;
bytes += ret;
-// if (ret == 0) {
-// ov_raw_seek (&vorbis_file, 0);
-// }
}
else {
+ size = 0;
break;
}
}
cvorbis.info.position = ov_time_tell(&vorbis_file);
- return 0;
+
+ return initsize - size;
}
int
diff --git a/gtkplaylist.c b/gtkplaylist.c
index 143e4353..cebf1df2 100644
--- a/gtkplaylist.c
+++ b/gtkplaylist.c
@@ -253,10 +253,7 @@ gtkps_playsong (void) {
}
else if (playlist_current_ptr) {
printf ("restart\n");
- codec_lock ();
- psdl_stop ();
- psdl_play (playlist_current_ptr);
- codec_unlock ();
+ ps_start_current ();
GtkWidget *widget = lookup_widget (mainwin, "playlist");
redraw_ps_row (widget, ps_get_idx_of (playlist_current_ptr));
}
@@ -264,11 +261,7 @@ gtkps_playsong (void) {
printf ("start under cursor\n");
playItem_t *it = ps_get_for_idx (playlist_row);
if (it) {
- codec_lock ();
ps_set_current (it);
- psdl_stop ();
- psdl_play (it);
- codec_unlock ();
}
GtkWidget *widget = lookup_widget (mainwin, "playlist");
redraw_ps_row (widget, playlist_row);
@@ -277,10 +270,6 @@ gtkps_playsong (void) {
printf ("play 1st in list\n");
ps_set_current (playlist_head);
if (playlist_current_ptr) {
- codec_lock ();
- psdl_stop ();
- psdl_play (playlist_current_ptr);
- codec_unlock ();
GtkWidget *widget = lookup_widget (mainwin, "playlist");
redraw_ps_row (widget, ps_get_idx_of (playlist_current_ptr));
}
@@ -293,15 +282,13 @@ gtkps_prevsong (void) {
playItem_t *prev = playlist_current_ptr;
if (playlist_current_ptr) {
+ printf ("gtkps_prevsong\n");
ps_set_current (playlist_current_ptr->prev);
}
if (!playlist_current_ptr) {
+ printf ("gtkps_prevsong2\n");
ps_set_current (playlist_tail);
}
- if (playlist_current_ptr) {
- psdl_stop ();
- psdl_play (playlist_current_ptr);
- }
if (playlist_current_ptr != prev) {
if (prev) {
redraw_ps_row (widget, ps_get_idx_of (prev));
@@ -317,17 +304,13 @@ gtkps_nextsong (void) {
GtkWidget *widget = lookup_widget (mainwin, "playlist");
playItem_t *prev = playlist_current_ptr;
if (playlist_current_ptr) {
+ printf ("gtkps_nextsong\n");
ps_set_current (playlist_current_ptr->next);
}
if (!playlist_current_ptr) {
+ printf ("gtkps_nextsong2\n");
ps_set_current (playlist_head);
}
- if (playlist_current_ptr) {
- codec_lock ();
- psdl_stop ();
- psdl_play (playlist_current_ptr);
- codec_unlock ();
- }
if (playlist_current_ptr != prev) {
if (prev) {
redraw_ps_row (widget, ps_get_idx_of (prev));
@@ -348,17 +331,13 @@ gtkps_randomsong (void) {
int r = rand () % ps_getcount ();
playItem_t *it = ps_get_for_idx (r);
if (it) {
+ printf ("gtkps_randomsong\n");
ps_set_current (it);
}
else {
+ printf ("gtkps_randomsong2\n");
ps_set_current (NULL);
}
- if (playlist_current_ptr) {
- codec_lock ();
- psdl_stop ();
- psdl_play (playlist_current_ptr);
- codec_unlock ();
- }
if (playlist_current_ptr != prev) {
if (prev) {
redraw_ps_row (widget, ps_get_idx_of (prev));
@@ -394,16 +373,13 @@ gtkps_playsongnum (int idx) {
if (playlist_current_ptr) {
prev = ps_get_idx_of (playlist_current_ptr);
}
+ printf ("gtkps_playsongnum\n");
ps_set_current (it);
if (prev != -1) {
redraw_ps_row (widget, prev);
}
redraw_ps_row (widget, idx);
}
- codec_lock ();
- psdl_stop ();
- psdl_play (playlist_current_ptr);
- codec_unlock ();
}
}
@@ -428,21 +404,28 @@ gtkps_update_songinfo (void) {
gtk_statusbar_pop (sb, sb_context_id);
gtk_statusbar_push (sb, sb_context_id, "Paused");
}
- else if (psdl_getcodec ()) {
- codec_t *c = psdl_getcodec ();
+ else if (playlist_current.codec) {
+ codec_lock ();
+ codec_t *c = playlist_current.codec;
int minpos = c->info.position / 60;
int secpos = c->info.position - minpos * 60;
int mindur = c->info.duration / 60;
int secdur = c->info.duration - mindur * 60;
+ const char *mode = c->info.channels == 1 ? "Mono" : "Stereo";
+ int samplerate = c->info.samplesPerSecond;
+ int bitspersample = c->info.bitsPerSample;
+ float pos = c->info.position;
+ int dur = c->info.duration;
+ codec_unlock ();
char str[1024];
- snprintf (str, 1024, "%dHz | %d bit | %s | Position %d:%02d | Duration %d:%02d", c->info.samplesPerSecond, c->info.bitsPerSample, c->info.channels == 1 ? "Mono" : "Stereo", minpos, secpos, mindur, secdur);
+ snprintf (str, 1024, "%dHz | %d bit | %s | Position %d:%02d | Duration %d:%02d", samplerate, bitspersample, mode, minpos, secpos, mindur, secdur);
gtk_statusbar_pop (sb, sb_context_id);
gtk_statusbar_push (sb, sb_context_id, str);
extern int g_disable_seekbar_handler;
g_disable_seekbar_handler = 1;
GtkRange *seekbar = GTK_RANGE (lookup_widget (mainwin, "playpos"));
- gtk_range_set_value (seekbar, c->info.position * 1000 / c->info.duration);
+ gtk_range_set_value (seekbar, pos * 1000 / dur);
g_disable_seekbar_handler = 0;
}
else {
diff --git a/main.c b/main.c
index a18dfd11..1508f5f1 100644
--- a/main.c
+++ b/main.c
@@ -20,6 +20,7 @@ void
psdl_thread (uintptr_t ctx) {
codec_init_locking ();
psdl_init ();
+ psdl_play ();
while (!psdl_terminate) {
uint32_t msg;
uintptr_t ctx;
diff --git a/playlist.c b/playlist.c
index e101fedf..cf000eaf 100644
--- a/playlist.c
+++ b/playlist.c
@@ -402,10 +402,67 @@ ps_item_free (playItem_t *it) {
void
ps_set_current (playItem_t *it) {
+ if (it) {
+ printf ("ps_set_current (%s)\n", it->displayname);
+ }
+ if (it == playlist_current_ptr) {
+ if (it && it->codec) {
+ codec_lock ();
+ playlist_current_ptr->codec->seek (0);
+ codec_unlock ();
+ }
+ return;
+ }
+ codec_lock ();
+ if (playlist_current_ptr) {
+ playlist_current_ptr->codec->free ();
+ }
ps_item_free (&playlist_current);
if (it) {
ps_item_copy (&playlist_current, it);
}
playlist_current_ptr = it;
+ if (it && it->codec) {
+ // don't do anything on fail, streamer will take care
+ it->codec->init (it->fname, it->tracknum, it->timestart, it->timeend);
+ }
+ if (playlist_current_ptr) {
+ streamer_reset ();
+ }
+ codec_unlock ();
}
+int
+ps_nextsong (void) {
+ if (playlist_current_ptr && playlist_current_ptr->next) {
+ ps_set_current (playlist_current_ptr->next);
+ return 0;
+ }
+ if (playlist_head) {
+ ps_set_current (playlist_head);
+ return 0;
+ }
+ ps_set_current (NULL);
+ return -1;
+}
+
+playItem_t *
+ps_getnext (void) {
+ if (playlist_current_ptr && playlist_current_ptr->next) {
+ return playlist_current_ptr->next;
+ }
+ if (playlist_head) {
+ return playlist_head;
+ }
+ return NULL;
+}
+
+void
+ps_start_current (void) {
+ playItem_t *it = playlist_current_ptr;
+ if (it && it->codec) {
+ // don't do anything on fail, streamer will take care
+ it->codec->free ();
+ it->codec->init (it->fname, it->tracknum, it->timestart, it->timeend);
+ }
+}
diff --git a/playlist.h b/playlist.h
index a46b82f7..b77b58b2 100644
--- a/playlist.h
+++ b/playlist.h
@@ -47,4 +47,13 @@ ps_add_cue (const char *cuename);
void
ps_set_current (playItem_t *it);
+// returns -1 if theres no next song, or playlist finished
+int
+ps_nextsong (void);
+
+// starts current playlist item from position 0
+// only if the item is still in playlist
+void
+ps_start_current (void);
+
#endif // __PLAYLIST_H
diff --git a/psdl.c b/psdl.c
index 23a38474..50352cd1 100644
--- a/psdl.c
+++ b/psdl.c
@@ -1,24 +1,18 @@
#include <SDL/SDL.h>
-#include <samplerate.h>
#include "psdl.h"
-#include "codec.h"
-#include "playlist.h"
-#include "messagepump.h"
-#include "messages.h"
#include "streamer.h"
static int sdl_player_numsamples = 4096;
int sdl_player_freq;
static SDL_AudioSpec spec;
static void psdl_callback (void *userdata, Uint8 *stream, int len);
-static codec_t *codec;
static float sdl_volume = 1;
int
psdl_init (void) {
SDL_AudioSpec obt;
int formats[] = { AUDIO_S16, -1 };
- int freqs[] = { 48000, 44100, -1 };
+ int freqs[] = { 22050, 48000, 44100, -1 };
const char *fmtnames[] = { "16 bit signed integer" };
int fmt, frq;
int success = 0;
@@ -60,14 +54,7 @@ psdl_free (void) {
}
int
-psdl_play (struct playItem_s *it) {
- if (!it) {
- return -1;
- }
- codec = it->codec;
- if (codec->init (it->fname, it->tracknum, it->timestart, it->timeend)) {
- return -1;
- }
+psdl_play (void) {
SDL_PauseAudio (0);
return 0;
}
@@ -75,17 +62,10 @@ psdl_play (struct playItem_s *it) {
int
psdl_stop (void) {
SDL_PauseAudio (1);
- if (codec) {
- codec->free ();
- codec = NULL;
- }
}
int
psdl_ispaused (void) {
- if (!codec) {
- return 0;
- }
return (SDL_GetAudioStatus () == SDL_AUDIO_PAUSED);
}
@@ -110,16 +90,12 @@ static void
psdl_callback (void* userdata, Uint8 *stream, int len) {
int bytesread = streamer_read (stream, len);
int ivolume = sdl_volume * 1000;
- if (bytesread < len) {
- memset (stream + bytesread, 0, len-bytesread);
- }
for (int i = 0; i < bytesread/2; i++) {
int16_t sample = (int16_t)(((int32_t)(((int16_t*)stream)[i])) * ivolume / 1000);
((int16_t*)stream)[i] = sample;
}
+ if (bytesread < len) {
+ memset (stream + bytesread, 0, len-bytesread);
+ }
}
-struct codec_s*
-psdl_getcodec (void) {
- return codec;
-}
diff --git a/psdl.h b/psdl.h
index 15be1389..d0dfd088 100644
--- a/psdl.h
+++ b/psdl.h
@@ -1,8 +1,6 @@
#ifndef __PSDL_H
#define __PSDL_H
-struct playItem_s;
-
int
psdl_init (void);
@@ -10,7 +8,7 @@ void
psdl_free (void);
int
-psdl_play (struct playItem_s *it);
+psdl_play (void);
int
psdl_stop (void);
@@ -27,7 +25,4 @@ psdl_unpause (void);
void
psdl_set_volume (float vol);
-struct codec_s*
-psdl_getcodec (void);
-
#endif // __PSDL_H
diff --git a/streamer.c b/streamer.c
index 1c72775f..7023c5a7 100644
--- a/streamer.c
+++ b/streamer.c
@@ -10,9 +10,9 @@ extern int sdl_player_freq; // hack!
static SRC_STATE *src;
static SRC_DATA srcdata;
static int codecleft;
-static char g_readbuffer[20000]; // hack!
-static float g_fbuffer[20000]; // hack!
-static float g_srcbuffer[20000]; // hack!
+static char g_readbuffer[200000]; // hack!
+static float g_fbuffer[200000]; // hack!
+static float g_srcbuffer[200000]; // hack!
int
streamer_init (void) {
@@ -33,80 +33,102 @@ streamer_free (void) {
}
}
+void
+streamer_reset (void) { // must be called when current song changes by external reasons
+ codecleft = 0;
+ src_reset (src);
+}
+
// returns number of bytes been read
int
streamer_read (char *bytes, int size) {
- codec_t *codec = playlist_current.codec;
- int bytesread = 0;
- if (!codec) {
- return 0;
- }
- // read and do SRC
- if (codec->info.samplesPerSecond == sdl_player_freq) {
- int i;
- if (codec->info.channels == 2) {
- codec_lock ();
- bytesread = codec->read (bytes, size);
+ int initsize = size;
+ for (;;) {
+ int bytesread = 0;
+ codec_lock ();
+ codec_t *codec = playlist_current.codec;
+ if (!codec) {
codec_unlock ();
+ break;
}
- else {
- codec_lock ();
- bytesread = codec->read (g_readbuffer, size/2);
- for (i = 0; i < size/4; i++) {
- int16_t sample = (int16_t)(((int32_t)(((int16_t*)g_readbuffer)[i])));
- ((int16_t*)bytes)[i*2+0] = sample;
- ((int16_t*)bytes)[i*2+1] = sample;
+ int nchannels = codec->info.channels;
+ int samplerate = codec->info.samplesPerSecond;
+ // read and do SRC
+ if (codec->info.samplesPerSecond == sdl_player_freq) {
+ int i;
+ if (codec->info.channels == 2) {
+ bytesread = codec->read (bytes, size);
+ codec_unlock ();
}
- bytesread *= 2;
- codec_unlock ();
- }
- }
- else {
- int nsamples = size/4;
- // convert to codec samplerate
- // add 5 extra samples for SRC
- nsamples = nsamples * codec->info.samplesPerSecond / sdl_player_freq + 5;
- // read data at source samplerate (with some room for SRC)
- codec_lock ();
- int nbytes = (nsamples - codecleft) * 2 * codec->info.channels;
- bytesread = codec->read (g_readbuffer, nbytes);
- // recalculate nsamples according to how many bytes we've got
- nsamples -= (nbytes - bytesread) / (2 * codec->info.channels);
- // convert to float, and apply soft volume
- int i;
- float *fbuffer = g_fbuffer + codecleft*2;
- if (codec->info.channels == 2) { // convert mono to stereo
- for (i = 0; i < (nsamples - codecleft) * 2; i++) {
- fbuffer[i] = ((int16_t *)g_readbuffer)[i]/32767.f;
+ else {
+ bytesread = codec->read (g_readbuffer, size/2);
+ codec_unlock ();
+ for (i = 0; i < size/4; i++) {
+ int16_t sample = (int16_t)(((int32_t)(((int16_t*)g_readbuffer)[i])));
+ ((int16_t*)bytes)[i*2+0] = sample;
+ ((int16_t*)bytes)[i*2+1] = sample;
+ }
+ bytesread *= 2;
}
}
- else if (codec->info.channels == 1) {
- for (i = 0; i < (nsamples - codecleft); i++) {
- fbuffer[i*2+0] = ((int16_t *)g_readbuffer)[i];
- fbuffer[i*2+1] = fbuffer[i*2+0];
+ else {
+ int nsamples = size/4;
+ // convert to codec samplerate
+ nsamples = nsamples * samplerate / sdl_player_freq * 2 ;
+ // read data at source samplerate (with some room for SRC)
+ int nbytes = (nsamples - codecleft) * 2 * nchannels;
+ bytesread = codec->read (g_readbuffer, nbytes);
+ codec_unlock ();
+ // recalculate nsamples according to how many bytes we've got
+ nsamples = bytesread / (2 * nchannels) + codecleft;
+ // convert to float
+ int i;
+ float *fbuffer = g_fbuffer + codecleft*2;
+ if (nchannels == 2) {
+ for (i = 0; i < (nsamples - codecleft) * 2; i++) {
+ fbuffer[i] = ((int16_t *)g_readbuffer)[i]/32767.f;
+ }
+ }
+ else if (nchannels == 1) { // convert mono to stereo
+ for (i = 0; i < (nsamples - codecleft); i++) {
+ fbuffer[i*2+0] = ((int16_t *)g_readbuffer)[i]/32767.f;
+ fbuffer[i*2+1] = fbuffer[i*2+0];
+ }
+ }
+ // convert samplerate
+ srcdata.data_in = g_fbuffer;
+ srcdata.data_out = g_srcbuffer;
+ srcdata.input_frames = nsamples;
+ srcdata.output_frames = size/4;
+ srcdata.src_ratio = (double)sdl_player_freq/samplerate;
+ srcdata.end_of_input = 0;
+// src_set_ratio (src, srcdata.src_ratio);
+ src_process (src, &srcdata);
+ //printf ("processed %d/%d samples (input=%d)\n", srcdata.output_frames_gen, srcdata.output_frames, srcdata.input_frames);
+ // convert back to s16 format
+ nbytes = size;
+ int genbytes = srcdata.output_frames_gen * 4;
+ bytesread = min(size, genbytes);
+ for (i = 0; i < bytesread/2; i++) {
+ ((int16_t*)bytes)[i] = (int16_t)(g_srcbuffer[i]*32767.f);
}
+ // calculate how many unused input samples left
+ codecleft = nsamples - srcdata.input_frames_used;
+ // copy spare samples for next update
+ memmove (g_fbuffer, &g_fbuffer[srcdata.input_frames_used*2], codecleft * 8);
}
- // convert samplerate
- srcdata.data_in = g_fbuffer;
- srcdata.data_out = g_srcbuffer;
- srcdata.input_frames = nsamples;
- srcdata.output_frames = size/4;
- srcdata.src_ratio = (double)sdl_player_freq/codec->info.samplesPerSecond;
- srcdata.end_of_input = 0;
- src_process (src, &srcdata);
- // convert back to s16 format
- nbytes = size;
- int genbytes = srcdata.output_frames_gen * 4;
- bytesread = min(size, genbytes);
-
- for (i = 0; i < bytesread/2; i++) {
- ((int16_t*)bytes)[i] = (int16_t)(g_srcbuffer[i]*32767.f);
+ bytes += bytesread;
+ size -= bytesread;
+ if (size == 0) {
+ return initsize;
+ }
+ else {
+ //printf ("eof (size=%d)\n", size);
+ // that means EOF
+ if (ps_nextsong () < 0) {
+ break;
+ }
}
- // calculate how many unused input samples left
- codecleft = nsamples - srcdata.input_frames_used;
- // copy spare samples for next update
- memmove (fbuffer, &fbuffer[srcdata.input_frames_used*2], codecleft * 8);
- codec_unlock ();
}
- return bytesread;
+ return initsize - size;
}
diff --git a/streamer.h b/streamer.h
index 2add3218..c3cb33d4 100644
--- a/streamer.h
+++ b/streamer.h
@@ -10,4 +10,7 @@ streamer_free (void);
int
streamer_read (char *bytes, int size);
+void
+streamer_reset (void);
+
#endif // __STREAMER_H