From a2aee035072a5d346c187a890539f72c6d5167a0 Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Thu, 5 Feb 2009 18:35:21 +0100 Subject: Time display for mp3 playback Time is now counted in milliseconds, not aac frames. Calculations seem to be inaccurate sometimes (about +-2 seconds). --- src/main.c | 31 +++++++++++------------------ src/player.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/player.h | 10 +++++++--- 3 files changed, 79 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/main.c b/src/main.c index 33ece10..c08fc57 100644 --- a/src/main.c +++ b/src/main.c @@ -45,11 +45,6 @@ THE SOFTWARE. #include "config.h" #include "ui.h" -inline float BarSamplesToSeconds (float samplerate, float channels, - float samples) { - return channels * 1000.0 * samples / samplerate; -} - int main (int argc, char **argv) { PianoHandle_t ph; static struct audioPlayer player; @@ -128,13 +123,11 @@ int main (int argc, char **argv) { while (!doQuit) { /* already played a song, clean up things/scrobble song */ if (player.mode == PLAYER_FINISHED_PLAYBACK) { - scrobbleSong.length = BarSamplesToSeconds (player.samplerate, - player.channels, player.sampleSizeN); - /* scrobble when >= nn% are played */ - if (BarSamplesToSeconds (player.samplerate, - player.channels, player.sampleSizeCurr) * 100 / - scrobbleSong.length >= - settings.lastfmScrobblePercent && + scrobbleSong.length = player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR; + /* scrobble when >= nn% are played; use seconds, not + * milliseconds */ + if (player.songPlayed / BAR_PLAYER_MS_TO_S_FACTOR * 100 / + scrobbleSong.length >= settings.lastfmScrobblePercent && settings.enableScrobbling) { WardrobeReturn_t wRet; @@ -224,14 +217,12 @@ int main (int argc, char **argv) { /* show time */ if (player.mode >= PLAYER_SAMPLESIZE_INITIALIZED && player.mode < PLAYER_FINISHED_PLAYBACK) { - float songLength = BarSamplesToSeconds (player.samplerate, - player.channels, player.sampleSizeN); - float songRemaining = songLength - - BarSamplesToSeconds (player.samplerate, - player.channels, player.sampleSizeCurr); - printf ("-%02i:%02i/%02i:%02i\r", (int) songRemaining/60, - (int) songRemaining%60, (int) songLength/60, - (int) songLength%60); + long int songRemaining = player.songDuration - player.songPlayed; + printf ("-%02i:%02i/%02i:%02i\r", + (int) songRemaining / BAR_PLAYER_MS_TO_S_FACTOR / 60, + (int) songRemaining / BAR_PLAYER_MS_TO_S_FACTOR % 60, + (int) player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR / 60, + (int) player.songDuration / BAR_PLAYER_MS_TO_S_FACTOR % 60); fflush (stdout); } } diff --git a/src/player.c b/src/player.c index 4266e44..e6edf2e 100644 --- a/src/player.c +++ b/src/player.c @@ -137,6 +137,10 @@ size_t BarPlayerAACCurlCb (void *ptr, size_t size, size_t nmemb, void *stream) { /* ao_play needs bytes: 1 sample = 16 bits = 2 bytes */ ao_play (player->audioOutDevice, (char *) aacDecoded, frameInfo.samples * 2); + /* add played frame length to played time, explained below */ + player->songPlayed += (float) frameInfo.samples * + (float) BAR_PLAYER_MS_TO_S_FACTOR / + (float) player->samplerate / (float) player->channels; player->bufferRead += frameInfo.bytesconsumed; player->sampleSizeCurr++; /* going through this loop can take up to a few seconds => @@ -214,6 +218,16 @@ size_t BarPlayerAACCurlCb (void *ptr, size_t size, size_t nmemb, void *stream) { sizeof (player->sampleSizeN)); player->bufferRead += 4; player->sampleSizeCurr = 0; + /* set up song duration (assuming one frame always contains + * the same number of samples); numbers are too huge => use + * float + * calculation: channels * number of frames * samples per + * frame / samplerate */ + /* FIXME: Hard-coded number of samples per frame */ + player->songDuration = (float) player->sampleSizeN * + 4096.0 * (float) BAR_PLAYER_MS_TO_S_FACTOR / + (float) player->samplerate / + (float) player->channels; break; } else { player->sampleSize[player->sampleSizeCurr] = @@ -297,7 +311,6 @@ size_t BarPlayerMp3CurlCb (void *ptr, size_t size, size_t nmemb, void *stream) { break; } } - mad_timer_add (&player->mp3Timer, player->mp3Frame.header.duration); mad_synth_frame (&player->mp3Synth, &player->mp3Frame); for (i = 0; i < player->mp3Synth.pcm.length; i++) { /* left channel */ @@ -321,12 +334,20 @@ size_t BarPlayerMp3CurlCb (void *ptr, size_t size, size_t nmemb, void *stream) { format.byte_format = AO_FMT_LITTLE; player->audioOutDevice = ao_open_live (audioOutDriver, &format, NULL); - player->mode = PLAYER_AUDIO_INITIALIZED; + /* must be > PLAYER_SAMPLESIZE_INITIALIZED, otherwise time won't + * be visible to user (ugly, but mp3 decoding != aac decoding) */ + player->mode = PLAYER_RECV_DATA; } /* samples * length * channels */ ao_play (player->audioOutDevice, (char *) madDecoded, player->mp3Synth.pcm.length * 2 * 2); + /* same calculation as in aac player; don't need to divide by channels, + * length is number of samples for _one_ channel */ + player->songPlayed += (float) player->mp3Synth.pcm.length * + (float) BAR_PLAYER_MS_TO_S_FACTOR / + (float) player->samplerate; + QUIT_PAUSE_CHECK; } while (player->mp3Stream.error != MAD_ERROR_BUFLEN); @@ -336,6 +357,40 @@ size_t BarPlayerMp3CurlCb (void *ptr, size_t size, size_t nmemb, void *stream) { return size*nmemb; } + +/* get file length; needed for mp3 only atm + * @param header line + * @param size of _one_ element + * @param number of elements + * @param optional data (player in this case) + * @return read data size or -1 + */ +size_t BarPlayerParseHeader (void *ptr, size_t size, size_t nmemb, + void *stream) { + char *data = ptr; + struct audioPlayer *player = stream; + char lengthBuffer[20]; + const char identString[] = "Content-Length: "; + size_t identStringLength = strlen (identString); + size_t contentLength = 0; + + /* avoid buffer overflow */ + if (size*nmemb > identStringLength && + size*nmemb - identStringLength < sizeof (lengthBuffer) - 1) { + if (memcmp (data, identString, identStringLength) == 0) { + memset (lengthBuffer, 0, sizeof (lengthBuffer)); + memcpy (lengthBuffer, data+identStringLength, size*nmemb - + identStringLength); + contentLength = atol (lengthBuffer); + /* force floating point division to avoid overflows; + * calculation: file size / (kbit/s * kilo / bits per byte) */ + /* FIXME: hardcoded kbits/s */ + player->songDuration = (float) contentLength / (128.0 * 1000.0 / + (float) BAR_PLAYER_MS_TO_S_FACTOR / 8.0); + } + } + return size*nmemb; +} #endif /* ENABLE_MAD */ /* player thread; for every song a new thread is started @@ -373,10 +428,13 @@ void *BarPlayerThread (void *data) { mad_stream_init (&player->mp3Stream); mad_frame_init (&player->mp3Frame); mad_synth_init (&player->mp3Synth); - mad_timer_reset (&player->mp3Timer); curl_easy_setopt (player->audioFd, CURLOPT_WRITEFUNCTION, BarPlayerMp3CurlCb); + curl_easy_setopt (player->audioFd, CURLOPT_HEADERFUNCTION, + BarPlayerParseHeader); + curl_easy_setopt (player->audioFd, CURLOPT_WRITEHEADER, + (void *) player); break; #endif /* ENABLE_MAD */ diff --git a/src/player.h b/src/player.h index eab03a9..ec77f16 100644 --- a/src/player.h +++ b/src/player.h @@ -40,6 +40,8 @@ THE SOFTWARE. #include #include +#define BAR_PLAYER_MS_TO_S_FACTOR 1000 + struct audioPlayer { /* buffer; should be large enough */ unsigned char buffer[CURL_MAX_WRITE_SIZE*2]; @@ -54,14 +56,17 @@ struct audioPlayer { PianoAudioFormat_t audioFormat; - size_t sampleSizeN; - size_t sampleSizeCurr; + /* duration and already played time; measured in milliseconds */ + unsigned long int songDuration; + unsigned long int songPlayed; /* aac */ #ifdef ENABLE_FAAD NeAACDecHandle aacHandle; /* stsz atom: sample sizes */ unsigned int *sampleSize; + size_t sampleSizeN; + size_t sampleSizeCurr; #endif /* mp3 */ @@ -69,7 +74,6 @@ struct audioPlayer { struct mad_stream mp3Stream; struct mad_frame mp3Frame; struct mad_synth mp3Synth; - mad_timer_t mp3Timer; #endif unsigned long samplerate; -- cgit v1.2.3