diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/cook.c | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/cook.c')
-rw-r--r-- | lib/rbcodec/codecs/cook.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/cook.c b/lib/rbcodec/codecs/cook.c new file mode 100644 index 0000000000..55188aad36 --- /dev/null +++ b/lib/rbcodec/codecs/cook.c | |||
@@ -0,0 +1,202 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
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 <string.h> | ||
23 | |||
24 | #include "logf.h" | ||
25 | #include "codeclib.h" | ||
26 | #include "inttypes.h" | ||
27 | #include "libcook/cook.h" | ||
28 | |||
29 | CODEC_HEADER | ||
30 | |||
31 | static RMContext rmctx IBSS_ATTR_COOK_LARGE_IRAM; | ||
32 | static RMPacket pkt IBSS_ATTR_COOK_LARGE_IRAM; | ||
33 | static COOKContext q IBSS_ATTR; | ||
34 | static int32_t rm_outbuf[2048] IBSS_ATTR_COOK_LARGE_IRAM MEM_ALIGN_ATTR; | ||
35 | |||
36 | static void init_rm(RMContext *rmctx) | ||
37 | { | ||
38 | memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); | ||
39 | } | ||
40 | |||
41 | /* this is the codec entry point */ | ||
42 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
43 | { | ||
44 | /* Nothing to do */ | ||
45 | return CODEC_OK; | ||
46 | (void)reason; | ||
47 | } | ||
48 | |||
49 | /* this is called for each file to process */ | ||
50 | enum codec_status codec_run(void) | ||
51 | { | ||
52 | static size_t buff_size; | ||
53 | int datasize, res, consumed, i, time_offset; | ||
54 | uint8_t *bit_buffer; | ||
55 | uint16_t fs,sps,h; | ||
56 | uint32_t packet_count; | ||
57 | int scrambling_unit_size, num_units; | ||
58 | size_t resume_offset; | ||
59 | intptr_t param = 0; | ||
60 | enum codec_command_action action = CODEC_ACTION_NULL; | ||
61 | |||
62 | if (codec_init()) { | ||
63 | DEBUGF("codec init failed\n"); | ||
64 | return CODEC_ERROR; | ||
65 | } | ||
66 | |||
67 | resume_offset = ci->id3->offset; | ||
68 | |||
69 | codec_set_replaygain(ci->id3); | ||
70 | ci->memset(&rmctx,0,sizeof(RMContext)); | ||
71 | ci->memset(&pkt,0,sizeof(RMPacket)); | ||
72 | ci->memset(&q,0,sizeof(COOKContext)); | ||
73 | |||
74 | ci->seek_buffer(0); | ||
75 | |||
76 | init_rm(&rmctx); | ||
77 | |||
78 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); | ||
79 | /* cook's sample representation is 21.11 | ||
80 | * DSP_SET_SAMPLE_DEPTH = 11 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 26 */ | ||
81 | ci->configure(DSP_SET_SAMPLE_DEPTH, 26); | ||
82 | ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ? | ||
83 | STEREO_MONO : STEREO_NONINTERLEAVED); | ||
84 | |||
85 | packet_count = rmctx.nb_packets; | ||
86 | rmctx.audio_framesize = rmctx.block_align; | ||
87 | rmctx.block_align = rmctx.sub_packet_size; | ||
88 | fs = rmctx.audio_framesize; | ||
89 | sps= rmctx.block_align; | ||
90 | h = rmctx.sub_packet_h; | ||
91 | scrambling_unit_size = h * (fs + PACKET_HEADER_SIZE); | ||
92 | |||
93 | res =cook_decode_init(&rmctx, &q); | ||
94 | if(res < 0) { | ||
95 | DEBUGF("failed to initialize cook decoder\n"); | ||
96 | return CODEC_ERROR; | ||
97 | } | ||
98 | |||
99 | /* check for a mid-track resume and force a seek time accordingly */ | ||
100 | if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { | ||
101 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; | ||
102 | num_units = (int)resume_offset / scrambling_unit_size; | ||
103 | /* put number of subpackets to skip in resume_offset */ | ||
104 | resume_offset /= (sps + PACKET_HEADER_SIZE); | ||
105 | param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); | ||
106 | action = CODEC_ACTION_SEEK_TIME; | ||
107 | } | ||
108 | else { | ||
109 | ci->set_elapsed(0); | ||
110 | } | ||
111 | |||
112 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | ||
113 | |||
114 | /* The main decoder loop */ | ||
115 | seek_start : | ||
116 | while(packet_count) | ||
117 | { | ||
118 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | ||
119 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | ||
120 | if(consumed < 0) { | ||
121 | DEBUGF("rm_get_packet failed\n"); | ||
122 | return CODEC_ERROR; | ||
123 | } | ||
124 | |||
125 | for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) | ||
126 | { | ||
127 | if (action == CODEC_ACTION_NULL) | ||
128 | action = ci->get_command(¶m); | ||
129 | |||
130 | if (action == CODEC_ACTION_HALT) | ||
131 | return CODEC_OK; | ||
132 | |||
133 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
134 | /* Do not allow seeking beyond the file's length */ | ||
135 | if ((unsigned) param > ci->id3->length) { | ||
136 | ci->set_elapsed(ci->id3->length); | ||
137 | ci->seek_complete(); | ||
138 | return CODEC_OK; | ||
139 | } | ||
140 | |||
141 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | ||
142 | packet_count = rmctx.nb_packets; | ||
143 | rmctx.audio_pkt_cnt = 0; | ||
144 | rmctx.frame_number = 0; | ||
145 | |||
146 | /* Seek to the start of the track */ | ||
147 | if (param == 0) { | ||
148 | ci->set_elapsed(0); | ||
149 | ci->seek_complete(); | ||
150 | action = CODEC_ACTION_NULL; | ||
151 | goto seek_start; | ||
152 | } | ||
153 | num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); | ||
154 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); | ||
155 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | ||
156 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | ||
157 | if(consumed < 0) { | ||
158 | DEBUGF("rm_get_packet failed\n"); | ||
159 | ci->seek_complete(); | ||
160 | return CODEC_ERROR; | ||
161 | } | ||
162 | packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; | ||
163 | rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); | ||
164 | while(rmctx.audiotimestamp > (unsigned) param) { | ||
165 | rmctx.audio_pkt_cnt = 0; | ||
166 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); | ||
167 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | ||
168 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | ||
169 | packet_count += rmctx.audio_pkt_cnt; | ||
170 | num_units--; | ||
171 | } | ||
172 | time_offset = param - rmctx.audiotimestamp; | ||
173 | i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); | ||
174 | ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); | ||
175 | ci->seek_complete(); | ||
176 | } | ||
177 | |||
178 | action = CODEC_ACTION_NULL; | ||
179 | |||
180 | res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align); | ||
181 | rmctx.frame_number++; | ||
182 | |||
183 | /* skip the first two frames; no valid audio */ | ||
184 | if(rmctx.frame_number < 3) continue; | ||
185 | |||
186 | if(res != rmctx.block_align) { | ||
187 | DEBUGF("codec error\n"); | ||
188 | return CODEC_ERROR; | ||
189 | } | ||
190 | |||
191 | ci->pcmbuf_insert(rm_outbuf, | ||
192 | rm_outbuf+q.samples_per_channel, | ||
193 | q.samples_per_channel); | ||
194 | ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); | ||
195 | } | ||
196 | packet_count -= rmctx.audio_pkt_cnt; | ||
197 | rmctx.audio_pkt_cnt = 0; | ||
198 | ci->advance_buffer(consumed); | ||
199 | } | ||
200 | |||
201 | return CODEC_OK; | ||
202 | } | ||