blob: c1930aa7b423f684fba32bc94540d7f42a174e57 [file] [log] [blame]
Mohamed Tarek26cee862009-08-04 13:54:06 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Mohamed Tareka67deef2009-11-18 01:00:43 +00008 * $Id$
Mohamed Tarek26cee862009-08-04 13:54:06 +00009 *
10 * Copyright (C) 2009 Mohamed Tarek
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "codeclib.h"
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>
27
28CODEC_HEADER
29
30#define BUFFER_SIZE 4096
31
32#define A52_SAMPLESPERFRAME (6*256)
33
34static a52_state_t *state;
Nils Wallménius6325ef92010-07-25 22:24:53 +000035static unsigned long samplesdone;
36static unsigned long frequency;
37static RMContext rmctx;
38static RMPacket pkt;
Mohamed Tarek26cee862009-08-04 13:54:06 +000039
40static void init_rm(RMContext *rmctx)
41{
42 memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext));
43}
44
45/* used outside liba52 */
46static uint8_t buf[3840] IBSS_ATTR;
47
Mohamed Tarekcc68b632009-08-06 05:45:54 +000048/* The following two functions, a52_decode_data and output_audio are taken from apps/codecs/a52.c */
Mohamed Tarek26cee862009-08-04 13:54:06 +000049static inline void output_audio(sample_t *samples)
50{
51 ci->yield();
52 ci->pcmbuf_insert(&samples[0], &samples[256], 256);
53}
54
55static void a52_decode_data(uint8_t *start, uint8_t *end)
56{
57 static uint8_t *bufptr = buf;
58 static uint8_t *bufpos = buf + 7;
59 /*
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 !
63 */
64 static int sample_rate;
65 static int flags;
66 int bit_rate;
67 int len;
68
69 while (1) {
70 len = end - start;
71 if (!len)
72 break;
73 if (len > bufpos - bufptr)
74 len = bufpos - bufptr;
75 memcpy(bufptr, start, len);
76 bufptr += len;
77 start += len;
78 if (bufptr == bufpos) {
79 if (bufpos == buf + 7) {
80 int length;
81
82 length = a52_syncinfo(buf, &flags, &sample_rate, &bit_rate);
83 if (!length) {
84 //DEBUGF("skip\n");
85 for (bufptr = buf; bufptr < buf + 6; bufptr++)
86 bufptr[0] = bufptr[1];
87 continue;
88 }
89 bufpos = buf + length;
90 } else {
91 /* Unity gain is 1 << 26, and we want to end up on 28 bits
92 of precision instead of the default 30.
93 */
94 level_t level = 1 << 24;
95 sample_t bias = 0;
96 int i;
97
98 /* This is the configuration for the downmixing: */
99 flags = A52_STEREO | A52_ADJUST_LEVEL;
100
101 if (a52_frame(state, buf, &flags, &level, bias))
102 goto error;
103 a52_dynrng(state, NULL, NULL);
104 frequency = sample_rate;
105
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))
110 goto error;
111 output_audio(a52_samples(state));
112 samplesdone += 256;
113 }
114 ci->set_elapsed(samplesdone/(frequency/1000));
115 bufptr = buf;
116 bufpos = buf + 7;
117 continue;
118 error:
119 //logf("Error decoding A52 stream\n");
120 bufptr = buf;
121 bufpos = buf + 7;
122 }
123 }
124 }
125}
126
Mohamed Tarek26cee862009-08-04 13:54:06 +0000127/* this is the codec entry point */
Michael Sevakisc537d592011-04-27 03:08:23 +0000128enum codec_status codec_main(enum codec_entry_call_reason reason)
129{
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);
134 }
135 else if (reason == CODEC_UNLOAD) {
136 if (state)
137 a52_free(state);
138 }
139
140 return CODEC_OK;
141}
142
143/* this is called for each file to process */
144enum codec_status codec_run(void)
Mohamed Tarek26cee862009-08-04 13:54:06 +0000145{
146 size_t n;
147 uint8_t *filebuf;
Michael Sevakisc537d592011-04-27 03:08:23 +0000148 int consumed, packet_offset;
Mohamed Tarek9669bbc2009-11-18 00:41:46 +0000149 int playback_on = -1;
Michael Sevakis85e40252011-02-20 15:27:10 +0000150 size_t resume_offset;
Michael Sevakisc537d592011-04-27 03:08:23 +0000151 intptr_t param;
152 enum codec_command_action action = CODEC_ACTION_NULL;
Michael Sevakis85e40252011-02-20 15:27:10 +0000153
Mohamed Tarek26cee862009-08-04 13:54:06 +0000154 if (codec_init()) {
Michael Sevakisc537d592011-04-27 03:08:23 +0000155 return CODEC_ERROR;
Mohamed Tarek26cee862009-08-04 13:54:06 +0000156 }
157
Michael Sevakis85e40252011-02-20 15:27:10 +0000158 resume_offset = ci->id3->offset;
Mohamed Tarek26cee862009-08-04 13:54:06 +0000159
160 ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
161 codec_set_replaygain(ci->id3);
162
Michael Sevakisc537d592011-04-27 03:08:23 +0000163 ci->seek_buffer(ci->id3->first_frame_offset);
164
Mohamed Tarek26cee862009-08-04 13:54:06 +0000165 /* Intializations */
166 state = a52_init(0);
167 ci->memset(&rmctx,0,sizeof(RMContext));
168 ci->memset(&pkt,0,sizeof(RMPacket));
169 init_rm(&rmctx);
170
Mohamed Tarek9669bbc2009-11-18 00:41:46 +0000171 /* check for a mid-track resume and force a seek time accordingly */
172 if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) {
173 resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
174 /* put number of subpackets to skip in resume_offset */
175 resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
Michael Sevakisc537d592011-04-27 03:08:23 +0000176 param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
177 action = CODEC_ACTION_SEEK_TIME;
Mohamed Tarek9669bbc2009-11-18 00:41:46 +0000178 }
Michael Sevakisc537d592011-04-27 03:08:23 +0000179 else {
180 /* Seek to the first packet */
181 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE );
182 }
Mohamed Tarek26cee862009-08-04 13:54:06 +0000183
184 /* The main decoding loop */
Mohamed Tarek9ad071d2009-08-04 18:57:10 +0000185 while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) {
Michael Sevakisc537d592011-04-27 03:08:23 +0000186 if (action == CODEC_ACTION_NULL)
187 action = ci->get_command(&param);
188
189 if (action == CODEC_ACTION_HALT)
Mohamed Tarek26cee862009-08-04 13:54:06 +0000190 break;
191
Michael Sevakisc537d592011-04-27 03:08:23 +0000192 if (action == CODEC_ACTION_SEEK_TIME) {
193 packet_offset = param / ((rmctx.block_align*8*1000)/rmctx.bit_rate);
194 ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE +
195 packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE));
Mohamed Tarekac2ce3a2009-08-04 18:36:44 +0000196 rmctx.audio_pkt_cnt = packet_offset;
Michael Sevakisc537d592011-04-27 03:08:23 +0000197 samplesdone = (rmctx.sample_rate/1000 * param);
198 ci->set_elapsed(samplesdone/(frequency/1000));
Mohamed Tarek26cee862009-08-04 13:54:06 +0000199 ci->seek_complete();
200 }
201
Michael Sevakisc537d592011-04-27 03:08:23 +0000202 action = CODEC_ACTION_NULL;
203
Mohamed Tarek26cee862009-08-04 13:54:06 +0000204 filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE);
205 consumed = rm_get_packet(&filebuf, &rmctx, &pkt);
Mohamed Tarekf0c6c882009-10-03 00:18:42 +0000206
207 if(consumed < 0 && playback_on != 0) {
208 if(playback_on == -1) {
209 /* Error only if packet-parsing failed and playback hadn't started */
210 DEBUGF("rm_get_packet failed\n");
211 return CODEC_ERROR;
212 }
213 else {
Michael Sevakisc537d592011-04-27 03:08:23 +0000214 break;
Mohamed Tarekf0c6c882009-10-03 00:18:42 +0000215 }
Mohamed Tarek26cee862009-08-04 13:54:06 +0000216 }
Mohamed Tarekf0c6c882009-10-03 00:18:42 +0000217
218 playback_on = 1;
Mohamed Tarek26cee862009-08-04 13:54:06 +0000219 a52_decode_data(filebuf, filebuf + rmctx.block_align);
220 ci->advance_buffer(pkt.length);
221 }
222
Michael Sevakisc537d592011-04-27 03:08:23 +0000223 return CODEC_OK;
Mohamed Tarek26cee862009-08-04 13:54:06 +0000224}