1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2010 Yoshihisa Uchida
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include "codecs/libpcm/support_formats.h"
27 /* Sun Audio file (Au file format) codec
30 * [1] Sun Microsystems, Inc., Header file for Audio, .au, 1992
31 * URL http://www.opengroup.org/public/pubs/external/auformat.html
32 * [2] Wikipedia, Au file format, URL: http://en.wikipedia.org/wiki/Sun_Audio
35 #define PCM_SAMPLE_SIZE (1024*2)
37 static int32_t samples
[PCM_SAMPLE_SIZE
] IBSS_ATTR
;
41 AU_FORMAT_UNSUPPORT
= 0, /* unsupported format */
42 AU_FORMAT_MULAW
, /* G.711 MULAW */
43 AU_FORMAT_PCM
, /* Linear PCM */
44 AU_FORMAT_IEEE_FLOAT
, /* IEEE float */
45 AU_FORMAT_ALAW
, /* G.711 ALAW */
48 static const char support_formats
[9][2] = {
49 { AU_FORMAT_UNSUPPORT
, 0 }, /* encoding */
50 { AU_FORMAT_MULAW
, 8 }, /* 1: G.711 MULAW */
51 { AU_FORMAT_PCM
, 8 }, /* 2: Linear PCM 8bit (signed) */
52 { AU_FORMAT_PCM
, 16 }, /* 3: Linear PCM 16bit (signed, big endian) */
53 { AU_FORMAT_PCM
, 24 }, /* 4: Linear PCM 24bit (signed, big endian) */
54 { AU_FORMAT_PCM
, 32 }, /* 5: Linear PCM 32bit (signed, big endian) */
55 { AU_FORMAT_IEEE_FLOAT
, 32 }, /* 6: Linear PCM float 32bit (signed, big endian) */
56 { AU_FORMAT_IEEE_FLOAT
, 64 }, /* 7: Linear PCM float 64bit (signed, big endian) */
57 /* encoding 8 - 26 unsupported. */
58 { AU_FORMAT_ALAW
, 8 }, /* 27: G.711 ALAW */
61 const struct pcm_entry au_codecs
[] = {
62 { AU_FORMAT_MULAW
, get_itut_g711_mulaw_codec
},
63 { AU_FORMAT_PCM
, get_linear_pcm_codec
},
64 { AU_FORMAT_IEEE_FLOAT
, get_ieee_float_codec
},
65 { AU_FORMAT_ALAW
, get_itut_g711_alaw_codec
},
70 static const struct pcm_codec
*get_au_codec(uint32_t formattag
)
74 for (i
= 0; i
< NUM_FORMATS
; i
++)
76 if (au_codecs
[i
].format_tag
== formattag
)
78 if (au_codecs
[i
].get_codec
)
79 return au_codecs
[i
].get_codec();
86 static unsigned int get_be32(uint8_t *buf
)
88 return (buf
[0] << 24) | (buf
[1] << 16) | (buf
[2] << 8) | buf
[3];
91 static int convert_au_format(unsigned int encoding
, struct pcm_format
*fmt
)
93 fmt
->formattag
= AU_FORMAT_UNSUPPORT
;
96 fmt
->formattag
= support_formats
[encoding
][0];
97 fmt
->bitspersample
= support_formats
[encoding
][1];
99 else if (encoding
== 27)
101 fmt
->formattag
= support_formats
[8][0];
102 fmt
->bitspersample
= support_formats
[8][1];
105 return fmt
->formattag
;
108 /* this is the codec entry point */
109 enum codec_status
codec_main(void)
111 int status
= CODEC_OK
;
112 struct pcm_format format
;
113 uint32_t bytesdone
, decodedsamples
;
119 off_t firstblockposn
; /* position of the first block in file */
120 const struct pcm_codec
*codec
;
123 /* Generic codec initialisation */
124 ci
->configure(DSP_SET_SAMPLE_DEPTH
, PCM_OUTPUT_DEPTH
-1);
128 DEBUGF("codec_init() error\n");
129 status
= CODEC_ERROR
;
133 while (!*ci
->taginfo_ready
&& !ci
->stop_codec
)
136 codec_set_replaygain(ci
->id3
);
138 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
139 bytesdone
= ci
->id3
->offset
;
141 ci
->memset(&format
, 0, sizeof(struct pcm_format
));
142 format
.is_signed
= true;
143 format
.is_little_endian
= false;
146 buf
= ci
->request_buffer(&n
, 24);
147 if (n
< 24 || (memcmp(buf
, ".snd", 4) != 0))
150 * headerless sun audio file
151 * It is decoded under conditions.
152 * format: G.711 mu-law
154 * frequency: 8000 kHz
157 format
.formattag
= AU_FORMAT_MULAW
;
159 format
.bitspersample
= 8;
160 format
.numbytes
= ci
->id3
->filesize
;
167 offset
= get_be32(buf
+ 4);
170 DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset
);
171 status
= CODEC_ERROR
;
175 format
.numbytes
= get_be32(buf
+ 8);
176 if (format
.numbytes
== (uint32_t)0xffffffff)
177 format
.numbytes
= ci
->id3
->filesize
- offset
;
179 format
.formattag
= convert_au_format(get_be32(buf
+ 12), &format
);
180 if (format
.formattag
== AU_FORMAT_UNSUPPORT
)
182 DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf
+ 12));
183 status
= CODEC_ERROR
;
186 /* skip sample rate */
187 format
.channels
= get_be32(buf
+ 20);
190 /* advance to first WAVE chunk */
191 ci
->advance_buffer(offset
);
193 firstblockposn
= offset
;
199 codec
= get_au_codec(format
.formattag
);
202 DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format
.formattag
);
203 status
= CODEC_ERROR
;
207 if (!codec
->set_format(&format
))
209 status
= CODEC_ERROR
;
213 if (format
.numbytes
== 0) {
214 DEBUGF("CODEC_ERROR: data size is 0\n");
215 status
= CODEC_ERROR
;
219 /* check chunksize */
220 if ((format
.chunksize
/ format
.blockalign
) * format
.samplesperblock
* format
.channels
222 format
.chunksize
= (PCM_SAMPLE_SIZE
/ format
.blockalign
) * format
.blockalign
;
223 if (format
.chunksize
== 0)
225 DEBUGF("CODEC_ERROR: chunksize is 0\n");
226 status
= CODEC_ERROR
;
230 ci
->configure(DSP_SWITCH_FREQUENCY
, ci
->id3
->frequency
);
231 if (format
.channels
== 2) {
232 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_INTERLEAVED
);
233 } else if (format
.channels
== 1) {
234 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_MONO
);
236 DEBUGF("CODEC_ERROR: more than 2 channels\n");
237 status
= CODEC_ERROR
;
241 /* make sure we're at the correct offset */
242 if (bytesdone
> (uint32_t) firstblockposn
) {
243 /* Round down to previous block */
244 struct pcm_pos
*newpos
= codec
->get_seek_pos(bytesdone
- firstblockposn
,
247 if (newpos
->pos
> format
.numbytes
)
249 if (ci
->seek_buffer(firstblockposn
+ newpos
->pos
))
251 bytesdone
= newpos
->pos
;
252 decodedsamples
= newpos
->samples
;
256 /* already where we need to be */
260 /* The main decoder loop */
263 while (!endofstream
) {
265 if (ci
->stop_codec
|| ci
->new_track
) {
270 /* 3rd args(read_buffer) is unnecessary in the format which Sun Audio supports. */
271 struct pcm_pos
*newpos
= codec
->get_seek_pos(ci
->seek_time
, PCM_SEEK_TIME
, NULL
);
273 if (newpos
->pos
> format
.numbytes
)
275 if (ci
->seek_buffer(firstblockposn
+ newpos
->pos
))
277 bytesdone
= newpos
->pos
;
278 decodedsamples
= newpos
->samples
;
283 aubuf
= (uint8_t *)ci
->request_buffer(&n
, format
.chunksize
);
285 break; /* End of stream */
286 if (bytesdone
+ n
> format
.numbytes
) {
287 n
= format
.numbytes
- bytesdone
;
291 status
= codec
->decode(aubuf
, n
, samples
, &bufcount
);
292 if (status
== CODEC_ERROR
)
294 DEBUGF("codec error\n");
298 ci
->pcmbuf_insert(samples
, NULL
, bufcount
);
299 ci
->advance_buffer(n
);
301 decodedsamples
+= bufcount
;
303 if (bytesdone
>= format
.numbytes
)
305 ci
->set_elapsed(decodedsamples
*1000LL/ci
->id3
->frequency
);
310 if (ci
->request_next_track())