summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-10-15 19:14:08 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-10-15 19:14:08 +0300
commit5fdbd18485bf487f3775e52aed1f4a18870a3acc (patch)
treea1516b83d65a3e7ded623eb7c46d99c86ea35e52 /src/audio
parentc3795ba3779ec79a94ad298fc5e72b6edfc4b4e7 (diff)
Player: Parsing Vorbis and MP3 metadata
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/player.c97
-rw-r--r--src/audio/player.h9
2 files changed, 98 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)
263static const char *mpegStr_(const mpg123_string *str) {
264 return str ? str->p : "";
265}
266#endif
267
237enum iDecoderStatus decodeMpeg_Decoder_(iDecoder *d) { 268enum 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
733const 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
681float time_Player(const iPlayer *d) { 743float 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
701iString *metadataLabel_Player(const iPlayer *d) { 763iString *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}
diff --git a/src/audio/player.h b/src/audio/player.h
index 268188aa..58947ef4 100644
--- a/src/audio/player.h
+++ b/src/audio/player.h
@@ -38,6 +38,14 @@ enum iPlayerFlag {
38 volumeGrabbed_PlayerFlag = iBit(2), 38 volumeGrabbed_PlayerFlag = iBit(2),
39}; 39};
40 40
41enum iPlayerTag {
42 title_PlayerTag,
43 artist_PlayerTag,
44 genre_PlayerTag,
45 date_PlayerTag,
46 max_PlayerTag,
47};
48
41void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data, 49void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data,
42 enum iPlayerUpdate update); 50 enum iPlayerUpdate update);
43 51
@@ -48,6 +56,7 @@ void setVolume_Player (iPlayer *, float volume);
48void setFlags_Player (iPlayer *, int flags, iBool set); 56void setFlags_Player (iPlayer *, int flags, iBool set);
49 57
50int flags_Player (const iPlayer *); 58int flags_Player (const iPlayer *);
59const iString *tag_Player (const iPlayer *, enum iPlayerTag tag);
51iBool isStarted_Player (const iPlayer *); 60iBool isStarted_Player (const iPlayer *);
52iBool isPaused_Player (const iPlayer *); 61iBool isPaused_Player (const iPlayer *);
53float volume_Player (const iPlayer *); 62float volume_Player (const iPlayer *);