1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2009 Mohamed Tarek
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/librm/rm.h>
24 #include <inttypes.h> /* Needed by a52.h */
25 #include <codecs/liba52/config-a52.h>
26 #include <codecs/liba52/a52.h>
30 #define BUFFER_SIZE 4096
32 #define A52_SAMPLESPERFRAME (6*256)
34 static a52_state_t
*state
;
35 static unsigned long samplesdone
;
36 static unsigned long frequency
;
37 static RMContext rmctx
;
40 static void init_rm(RMContext
*rmctx
)
42 memcpy(rmctx
, (void*)(( (intptr_t)ci
->id3
->id3v2buf
+ 3 ) &~ 3), sizeof(RMContext
));
45 /* used outside liba52 */
46 static uint8_t buf
[3840] IBSS_ATTR
;
48 /* The following two functions, a52_decode_data and output_audio are taken from apps/codecs/a52.c */
49 static inline void output_audio(sample_t
*samples
)
52 ci
->pcmbuf_insert(&samples
[0], &samples
[256], 256);
55 static void a52_decode_data(uint8_t *start
, uint8_t *end
)
57 static uint8_t *bufptr
= buf
;
58 static uint8_t *bufpos
= buf
+ 7;
60 * sample_rate and flags are static because this routine could
61 * exit between the a52_syncinfo() and the ao_setup(), and we want
62 * to have the same values when we get back !
64 static int sample_rate
;
73 if (len
> bufpos
- bufptr
)
74 len
= bufpos
- bufptr
;
75 memcpy(bufptr
, start
, len
);
78 if (bufptr
== bufpos
) {
79 if (bufpos
== buf
+ 7) {
82 length
= a52_syncinfo(buf
, &flags
, &sample_rate
, &bit_rate
);
85 for (bufptr
= buf
; bufptr
< buf
+ 6; bufptr
++)
86 bufptr
[0] = bufptr
[1];
89 bufpos
= buf
+ length
;
91 /* Unity gain is 1 << 26, and we want to end up on 28 bits
92 of precision instead of the default 30.
94 level_t level
= 1 << 24;
98 /* This is the configuration for the downmixing: */
99 flags
= A52_STEREO
| A52_ADJUST_LEVEL
;
101 if (a52_frame(state
, buf
, &flags
, &level
, bias
))
103 a52_dynrng(state
, NULL
, NULL
);
104 frequency
= sample_rate
;
106 /* An A52 frame consists of 6 blocks of 256 samples
107 So we decode and output them one block at a time */
108 for (i
= 0; i
< 6; i
++) {
109 if (a52_block(state
))
111 output_audio(a52_samples(state
));
114 ci
->set_elapsed(samplesdone
/(frequency
/1000));
119 //logf("Error decoding A52 stream\n");
127 /* this is the codec entry point */
128 enum codec_status
codec_main(enum codec_entry_call_reason reason
)
130 if (reason
== CODEC_LOAD
) {
131 /* Generic codec initialisation */
132 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_NONINTERLEAVED
);
133 ci
->configure(DSP_SET_SAMPLE_DEPTH
, 28);
135 else if (reason
== CODEC_UNLOAD
) {
143 /* this is called for each file to process */
144 enum codec_status
codec_run(void)
148 int consumed
, packet_offset
;
149 int playback_on
= -1;
150 size_t resume_offset
;
152 enum codec_command_action action
= CODEC_ACTION_NULL
;
158 resume_offset
= ci
->id3
->offset
;
160 ci
->configure(DSP_SWITCH_FREQUENCY
, ci
->id3
->frequency
);
161 codec_set_replaygain(ci
->id3
);
163 ci
->seek_buffer(ci
->id3
->first_frame_offset
);
167 ci
->memset(&rmctx
,0,sizeof(RMContext
));
168 ci
->memset(&pkt
,0,sizeof(RMPacket
));
173 /* check for a mid-track resume and force a seek time accordingly */
174 if(resume_offset
> rmctx
.data_offset
+ DATA_HEADER_SIZE
) {
175 resume_offset
-= rmctx
.data_offset
+ DATA_HEADER_SIZE
;
176 /* put number of subpackets to skip in resume_offset */
177 resume_offset
/= (rmctx
.block_align
+ PACKET_HEADER_SIZE
);
178 param
= (int)resume_offset
* ((rmctx
.block_align
* 8 * 1000)/rmctx
.bit_rate
);
179 action
= CODEC_ACTION_SEEK_TIME
;
182 /* Seek to the first packet */
184 ci
->advance_buffer(rmctx
.data_offset
+ DATA_HEADER_SIZE
);
187 /* The main decoding loop */
188 while((unsigned)rmctx
.audio_pkt_cnt
< rmctx
.nb_packets
) {
189 if (action
== CODEC_ACTION_NULL
)
190 action
= ci
->get_command(¶m
);
192 if (action
== CODEC_ACTION_HALT
)
195 if (action
== CODEC_ACTION_SEEK_TIME
) {
196 packet_offset
= param
/ ((rmctx
.block_align
*8*1000)/rmctx
.bit_rate
);
197 ci
->seek_buffer(rmctx
.data_offset
+ DATA_HEADER_SIZE
+
198 packet_offset
*(rmctx
.block_align
+ PACKET_HEADER_SIZE
));
199 rmctx
.audio_pkt_cnt
= packet_offset
;
200 samplesdone
= (rmctx
.sample_rate
/1000 * param
);
201 ci
->set_elapsed(samplesdone
/(frequency
/1000));
205 action
= CODEC_ACTION_NULL
;
207 filebuf
= ci
->request_buffer(&n
, rmctx
.block_align
+ PACKET_HEADER_SIZE
);
208 consumed
= rm_get_packet(&filebuf
, &rmctx
, &pkt
);
210 if(consumed
< 0 && playback_on
!= 0) {
211 if(playback_on
== -1) {
212 /* Error only if packet-parsing failed and playback hadn't started */
213 DEBUGF("rm_get_packet failed\n");
222 a52_decode_data(filebuf
, filebuf
+ rmctx
.block_align
);
223 ci
->advance_buffer(pkt
.length
);