Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
Mohamed Tarek | a67deef | 2009-11-18 01:00:43 +0000 | [diff] [blame] | 8 | * $Id$ |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 9 | * |
| 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 | |
| 28 | CODEC_HEADER |
| 29 | |
| 30 | #define BUFFER_SIZE 4096 |
| 31 | |
| 32 | #define A52_SAMPLESPERFRAME (6*256) |
| 33 | |
| 34 | static a52_state_t *state; |
Nils Wallménius | 6325ef9 | 2010-07-25 22:24:53 +0000 | [diff] [blame] | 35 | static unsigned long samplesdone; |
| 36 | static unsigned long frequency; |
| 37 | static RMContext rmctx; |
| 38 | static RMPacket pkt; |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 39 | |
| 40 | static 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 */ |
| 46 | static uint8_t buf[3840] IBSS_ATTR; |
| 47 | |
Mohamed Tarek | cc68b63 | 2009-08-06 05:45:54 +0000 | [diff] [blame] | 48 | /* The following two functions, a52_decode_data and output_audio are taken from apps/codecs/a52.c */ |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 49 | static inline void output_audio(sample_t *samples) |
| 50 | { |
| 51 | ci->yield(); |
| 52 | ci->pcmbuf_insert(&samples[0], &samples[256], 256); |
| 53 | } |
| 54 | |
| 55 | static 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 Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 127 | /* this is the codec entry point */ |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 128 | enum 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 */ |
| 144 | enum codec_status codec_run(void) |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 145 | { |
| 146 | size_t n; |
| 147 | uint8_t *filebuf; |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 148 | int consumed, packet_offset; |
Mohamed Tarek | 9669bbc | 2009-11-18 00:41:46 +0000 | [diff] [blame] | 149 | int playback_on = -1; |
Michael Sevakis | 85e4025 | 2011-02-20 15:27:10 +0000 | [diff] [blame] | 150 | size_t resume_offset; |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 151 | intptr_t param; |
| 152 | enum codec_command_action action = CODEC_ACTION_NULL; |
Michael Sevakis | 85e4025 | 2011-02-20 15:27:10 +0000 | [diff] [blame] | 153 | |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 154 | if (codec_init()) { |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 155 | return CODEC_ERROR; |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 156 | } |
| 157 | |
Michael Sevakis | 85e4025 | 2011-02-20 15:27:10 +0000 | [diff] [blame] | 158 | resume_offset = ci->id3->offset; |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 159 | |
| 160 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
| 161 | codec_set_replaygain(ci->id3); |
| 162 | |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 163 | ci->seek_buffer(ci->id3->first_frame_offset); |
| 164 | |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 165 | /* 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 Tarek | 9669bbc | 2009-11-18 00:41:46 +0000 | [diff] [blame] | 171 | /* 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 Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 176 | param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); |
| 177 | action = CODEC_ACTION_SEEK_TIME; |
Mohamed Tarek | 9669bbc | 2009-11-18 00:41:46 +0000 | [diff] [blame] | 178 | } |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 179 | else { |
| 180 | /* Seek to the first packet */ |
| 181 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); |
| 182 | } |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 183 | |
| 184 | /* The main decoding loop */ |
Mohamed Tarek | 9ad071d | 2009-08-04 18:57:10 +0000 | [diff] [blame] | 185 | while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) { |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 186 | if (action == CODEC_ACTION_NULL) |
| 187 | action = ci->get_command(¶m); |
| 188 | |
| 189 | if (action == CODEC_ACTION_HALT) |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 190 | break; |
| 191 | |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 192 | 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 Tarek | ac2ce3a | 2009-08-04 18:36:44 +0000 | [diff] [blame] | 196 | rmctx.audio_pkt_cnt = packet_offset; |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 197 | samplesdone = (rmctx.sample_rate/1000 * param); |
| 198 | ci->set_elapsed(samplesdone/(frequency/1000)); |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 199 | ci->seek_complete(); |
| 200 | } |
| 201 | |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 202 | action = CODEC_ACTION_NULL; |
| 203 | |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 204 | filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE); |
| 205 | consumed = rm_get_packet(&filebuf, &rmctx, &pkt); |
Mohamed Tarek | f0c6c88 | 2009-10-03 00:18:42 +0000 | [diff] [blame] | 206 | |
| 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 Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 214 | break; |
Mohamed Tarek | f0c6c88 | 2009-10-03 00:18:42 +0000 | [diff] [blame] | 215 | } |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 216 | } |
Mohamed Tarek | f0c6c88 | 2009-10-03 00:18:42 +0000 | [diff] [blame] | 217 | |
| 218 | playback_on = 1; |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 219 | a52_decode_data(filebuf, filebuf + rmctx.block_align); |
| 220 | ci->advance_buffer(pkt.length); |
| 221 | } |
| 222 | |
Michael Sevakis | c537d59 | 2011-04-27 03:08:23 +0000 | [diff] [blame^] | 223 | return CODEC_OK; |
Mohamed Tarek | 26cee86 | 2009-08-04 13:54:06 +0000 | [diff] [blame] | 224 | } |