summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2005-07-05 08:43:36 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2005-07-05 08:43:36 +0000
commiteaf8b2d76d3f69b919ff1c3a55e7ad2f07456bd4 (patch)
tree74c9b4d7e08b2378b499d63f13c8ea70f74f8351
parenta10bb59331b5e48adf307c4538333e9a4e036db9 (diff)
downloadrockbox-eaf8b2d76d3f69b919ff1c3a55e7ad2f07456bd4.tar.gz
rockbox-eaf8b2d76d3f69b919ff1c3a55e7ad2f07456bd4.zip
Patch #1232549 by Ryan Jackson, adds seeking and comments to Vorbis playback
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7025 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/codecs.c1
-rw-r--r--apps/codecs.h4
-rw-r--r--apps/codecs/lib/xxx2wav.c6
-rw-r--r--apps/codecs/vorbis.c149
-rw-r--r--apps/metadata.c223
-rw-r--r--docs/CREDITS1
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/common/memchr.c116
-rw-r--r--firmware/export/id3.h3
9 files changed, 471 insertions, 33 deletions
diff --git a/apps/codecs.c b/apps/codecs.c
index 16338c263c..8b4f9b5f32 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -243,6 +243,7 @@ struct codec_api ci = {
243 logf, 243 logf,
244#endif 244#endif
245 245
246 memchr,
246}; 247};
247 248
248int codec_load_ram(char* codecptr, size_t size, void* ptr2, size_t bufwrap) 249int codec_load_ram(char* codecptr, size_t size, void* ptr2, size_t bufwrap)
diff --git a/apps/codecs.h b/apps/codecs.h
index 1a03139be8..5878ca9185 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -240,7 +240,7 @@ struct codec_api {
240#endif 240#endif
241#if CONFIG_HWCODEC == MASNONE 241#if CONFIG_HWCODEC == MASNONE
242 void (*pcm_play_data)(const unsigned char *start, int size, 242 void (*pcm_play_data)(const unsigned char *start, int size,
243 void (*get_more)(unsigned char** start, long*size)); 243 void (*get_more)(unsigned char** start, long*size));
244 void (*pcm_play_stop)(void); 244 void (*pcm_play_stop)(void);
245 void (*pcm_set_frequency)(unsigned int frequency); 245 void (*pcm_set_frequency)(unsigned int frequency);
246 bool (*pcm_is_playing)(void); 246 bool (*pcm_is_playing)(void);
@@ -326,6 +326,8 @@ struct codec_api {
326#ifdef ROCKBOX_HAS_LOGF 326#ifdef ROCKBOX_HAS_LOGF
327 void (*logf)(const char *fmt, ...); 327 void (*logf)(const char *fmt, ...);
328#endif 328#endif
329
330 void *(*memchr)(const void *s1, int c, size_t n);
329}; 331};
330 332
331/* defined by the codec loader (codec.c) */ 333/* defined by the codec loader (codec.c) */
diff --git a/apps/codecs/lib/xxx2wav.c b/apps/codecs/lib/xxx2wav.c
index 1323e07475..b437ca56db 100644
--- a/apps/codecs/lib/xxx2wav.c
+++ b/apps/codecs/lib/xxx2wav.c
@@ -91,11 +91,7 @@ int memcmp(const void *s1, const void *s2, size_t n) {
91} 91}
92 92
93void* memchr(const void *s, int c, size_t n) { 93void* memchr(const void *s, int c, size_t n) {
94 /* TO DO: Implement for Tremor */ 94 return(local_rb->memchr(s,c,n));
95 (void)s;
96 (void)c;
97 (void)n;
98 return(NULL);
99} 95}
100 96
101void* memmove(const void *s1, const void *s2, size_t n) { 97void* memmove(const void *s1, const void *s2, size_t n) {
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c
index 9afeb053e1..946f2f9377 100644
--- a/apps/codecs/vorbis.c
+++ b/apps/codecs/vorbis.c
@@ -20,6 +20,7 @@
20#include "codecs.h" 20#include "codecs.h"
21 21
22#include "Tremor/ivorbisfile.h" 22#include "Tremor/ivorbisfile.h"
23#include "Tremor/ogg.h"
23#include "playback.h" 24#include "playback.h"
24#include "dsp.h" 25#include "dsp.h"
25#include "lib/codeclib.h" 26#include "lib/codeclib.h"
@@ -51,15 +52,30 @@ size_t read_handler(void *ptr, size_t size, size_t nmemb, void *datasource)
51 return rb->read_filebuf(ptr, nmemb*size); 52 return rb->read_filebuf(ptr, nmemb*size);
52} 53}
53 54
54int seek_handler(void *datasource, ogg_int64_t offset, int whence) 55int initial_seek_handler(void *datasource, ogg_int64_t offset, int whence) {
55{
56 /* We are not seekable at the moment */
57 (void)datasource; 56 (void)datasource;
58 (void)offset; 57 (void)offset;
59 (void)whence; 58 (void)whence;
60 return -1; 59 return -1;
61} 60}
62 61
62int seek_handler(void *datasource, ogg_int64_t offset, int whence)
63{
64 (void)datasource;
65
66 if ( whence == SEEK_CUR ) {
67 offset += rb->curpos;
68 } else if ( whence == SEEK_END ) {
69 offset += rb->filesize;
70 }
71
72 if (rb->seek_buffer(offset)) {
73 return 0;
74 }
75
76 return -1;
77}
78
63int close_handler(void *datasource) 79int close_handler(void *datasource)
64{ 80{
65 (void)datasource; 81 (void)datasource;
@@ -93,22 +109,26 @@ enum codec_status codec_start(struct codec_api* api)
93 long n; 109 long n;
94 int current_section; 110 int current_section;
95 int eof; 111 int eof;
112 ogg_int64_t vf_offsets[2];
113 ogg_int64_t vf_dataoffsets;
114 ogg_uint32_t vf_serialnos;
115 ogg_int64_t vf_pcmlengths[2];
116 int current_stereo_mode = -1;
96 117
97 TEST_CODEC_API(api); 118 TEST_CODEC_API(api);
98 119
99 /* if you are using a global api pointer, don't forget to copy it! 120 /* if you are using a global api pointer, don't forget to copy it!
100 otherwise you will get lovely "I04: IllInstr" errors... :-) */ 121 otherwise you will get lovely "I04: IllInstr" errors... :-) */
101 rb = api; 122 rb = api;
102 123
103#ifdef USE_IRAM 124 #ifdef USE_IRAM
104 rb->memcpy(iramstart, iramcopy, iramend-iramstart); 125 rb->memcpy(iramstart, iramcopy, iramend-iramstart);
105#endif 126 #endif
106 127
107 rb->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); 128 rb->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2));
108 rb->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*64)); 129 rb->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*64));
109 130
110 rb->configure(DSP_DITHER, (bool *)false); 131 rb->configure(DSP_DITHER, (bool *)false);
111 rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED);
112 rb->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); 132 rb->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16));
113 133
114/* We need to flush reserver memory every track load. */ 134/* We need to flush reserver memory every track load. */
@@ -129,44 +149,123 @@ enum codec_status codec_start(struct codec_api* api)
129 149
130 /* Create a decoder instance */ 150 /* Create a decoder instance */
131 callbacks.read_func=read_handler; 151 callbacks.read_func=read_handler;
132 callbacks.seek_func=seek_handler; 152 callbacks.seek_func=initial_seek_handler;
133 callbacks.tell_func=tell_handler; 153 callbacks.tell_func=tell_handler;
134 callbacks.close_func=close_handler; 154 callbacks.close_func=close_handler;
135 155
156 /* Open a non-seekable stream */
136 error=ov_open_callbacks(rb,&vf,NULL,0,callbacks); 157 error=ov_open_callbacks(rb,&vf,NULL,0,callbacks);
137 158
159 /* If the non-seekable open was successful, we need to supply the missing
160 * data to make it seekable. This is a hack, but it's reasonable since we
161 * don't want to read the whole file into the buffer before we start
162 * playing. Using Tremor's seekable open routine would cause us to do
163 * this, so we pretend not to be seekable at first. Then we fill in the
164 * missing fields of vf with 1) information in rb->id3, and 2) info
165 * obtained by Tremor in the above ov_open call.
166 *
167 * Note that this assumes there is only ONE logical Vorbis bitstream in our
168 * physical Ogg bitstream. This is verified in metadata.c, well before we
169 * get here.
170 */
171 if ( !error ) {
172 //rb->logf("no error");
173 /* FIXME Should these be dynamically allocated? */
174 vf.offsets = vf_offsets;
175 vf.dataoffsets = &vf_dataoffsets;
176 vf.serialnos = &vf_serialnos;
177 vf.pcmlengths = vf_pcmlengths;
178
179 vf.offsets[0] = 0;
180 vf.offsets[1] = rb->id3->filesize;
181 vf.dataoffsets[0] = vf.offset;
182 vf.pcmlengths[0] = 0;
183 vf.pcmlengths[1] = rb->id3->samples;
184 vf.serialnos[0] = vf.current_serialno;
185 vf.callbacks.seek_func=seek_handler;
186 vf.seekable = 1;
187 vf.offset = 58; /* length of Ogg header */
188 vf.end = rb->id3->filesize;
189 vf.ready_state = OPENED;
190 vf.links = 1;
191
192 /*if(ov_raw_seek(&vf,0)){
193 rb->logf("seek err");
194 }
195 */
196
197 } else {
198 //rb->logf("ov_open: %d", error);
199 }
200
138 vi=ov_info(&vf,-1); 201 vi=ov_info(&vf,-1);
139 202
140 if (vi==NULL) { 203 if (vi==NULL) {
141 // rb->splash(HZ*2, true, "Vorbis Error"); 204 //rb->splash(HZ*2, true, "Vorbis Error");
142 return CODEC_ERROR; 205 return CODEC_ERROR;
143 } 206 }
144 207
208 rb->configure(DSP_SET_FREQUENCY, (int *)rb->id3->frequency);
209
210 if (vi->channels == 2) {
211 if (current_stereo_mode != STEREO_INTERLEAVED) {
212 rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED);
213 current_stereo_mode = STEREO_INTERLEAVED;
214 }
215 } else if (vi->channels == 1) {
216 if (current_stereo_mode != STEREO_MONO) {
217 rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
218 current_stereo_mode = STEREO_MONO;
219 }
220 }
221
145 eof=0; 222 eof=0;
223 rb->yield();
146 while (!eof) { 224 while (!eof) {
225 if (rb->stop_codec || rb->reload_codec)
226 break ;
227
228 if (rb->seek_time) {
229
230 if (ov_time_seek(&vf, rb->seek_time)) {
231 //rb->logf("ov_time_seek failed");
232 }
233 rb->seek_time = 0;
234 }
235
147 /* Read host-endian signed 16 bit PCM samples */ 236 /* Read host-endian signed 16 bit PCM samples */
148 n=ov_read(&vf,pcmbuf,sizeof(pcmbuf),&current_section); 237 n=ov_read(&vf,pcmbuf,sizeof(pcmbuf),&current_section);
149 238
150 if (n==0) { 239 if (n==0) {
151 eof=1; 240 eof=1;
152 } 241 } else if (n < 0) {
153 else if (n < 0) {
154 DEBUGF("Error decoding frame\n"); 242 DEBUGF("Error decoding frame\n");
155 } else { 243 } else {
156 rb->yield(); 244 while (!rb->audiobuffer_insert(pcmbuf, n)) {
157 if (rb->stop_codec || rb->reload_codec)
158 break ;
159
160 while (!rb->audiobuffer_insert(pcmbuf, n))
161 rb->yield(); 245 rb->yield();
162 246 if ( rb->seek_time ) {
163 rb->set_elapsed(ov_time_tell(&vf)); 247 /* Hmmm, a seek was requested. Throw out the
248 * buffer and go back to the top of the loop.
249 */
250 break;
251 }
252 }
253 if ( !rb->seek_time ) {
254 rb->set_elapsed(ov_time_tell(&vf));
255 rb->yield();
256 }
164 } 257 }
165 } 258 }
166
167 if (rb->request_next_track())
168 goto next_track;
169 259
260 if (rb->request_next_track()) {
261 /* Clean things up for the next track */
262 vf.dataoffsets = NULL;
263 vf.offsets = NULL;
264 vf.serialnos = NULL;
265 vf.pcmlengths = NULL;
266 ov_clear(&vf);
267 goto next_track;
268 }
269
170 return CODEC_OK; 270 return CODEC_OK;
171} 271}
172
diff --git a/apps/metadata.c b/apps/metadata.c
index f59917ceb8..6ba2331d26 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -91,9 +91,14 @@ const long wavpack_sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000,
91 91
92static bool get_apetag_info (struct mp3entry *entry, int fd); 92static bool get_apetag_info (struct mp3entry *entry, int fd);
93 93
94static bool get_vorbis_comments (struct mp3entry *entry, int fd);
95
96static void little_endian_to_native (void *data, char *format);
97
94bool get_metadata(struct track_info* track, int fd, const char* trackname, 98bool get_metadata(struct track_info* track, int fd, const char* trackname,
95 bool v1first) { 99 bool v1first) {
96 unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes; 100 unsigned long totalsamples,bytespersample,channels,bitspersample,numbytes;
101 unsigned long serialno=0, last_serialno=0;
97 int bytesperframe; 102 int bytesperframe;
98 unsigned char* buf; 103 unsigned char* buf;
99 int i,j,eof; 104 int i,j,eof;
@@ -259,13 +264,29 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
259 return(false); 264 return(false);
260 } 265 }
261 266
267 /* We need to ensure the serial number from this page is the
268 * same as the one from the last page (since we only support
269 * a single bitstream).
270 */
271 serialno = buf[14]|(buf[15]<<8)|(buf[16]<<16)|(buf[17]<<24);
272
262 /* Ogg stores integers in little-endian format. */ 273 /* Ogg stores integers in little-endian format. */
263 track->id3.filesize=filesize(fd); 274 track->id3.filesize=filesize(fd);
264 track->id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24); 275 track->id3.frequency=buf[40]|(buf[41]<<8)|(buf[42]<<16)|(buf[43]<<24);
265 channels=buf[39]; 276 channels=buf[39];
266 277
278 if ( !get_vorbis_comments(&(track->id3), fd) ) {
279 logf("get_vorbis_comments failed");
280 return(false);
281 }
282
283 /* Set id3 genre to something bogus, otherwise vorbis tracks
284 * without genre tags will show up as 'Blues'
285 */
286 track->id3.genre=255;
287
267 /* We now need to search for the last page in the file - identified by 288 /* We now need to search for the last page in the file - identified by
268 by ('O','g','g','S',0) and retrieve totalsamples */ 289 by ('O','g','g','S',0) and retrieve totalsamples */
269 290
270 lseek(fd, -32*1024, SEEK_END); 291 lseek(fd, -32*1024, SEEK_END);
271 eof=0; 292 eof=0;
@@ -283,8 +304,9 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
283 i=0; 304 i=0;
284 while (i < (j-5)) { 305 while (i < (j-5)) {
285 if (memcmp(&buf[i],"OggS",5)==0) { 306 if (memcmp(&buf[i],"OggS",5)==0) {
286 if (i < (j-10)) { 307 if (i < (j-17)) {
287 totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24); 308 totalsamples=(buf[i+6])|(buf[i+7]<<8)|(buf[i+8]<<16)|(buf[i+9]<<24);
309 last_serialno=(buf[i+14])|(buf[i+15]<<8)|(buf[i+16]<<16)|(buf[i+17]<<24);
288 j=0; /* We can discard the rest of the buffer */ 310 j=0; /* We can discard the rest of the buffer */
289 } else { 311 } else {
290 break; 312 break;
@@ -300,7 +322,18 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
300 j=0; 322 j=0;
301 } 323 }
302 } 324 }
325
326 /* This file has mutiple vorbis bitstreams (or is corrupt) */
327 /* FIXME we should display an error here */
328 if (serialno != last_serialno) {
329 track->taginfo_ready=false;
330 logf("serialno mismatch");
331 logf("%ld", serialno);
332 logf("%ld", last_serialno);
333 return false;
334 }
303 335
336 track->id3.samples=totalsamples;
304 track->id3.length=(totalsamples/track->id3.frequency)*1000; 337 track->id3.length=(totalsamples/track->id3.frequency)*1000;
305 338
306 /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */ 339 /* The following calculation should use datasize, not filesize (i.e. exclude comments etc) */
@@ -712,3 +745,189 @@ static void UTF8ToAnsi (unsigned char *pUTF8)
712 *pAnsi = 0; 745 *pAnsi = 0;
713} 746}
714 747
748/* This function extracts the information stored in the Vorbis comment header
749 * and stores it in id3v2buf of the current track. Currently the combined
750 * lengths of title, genre, album, and artist must be no longer than 296 bytes
751 * (the remaining 4 bytes are the null bytes at the end of the strings). This
752 * is wrong, since vorbis comments can be up to 2^32 - 1 bytes long. In
753 * practice I don't think this limitation will cause a problem.
754 *
755 * According to the docs, a vorbis bitstream *must* have a comment packet even
756 * if that packet is empty. Therefore if this function returns false the
757 * bitstream is corrupt and shouldn't be used.
758 *
759 * Additionally, vorbis comments *may* take up more than one Ogg page, and this
760 * only looks at the first page of comments.
761 */
762static bool get_vorbis_comments (struct mp3entry *entry, int fd)
763{
764 int vendor_length;
765 int comment_count;
766 int comment_length;
767 int i = 0;
768 unsigned char temp[300];
769 int buffer_remaining = sizeof(entry->id3v2buf);
770 char *buffer = entry->id3v2buf;
771 char **p = NULL;
772 int segments;
773 int packet_remaining = 0;
774
775 /* Comments are in second Ogg page */
776 if ( lseek(fd, 58, SEEK_SET) < 0 ) {
777 return false;
778 }
779
780 /* Minimum header length for Ogg pages is 27 */
781 if (read(fd, temp, 27) < 27) {
782 return false;
783 }
784
785 if (memcmp(temp,"OggS",4)!=0) {
786 logf("1: Not an Ogg Vorbis file");
787 return(false);
788 }
789
790 segments=temp[26];
791 /* read in segment table */
792 if (read(fd, temp, segments) < segments) {
793 return false;
794 }
795
796 /* The second packet in a vorbis stream is the comment packet. It *may*
797 * extend beyond the second page, but usually does not. Here we find the
798 * length of the comment packet (or the rest of the page if the comment
799 * packet extends to the third page).
800 */
801 for (i = 0; i < segments; i++) {
802 packet_remaining += temp[i];
803 /* The last segment of a packet is always < 255 bytes */
804 if (temp[i] < 255) {
805 break;
806 }
807 }
808
809 /* Now read in packet header (type and id string) */
810 if(read(fd, temp, 7) < 7) {
811 return false;
812 }
813
814 /* The first byte of a packet is the packet type; comment packets are
815 * type 3.
816 */
817 if ((temp[0] != 3) || (memcmp(temp + 1,"vorbis",6)!=0)) {
818 logf("Not a vorbis comment packet");
819 return false;
820 }
821
822 packet_remaining -= 7;
823
824
825 /* We've read in all header info, now start reading comments */
826
827 if (read(fd, &vendor_length, 4) < 4) {
828 return false;
829 }
830 little_endian_to_native(&vendor_length, "L");
831 lseek(fd, vendor_length, SEEK_CUR);
832
833 if (read(fd, &comment_count, 4) < 4) {
834 return false;
835 }
836 little_endian_to_native(&comment_count, "L");
837 packet_remaining -= (vendor_length + 8);
838 if ( packet_remaining <= 0 ) {
839 return true;
840 }
841
842 for ( i = 0; i < comment_count; i++ ) {
843 int name_length = 0;
844
845 if (read(fd, &comment_length, 4) < 4) {
846 return false;
847 }
848
849 little_endian_to_native(&comment_length, "L");
850
851 /* Quit if we've passed the end of the page */
852 packet_remaining -= (comment_length + 4);
853 if ( packet_remaining <= 0 ) {
854 return true;
855 }
856
857 /* Skip comment if it won't fit in buffer */
858 if ( (unsigned int)comment_length >= sizeof(temp) ) {
859 lseek(fd, comment_length, SEEK_CUR);
860 continue;
861 }
862
863 if ( read(fd, temp, comment_length) < comment_length ) {
864 return false;
865 }
866
867 temp[comment_length] = '\0';
868 UTF8ToAnsi(temp);
869 comment_length = strlen(temp);
870
871 if (strncasecmp(temp, "TITLE=", 6) == 0) {
872 name_length = 5;
873 p = &(entry->title);
874 } else if (strncasecmp(temp, "ALBUM=", 6) == 0) {
875 name_length = 5;
876 p = &(entry->album);
877 } else if (strncasecmp(temp, "ARTIST=", 7) == 0) {
878 name_length = 6;
879 p = &(entry->artist);
880 } else if (strncasecmp(temp, "GENRE=", 6) == 0) {
881 name_length = 5;
882 p = &(entry->genre_string);
883 } else if (strncasecmp(temp, "DATE=", 5) == 0) {
884 int j=0;
885 /* verify that this is a number */
886 /* Note: vorbis uses UTF-8 for its comments, so it is
887 * safe to compare the values against ASCII 0 and 9
888 */
889 while ( j < (comment_length - 5) ) {
890 if ( (temp[5+j] < '0') || (temp[5+j] > '9') ) {
891 break;
892 }
893 j++;
894 }
895 if ( j == (comment_length - 5) ) {
896 p = NULL;
897 entry->year = atoi(temp + 5);
898 }
899 } else if (strncasecmp(temp, "TRACKNUMBER=", 12) == 0) {
900 int j=0;
901 /* verify that this is a number */
902 /* Note: vorbis uses UTF-8 for its comments, so it is
903 * safe to compare the values against ASCII 0 and 9
904 */
905 while ( j < (comment_length - 12) ) {
906 if ( (temp[12+j] < '0') || (temp[12+j] > '9') ) {
907 break;
908 }
909 j++;
910 }
911 if ( j == (comment_length - 12) ) {
912 p = NULL;
913 entry->tracknum = atoi(temp + 12);
914 }
915 } else {
916 p = NULL;
917 }
918
919 if (p) {
920 comment_length -= (name_length + 1);
921 if ( comment_length < buffer_remaining ) {
922 strncpy(buffer, temp + name_length + 1, comment_length);
923 buffer[comment_length] = '\0';
924 *p = buffer;
925 buffer += comment_length + 1;
926 buffer_remaining -= comment_length + 1;
927 }
928 }
929 }
930
931 return true;
932}
933
diff --git a/docs/CREDITS b/docs/CREDITS
index e055051f89..caef9240bb 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -125,3 +125,4 @@ Bryan Vandyke
125Hristo Kovachev 125Hristo Kovachev
126Sander Sweers 126Sander Sweers
127Antonius Hellman 127Antonius Hellman
128Ryan Jackson
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 1a1eacbdc4..199c4cb80e 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -14,6 +14,7 @@ common/file.c
14common/disk.c 14common/disk.c
15common/errno.c 15common/errno.c
16common/memcmp.c 16common/memcmp.c
17common/memchr.c
17common/qsort.c 18common/qsort.c
18common/random.c 19common/random.c
19common/sprintf.c 20common/sprintf.c
diff --git a/firmware/common/memchr.c b/firmware/common/memchr.c
new file mode 100644
index 0000000000..a7ff222a61
--- /dev/null
+++ b/firmware/common/memchr.c
@@ -0,0 +1,116 @@
1/*
2FUNCTION
3 <<memchr>>---search for character in memory
4
5INDEX
6 memchr
7
8ANSI_SYNOPSIS
9 #include <string.h>
10 void * memchr(const void *<[s1]>, int <[c]>, size_t <[n]>);
11
12TRAD_SYNOPSIS
13 #include <string.h>
14 void * memchr(<[s1]>, <[c]>, <[n]>);
15 void *<[string]>;
16 int *<[c]>;
17 size_t *<[n]>;
18
19DESCRIPTION
20 This function scans the first <[n]> bytes of the memory pointed
21 to by <[s1]> for the character <[c]> (converted to a char).
22
23RETURNS
24 Returns a pointer to the matching byte, or a null pointer if
25 <[c]> does not occur in <[s1]>.
26
27PORTABILITY
28<<memchr>> is ANSI C.
29
30<<memchr>> requires no supporting OS subroutines.
31
32QUICKREF
33 memchr ansi pure
34*/
35
36#include <string.h>
37#include <limits.h>
38
39/* Nonzero if X is not aligned on a "long" boundary. */
40#define UNALIGNED(X) ((long)X & (sizeof (long) - 1))
41
42/* How many bytes are loaded each iteration of the word copy loop. */
43#define LBLOCKSIZE (sizeof (long))
44
45#if LONG_MAX == 2147483647L
46#define DETECTNULL(X) (((X) - 0x01010101) & ~(X) & 0x80808080)
47#else
48#if LONG_MAX == 9223372036854775807L
49/* Nonzero if X (a long int) contains a NULL byte. */
50#define DETECTNULL(X) (((X) - 0x0101010101010101) & ~(X) & 0x8080808080808080)
51#else
52#error long int is not a 32bit or 64bit type.
53#endif
54#endif
55
56/* DETECTCHAR returns nonzero if (long)X contains the byte used
57 to fill (long)MASK. */
58#define DETECTCHAR(X,MASK) (DETECTNULL(X ^ MASK))
59
60void *
61_DEFUN (memchr, (s1, i, n),
62 _CONST void *s1 _AND
63 int i _AND size_t n)
64{
65 _CONST unsigned char *s = (_CONST unsigned char *)s1;
66#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
67 unsigned char c = (unsigned char)i;
68
69 while (n-- > 0)
70 {
71 if (*s == c)
72 {
73 return (void *)s;
74 }
75 s++;
76 }
77
78 return NULL;
79#else
80 unsigned char c = (unsigned char)i;
81 unsigned long mask,j;
82 unsigned long *aligned_addr;
83
84 if (!UNALIGNED (s))
85 {
86 mask = 0;
87 for (j = 0; j < LBLOCKSIZE; j++)
88 mask = (mask << 8) | c;
89
90 aligned_addr = (unsigned long*)s;
91 while ((!DETECTCHAR (*aligned_addr, mask)) && (n>LBLOCKSIZE))
92 {
93 aligned_addr++;
94 n -= LBLOCKSIZE;
95 }
96
97 /* The block of bytes currently pointed to by aligned_addr
98 may contain the target character or there may be less than
99 LBLOCKSIZE bytes left to search. We check the last few
100 bytes using the bytewise search. */
101
102 s = (unsigned char*)aligned_addr;
103 }
104
105 while (n-- > 0)
106 {
107 if (*s == c)
108 {
109 return (void *)s;
110 }
111 s++;
112 }
113
114 return NULL;
115#endif /* not PREFER_SIZE_OVER_SPEED */
116}
diff --git a/firmware/export/id3.h b/firmware/export/id3.h
index a1fff903f0..8fd41a1286 100644
--- a/firmware/export/id3.h
+++ b/firmware/export/id3.h
@@ -78,6 +78,9 @@ struct mp3entry {
78 unsigned int length; /* song length */ 78 unsigned int length; /* song length */
79 unsigned int elapsed; /* ms played */ 79 unsigned int elapsed; /* ms played */
80 80
81 /* Added for Vorbis */
82 unsigned long samples; /* number of samples in track */
83
81 /* MP3 stream specific info */ 84 /* MP3 stream specific info */
82 long bpf; /* bytes per frame */ 85 long bpf; /* bytes per frame */
83 long tpf; /* time per frame */ 86 long tpf; /* time per frame */