diff options
Diffstat (limited to 'src/audio/player.c')
-rw-r--r-- | src/audio/player.c | 97 |
1 files changed, 89 insertions, 8 deletions
diff --git a/src/audio/player.c b/src/audio/player.c index 13c8c58b..c98e2837 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -71,9 +71,13 @@ struct Impl_Decoder { | |||
71 | iArray pendingOutput; | 71 | iArray pendingOutput; |
72 | uint64_t currentSample; | 72 | uint64_t currentSample; |
73 | uint64_t totalSamples; /* zero if unknown */ | 73 | uint64_t totalSamples; /* zero if unknown */ |
74 | iMutex tagMutex; | ||
75 | iString tags[max_PlayerTag]; | ||
74 | stb_vorbis * vorbis; | 76 | stb_vorbis * vorbis; |
75 | #if defined (LAGRANGE_ENABLE_MPG123) | 77 | #if defined (LAGRANGE_ENABLE_MPG123) |
76 | mpg123_handle * mpeg; | 78 | mpg123_handle * mpeg; |
79 | mpg123_id3v1 * id3v1; | ||
80 | mpg123_id3v2 * id3v2; | ||
77 | #endif | 81 | #endif |
78 | }; | 82 | }; |
79 | 83 | ||
@@ -186,6 +190,27 @@ static enum iDecoderStatus decodeVorbis_Decoder_(iDecoder *d) { | |||
186 | } | 190 | } |
187 | d->inputPos += consumed; | 191 | d->inputPos += consumed; |
188 | unlock_Mutex(&d->input->mtx); | 192 | unlock_Mutex(&d->input->mtx); |
193 | /* Check the metadata. */ { | ||
194 | const stb_vorbis_comment com = stb_vorbis_get_comment(d->vorbis); | ||
195 | // printf("vendor: {%s}\n", comment.vendor); | ||
196 | lock_Mutex(&d->tagMutex); | ||
197 | for (int i = 0; i < com.comment_list_length; ++i) { | ||
198 | const char *comStr = com.comment_list[i]; | ||
199 | if (!iCmpStrN(comStr, "ARTIST=", 7)) { | ||
200 | setCStr_String(&d->tags[artist_PlayerTag], comStr + 7); | ||
201 | } | ||
202 | else if (!iCmpStrN(comStr, "DATE=", 5)) { | ||
203 | setCStr_String(&d->tags[date_PlayerTag], comStr + 5); | ||
204 | } | ||
205 | else if (!iCmpStrN(comStr, "TITLE=", 6)) { | ||
206 | setCStr_String(&d->tags[title_PlayerTag], comStr + 6); | ||
207 | } | ||
208 | else if (!iCmpStrN(comStr, "GENRE=", 6)) { | ||
209 | setCStr_String(&d->tags[genre_PlayerTag], comStr + 6); | ||
210 | } | ||
211 | } | ||
212 | unlock_Mutex(&d->tagMutex); | ||
213 | } | ||
189 | } | 214 | } |
190 | if (d->totalSamples == 0 && d->input->isComplete) { | 215 | if (d->totalSamples == 0 && d->input->isComplete) { |
191 | /* Time to check the stream size. */ | 216 | /* Time to check the stream size. */ |
@@ -234,6 +259,12 @@ static enum iDecoderStatus decodeVorbis_Decoder_(iDecoder *d) { | |||
234 | return status; | 259 | return status; |
235 | } | 260 | } |
236 | 261 | ||
262 | #if defined (LAGRANGE_ENABLE_MPG123) | ||
263 | static const char *mpegStr_(const mpg123_string *str) { | ||
264 | return str ? str->p : ""; | ||
265 | } | ||
266 | #endif | ||
267 | |||
237 | enum iDecoderStatus decodeMpeg_Decoder_(iDecoder *d) { | 268 | enum iDecoderStatus decodeMpeg_Decoder_(iDecoder *d) { |
238 | enum iDecoderStatus status = ok_DecoderStatus; | 269 | enum iDecoderStatus status = ok_DecoderStatus; |
239 | #if defined (LAGRANGE_ENABLE_MPG123) | 270 | #if defined (LAGRANGE_ENABLE_MPG123) |
@@ -280,13 +311,24 @@ enum iDecoderStatus decodeMpeg_Decoder_(iDecoder *d) { | |||
280 | break; | 311 | break; |
281 | } | 312 | } |
282 | } | 313 | } |
283 | /* Check if we know the total length already. This info should be available eventually. */ | 314 | if (!d->id3v1 &&!d->id3v2) { |
284 | if (d->totalSamples == 0) { | 315 | mpg123_id3(d->mpeg, &d->id3v1, &d->id3v2); |
285 | const off_t off = mpg123_length(d->mpeg); | 316 | /* TODO: These tags can change during decoding, so checking just once isn't quite right. |
286 | if (off != MPG123_ERR) { | 317 | Shouldn't check every time either, though... */ |
287 | d->totalSamples = off; | 318 | if (d->id3v2) { |
319 | lock_Mutex(&d->tagMutex); | ||
320 | setCStr_String(&d->tags[title_PlayerTag], mpegStr_(d->id3v2->title)); | ||
321 | setCStr_String(&d->tags[artist_PlayerTag], mpegStr_(d->id3v2->artist)); | ||
322 | setCStr_String(&d->tags[genre_PlayerTag], mpegStr_(d->id3v2->genre)); | ||
323 | setCStr_String(&d->tags[date_PlayerTag], mpegStr_(d->id3v2->year)); | ||
324 | unlock_Mutex(&d->tagMutex); | ||
288 | } | 325 | } |
289 | } | 326 | } |
327 | /* Check if we know the total length already. This info should be available eventually. */ | ||
328 | const off_t off = mpg123_length(d->mpeg); | ||
329 | if (off > 0) { | ||
330 | d->totalSamples = off; | ||
331 | } | ||
290 | writePending_Decoder_(d); | 332 | writePending_Decoder_(d); |
291 | #endif | 333 | #endif |
292 | return status; | 334 | return status; |
@@ -349,9 +391,15 @@ void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) { | |||
349 | spec->output.format, | 391 | spec->output.format, |
350 | spec->output.channels, | 392 | spec->output.channels, |
351 | spec->output.samples * 2); | 393 | spec->output.samples * 2); |
394 | init_Mutex(&d->tagMutex); | ||
395 | iForIndices(i, d->tags) { | ||
396 | init_String(&d->tags[i]); | ||
397 | } | ||
352 | d->vorbis = NULL; | 398 | d->vorbis = NULL; |
353 | #if defined (LAGRANGE_ENABLE_MPG123) | 399 | #if defined (LAGRANGE_ENABLE_MPG123) |
354 | d->mpeg = NULL; | 400 | d->mpeg = NULL; |
401 | d->id3v1 = NULL; | ||
402 | d->id3v2 = NULL; | ||
355 | #endif | 403 | #endif |
356 | init_Mutex(&d->outputMutex); | 404 | init_Mutex(&d->outputMutex); |
357 | d->thread = new_Thread(run_Decoder_); | 405 | d->thread = new_Thread(run_Decoder_); |
@@ -368,6 +416,10 @@ void deinit_Decoder(iDecoder *d) { | |||
368 | deinit_Mutex(&d->outputMutex); | 416 | deinit_Mutex(&d->outputMutex); |
369 | deinit_SampleBuf(&d->output); | 417 | deinit_SampleBuf(&d->output); |
370 | deinit_Array(&d->pendingOutput); | 418 | deinit_Array(&d->pendingOutput); |
419 | iForIndices(i, d->tags) { | ||
420 | deinit_String(&d->tags[i]); | ||
421 | } | ||
422 | deinit_Mutex(&d->tagMutex); | ||
371 | if (d->vorbis) { | 423 | if (d->vorbis) { |
372 | stb_vorbis_close(d->vorbis); | 424 | stb_vorbis_close(d->vorbis); |
373 | } | 425 | } |
@@ -522,7 +574,7 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) { | |||
522 | content.output.freq = info.sample_rate; | 574 | content.output.freq = info.sample_rate; |
523 | content.output.channels = numChannels; | 575 | content.output.channels = numChannels; |
524 | content.output.format = AUDIO_F32; | 576 | content.output.format = AUDIO_F32; |
525 | content.inputFormat = AUDIO_F32; /* actually stb_vorbis provides floats */ | 577 | content.inputFormat = AUDIO_F32; /* actually stb_vorbis provides floats */ |
526 | stb_vorbis_close(vrb); | 578 | stb_vorbis_close(vrb); |
527 | } | 579 | } |
528 | else if (content.type == mpeg_DecoderType) { | 580 | else if (content.type == mpeg_DecoderType) { |
@@ -678,6 +730,16 @@ int flags_Player(const iPlayer *d) { | |||
678 | return d->flags; | 730 | return d->flags; |
679 | } | 731 | } |
680 | 732 | ||
733 | const iString *tag_Player(const iPlayer *d, enum iPlayerTag tag) { | ||
734 | const iString *str = NULL; | ||
735 | if (d->decoder) { | ||
736 | lock_Mutex(&d->decoder->tagMutex); | ||
737 | str = collect_String(copy_String(&d->decoder->tags[tag])); | ||
738 | unlock_Mutex(&d->decoder->tagMutex); | ||
739 | } | ||
740 | return str ? str : collectNew_String(); | ||
741 | } | ||
742 | |||
681 | float time_Player(const iPlayer *d) { | 743 | float time_Player(const iPlayer *d) { |
682 | if (!d->decoder) return 0; | 744 | if (!d->decoder) return 0; |
683 | return (float) ((double) d->decoder->currentSample / (double) d->spec.freq); | 745 | return (float) ((double) d->decoder->currentSample / (double) d->spec.freq); |
@@ -699,7 +761,26 @@ float streamProgress_Player(const iPlayer *d) { | |||
699 | } | 761 | } |
700 | 762 | ||
701 | iString *metadataLabel_Player(const iPlayer *d) { | 763 | iString *metadataLabel_Player(const iPlayer *d) { |
702 | return newFormat_String("%d-bit %s %d Hz", SDL_AUDIO_BITSIZE(d->decoder->inputFormat), | 764 | iString *meta = new_String(); |
765 | if (d->decoder) { | ||
766 | lock_Mutex(&d->decoder->tagMutex); | ||
767 | const iString *tags = d->decoder->tags; | ||
768 | if (!isEmpty_String(&tags[title_PlayerTag])) { | ||
769 | appendFormat_String(meta, "Title: %s\n", cstr_String(&tags[title_PlayerTag])); | ||
770 | } | ||
771 | if (!isEmpty_String(&tags[artist_PlayerTag])) { | ||
772 | appendFormat_String(meta, "Artist: %s\n", cstr_String(&tags[artist_PlayerTag])); | ||
773 | } | ||
774 | if (!isEmpty_String(&tags[genre_PlayerTag])) { | ||
775 | appendFormat_String(meta, "Genre: %s\n", cstr_String(&tags[genre_PlayerTag])); | ||
776 | } | ||
777 | if (!isEmpty_String(&tags[date_PlayerTag])) { | ||
778 | appendFormat_String(meta, "Date: %s\n", cstr_String(&tags[date_PlayerTag])); | ||
779 | } | ||
780 | unlock_Mutex(&d->decoder->tagMutex); | ||
781 | } | ||
782 | appendFormat_String(meta, "%d-bit %s %d Hz", SDL_AUDIO_BITSIZE(d->decoder->inputFormat), | ||
703 | SDL_AUDIO_ISFLOAT(d->decoder->inputFormat) ? "float" : "integer", | 783 | SDL_AUDIO_ISFLOAT(d->decoder->inputFormat) ? "float" : "integer", |
704 | d->spec.freq); | 784 | d->spec.freq); |
785 | return meta; | ||
705 | } | 786 | } |