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 static 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(enum codec_entry_call_reason reason
)
111 if (reason
== CODEC_LOAD
) {
112 /* Generic codec initialisation */
113 ci
->configure(DSP_SET_SAMPLE_DEPTH
, PCM_OUTPUT_DEPTH
-1);
119 /* this is called for each file to process */
120 enum codec_status
codec_run(void)
122 struct pcm_format format
;
123 uint32_t bytesdone
, decodedsamples
;
129 off_t firstblockposn
; /* position of the first block in file */
130 const struct pcm_codec
*codec
;
135 DEBUGF("codec_init() error\n");
139 codec_set_replaygain(ci
->id3
);
141 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
142 bytesdone
= ci
->id3
->offset
;
144 ci
->memset(&format
, 0, sizeof(struct pcm_format
));
145 format
.is_signed
= true;
146 format
.is_little_endian
= false;
150 buf
= ci
->request_buffer(&n
, 24);
151 if (n
< 24 || (memcmp(buf
, ".snd", 4) != 0))
154 * headerless sun audio file
155 * It is decoded under conditions.
156 * format: G.711 mu-law
158 * frequency: 8000 kHz
161 format
.formattag
= AU_FORMAT_MULAW
;
163 format
.bitspersample
= 8;
164 format
.numbytes
= ci
->id3
->filesize
;
171 offset
= get_be32(buf
+ 4);
174 DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset
);
178 format
.numbytes
= get_be32(buf
+ 8);
179 if (format
.numbytes
== (uint32_t)0xffffffff)
180 format
.numbytes
= ci
->id3
->filesize
- offset
;
182 format
.formattag
= convert_au_format(get_be32(buf
+ 12), &format
);
183 if (format
.formattag
== AU_FORMAT_UNSUPPORT
)
185 DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf
+ 12));
188 /* skip sample rate */
189 format
.channels
= get_be32(buf
+ 20);
192 /* advance to first WAVE chunk */
193 ci
->advance_buffer(offset
);
195 firstblockposn
= offset
;
201 codec
= get_au_codec(format
.formattag
);
204 DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format
.formattag
);
208 if (!codec
->set_format(&format
))
213 if (format
.numbytes
== 0) {
214 DEBUGF("CODEC_ERROR: data size is 0\n");
218 /* check chunksize */
219 if ((format
.chunksize
/ format
.blockalign
) * format
.samplesperblock
* format
.channels
221 format
.chunksize
= (PCM_SAMPLE_SIZE
/ format
.blockalign
) * format
.blockalign
;
222 if (format
.chunksize
== 0)
224 DEBUGF("CODEC_ERROR: chunksize is 0\n");
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");
238 /* make sure we're at the correct offset */
239 if (bytesdone
> (uint32_t) firstblockposn
) {
240 /* Round down to previous block */
241 struct pcm_pos
*newpos
= codec
->get_seek_pos(bytesdone
- firstblockposn
,
244 if (newpos
->pos
> format
.numbytes
)
246 if (ci
->seek_buffer(firstblockposn
+ newpos
->pos
))
248 bytesdone
= newpos
->pos
;
249 decodedsamples
= newpos
->samples
;
252 /* already where we need to be */
256 /* The main decoder loop */
259 while (!endofstream
) {
260 enum codec_command_action action
= ci
->get_command(¶m
);
262 if (action
== CODEC_ACTION_HALT
)
265 if (action
== CODEC_ACTION_SEEK_TIME
) {
266 /* 3rd args(read_buffer) is unnecessary in the format which Sun Audio supports. */
267 struct pcm_pos
*newpos
= codec
->get_seek_pos(param
, PCM_SEEK_TIME
, NULL
);
269 if (newpos
->pos
> format
.numbytes
)
271 ci
->set_elapsed(ci
->id3
->length
);
276 if (ci
->seek_buffer(firstblockposn
+ newpos
->pos
))
278 bytesdone
= newpos
->pos
;
279 decodedsamples
= newpos
->samples
;
282 ci
->set_elapsed(decodedsamples
*1000LL/ci
->id3
->frequency
);
286 aubuf
= (uint8_t *)ci
->request_buffer(&n
, format
.chunksize
);
288 break; /* End of stream */
289 if (bytesdone
+ n
> format
.numbytes
) {
290 n
= format
.numbytes
- bytesdone
;
294 if (codec
->decode(aubuf
, n
, samples
, &bufcount
) == CODEC_ERROR
)
296 DEBUGF("codec error\n");
300 ci
->pcmbuf_insert(samples
, NULL
, bufcount
);
301 ci
->advance_buffer(n
);
303 decodedsamples
+= bufcount
;
305 if (bytesdone
>= format
.numbytes
)
307 ci
->set_elapsed(decodedsamples
*1000LL/ci
->id3
->frequency
);