diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2009-07-08 21:30:31 +0200 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2009-07-08 22:50:46 +0200 |
commit | 425f2d8d2b252e027593808a8e297193e2c9f7de (patch) | |
tree | e848ed02ac5f68839124ffb4144dbfad31bd4be6 | |
parent | bb6da46236137f12be28147f93ea9654c5ccc125 (diff) |
[WIP] seamless playback
-rw-r--r-- | cmp3.c | 35 | ||||
-rw-r--r-- | cvorbis.c | 21 | ||||
-rw-r--r-- | gtkplaylist.c | 55 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | playlist.c | 57 | ||||
-rw-r--r-- | playlist.h | 9 | ||||
-rw-r--r-- | psdl.c | 34 | ||||
-rw-r--r-- | psdl.h | 7 | ||||
-rw-r--r-- | streamer.c | 156 | ||||
-rw-r--r-- | streamer.h | 3 |
10 files changed, 209 insertions, 169 deletions
@@ -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); @@ -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 { @@ -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; @@ -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); + } +} @@ -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 @@ -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; -} @@ -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 @@ -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; } @@ -10,4 +10,7 @@ streamer_free (void); int streamer_read (char *bytes, int size); +void +streamer_reset (void); + #endif // __STREAMER_H |