diff options
author | Dave Chapman <dave@dchapman.com> | 2005-09-22 21:55:37 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2005-09-22 21:55:37 +0000 |
commit | 139c1cb82491886f600ef5014b79acb49f2c510c (patch) | |
tree | 4d65d0037983b78007919268fe04b9c56438458e /apps/metadata.c | |
parent | 8026f0fe05cafc2adfb75b90e9bd65b9ea1f02cf (diff) | |
download | rockbox-139c1cb82491886f600ef5014b79acb49f2c510c.tar.gz rockbox-139c1cb82491886f600ef5014b79acb49f2c510c.zip |
First version of ALAC (Apple Lossless) decoder
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7547 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/metadata.c')
-rw-r--r-- | apps/metadata.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/apps/metadata.c b/apps/metadata.c index 2883b01407..b8fbd65738 100644 --- a/apps/metadata.c +++ b/apps/metadata.c | |||
@@ -75,6 +75,7 @@ static const struct format_list formats[] = | |||
75 | { AFMT_A52, "a52" }, | 75 | { AFMT_A52, "a52" }, |
76 | { AFMT_A52, "ac3" }, | 76 | { AFMT_A52, "ac3" }, |
77 | { AFMT_WAVPACK, "wv" }, | 77 | { AFMT_WAVPACK, "wv" }, |
78 | { AFMT_ALAC, "m4a" }, | ||
78 | }; | 79 | }; |
79 | 80 | ||
80 | static const unsigned short a52_bitrates[] = | 81 | static const unsigned short a52_bitrates[] = |
@@ -180,6 +181,30 @@ static void convert_endian(void *data, const char *format) | |||
180 | } | 181 | } |
181 | } | 182 | } |
182 | 183 | ||
184 | /* read_uint32be() - read an unsigned integer from a big-endian | ||
185 | (e.g. Quicktime) file. This is used by the .m4a parser | ||
186 | */ | ||
187 | #ifdef ROCKBOX_BIG_ENDIAN | ||
188 | #define read_uint32be(fd,buf) read((fd),(buf),4) | ||
189 | #else | ||
190 | int read_uint32be(int fd, unsigned int* buf) { | ||
191 | char tmp; | ||
192 | char* p=(char*)buf; | ||
193 | size_t n; | ||
194 | |||
195 | n=read(fd,tmp,4); | ||
196 | if (n==4) { | ||
197 | tmp=p[0]; | ||
198 | p[0]=p[3]; | ||
199 | p[1]=p[2]; | ||
200 | p[2]=p[1]; | ||
201 | p[3]=tmp; | ||
202 | } | ||
203 | |||
204 | return(n); | ||
205 | } | ||
206 | #endif | ||
207 | |||
183 | /* Read an unaligned 32-bit little endian long from buffer. */ | 208 | /* Read an unaligned 32-bit little endian long from buffer. */ |
184 | static unsigned long get_long(void* buf) | 209 | static unsigned long get_long(void* buf) |
185 | { | 210 | { |
@@ -264,6 +289,37 @@ static void convert_utf8(char* utf8) | |||
264 | *dest = 0; | 289 | *dest = 0; |
265 | } | 290 | } |
266 | 291 | ||
292 | |||
293 | /* Read a string tag from an M4A file */ | ||
294 | void read_m4a_tag_string(int fd, int len,char** bufptr,size_t* bytes_remaining, char** dest) | ||
295 | { | ||
296 | int data_length; | ||
297 | |||
298 | if (bytes_remaining==0) { | ||
299 | lseek(fd,len,SEEK_CUR); /* Skip everything */ | ||
300 | } else { | ||
301 | /* Skip the data tag header - maybe we should parse it properly? */ | ||
302 | lseek(fd,16,SEEK_CUR); | ||
303 | len-=16; | ||
304 | |||
305 | *dest=*bufptr; | ||
306 | if ((size_t)len+1 > *bytes_remaining) { | ||
307 | read(fd,*bufptr,*bytes_remaining-1); | ||
308 | lseek(fd,len-(*bytes_remaining-1),SEEK_CUR); | ||
309 | *bufptr+=(*bytes_remaining-1); | ||
310 | } else { | ||
311 | read(fd,*bufptr,len); | ||
312 | *bufptr+=len; | ||
313 | } | ||
314 | **bufptr=(char)0; | ||
315 | |||
316 | convert_utf8(*dest); | ||
317 | data_length = strlen(*dest)+1; | ||
318 | *bufptr=(*dest)+data_length; | ||
319 | *bytes_remaining-=data_length; | ||
320 | } | ||
321 | } | ||
322 | |||
267 | /* Parse the tag (the name-value pair) and fill id3 and buffer accordingly. | 323 | /* Parse the tag (the name-value pair) and fill id3 and buffer accordingly. |
268 | * String values to keep are written to buf. Returns number of bytes written | 324 | * String values to keep are written to buf. Returns number of bytes written |
269 | * to buf (including end nil). | 325 | * to buf (including end nil). |
@@ -887,6 +943,280 @@ static bool get_wave_metadata(int fd, struct mp3entry* id3) | |||
887 | } | 943 | } |
888 | 944 | ||
889 | 945 | ||
946 | |||
947 | static bool get_alac_metadata(int fd, struct mp3entry* id3) | ||
948 | { | ||
949 | unsigned char* buf; | ||
950 | unsigned long totalsamples; | ||
951 | int i,j,k; | ||
952 | size_t n; | ||
953 | size_t bytes_remaining; | ||
954 | char* id3buf; | ||
955 | unsigned int compressedsize; | ||
956 | unsigned int sample_count; | ||
957 | unsigned int sample_duration; | ||
958 | int numentries; | ||
959 | int entry_size; | ||
960 | int size_remaining; | ||
961 | int chunk_len; | ||
962 | unsigned char chunk_id[4]; | ||
963 | int sub_chunk_len; | ||
964 | unsigned char sub_chunk_id[4]; | ||
965 | |||
966 | /* A simple parser to read vital metadata from an ALAC file. | ||
967 | This parser also works for AAC files - they are both stored in | ||
968 | a Quicktime M4A container. */ | ||
969 | |||
970 | /* Use the trackname part of the id3 structure as a temporary buffer */ | ||
971 | buf=id3->path; | ||
972 | |||
973 | lseek(fd, 0, SEEK_SET); | ||
974 | |||
975 | totalsamples=0; | ||
976 | compressedsize=0; | ||
977 | /* read the chunks - we stop when we find the mdat chunk and set compressedsize */ | ||
978 | while (compressedsize==0) { | ||
979 | n=read_uint32be(fd,&chunk_len); | ||
980 | |||
981 | // This means it was a 64-bit file, so we have problems. | ||
982 | if (chunk_len == 1) { | ||
983 | logf("need 64bit support\n"); | ||
984 | return false; | ||
985 | } | ||
986 | |||
987 | n=read(fd,&chunk_id,4); | ||
988 | if (memcmp(&chunk_id,"ftyp",4)==0) { | ||
989 | /* Check for M4A type */ | ||
990 | n=read(fd,&chunk_id,4); | ||
991 | if (memcmp(&chunk_id,"M4A ",4)!=0) { | ||
992 | logf("Not an M4A file, aborting\n"); | ||
993 | return false; | ||
994 | } | ||
995 | /* Skip rest of chunk */ | ||
996 | lseek(fd, chunk_len - 8 - 4, SEEK_CUR); /* FIXME not 8 */ | ||
997 | } else if (memcmp(&chunk_id,"moov",4)==0) { | ||
998 | size_remaining=chunk_len - 8; /* FIXME not 8 */ | ||
999 | |||
1000 | while (size_remaining > 0) { | ||
1001 | n=read_uint32be(fd,&sub_chunk_len); | ||
1002 | if ((sub_chunk_len < 1) || (sub_chunk_len > size_remaining)) { | ||
1003 | logf("Strange sub_chunk_len value inside moov: %d (remaining: %d)\n",sub_chunk_len,size_remaining); | ||
1004 | return false; | ||
1005 | } | ||
1006 | n=read(fd,&sub_chunk_id,4); | ||
1007 | size_remaining-=8; | ||
1008 | |||
1009 | if (memcmp(&sub_chunk_id,"mvhd",4)==0) { | ||
1010 | /* We don't need anything from here - skip */ | ||
1011 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1012 | size_remaining-=(sub_chunk_len-8); | ||
1013 | } else if (memcmp(&sub_chunk_id,"udta",4)==0) { | ||
1014 | /* The udta chunk contains the metadata - track, artist, album etc. | ||
1015 | The format appears to be: | ||
1016 | udta | ||
1017 | meta | ||
1018 | hdlr | ||
1019 | ilst | ||
1020 | .nam | ||
1021 | [rest of tags] | ||
1022 | free | ||
1023 | |||
1024 | NOTE: This code was written by examination of some .m4a files | ||
1025 | produced by iTunes v4.9 - it may not therefore be 100% | ||
1026 | compliant with all streams. But it should fail gracefully. | ||
1027 | */ | ||
1028 | j=(sub_chunk_len-8); | ||
1029 | size_remaining-=j; | ||
1030 | n=read_uint32be(fd,&sub_chunk_len); | ||
1031 | n=read(fd,&sub_chunk_id,4); | ||
1032 | j-=8; | ||
1033 | if (memcmp(&sub_chunk_id,"meta",4)==0) { | ||
1034 | lseek(fd, 4, SEEK_CUR); | ||
1035 | j-=4; | ||
1036 | n=read_uint32be(fd,&sub_chunk_len); | ||
1037 | n=read(fd,&sub_chunk_id,4); | ||
1038 | j-=8; | ||
1039 | if (memcmp(&sub_chunk_id,"hdlr",4)==0) { | ||
1040 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); | ||
1041 | j-=(sub_chunk_len - 8); | ||
1042 | n=read_uint32be(fd,&sub_chunk_len); | ||
1043 | n=read(fd,&sub_chunk_id,4); | ||
1044 | j-=8; | ||
1045 | if (memcmp(&sub_chunk_id,"ilst",4)==0) { | ||
1046 | /* Here are the actual tags. We use the id3v2 300-byte buffer | ||
1047 | to store the string data */ | ||
1048 | bytes_remaining=sizeof(id3->id3v2buf); | ||
1049 | id3->genre=255; /* Not every track is the Blues */ | ||
1050 | id3buf=id3->id3v2buf; | ||
1051 | k=sub_chunk_len-8; | ||
1052 | j-=k; | ||
1053 | while (k > 0) { | ||
1054 | n=read_uint32be(fd,&sub_chunk_len); | ||
1055 | n=read(fd,&sub_chunk_id,4); | ||
1056 | k-=8; | ||
1057 | if (memcmp(sub_chunk_id,"\251nam",4)==0) { | ||
1058 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->title); | ||
1059 | } else if (memcmp(sub_chunk_id,"\251ART",4)==0) { | ||
1060 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->artist); | ||
1061 | } else if (memcmp(sub_chunk_id,"\251alb",4)==0) { | ||
1062 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->album); | ||
1063 | } else if (memcmp(sub_chunk_id,"\251gen",4)==0) { | ||
1064 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->genre_string); | ||
1065 | } else if (memcmp(sub_chunk_id,"\251day",4)==0) { | ||
1066 | read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->year_string); | ||
1067 | } else if (memcmp(sub_chunk_id,"trkn",4)==0) { | ||
1068 | if (sub_chunk_len==0x20) { | ||
1069 | read(fd,buf,sub_chunk_len-8); | ||
1070 | id3->tracknum=buf[19]; | ||
1071 | } else { | ||
1072 | lseek(fd, sub_chunk_len-8,SEEK_CUR); | ||
1073 | } | ||
1074 | } else { | ||
1075 | lseek(fd, sub_chunk_len-8,SEEK_CUR); | ||
1076 | } | ||
1077 | k-=(sub_chunk_len-8); | ||
1078 | } | ||
1079 | } | ||
1080 | } | ||
1081 | } | ||
1082 | /* Skip any remaining data in udta chunk */ | ||
1083 | lseek(fd, j, SEEK_CUR); | ||
1084 | } else if (memcmp(&sub_chunk_id,"trak",4)==0) { | ||
1085 | /* Format of trak chunk: | ||
1086 | tkhd | ||
1087 | mdia | ||
1088 | mdhd | ||
1089 | hdlr | ||
1090 | minf | ||
1091 | smhd | ||
1092 | dinf | ||
1093 | stbl | ||
1094 | stsd - Samplerate, Samplesize, Numchannels | ||
1095 | stts - time_to_sample array - RLE'd table containing duration of each block | ||
1096 | stsz - sample_byte_size array - ?Size in bytes of each compressed block | ||
1097 | stsc - Seek table related? | ||
1098 | stco - Seek table related? | ||
1099 | */ | ||
1100 | |||
1101 | /* Skip tkhd - not needed */ | ||
1102 | n=read_uint32be(fd,&sub_chunk_len); | ||
1103 | n=read(fd,&sub_chunk_id,4); | ||
1104 | if (memcmp(&sub_chunk_id,"tkhd",4)!=0) { | ||
1105 | logf("Expecting tkhd\n"); | ||
1106 | return false; | ||
1107 | } | ||
1108 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1109 | size_remaining-=sub_chunk_len; | ||
1110 | |||
1111 | /* Process mdia */ | ||
1112 | n=read_uint32be(fd,&sub_chunk_len); | ||
1113 | n=read(fd,&sub_chunk_id,4); | ||
1114 | if (memcmp(&sub_chunk_id,"mdia",4)!=0) { | ||
1115 | logf("Expecting mdia\n"); | ||
1116 | return false; | ||
1117 | } | ||
1118 | size_remaining-=sub_chunk_len; | ||
1119 | j=sub_chunk_len-8; | ||
1120 | |||
1121 | while (j > 0) { | ||
1122 | n=read_uint32be(fd,&sub_chunk_len); | ||
1123 | n=read(fd,&sub_chunk_id,4); | ||
1124 | j-=4; | ||
1125 | if (memcmp(&sub_chunk_id,"minf",4)==0) { | ||
1126 | j=sub_chunk_len-8; | ||
1127 | } else if (memcmp(&sub_chunk_id,"stbl",4)==0) { | ||
1128 | j=sub_chunk_len-8; | ||
1129 | } else if (memcmp(&sub_chunk_id,"stsd",4)==0) { | ||
1130 | n=read(fd,buf,sub_chunk_len-8); | ||
1131 | j-=sub_chunk_len; | ||
1132 | i=0; | ||
1133 | /* Skip version and flags */ | ||
1134 | i+=4; | ||
1135 | |||
1136 | numentries=(buf[i]<<24)|(buf[i+1]<<16)|(buf[i+2]<<8)|buf[i+3]; | ||
1137 | i+=4; | ||
1138 | if (numentries!=1) { | ||
1139 | logf("ERROR: Expecting only one entry in stsd\n"); | ||
1140 | } | ||
1141 | |||
1142 | entry_size=(buf[i]<<24)|(buf[i+1]<<16)|(buf[i+2]<<8)|buf[i+3]; | ||
1143 | i+=4; | ||
1144 | |||
1145 | /* Check the codec type - 'alac' for ALAC, 'mp4a' for AAC */ | ||
1146 | if (memcmp(&buf[i],"alac",4)!=0) { | ||
1147 | logf("Not an ALAC file\n"); | ||
1148 | return false; | ||
1149 | } | ||
1150 | |||
1151 | //numchannels=(buf[i+20]<<8)|buf[i+21]; /* Not used - assume Stereo */ | ||
1152 | //samplesize=(buf[i+22]<<8)|buf[i+23]; /* Not used - assume 16-bit */ | ||
1153 | |||
1154 | /* Samplerate is 32-bit fixed point, but this works for < 65536 Hz */ | ||
1155 | id3->frequency=(buf[i+28]<<8)|buf[i+29]; | ||
1156 | } else if (memcmp(&sub_chunk_id,"stts",4)==0) { | ||
1157 | j-=sub_chunk_len; | ||
1158 | i=8; | ||
1159 | n=read(fd,buf,8); | ||
1160 | i+=8; | ||
1161 | numentries=(buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]; | ||
1162 | for (k=0;k<numentries;k++) { | ||
1163 | n=read_uint32be(fd,&sample_count); | ||
1164 | n=read_uint32be(fd,&sample_duration); | ||
1165 | totalsamples+=sample_count*sample_duration; | ||
1166 | i+=8; | ||
1167 | } | ||
1168 | if (i > 0) lseek(fd, sub_chunk_len - i, SEEK_CUR); | ||
1169 | } else if (memcmp(&sub_chunk_id,"stsz",4)==0) { | ||
1170 | j-=sub_chunk_len; | ||
1171 | i=8; | ||
1172 | n=read(fd,buf,8); | ||
1173 | i+=8; | ||
1174 | numentries=(buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]; | ||
1175 | for (k=0;k<numentries;k++) { | ||
1176 | n=read_uint32be(fd,&sample_count); | ||
1177 | n=read_uint32be(fd,&sample_duration); | ||
1178 | totalsamples+=sample_count*sample_duration; | ||
1179 | i+=8; | ||
1180 | } | ||
1181 | if (i > 0) lseek(fd, sub_chunk_len - i, SEEK_CUR); | ||
1182 | } else { | ||
1183 | lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1184 | j-=sub_chunk_len; | ||
1185 | } | ||
1186 | } | ||
1187 | } else { | ||
1188 | logf("Unexpected sub_chunk_id inside moov: %c%c%c%c\n", | ||
1189 | sub_chunk_id[0],sub_chunk_id[1],sub_chunk_id[2],sub_chunk_id[3]); | ||
1190 | return false; | ||
1191 | } | ||
1192 | } | ||
1193 | } else if (memcmp(&chunk_id,"mdat",4)==0) { | ||
1194 | /* once we hit mdat we stop reading and return. | ||
1195 | * this is on the assumption that there is no furhter interesting | ||
1196 | * stuff in the stream. if there is stuff will fail (:()). | ||
1197 | * But we need the read pointer to be at the mdat stuff | ||
1198 | * for the decoder. And we don't want to rely on fseek/ftell, | ||
1199 | * as they may not always be avilable */ | ||
1200 | lseek(fd, chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1201 | compressedsize=chunk_len-8; | ||
1202 | } else if (memcmp(&chunk_id,"free",4)==0) { | ||
1203 | /* these following atoms can be skipped !!!! */ | ||
1204 | lseek(fd, chunk_len - 8, SEEK_CUR); /* FIXME not 8 */ | ||
1205 | } else { | ||
1206 | logf("(top) unknown chunk id: %c%c%c%c\n", chunk_id[0],chunk_id[1],chunk_id[2],chunk_id[3]); | ||
1207 | return false; | ||
1208 | } | ||
1209 | } | ||
1210 | |||
1211 | id3->vbr=true; /* All ALAC files are VBR */ | ||
1212 | id3->filesize=filesize(fd); | ||
1213 | id3->samples=totalsamples; | ||
1214 | id3->length=(10*totalsamples)/(id3->frequency/100); | ||
1215 | id3->bitrate=(compressedsize*8)/id3->length;; | ||
1216 | |||
1217 | return true; | ||
1218 | } | ||
1219 | |||
890 | /* Simple file type probing by looking at the filename extension. */ | 1220 | /* Simple file type probing by looking at the filename extension. */ |
891 | unsigned int probe_file_format(const char *filename) | 1221 | unsigned int probe_file_format(const char *filename) |
892 | { | 1222 | { |
@@ -1064,6 +1394,14 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname, | |||
1064 | track->id3.length = (totalsamples / track->id3.frequency) * 1000; | 1394 | track->id3.length = (totalsamples / track->id3.frequency) * 1000; |
1065 | break; | 1395 | break; |
1066 | 1396 | ||
1397 | case AFMT_ALAC: | ||
1398 | if (!get_alac_metadata(fd, &(track->id3))) | ||
1399 | { | ||
1400 | // return false; | ||
1401 | } | ||
1402 | |||
1403 | break; | ||
1404 | |||
1067 | /* If we don't know how to read the metadata, just store the filename */ | 1405 | /* If we don't know how to read the metadata, just store the filename */ |
1068 | default: | 1406 | default: |
1069 | break; | 1407 | break; |