summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/codecs/Makefile1
-rw-r--r--apps/codecs/SOURCES1
-rw-r--r--apps/codecs/aiff.c314
-rw-r--r--apps/metadata.c81
-rw-r--r--apps/playback.c5
-rw-r--r--apps/tree.c4
6 files changed, 404 insertions, 2 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile
index d0cd97db9b..915e922278 100644
--- a/apps/codecs/Makefile
+++ b/apps/codecs/Makefile
@@ -63,6 +63,7 @@ $(OBJDIR)/wavpack.elf: $(OBJDIR)/wavpack.o $(CODECDEPS) $(BUILDDIR)/libwavpack.a
63$(OBJDIR)/alac.elf: $(OBJDIR)/alac.o $(CODECDEPS) $(BUILDDIR)/libalac.a $(BUILDDIR)/libm4a.a 63$(OBJDIR)/alac.elf: $(OBJDIR)/alac.o $(CODECDEPS) $(BUILDDIR)/libalac.a $(BUILDDIR)/libm4a.a
64$(OBJDIR)/aac.elf: $(OBJDIR)/aac.o $(CODECDEPS) $(BUILDDIR)/libfaad.a $(BUILDDIR)/libm4a.a 64$(OBJDIR)/aac.elf: $(OBJDIR)/aac.o $(CODECDEPS) $(BUILDDIR)/libfaad.a $(BUILDDIR)/libm4a.a
65$(OBJDIR)/shorten.elf: $(OBJDIR)/shorten.o $(CODECDEPS) $(BUILDDIR)/libffmpegFLAC.a 65$(OBJDIR)/shorten.elf: $(OBJDIR)/shorten.o $(CODECDEPS) $(BUILDDIR)/libffmpegFLAC.a
66$(OBJDIR)/aiff.elf: $(OBJDIR)/aiff.o $(CODECDEPS)
66 67
67$(OBJDIR)/%.elf: $(OBJDIR)/%.o $(CODECDEPS) 68$(OBJDIR)/%.elf: $(OBJDIR)/%.o $(CODECDEPS)
68 $(ELFIT) 69 $(ELFIT)
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES
index 911ee3b705..05172d6da9 100644
--- a/apps/codecs/SOURCES
+++ b/apps/codecs/SOURCES
@@ -11,4 +11,5 @@ alac.c
11aac.c 11aac.c
12#endif 12#endif
13shorten.c 13shorten.c
14aiff.c
14#endif 15#endif
diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c
new file mode 100644
index 0000000000..091e621bb7
--- /dev/null
+++ b/apps/codecs/aiff.c
@@ -0,0 +1,314 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2005 Jvo Studer
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#include "codeclib.h"
21#include "inttypes.h"
22
23CODEC_HEADER
24
25struct codec_api* rb;
26
27/* This codec supports AIFF files with the following formats:
28 * - PCM, 8 and 16 bits, mono or stereo
29 */
30
31enum
32{
33 AIFF_FORMAT_PCM = 0x0001, /* AIFF PCM Format (big endian) */
34 IEEE_FORMAT_FLOAT = 0x0003, /* IEEE Float */
35 AIFF_FORMAT_ALAW = 0x0004, /* AIFC ALaw compressed */
36 AIFF_FORMAT_ULAW = 0x0005 /* AIFC uLaw compressed */
37};
38
39/* Maximum number of bytes to process in one iteration */
40/* for 44.1kHz stereo 16bits, this represents 0.023s ~= 1/50s */
41#define AIF_CHUNK_SIZE (1024*2)
42
43#ifdef USE_IRAM
44extern char iramcopy[];
45extern char iramstart[];
46extern char iramend[];
47extern char iedata[];
48extern char iend[];
49#endif
50
51static int16_t int16_samples[AIF_CHUNK_SIZE] IBSS_ATTR;
52
53
54/* this is the codec entry point */
55enum codec_status codec_start(struct codec_api* api)
56{
57 struct codec_api* ci;
58 uint32_t numbytes, bytesdone;
59 uint16_t numChannels = 0;
60 uint32_t numSampleFrames = 0;
61 uint16_t sampleSize = 0;
62 uint32_t sampleRate = 0;
63 uint32_t i;
64 size_t n, aifbufsize;
65 int endofstream;
66 unsigned char* buf;
67 uint16_t* aifbuf;
68 long chunksize;
69 uint32_t offset2snd = 0;
70 uint16_t blockSize = 0;
71 uint32_t avgbytespersec = 0;
72 off_t firstblockposn; /* position of the first block in file */
73 int shortorlong = 1; /* do we output shorts (1) or longs (2)? */
74 int32_t * const int32_samples = (int32_t*)int16_samples;
75
76 /* Generic codec initialisation */
77 rb = api;
78 ci = api;
79
80#ifdef USE_IRAM
81 ci->memcpy(iramstart, iramcopy, iramend-iramstart);
82 ci->memset(iedata, 0, iend - iedata);
83#endif
84
85 ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
86 ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));
87
88 ci->configure(DSP_DITHER, (bool *)false);
89
90 next_track:
91
92 if (codec_init(api)) {
93 i = CODEC_ERROR;
94 goto exit;
95 }
96
97 while (!*ci->taginfo_ready)
98 ci->yield();
99
100 /* assume the AIFF header is less than 1024 bytes */
101 buf=ci->request_buffer((long *)&n,1024);
102 if (n<44) {
103 i = CODEC_ERROR;
104 goto exit;
105 }
106 if ((memcmp(buf,"FORM",4)!=0) || (memcmp(&buf[8],"AIFF",4)!=0)) {
107 i = CODEC_ERROR;
108 goto exit;
109 }
110
111 buf += 12;
112 n -= 12;
113 numbytes = 0;
114
115 /* read until 'SSND' chunk, which typically is last */
116 while(numbytes == 0 && n >= 8) {
117 /* chunkSize */
118 i = ((buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]);
119 if (memcmp(buf,"COMM",4)==0) {
120 if (i != 18) {
121 DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu != 18\n",i);
122 i = CODEC_ERROR;
123 goto exit;
124 }
125 /* numChannels */
126 numChannels = ((buf[8]<<8)|buf[9]);
127 /* numSampleFrames */
128 numSampleFrames = ((buf[10]<<24)|(buf[11]<<16)|(buf[12]<<8)|buf[13]);
129 /* sampleSize */
130 sampleSize = ((buf[14]<<8)|buf[15]);
131 /* sampleRate (don't use last 4 bytes, only integer fs) */
132 if (buf[16] != 0x40) {
133 DEBUGF("CODEC_ERROR: wierd sampling rate (no @)\n",i);
134 i = CODEC_ERROR;
135 goto exit;
136 }
137 sampleRate = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1;
138 sampleRate = sampleRate >> (16+14-buf[17]);
139 /* calc average bytes per second */
140 avgbytespersec = sampleRate*numChannels*sampleSize/8;
141 }
142 else if (memcmp(buf,"SSND",4)==0) {
143 if (sampleSize == 0) {
144 DEBUGF("CODEC_ERROR: unsupported chunk order\n");
145 i = CODEC_ERROR;
146 goto exit;
147 }
148 /* offset2snd */
149 offset2snd = ((buf[8]<<8)|buf[9]);
150 /* blockSize */
151 blockSize = ((buf[10]<<8)|buf[11]);
152 if (blockSize == 0)
153 blockSize = numChannels*sampleSize;
154 numbytes = i-8-offset2snd;
155 i = 8+offset2snd; /* advance to the beginning of data */
156 }
157 else {
158 DEBUGF("unsupported AIFF chunk: '%c%c%c%c', size=%lu\n",
159 buf[0], buf[1], buf[2], buf[3], i);
160 }
161
162 if (i & 0x01) /* odd chunk sizes must be padded */
163 i++;
164 buf += i+8;
165 if (n < (i+8)) {
166 DEBUGF("CODEC_ERROR: AIFF header size > 1024\n");
167 i = CODEC_ERROR;
168 goto exit;
169 }
170 n -= i+8;
171 } /* while 'SSND' */
172
173 if (numChannels == 0) {
174 DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n");
175 i = CODEC_ERROR;
176 goto exit;
177 }
178 if (numbytes == 0) {
179 DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n");
180 i = CODEC_ERROR;
181 goto exit;
182 }
183 if (sampleSize > 24) {
184 DEBUGF("CODEC_ERROR: PCM with more than 24 bits per sample "
185 "is unsupported\n");
186 i = CODEC_ERROR;
187 goto exit;
188 }
189
190 ci->configure(CODEC_DSP_ENABLE, (bool *)true);
191 ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency));
192
193 if (sampleSize <= 16) {
194 ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16));
195 } else {
196 shortorlong = 2;
197 ci->configure(DSP_DITHER, (bool *)false);
198 ci->configure(DSP_SET_SAMPLE_DEPTH, (long *) (32));
199 ci->configure(DSP_SET_CLIP_MAX, (long *) (2147483647));
200 ci->configure(DSP_SET_CLIP_MIN, (long *) (-2147483647-1));
201 }
202
203 if (numChannels == 2) {
204 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED);
205 } else if (numChannels == 1) {
206 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_MONO);
207 } else {
208 DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n");
209 i = CODEC_ERROR;
210 goto exit;
211 }
212
213 firstblockposn = (1024-n);
214 ci->advance_buffer(firstblockposn);
215
216 /* The main decoder loop */
217
218 bytesdone=0;
219 ci->set_elapsed(0);
220 endofstream=0;
221 /* chunksize is computed so that one chunk is about 1/50s.
222 * this make 4096 for 44.1kHz 16bits stereo.
223 * It also has to be a multiple of blockalign */
224 chunksize = (1 + avgbytespersec / (50*blockSize)) * blockSize;
225 /* check that the output buffer is big enough (convert to samplespersec,
226 then round to the blockSize multiple below) */
227 if (((uint64_t)chunksize*ci->id3->frequency*numChannels*shortorlong)
228 / (uint64_t)avgbytespersec >= AIF_CHUNK_SIZE) {
229 chunksize = ((uint64_t)AIF_CHUNK_SIZE * avgbytespersec
230 / ((uint64_t)ci->id3->frequency * numChannels * shortorlong
231 * blockSize)) * blockSize;
232 }
233
234 while (!endofstream) {
235 uint8_t *aifbuf8;
236
237 ci->yield();
238 if (ci->stop_codec || ci->reload_codec) {
239 break;
240 }
241
242 if (ci->seek_time) {
243 uint32_t newpos;
244
245 /* use avgbytespersec to round to the closest blockalign multiple,
246 add firstblockposn. 64-bit casts to avoid overflows. */
247 newpos = (((uint64_t)avgbytespersec * (ci->seek_time - 1))
248 / (1000LL*blockSize)) * blockSize;
249 if (newpos > numbytes)
250 break;
251 if (ci->seek_buffer(firstblockposn + newpos)) {
252 bytesdone = newpos;
253 }
254 ci->seek_complete();
255 }
256 aifbuf=ci->request_buffer((long *)&n,chunksize);
257 aifbuf8 = (uint8_t*)aifbuf;
258
259 if (n==0)
260 break; /* End of stream */
261
262 if (bytesdone + n > numbytes) {
263 n = numbytes - bytesdone;
264 endofstream = 1;
265 }
266
267 aifbufsize = sizeof(int16_samples);
268
269 if (sampleSize > 24) {
270 for (i=0;i<n;i+=4) {
271 int32_samples[i/4]=(int32_t)((aifbuf8[i]<<24)|
272 (aifbuf8[i+1]<<16)|(aifbuf8[i+2]<<8)|aifbuf8[i+3]);
273 }
274 aifbufsize = n;
275 } else if (sampleSize > 16) {
276 for (i=0;i<n;i+=3) {
277 int32_samples[i/3]=(int32_t)((aifbuf8[i]<<24)|
278 (aifbuf8[i+1]<<16)|(aifbuf8[i+2]<<8));
279 }
280 aifbufsize = n*4/3;
281 } else if (sampleSize > 8) {
282 /* copy data. */
283 for (i=0;i<n;i+=2) {
284 int16_samples[i/2]=(int16_t)((aifbuf8[i]<<8)|aifbuf8[i+1]);
285 }
286 aifbufsize = n;
287 } else {
288 for (i=0;i<n;i++) {
289 int16_samples[i] = (aifbuf8[i]<<8) - 0x8000;
290 }
291 aifbufsize = n*2;
292 }
293
294 while (!ci->pcmbuf_insert((char*)int16_samples, aifbufsize)) {
295 ci->yield();
296 }
297
298 ci->advance_buffer(n);
299 bytesdone += n;
300 if (bytesdone >= numbytes) {
301 endofstream=1;
302 }
303
304 ci->set_elapsed(bytesdone*1000LL/avgbytespersec);
305 }
306
307 if (ci->request_next_track())
308 goto next_track;
309
310 i = CODEC_OK;
311exit:
312 return i;
313}
314
diff --git a/apps/metadata.c b/apps/metadata.c
index 41ea0196c0..531969b8aa 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -78,6 +78,8 @@ static const struct format_list formats[] =
78 { AFMT_ALAC, "m4a" }, 78 { AFMT_ALAC, "m4a" },
79 { AFMT_AAC, "mp4" }, 79 { AFMT_AAC, "mp4" },
80 { AFMT_SHN, "shn" }, 80 { AFMT_SHN, "shn" },
81 { AFMT_AIFF, "aif" },
82 { AFMT_AIFF, "aiff" },
81}; 83};
82 84
83static const unsigned short a52_bitrates[] = 85static const unsigned short a52_bitrates[] =
@@ -894,7 +896,6 @@ static bool get_wave_metadata(int fd, struct mp3entry* id3)
894} 896}
895 897
896 898
897
898static bool get_m4a_metadata(int fd, struct mp3entry* id3) 899static bool get_m4a_metadata(int fd, struct mp3entry* id3)
899{ 900{
900 unsigned char* buf; 901 unsigned char* buf;
@@ -1245,6 +1246,76 @@ static bool get_musepack_metadata(int fd, struct mp3entry *id3)
1245 return true; 1246 return true;
1246} 1247}
1247 1248
1249static bool get_aiff_metadata(int fd, struct mp3entry* id3)
1250{
1251 /* Use the trackname part of the id3 structure as a temporary buffer */
1252 unsigned char* buf = id3->path;
1253 unsigned long numChannels = 0;
1254 unsigned long numSampleFrames = 0;
1255 unsigned long sampleSize = 0;
1256 unsigned long sampleRate = 0;
1257 unsigned long numbytes = 0;
1258 int read_bytes;
1259 int i;
1260
1261 if ((lseek(fd, 0, SEEK_SET) < 0)
1262 || ((read_bytes = read(fd, buf, sizeof(id3->path))) < 44))
1263 {
1264 return false;
1265 }
1266
1267 if ((memcmp(buf, "FORM",4) != 0)
1268 || (memcmp(&buf[8], "AIFF", 4) !=0 ))
1269 {
1270 return false;
1271 }
1272
1273 buf += 12;
1274 read_bytes -= 12;
1275
1276 while ((numbytes == 0) && (read_bytes >= 8))
1277 {
1278 /* chunkSize */
1279 i = ((buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7]);
1280
1281 if (memcmp(buf, "COMM", 4) == 0)
1282 {
1283 /* numChannels */
1284 numChannels = ((buf[8]<<8)|buf[9]);
1285 /* numSampleFrames */
1286 numSampleFrames =((buf[10]<<24)|(buf[11]<<16)|(buf[12]<<8)|buf[13]);
1287 /* sampleSize */
1288 sampleSize = ((buf[14]<<8)|buf[15]);
1289 /* sampleRate */
1290 sampleRate = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21]);
1291 sampleRate = sampleRate >> (16+14-buf[17]);
1292 /* save format infos */
1293 id3->bitrate = (sampleSize * numChannels * sampleRate) / 1000;
1294 id3->frequency = sampleRate;
1295 id3->length = (numSampleFrames / id3->frequency) * 1000;
1296 id3->vbr = false; /* AIFF files are CBR */
1297 id3->filesize = filesize(fd);
1298 }
1299 else if (memcmp(buf, "SSND", 4) == 0)
1300 {
1301 numbytes = i - 8;
1302 }
1303
1304 if (i & 0x01)
1305 {
1306 i++; /* odd chunk sizes must be padded */
1307 }
1308 buf += i + 8;
1309 read_bytes -= i + 8;
1310 }
1311
1312 if ((numbytes == 0) || (numChannels == 0))
1313 {
1314 return false;
1315 }
1316 return true;
1317}
1318
1248/* Simple file type probing by looking at the filename extension. */ 1319/* Simple file type probing by looking at the filename extension. */
1249static unsigned int probe_file_format(const char *filename) 1320static unsigned int probe_file_format(const char *filename)
1250{ 1321{
@@ -1448,6 +1519,14 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
1448 /* TODO: read the id3v2 header if it exists */ 1519 /* TODO: read the id3v2 header if it exists */
1449 break; 1520 break;
1450 1521
1522 case AFMT_AIFF:
1523 if (!get_aiff_metadata(fd, &(track->id3)))
1524 {
1525 return false;
1526 }
1527
1528 break;
1529
1451 default: 1530 default:
1452 /* If we don't know how to read the metadata, assume we can't play 1531 /* If we don't know how to read the metadata, assume we can't play
1453 the file */ 1532 the file */
diff --git a/apps/playback.c b/apps/playback.c
index 22ee3362c4..5ed6c5e00c 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -81,6 +81,7 @@ static volatile bool paused;
81#define CODEC_ALAC "/.rockbox/codecs/alac.codec" 81#define CODEC_ALAC "/.rockbox/codecs/alac.codec"
82#define CODEC_AAC "/.rockbox/codecs/aac.codec" 82#define CODEC_AAC "/.rockbox/codecs/aac.codec"
83#define CODEC_SHN "/.rockbox/codecs/shorten.codec" 83#define CODEC_SHN "/.rockbox/codecs/shorten.codec"
84#define CODEC_AIFF "/.rockbox/codecs/aiff.codec"
84 85
85#define AUDIO_DEFAULT_FIRST_LIMIT (1024*1024*10) 86#define AUDIO_DEFAULT_FIRST_LIMIT (1024*1024*10)
86#define AUDIO_FILL_CYCLE (1024*256) 87#define AUDIO_FILL_CYCLE (1024*256)
@@ -950,6 +951,10 @@ static bool loadcodec(bool start_play)
950 logf("Codec: SHN"); 951 logf("Codec: SHN");
951 codec_path = CODEC_SHN; 952 codec_path = CODEC_SHN;
952 break; 953 break;
954 case AFMT_AIFF:
955 logf("Codec: PCM AIFF");
956 codec_path = CODEC_AIFF;
957 break;
953 default: 958 default:
954 logf("Codec: Unsupported"); 959 logf("Codec: Unsupported");
955 codec_path = NULL; 960 codec_path = NULL;
diff --git a/apps/tree.c b/apps/tree.c
index 15624d7618..74c1059a60 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -85,7 +85,7 @@ const struct filetype filetypes[] = {
85 { "ogg", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 85 { "ogg", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
86 { "wma", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 86 { "wma", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
87 { "wav", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 87 { "wav", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
88 { "flac", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 88 { "flac",TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
89 { "ac3", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 89 { "ac3", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
90 { "a52", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 90 { "a52", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
91 { "mpc", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 91 { "mpc", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
@@ -93,6 +93,8 @@ const struct filetype filetypes[] = {
93 { "m4a", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 93 { "m4a", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
94 { "mp4", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 94 { "mp4", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
95 { "shn", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 95 { "shn", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
96 { "aif", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
97 { "aiff",TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
96#endif 98#endif
97 { "m3u", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, 99 { "m3u", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
98 { "cfg", TREE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG }, 100 { "cfg", TREE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG },