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 ci
->memset(&format
, 0, sizeof(struct pcm_format
));
139 format
.is_signed
= true;
140 format
.is_little_endian
= false;
143 buf
= ci
->request_buffer(&n
, 24);
144 if (n
< 24 || (memcmp(buf
, ".snd", 4) != 0))
147 * headerless sun audio file
148 * It is decoded under conditions.
149 * format: G.711 mu-law
151 * frequency: 8000 kHz
154 format
.formattag
= AU_FORMAT_MULAW
;
156 format
.bitspersample
= 8;
157 format
.numbytes
= ci
->id3
->filesize
;
164 offset
= get_be32(buf
+ 4);
167 DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset
);
168 status
= CODEC_ERROR
;
172 format
.numbytes
= get_be32(buf
+ 8);
173 if (format
.numbytes
== (uint32_t)0xffffffff)
174 format
.numbytes
= ci
->id3
->filesize
- offset
;
176 format
.formattag
= convert_au_format(get_be32(buf
+ 12), &format
);
177 if (format
.formattag
== AU_FORMAT_UNSUPPORT
)
179 DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf
+ 12));
180 status
= CODEC_ERROR
;
183 /* skip sample rate */
184 format
.channels
= get_be32(buf
+ 20);
187 /* advance to first WAVE chunk */
188 ci
->advance_buffer(offset
);
190 firstblockposn
= offset
;
197 codec
= get_au_codec(format
.formattag
);
200 DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format
.formattag
);
201 status
= CODEC_ERROR
;
205 if (!codec
->set_format(&format
))
207 status
= CODEC_ERROR
;
211 if (format
.numbytes
== 0) {
212 DEBUGF("CODEC_ERROR: data size is 0\n");
213 status
= CODEC_ERROR
;
217 /* check chunksize */
218 if ((format
.chunksize
/ format
.blockalign
) * format
.samplesperblock
* format
.channels
220 format
.chunksize
= (PCM_SAMPLE_SIZE
/ format
.blockalign
) * format
.blockalign
;
221 if (format
.chunksize
== 0)
223 DEBUGF("CODEC_ERROR: chunksize is 0\n");
224 status
= CODEC_ERROR
;
228 ci
->configure(DSP_SWITCH_FREQUENCY
, ci
->id3
->frequency
);
229 if (format
.channels
== 2) {
230 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_INTERLEAVED
);
231 } else if (format
.channels
== 1) {
232 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_MONO
);
234 DEBUGF("CODEC_ERROR: more than 2 channels\n");
235 status
= CODEC_ERROR
;
239 /* The main decoder loop */
242 while (!endofstream
) {
244 if (ci
->stop_codec
|| ci
->new_track
) {
249 /* 2nd args(read_buffer) is unnecessary in the format which Sun Audio supports. */
250 struct pcm_pos
*newpos
= codec
->get_seek_pos(ci
->seek_time
, NULL
);
252 if (newpos
->pos
> format
.numbytes
)
254 if (ci
->seek_buffer(firstblockposn
+ newpos
->pos
))
256 bytesdone
= newpos
->pos
;
257 decodedsamples
= newpos
->samples
;
262 aubuf
= (uint8_t *)ci
->request_buffer(&n
, format
.chunksize
);
264 break; /* End of stream */
265 if (bytesdone
+ n
> format
.numbytes
) {
266 n
= format
.numbytes
- bytesdone
;
270 status
= codec
->decode(aubuf
, n
, samples
, &bufcount
);
271 if (status
== CODEC_ERROR
)
273 DEBUGF("codec error\n");
277 ci
->pcmbuf_insert(samples
, NULL
, bufcount
);
278 ci
->advance_buffer(n
);
280 decodedsamples
+= bufcount
;
282 if (bytesdone
>= format
.numbytes
)
284 ci
->set_elapsed(decodedsamples
*1000LL/ci
->id3
->frequency
);
289 if (ci
->request_next_track())