blob: a6b4a1153efbca9ffec7949ae46b978e64fde44d [file] [log] [blame]
Mohamed Tareke184ef12009-07-06 22:40:45 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Mohamed Tareka67deef2009-11-18 01:00:43 +00008 * $Id$
9 *
Mohamed Tareke184ef12009-07-06 22:40:45 +000010 * 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 <string.h>
23
24#include "logf.h"
25#include "codeclib.h"
26#include "inttypes.h"
27#include "libcook/cook.h"
28
Mohamed Tareke184ef12009-07-06 22:40:45 +000029CODEC_HEADER
30
Nils Wallménius6325ef92010-07-25 22:24:53 +000031static RMContext rmctx;
32static RMPacket pkt;
33static COOKContext q IBSS_ATTR;
34static int32_t rm_outbuf[2048];
Mohamed Tareke184ef12009-07-06 22:40:45 +000035
36static void init_rm(RMContext *rmctx)
37{
Maurus Cuelenaere851b0e32009-07-08 17:18:59 +000038 memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext));
Mohamed Tareke184ef12009-07-06 22:40:45 +000039}
40
Michael Sevakisc537d592011-04-27 03:08:23 +000041/* this is called for each file to process */
42enum codec_status codec_run(void)
43{
Mohamed Tareke184ef12009-07-06 22:40:45 +000044 static size_t buff_size;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +000045 int datasize, res, consumed, i, time_offset;
Mohamed Tareke184ef12009-07-06 22:40:45 +000046 uint8_t *bit_buffer;
Mohamed Tareke184ef12009-07-06 22:40:45 +000047 uint16_t fs,sps,h;
48 uint32_t packet_count;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +000049 int scrambling_unit_size, num_units;
Michael Sevakis85e40252011-02-20 15:27:10 +000050 size_t resume_offset;
Michael Sevakisc537d592011-04-27 03:08:23 +000051 intptr_t param = 0;
52 enum codec_command_action action = CODEC_ACTION_NULL;
Mohamed Tareke184ef12009-07-06 22:40:45 +000053
Mohamed Tareke184ef12009-07-06 22:40:45 +000054 if (codec_init()) {
55 DEBUGF("codec init failed\n");
56 return CODEC_ERROR;
57 }
Michael Sevakis85e40252011-02-20 15:27:10 +000058
Michael Sevakis85e40252011-02-20 15:27:10 +000059 resume_offset = ci->id3->offset;
Mohamed Tareke184ef12009-07-06 22:40:45 +000060
61 codec_set_replaygain(ci->id3);
62 ci->memset(&rmctx,0,sizeof(RMContext));
63 ci->memset(&pkt,0,sizeof(RMPacket));
64 ci->memset(&q,0,sizeof(COOKContext));
65
Michael Sevakisc537d592011-04-27 03:08:23 +000066 ci->seek_buffer(0);
67
Mohamed Tareke184ef12009-07-06 22:40:45 +000068 init_rm(&rmctx);
69
70 ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
Andree Buschmann3d6faa02010-02-21 19:47:05 +000071 /* cook's sample representation is 21.11
72 * DSP_SET_SAMPLE_DEPTH = 11 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 26 */
73 ci->configure(DSP_SET_SAMPLE_DEPTH, 26);
Mohamed Tareke184ef12009-07-06 22:40:45 +000074 ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ?
Andree Buschmann3d6faa02010-02-21 19:47:05 +000075 STEREO_MONO : STEREO_NONINTERLEAVED);
Mohamed Tareke184ef12009-07-06 22:40:45 +000076
77 packet_count = rmctx.nb_packets;
78 rmctx.audio_framesize = rmctx.block_align;
79 rmctx.block_align = rmctx.sub_packet_size;
80 fs = rmctx.audio_framesize;
81 sps= rmctx.block_align;
82 h = rmctx.sub_packet_h;
83 scrambling_unit_size = h*fs;
84
85 res =cook_decode_init(&rmctx, &q);
86 if(res < 0) {
87 DEBUGF("failed to initialize cook decoder\n");
88 return CODEC_ERROR;
89 }
Michael Sevakisc537d592011-04-27 03:08:23 +000090
Mohamed Tarek9669bbc2009-11-18 00:41:46 +000091 /* check for a mid-track resume and force a seek time accordingly */
92 if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) {
93 resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE;
94 num_units = (int)resume_offset / scrambling_unit_size;
95 /* put number of subpackets to skip in resume_offset */
96 resume_offset /= (sps + PACKET_HEADER_SIZE);
Michael Sevakisc537d592011-04-27 03:08:23 +000097 param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
98 action = CODEC_ACTION_SEEK_TIME;
Mohamed Tarek9669bbc2009-11-18 00:41:46 +000099 }
Mohamed Tareke184ef12009-07-06 22:40:45 +0000100
101 ci->set_elapsed(0);
102 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
103
Michael Sevakisc537d592011-04-27 03:08:23 +0000104 /* The main decoder loop */
Mohamed Tarekd2595682009-07-08 20:23:24 +0000105seek_start :
106 while(packet_count)
107 {
108 bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
109 consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
110 if(consumed < 0) {
111 DEBUGF("rm_get_packet failed\n");
112 return CODEC_ERROR;
113 }
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000114
Mohamed Tarekd2595682009-07-08 20:23:24 +0000115 for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++)
Michael Sevakisc537d592011-04-27 03:08:23 +0000116 {
117 if (action == CODEC_ACTION_NULL)
118 action = ci->get_command(&param);
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000119
Michael Sevakisc537d592011-04-27 03:08:23 +0000120 if (action == CODEC_ACTION_HALT)
121 return CODEC_OK;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000122
Michael Sevakisc537d592011-04-27 03:08:23 +0000123 if (action == CODEC_ACTION_SEEK_TIME) {
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000124 /* Do not allow seeking beyond the file's length */
Michael Sevakisc537d592011-04-27 03:08:23 +0000125 if ((unsigned) param > ci->id3->length) {
126 ci->set_elapsed(ci->id3->length);
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000127 ci->seek_complete();
Michael Sevakisc537d592011-04-27 03:08:23 +0000128 return CODEC_OK;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000129 }
130
Mohamed Tarekd2595682009-07-08 20:23:24 +0000131 ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
Mohamed Tarekd2595682009-07-08 20:23:24 +0000132 packet_count = rmctx.nb_packets;
133 rmctx.audio_pkt_cnt = 0;
134 rmctx.frame_number = 0;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000135
136 /* Seek to the start of the track */
Michael Sevakisc537d592011-04-27 03:08:23 +0000137 if (param == 0) {
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000138 ci->set_elapsed(0);
139 ci->seek_complete();
Michael Sevakisc537d592011-04-27 03:08:23 +0000140 action = CODEC_ACTION_NULL;
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000141 goto seek_start;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000142 }
Michael Sevakisc537d592011-04-27 03:08:23 +0000143 num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps));
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000144 ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units);
145 bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
146 consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
147 if(consumed < 0) {
148 DEBUGF("rm_get_packet failed\n");
Michael Sevakisc537d592011-04-27 03:08:23 +0000149 ci->seek_complete();
150 return CODEC_ERROR;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000151 }
152 packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units;
Michael Sevakisc537d592011-04-27 03:08:23 +0000153 rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate));
154 while(rmctx.audiotimestamp > (unsigned) param) {
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000155 rmctx.audio_pkt_cnt = 0;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000156 ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1));
157 bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size);
158 consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt);
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000159 packet_count += rmctx.audio_pkt_cnt;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000160 num_units--;
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000161 }
Michael Sevakisc537d592011-04-27 03:08:23 +0000162 time_offset = param - rmctx.audiotimestamp;
Mohamed Tarekb5b9cb02009-07-13 10:06:19 +0000163 i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate));
164 ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i);
165 ci->seek_complete();
Michael Sevakisc537d592011-04-27 03:08:23 +0000166 }
167
168 action = CODEC_ACTION_NULL;
169
Andree Buschmann3d6faa02010-02-21 19:47:05 +0000170 res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align);
Mohamed Tarekd2595682009-07-08 20:23:24 +0000171 rmctx.frame_number++;
172
173 /* skip the first two frames; no valid audio */
174 if(rmctx.frame_number < 3) continue;
175
176 if(res != rmctx.block_align) {
177 DEBUGF("codec error\n");
Mohamed Tareke184ef12009-07-06 22:40:45 +0000178 return CODEC_ERROR;
179 }
Mohamed Tareke184ef12009-07-06 22:40:45 +0000180
Andree Buschmann3d6faa02010-02-21 19:47:05 +0000181 ci->pcmbuf_insert(rm_outbuf,
182 rm_outbuf+q.samples_per_channel,
183 q.samples_per_channel);
Mohamed Tarekedf3af22009-07-12 14:36:06 +0000184 ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i);
Mohamed Tareke184ef12009-07-06 22:40:45 +0000185 }
Mohamed Tarekd2595682009-07-08 20:23:24 +0000186 packet_count -= rmctx.audio_pkt_cnt;
187 rmctx.audio_pkt_cnt = 0;
188 ci->advance_buffer(consumed);
Mohamed Tareke184ef12009-07-06 22:40:45 +0000189 }
Mohamed Tarekd2595682009-07-08 20:23:24 +0000190
Michael Sevakisc537d592011-04-27 03:08:23 +0000191 return CODEC_OK;
Mohamed Tareke184ef12009-07-06 22:40:45 +0000192}