summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2005-06-14 22:27:57 +0000
committerDave Chapman <dave@dchapman.com>2005-06-14 22:27:57 +0000
commit3ad485b15a51e92a23540429b64e89656555bba6 (patch)
treea84ff392ab5e5cc6a6e170e7607996d7fd4d13d3 /apps
parent88a89e0cdbfc4d745c48ff607b0774192d66218c (diff)
downloadrockbox-3ad485b15a51e92a23540429b64e89656555bba6.tar.gz
rockbox-3ad485b15a51e92a23540429b64e89656555bba6.zip
Move metadata parsing code from playback.c into metadata.c
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6714 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/metadata.c342
-rw-r--r--apps/metadata.h31
-rw-r--r--apps/playback.c357
-rw-r--r--apps/playback.h29
5 files changed, 419 insertions, 341 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 5e58816d4e..d6ea2c9491 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -50,4 +50,5 @@ recorder/recording.c
50#endif 50#endif
51#ifdef IRIVER_H100 51#ifdef IRIVER_H100
52playback.c 52playback.c
53metadata.c
53#endif 54#endif
diff --git a/apps/metadata.c b/apps/metadata.c
new file mode 100644
index 0000000000..35ed900efb
--- /dev/null
+++ b/apps/metadata.c
@@ -0,0 +1,342 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Dave Chapman
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#include <ctype.h>
23
24#include "metadata.h"
25#include "mp3_playback.h"
26#include "mp3data.h"
27#include "logf.h"
28
29/* Simple file type probing by looking filename extension. */
30int probe_file_format(const char *filename)
31{
32 char *suffix;
33
34 suffix = strrchr(filename, '.');
35 if (suffix == NULL)
36 return AFMT_UNKNOWN;
37 suffix += 1;
38
39 if (!strcasecmp("mp1", suffix))
40 return AFMT_MPA_L1;
41 else if (!strcasecmp("mp2", suffix))
42 return AFMT_MPA_L2;
43 else if (!strcasecmp("mpa", suffix))
44 return AFMT_MPA_L2;
45 else if (!strcasecmp("mp3", suffix))
46 return AFMT_MPA_L3;
47 else if (!strcasecmp("ogg", suffix))
48 return AFMT_OGG_VORBIS;
49 else if (!strcasecmp("wav", suffix))
50 return AFMT_PCM_WAV;
51 else if (!strcasecmp("flac", suffix))
52 return AFMT_FLAC;
53 else if (!strcasecmp("mpc", suffix))
54 return AFMT_MPC;
55 else if (!strcasecmp("aac", suffix))
56 return AFMT_AAC;
57 else if (!strcasecmp("ape", suffix))
58 return AFMT_APE;
59 else if (!strcasecmp("wma", suffix))
60 return AFMT_WMA;
61 else if ((!strcasecmp("a52", suffix)) || (!strcasecmp("ac3", suffix)))
62 return AFMT_A52;
63 else if (!strcasecmp("rm", suffix))
64 return AFMT_REAL;
65 else if (!strcasecmp("wv", suffix))
66 return AFMT_WAVPACK;
67
68 return AFMT_UNKNOWN;
69
70}
71
72/* Get metadata for track - return false if parsing showed problems with the
73 file that would prevent playback. */
74
75bool get_metadata(struct track_info* track, int fd, const char* trackname,
76 bool v1first) {
77 unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes;
78 unsigned char* buf;
79 int i,j,eof;
80 int rc;
81
82 /* Load codec specific track tag information. */
83 switch (track->codectype) {
84 case AFMT_MPA_L2:
85 case AFMT_MPA_L3:
86 /* Should check the return value. */
87 mp3info(&track->id3, trackname, v1first);
88 lseek(fd, 0, SEEK_SET);
89
90 /* This is too slow to execute on some files. */
91 get_mp3file_info(fd, &track->mp3data);
92 lseek(fd, 0, SEEK_SET);
93
94 /*
95 logf("T:%s", track->id3.title);
96 logf("L:%d", track->id3.length);
97 logf("O:%d", track->id3.first_frame_offset);
98 logf("F:%d", track->id3.frequency);
99 */
100 track->taginfo_ready = true;
101 break ;
102
103 case AFMT_PCM_WAV:
104 /* Use the trackname part of the id3 structure as a temporary buffer */
105 buf=track->id3.path;
106
107 lseek(fd, 0, SEEK_SET);
108
109 rc = read(fd, buf, 44);
110 if (rc < 44) {
111 return false;
112 }
113
114 if ((memcmp(buf,"RIFF",4)!=0) ||
115 (memcmp(&buf[8],"WAVEfmt",7)!=0)) {
116 logf("%s is not a WAV file\n",trackname);
117 return(false);
118 }
119
120 /* FIX: Correctly parse WAV header - we assume canonical
121 44-byte header */
122
123 bitspersample=buf[34];
124 channels=buf[22];
125
126 if ((bitspersample!=16) || (channels != 2)) {
127 logf("Unsupported WAV file - %d bitspersample, %d channels\n",
128 bitspersample,channels);
129 return(false);
130 }
131
132 bytespersample=((bitspersample/8)*channels);
133 numbytes=(buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24));
134 totalsamples=numbytes/bytespersample;
135
136 track->id3.vbr=false; /* All WAV files are CBR */
137 track->id3.filesize=filesize(fd);
138 track->id3.frequency=buf[24]|(buf[25]<<8)|(buf[26]<<16)|(buf[27]<<24);
139
140 /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
141 track->id3.length=(totalsamples/track->id3.frequency)*1000;
142 track->id3.bitrate=(track->id3.frequency*bytespersample)/(1000/8);
143
144 lseek(fd, 0, SEEK_SET);
145 strncpy(track->id3.path,trackname,sizeof(track->id3.path));
146 track->taginfo_ready = true;
147
148 break;
149
150 case AFMT_FLAC:
151 /* A simple parser to read vital metadata from a FLAC file - length, frequency, bitrate etc. */
152 /* This code should either be moved to a seperate file, or discarded in favour of the libFLAC code */
153 /* The FLAC stream specification can be found at http://flac.sourceforge.net/format.html#stream */
154
155 /* Use the trackname part of the id3 structure as a temporary buffer */
156 buf=track->id3.path;
157
158 lseek(fd, 0, SEEK_SET);
159
160 rc = read(fd, buf, 4);
161 if (rc < 4) {
162 return false;
163 }
164
165 if (memcmp(buf,"fLaC",4)!=0) {
166 logf("%s is not a FLAC file\n",trackname);
167 return(false);
168 }
169
170 while (1) {
171 rc = read(fd, buf, 4);
172 i = (buf[1]<<16)|(buf[2]<<8)|buf[3]; /* The length of the block */
173
174 if ((buf[0]&0x7f)==0) { /* 0 is the STREAMINFO block */
175 rc = read(fd, buf, i); /* FIXME: Don't trust the value of i */
176 if (rc < 0) {
177 return false;
178 }
179 track->id3.vbr=true; /* All FLAC files are VBR */
180 track->id3.filesize=filesize(fd);
181
182 track->id3.frequency=(buf[10]<<12)|(buf[11]<<4)|((buf[12]&0xf0)>>4);
183
184 /* NOT NEEDED: bitspersample=(((buf[12]&0x01)<<4)|((buf[13]&0xf0)>>4))+1; */
185
186 /* totalsamples is a 36-bit field, but we assume <= 32 bits are used */
187 totalsamples=(buf[14]<<24)|(buf[15]<<16)|(buf[16]<<8)|buf[17];
188
189 /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
190 track->id3.length=(totalsamples/track->id3.frequency)*1000;
191 track->id3.bitrate=(filesize(fd)*8)/track->id3.length;
192 } else if ((buf[0]&0x7f)==4) { /* 4 is the VORBIS_COMMENT block */
193
194 /* The next i bytes of the file contain the VORBIS COMMENTS - just skip them for now. */
195 lseek(fd, i, SEEK_CUR);
196
197 } else {
198 if (buf[0]&0x80) { /* If we have reached the last metadata block, abort. */
199 break;
200 } else {
201 lseek(fd, i, SEEK_CUR); /* Skip to next metadata block */
202 }
203 }
204 }
205
206 lseek(fd, 0, SEEK_SET);
207 strncpy(track->id3.path,trackname,sizeof(track->id3.path));
208 track->taginfo_ready = true;
209 break;
210
211 case AFMT_OGG_VORBIS:
212 /* A simple parser to read vital metadata from an Ogg Vorbis file */
213
214 /* An Ogg File is split into pages, each starting with the string
215 "OggS". Each page has a timestamp (in PCM samples) referred to as
216 the "granule position".
217
218 An Ogg Vorbis has the following structure:
219 1) Identification header (containing samplerate, numchannels, etc)
220 2) Comment header - containing the Vorbis Comments
221 3) Setup header - containing codec setup information
222 4) Many audio packets...
223
224 */
225
226 /* Use the trackname part of the id3 structure as a temporary buffer */
227 buf=track->id3.path;
228
229 lseek(fd, 0, SEEK_SET);
230
231 rc = read(fd, buf, 58);
232 if (rc < 4) {
233 return false;
234 }
235
236 if ((memcmp(buf,"OggS",4)!=0) || (memcmp(&buf[29],"vorbis",6)!=0)) {
237 logf("%s is not an Ogg Vorbis file\n",trackname);
238 return(false);
239 }
240
241 /* Ogg stores integers in little-endian format. */
242 track->id3.filesize=filesize(fd);
243 track->id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24);
244 channels=buf[39];
245
246 /* We now need to search for the last page in the file - identified by
247 by ('O','g','g','S',0) and retrieve totalsamples */
248
249 lseek(fd, -32*1024, SEEK_END);
250 eof=0;
251 j=0; /* The number of bytes currently in buffer */
252 i=0;
253 totalsamples=0;
254 while (!eof) {
255 rc = read(fd, &buf[j], MAX_PATH-j);
256 if (rc <= 0) {
257 eof=1;
258 } else {
259 j+=rc;
260 }
261 /* Inefficient (but simple) search */
262 i=0;
263 while (i < (j-5)) {
264 if (memcmp(&buf[i],"OggS",5)==0) {
265 if (i < (j-10)) {
266 totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24);
267 j=0; /* We can discard the rest of the buffer */
268 } else {
269 break;
270 }
271 } else {
272 i++;
273 }
274 }
275 if (i < (j-5)) {
276 /* Move OggS to start of buffer */
277 while(i>0) buf[i--]=buf[j--];
278 } else {
279 j=0;
280 }
281 }
282
283 track->id3.length=(totalsamples/track->id3.frequency)*1000;
284
285 /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */
286 track->id3.bitrate=(filesize(fd)*8)/track->id3.length;
287 track->id3.vbr=true;
288
289 lseek(fd, 0, SEEK_SET);
290 strncpy(track->id3.path,trackname,sizeof(track->id3.path));
291 track->taginfo_ready = true;
292 break;
293
294 case AFMT_WAVPACK:
295 /* A simple parser to read basic information from a WavPack file.
296 * This will fail on WavPack files that don't have the WavPack header
297 * as the first thing (i.e. self-extracting WavPack files) or WavPack
298 * files that have so much extra RIFF data stored in the first block
299 * that they don't have samples (very rare, I would think).
300 */
301
302 /* Use the trackname part of the id3 structure as a temporary buffer */
303 buf=track->id3.path;
304
305 lseek(fd, 0, SEEK_SET);
306
307 rc = read(fd, buf, 32);
308 if (rc < 32) {
309 return false;
310 }
311
312 if (memcmp (buf, "wvpk", 4) != 0 || buf [9] != 4 || buf [8] < 2) {
313 logf ("%s is not a WavPack file\n", trackname);
314 return (false);
315 }
316
317 track->id3.vbr = true; /* All WavPack files are VBR */
318 track->id3.filesize = filesize (fd);
319 track->id3.frequency = 44100;
320
321 if ((buf [20] | buf [21] | buf [22] | buf [23]) &&
322 (buf [12] & buf [13] & buf [14] & buf [15]) != 0xff) {
323 totalsamples = (buf[15] << 24) | (buf[14] << 16) | (buf[13] << 8) | buf[12];
324 track->id3.length = (totalsamples + 220) / 441 * 10;
325 track->id3.bitrate = filesize (fd) /
326 (track->id3.length / 8);
327 }
328
329 lseek (fd, 0, SEEK_SET);
330 strncpy (track->id3.path, trackname, sizeof (track->id3.path));
331 track->taginfo_ready = true;
332 break;
333
334 /* If we don't know how to read the metadata, just store the filename */
335 default:
336 strncpy(track->id3.path,trackname,sizeof(track->id3.path));
337 track->taginfo_ready = true;
338 break;
339 }
340
341 return true;
342}
diff --git a/apps/metadata.h b/apps/metadata.h
new file mode 100644
index 0000000000..79ca2a21a9
--- /dev/null
+++ b/apps/metadata.h
@@ -0,0 +1,31 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Dave Chapman
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#ifndef _METADATA_H
21#define _METADATA_H
22
23#include "playback.h"
24
25int probe_file_format(const char *filename);
26bool get_metadata(struct track_info* track, int fd, const char* trackname,
27 bool v1first);
28
29#endif
30
31
diff --git a/apps/playback.c b/apps/playback.c
index 0828d58ce0..d0b2882204 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -58,6 +58,7 @@
58#include "bookmark.h" 58#include "bookmark.h"
59#include "misc.h" 59#include "misc.h"
60#include "sound.h" 60#include "sound.h"
61#include "metadata.h"
61 62
62static volatile bool playing; 63static volatile bool playing;
63static volatile bool paused; 64static volatile bool paused;
@@ -124,23 +125,6 @@ static volatile int codecbufused;
124static volatile int buf_ridx; 125static volatile int buf_ridx;
125static volatile int buf_widx; 126static volatile int buf_widx;
126 127
127#define MAX_TRACK 10
128struct track_info {
129 struct mp3entry id3; /* TAG metadata */
130 struct mp3info mp3data; /* MP3 metadata */
131 char *codecbuf; /* Pointer to codec buffer */
132 size_t codecsize; /* Codec length in bytes */
133 int codectype; /* Codec type (example AFMT_MPA_L3) */
134
135 off_t filerem; /* Remaining bytes of file NOT in buffer */
136 off_t filesize; /* File total length */
137 off_t filepos; /* Read position of file for next buffer fill */
138 off_t start_pos; /* Position to first bytes of file in buffer */
139 volatile int available; /* Available bytes to read from buffer */
140 bool taginfo_ready; /* Is metadata read */
141 int playlist_offset; /* File location in playlist */
142};
143
144/* Track information (count in file buffer, read/write indexes for 128/* Track information (count in file buffer, read/write indexes for
145 track ring structure. */ 129 track ring structure. */
146static int track_count; 130static int track_count;
@@ -394,49 +378,6 @@ void codec_configure_callback(int setting, void *value)
394 } 378 }
395} 379}
396 380
397/* Simple file type probing by looking filename extension. */
398int probe_file_format(const char *filename)
399{
400 char *suffix;
401
402 suffix = strrchr(filename, '.');
403 if (suffix == NULL)
404 return AFMT_UNKNOWN;
405 suffix += 1;
406
407 if (!strcasecmp("mp1", suffix))
408 return AFMT_MPA_L1;
409 else if (!strcasecmp("mp2", suffix))
410 return AFMT_MPA_L2;
411 else if (!strcasecmp("mpa", suffix))
412 return AFMT_MPA_L2;
413 else if (!strcasecmp("mp3", suffix))
414 return AFMT_MPA_L3;
415 else if (!strcasecmp("ogg", suffix))
416 return AFMT_OGG_VORBIS;
417 else if (!strcasecmp("wav", suffix))
418 return AFMT_PCM_WAV;
419 else if (!strcasecmp("flac", suffix))
420 return AFMT_FLAC;
421 else if (!strcasecmp("mpc", suffix))
422 return AFMT_MPC;
423 else if (!strcasecmp("aac", suffix))
424 return AFMT_AAC;
425 else if (!strcasecmp("ape", suffix))
426 return AFMT_APE;
427 else if (!strcasecmp("wma", suffix))
428 return AFMT_WMA;
429 else if ((!strcasecmp("a52", suffix)) || (!strcasecmp("ac3", suffix)))
430 return AFMT_A52;
431 else if (!strcasecmp("rm", suffix))
432 return AFMT_REAL;
433 else if (!strcasecmp("wv", suffix))
434 return AFMT_WAVPACK;
435
436 return AFMT_UNKNOWN;
437
438}
439
440void yield_codecs(void) 381void yield_codecs(void)
441{ 382{
442 yield(); 383 yield();
@@ -620,10 +561,6 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
620 off_t size; 561 off_t size;
621 int rc, i; 562 int rc, i;
622 int copy_n; 563 int copy_n;
623 /* Used by the metadata parsers */
624 unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes;
625 unsigned char* buf;
626 int j,eof;
627 564
628 if (track_count >= MAX_TRACK) 565 if (track_count >= MAX_TRACK)
629 return false; 566 return false;
@@ -673,283 +610,25 @@ bool audio_load_track(int offset, bool start_play, int peek_offset)
673 close(fd); 610 close(fd);
674 return false; 611 return false;
675 } 612 }
676
677 /* Load codec specific track tag information. */
678 switch (tracks[track_widx].codectype) {
679 case AFMT_MPA_L2:
680 case AFMT_MPA_L3:
681 /* Should check the return value. */
682 mp3info(&tracks[track_widx].id3, trackname, v1first);
683 lseek(fd, 0, SEEK_SET);
684 /* This is too slow to execute on some files. */
685 get_mp3file_info(fd, &tracks[track_widx].mp3data);
686 if (offset) {
687 lseek(fd, offset, SEEK_SET);
688 tracks[track_widx].id3.offset = offset;
689 mp3_set_elapsed(&tracks[track_widx].id3);
690 tracks[track_widx].filepos = offset;
691 tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
692 ci.curpos = offset;
693 tracks[track_widx].start_pos = offset;
694 } else {
695 lseek(fd, 0, SEEK_SET);
696 }
697 /*
698 logf("T:%s", tracks[track_widx].id3.title);
699 logf("L:%d", tracks[track_widx].id3.length);
700 logf("O:%d", tracks[track_widx].id3.first_frame_offset);
701 logf("F:%d", tracks[track_widx].id3.frequency);
702 */
703 tracks[track_widx].taginfo_ready = true;
704 break ;
705
706 case AFMT_PCM_WAV:
707 /* Use the trackname part of the id3 structure as a temporary buffer */
708 buf=tracks[track_widx].id3.path;
709
710 lseek(fd, 0, SEEK_SET);
711
712 rc = read(fd, buf, 44);
713 if (rc < 44) {
714 close(fd);
715 return false;
716 }
717
718 if ((memcmp(buf,"RIFF",4)!=0) ||
719 (memcmp(&buf[8],"WAVEfmt",7)!=0)) {
720 logf("%s is not a WAV file\n",trackname);
721 close(fd);
722 return(false);
723 }
724
725 /* FIX: Correctly parse WAV header - we assume canonical
726 44-byte header */
727
728 bitspersample=buf[34];
729 channels=buf[22];
730
731 if ((bitspersample!=16) || (channels != 2)) {
732 logf("Unsupported WAV file - %d bitspersample, %d channels\n",
733 bitspersample,channels);
734 close(fd);
735 return(false);
736 }
737
738 bytespersample=((bitspersample/8)*channels);
739 numbytes=(buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24));
740 totalsamples=numbytes/bytespersample;
741 613
742 tracks[track_widx].id3.vbr=false; /* All WAV files are CBR */ 614 if (!get_metadata(&tracks[track_widx],fd,trackname,v1first)) {
743 tracks[track_widx].id3.filesize=filesize(fd); 615 close(fd);
744 tracks[track_widx].id3.frequency=buf[24]|(buf[25]<<8)|(buf[26]<<16)|(buf[27]<<24); 616 return false;
745
746 /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
747 tracks[track_widx].id3.length=(totalsamples/tracks[track_widx].id3.frequency)*1000;
748 tracks[track_widx].id3.bitrate=(tracks[track_widx].id3.frequency*bytespersample)/(1000/8);
749
750 lseek(fd, 0, SEEK_SET);
751 strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
752 tracks[track_widx].taginfo_ready = true;
753
754 break;
755
756 case AFMT_FLAC:
757 /* A simple parser to read vital metadata from a FLAC file - length, frequency, bitrate etc. */
758 /* This code should either be moved to a seperate file, or discarded in favour of the libFLAC code */
759 /* The FLAC stream specification can be found at http://flac.sourceforge.net/format.html#stream */
760
761 /* Use the trackname part of the id3 structure as a temporary buffer */
762 buf=tracks[track_widx].id3.path;
763
764 lseek(fd, 0, SEEK_SET);
765
766 rc = read(fd, buf, 4);
767 if (rc < 4) {
768 close(fd);
769 return false;
770 }
771
772 if (memcmp(buf,"fLaC",4)!=0) {
773 logf("%s is not a FLAC file\n",trackname);
774 close(fd);
775 return(false);
776 }
777
778 while (1) {
779 rc = read(fd, buf, 4);
780 i = (buf[1]<<16)|(buf[2]<<8)|buf[3]; /* The length of the block */
781
782 if ((buf[0]&0x7f)==0) { /* 0 is the STREAMINFO block */
783 rc = read(fd, buf, i); /* FIXME: Don't trust the value of i */
784 if (rc < 0) {
785 close(fd);
786 return false;
787 }
788 tracks[track_widx].id3.vbr=true; /* All FLAC files are VBR */
789 tracks[track_widx].id3.filesize=filesize(fd);
790
791 tracks[track_widx].id3.frequency=(buf[10]<<12)|(buf[11]<<4)|((buf[12]&0xf0)>>4);
792
793 /* NOT NEEDED: bitspersample=(((buf[12]&0x01)<<4)|((buf[13]&0xf0)>>4))+1; */
794
795 /* totalsamples is a 36-bit field, but we assume <= 32 bits are used */
796 totalsamples=(buf[14]<<24)|(buf[15]<<16)|(buf[16]<<8)|buf[17];
797
798 /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */
799 tracks[track_widx].id3.length=(totalsamples/tracks[track_widx].id3.frequency)*1000;
800 tracks[track_widx].id3.bitrate=(filesize(fd)*8)/tracks[track_widx].id3.length;
801 } else if ((buf[0]&0x7f)==4) { /* 4 is the VORBIS_COMMENT block */
802
803 /* The next i bytes of the file contain the VORBIS COMMENTS - just skip them for now. */
804 lseek(fd, i, SEEK_CUR);
805
806 } else {
807 if (buf[0]&0x80) { /* If we have reached the last metadata block, abort. */
808 break;
809 } else {
810 lseek(fd, i, SEEK_CUR); /* Skip to next metadata block */
811 }
812 }
813 }
814
815 lseek(fd, 0, SEEK_SET);
816 strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
817 tracks[track_widx].taginfo_ready = true;
818 break;
819
820 case AFMT_OGG_VORBIS:
821 /* A simple parser to read vital metadata from an Ogg Vorbis file */
822
823 /* An Ogg File is split into pages, each starting with the string
824 "OggS". Each page has a timestamp (in PCM samples) referred to as
825 the "granule position".
826
827 An Ogg Vorbis has the following structure:
828 1) Identification header (containing samplerate, numchannels, etc)
829 2) Comment header - containing the Vorbis Comments
830 3) Setup header - containing codec setup information
831 4) Many audio packets...
832
833 */
834
835 /* Use the trackname part of the id3 structure as a temporary buffer */
836 buf=tracks[track_widx].id3.path;
837
838 lseek(fd, 0, SEEK_SET);
839
840 rc = read(fd, buf, 58);
841 if (rc < 4) {
842 close(fd);
843 return false;
844 }
845
846 if ((memcmp(buf,"OggS",4)!=0) || (memcmp(&buf[29],"vorbis",6)!=0)) {
847 logf("%s is not an Ogg Vorbis file\n",trackname);
848 close(fd);
849 return(false);
850 }
851
852 /* Ogg stores integers in little-endian format. */
853 tracks[track_widx].id3.filesize=filesize(fd);
854 tracks[track_widx].id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24);
855 channels=buf[39];
856
857 /* We now need to search for the last page in the file - identified by
858 by ('O','g','g','S',0) and retrieve totalsamples */
859
860 lseek(fd, -32*1024, SEEK_END);
861 eof=0;
862 j=0; /* The number of bytes currently in buffer */
863 i=0;
864 totalsamples=0;
865 while (!eof) {
866 rc = read(fd, &buf[j], MAX_PATH-j);
867 if (rc <= 0) {
868 eof=1;
869 } else {
870 j+=rc;
871 }
872 /* Inefficient (but simple) search */
873 i=0;
874 while (i < (j-5)) {
875 if (memcmp(&buf[i],"OggS",5)==0) {
876 if (i < (j-10)) {
877 totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24);
878 j=0; /* We can discard the rest of the buffer */
879 } else {
880 break;
881 }
882 } else {
883 i++;
884 }
885 }
886 if (i < (j-5)) {
887 /* Move OggS to start of buffer */
888 while(i>0) buf[i--]=buf[j--];
889 } else {
890 j=0;
891 }
892 }
893
894 tracks[track_widx].id3.length=(totalsamples/tracks[track_widx].id3.frequency)*1000;
895
896 /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */
897 tracks[track_widx].id3.bitrate=(filesize(fd)*8)/tracks[track_widx].id3.length;
898 tracks[track_widx].id3.vbr=true;
899
900 lseek(fd, 0, SEEK_SET);
901 strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
902 tracks[track_widx].taginfo_ready = true;
903 break;
904
905 case AFMT_WAVPACK:
906 /* A simple parser to read basic information from a WavPack file.
907 * This will fail on WavPack files that don't have the WavPack header
908 * as the first thing (i.e. self-extracting WavPack files) or WavPack
909 * files that have so much extra RIFF data stored in the first block
910 * that they don't have samples (very rare, I would think).
911 */
912
913 /* Use the trackname part of the id3 structure as a temporary buffer */
914 buf=tracks[track_widx].id3.path;
915
916 lseek(fd, 0, SEEK_SET);
917
918 rc = read(fd, buf, 32);
919 if (rc < 32) {
920 close(fd);
921 return false;
922 }
923
924 if (memcmp (buf, "wvpk", 4) != 0 || buf [9] != 4 || buf [8] < 2) {
925 logf ("%s is not a WavPack file\n", trackname);
926 close (fd);
927 return (false);
928 }
929
930 tracks[track_widx].id3.vbr = true; /* All WavPack files are VBR */
931 tracks[track_widx].id3.filesize = filesize (fd);
932 tracks[track_widx].id3.frequency = 44100;
933
934 if ((buf [20] | buf [21] | buf [22] | buf [23]) &&
935 (buf [12] & buf [13] & buf [14] & buf [15]) != 0xff) {
936 totalsamples = (buf[15] << 24) | (buf[14] << 16) | (buf[13] << 8) | buf[12];
937 tracks[track_widx].id3.length = (totalsamples + 220) / 441 * 10;
938 tracks[track_widx].id3.bitrate = filesize (fd) /
939 (tracks[track_widx].id3.length / 8);
940 }
941
942 lseek (fd, 0, SEEK_SET);
943 strncpy (tracks[track_widx].id3.path, trackname, sizeof (tracks[track_widx].id3.path));
944 tracks[track_widx].taginfo_ready = true;
945 break;
946
947 /* If we don't know how to read the metadata, just store the filename */
948 default:
949 strncpy(tracks[track_widx].id3.path,trackname,sizeof(tracks[track_widx].id3.path));
950 tracks[track_widx].taginfo_ready = true;
951 break;
952 } 617 }
618
619 /* Starting playback from an offset is only support in MPA at the moment */
620 if (offset > 0) {
621 if ((tracks[track_widx].codectype==AFMT_MPA_L2) ||
622 (tracks[track_widx].codectype==AFMT_MPA_L3)) {
623 lseek(fd, offset, SEEK_SET);
624 tracks[track_widx].id3.offset = offset;
625 mp3_set_elapsed(&tracks[track_widx].id3);
626 tracks[track_widx].filepos = offset;
627 tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
628 ci.curpos = offset;
629 tracks[track_widx].start_pos = offset;
630 }
631 }
953 632
954 if (start_play) { 633 if (start_play) {
955 track_count++; 634 track_count++;
diff --git a/apps/playback.h b/apps/playback.h
index 4cdece75c9..1df283e6b8 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -17,8 +17,15 @@
17 * 17 *
18 ****************************************************************************/ 18 ****************************************************************************/
19 19
20#ifndef _AUDIO_H 20#ifndef _PLAYBACK_H
21#define _AUDIO_H 21#define _PLAYBACK_H
22
23#include <stdlib.h>
24#include <ctype.h>
25#include <stdbool.h>
26
27#include "id3.h"
28#include "mp3data.h"
22 29
23/* Supported file types. */ 30/* Supported file types. */
24#define AFMT_MPA_L1 0x0001 // MPEG Audio layer 1 31#define AFMT_MPA_L1 0x0001 // MPEG Audio layer 1
@@ -45,6 +52,24 @@
45/* Not yet implemented. */ 52/* Not yet implemented. */
46#define CODEC_SET_AUDIOBUF_WATERMARK 4 53#define CODEC_SET_AUDIOBUF_WATERMARK 4
47 54
55#define MAX_TRACK 10
56struct track_info {
57 struct mp3entry id3; /* TAG metadata */
58 struct mp3info mp3data; /* MP3 metadata */
59 char *codecbuf; /* Pointer to codec buffer */
60 size_t codecsize; /* Codec length in bytes */
61 int codectype; /* Codec type (example AFMT_MPA_L3) */
62
63 off_t filerem; /* Remaining bytes of file NOT in buffer */
64 off_t filesize; /* File total length */
65 off_t filepos; /* Read position of file for next buffer fill */
66 off_t start_pos; /* Position to first bytes of file in buffer */
67 volatile int available; /* Available bytes to read from buffer */
68 bool taginfo_ready; /* Is metadata read */
69 int playlist_offset; /* File location in playlist */
70};
71
72
48/* Codec Interface */ 73/* Codec Interface */
49struct codec_api { 74struct codec_api {
50 off_t filesize; /* Total file length */ 75 off_t filesize; /* Total file length */