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/atrac3_rm.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/atrac3_rm.c')
-rw-r--r-- | lib/rbcodec/codecs/atrac3_rm.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/atrac3_rm.c b/lib/rbcodec/codecs/atrac3_rm.c new file mode 100644 index 0000000000..997507425e --- /dev/null +++ b/lib/rbcodec/codecs/atrac3_rm.c | |||
@@ -0,0 +1,215 @@ | |||
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 "libatrac/atrac3.h" | ||
28 | |||
29 | CODEC_HEADER | ||
30 | |||
31 | static RMContext rmctx IBSS_ATTR_LARGE_IRAM; | ||
32 | static RMPacket pkt IBSS_ATTR_LARGE_IRAM; | ||
33 | static ATRAC3Context q IBSS_ATTR; | ||
34 | |||
35 | static void init_rm(RMContext *rmctx) | ||
36 | { | ||
37 | /* initialize the RMContext */ | ||
38 | memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); | ||
39 | |||
40 | /* and atrac3 expects extadata in id3v2buf, so we shall give it that */ | ||
41 | memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char)); | ||
42 | } | ||
43 | |||
44 | /* this is the codec entry point */ | ||
45 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
46 | { | ||
47 | /* Nothing to do */ | ||
48 | return CODEC_OK; | ||
49 | (void)reason; | ||
50 | } | ||
51 | |||
52 | /* this is called for each file to process */ | ||
53 | enum codec_status codec_run(void) | ||
54 | { | ||
55 | static size_t buff_size; | ||
56 | int datasize, res, consumed, i, time_offset; | ||
57 | uint8_t *bit_buffer; | ||
58 | uint16_t fs,sps,h; | ||
59 | uint32_t packet_count; | ||
60 | int scrambling_unit_size, num_units, elapsed = 0; | ||
61 | int playback_on = -1; | ||
62 | size_t resume_offset; | ||
63 | intptr_t param; | ||
64 | enum codec_command_action action = CODEC_ACTION_NULL; | ||
65 | |||
66 | if (codec_init()) { | ||
67 | DEBUGF("codec init failed\n"); | ||
68 | return CODEC_ERROR; | ||
69 | } | ||
70 | |||
71 | resume_offset = ci->id3->offset; | ||
72 | |||
73 | codec_set_replaygain(ci->id3); | ||
74 | ci->memset(&rmctx,0,sizeof(RMContext)); | ||
75 | ci->memset(&pkt,0,sizeof(RMPacket)); | ||
76 | ci->memset(&q,0,sizeof(ATRAC3Context)); | ||
77 | |||
78 | ci->seek_buffer(0); | ||
79 | init_rm(&rmctx); | ||
80 | |||
81 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); | ||
82 | ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */ | ||
83 | ci->configure(DSP_SET_STEREO_MODE, rmctx.nb_channels == 1 ? | ||
84 | STEREO_MONO : STEREO_NONINTERLEAVED); | ||
85 | |||
86 | packet_count = rmctx.nb_packets; | ||
87 | rmctx.audio_framesize = rmctx.block_align; | ||
88 | rmctx.block_align = rmctx.sub_packet_size; | ||
89 | fs = rmctx.audio_framesize; | ||
90 | sps= rmctx.block_align; | ||
91 | h = rmctx.sub_packet_h; | ||
92 | scrambling_unit_size = h * (fs + PACKET_HEADER_SIZE); | ||
93 | |||
94 | res = atrac3_decode_init(&q, ci->id3); | ||
95 | if(res < 0) { | ||
96 | DEBUGF("failed to initialize RM atrac decoder\n"); | ||
97 | return CODEC_ERROR; | ||
98 | } | ||
99 | |||
100 | /* check for a mid-track resume and force a seek time accordingly */ | ||
101 | if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { | ||
102 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; | ||
103 | num_units = (int)resume_offset / scrambling_unit_size; | ||
104 | /* put number of subpackets to skip in resume_offset */ | ||
105 | resume_offset /= (sps + PACKET_HEADER_SIZE); | ||
106 | param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); | ||
107 | action = CODEC_ACTION_SEEK_TIME; | ||
108 | } | ||
109 | else { | ||
110 | ci->set_elapsed(0); | ||
111 | } | ||
112 | |||
113 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | ||
114 | |||
115 | /* The main decoder loop */ | ||
116 | seek_start : | ||
117 | while((unsigned)elapsed < rmctx.duration) | ||
118 | { | ||
119 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | ||
120 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | ||
121 | if(consumed < 0 && playback_on != 0) { | ||
122 | if(playback_on == -1) { | ||
123 | /* Error only if packet-parsing failed and playback hadn't started */ | ||
124 | DEBUGF("rm_get_packet failed\n"); | ||
125 | return CODEC_ERROR; | ||
126 | } | ||
127 | else | ||
128 | return CODEC_OK; | ||
129 | } | ||
130 | |||
131 | for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) | ||
132 | { | ||
133 | if (action == CODEC_ACTION_NULL) | ||
134 | action = ci->get_command(¶m); | ||
135 | |||
136 | if (action == CODEC_ACTION_HALT) | ||
137 | return CODEC_OK; | ||
138 | |||
139 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
140 | /* Do not allow seeking beyond the file's length */ | ||
141 | if ((unsigned) param > ci->id3->length) { | ||
142 | ci->set_elapsed(ci->id3->length); | ||
143 | ci->seek_complete(); | ||
144 | return CODEC_OK; | ||
145 | } | ||
146 | |||
147 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | ||
148 | packet_count = rmctx.nb_packets; | ||
149 | rmctx.audio_pkt_cnt = 0; | ||
150 | rmctx.frame_number = 0; | ||
151 | |||
152 | /* Seek to the start of the track */ | ||
153 | if (param == 0) { | ||
154 | ci->set_elapsed(0); | ||
155 | ci->seek_complete(); | ||
156 | action = CODEC_ACTION_NULL; | ||
157 | goto seek_start; | ||
158 | } | ||
159 | num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); | ||
160 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); | ||
161 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | ||
162 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | ||
163 | if(consumed < 0 && playback_on != 0) { | ||
164 | if(playback_on == -1) { | ||
165 | /* Error only if packet-parsing failed and playback hadn't started */ | ||
166 | DEBUGF("rm_get_packet failed\n"); | ||
167 | return CODEC_ERROR; | ||
168 | } | ||
169 | else | ||
170 | return CODEC_OK; | ||
171 | } | ||
172 | |||
173 | packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; | ||
174 | rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); | ||
175 | while(rmctx.audiotimestamp > (unsigned) param) { | ||
176 | rmctx.audio_pkt_cnt = 0; | ||
177 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); | ||
178 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | ||
179 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | ||
180 | packet_count += rmctx.audio_pkt_cnt; | ||
181 | num_units--; | ||
182 | } | ||
183 | time_offset = param - rmctx.audiotimestamp; | ||
184 | i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); | ||
185 | elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i; | ||
186 | ci->set_elapsed(elapsed); | ||
187 | ci->seek_complete(); | ||
188 | } | ||
189 | |||
190 | action = CODEC_ACTION_NULL; | ||
191 | |||
192 | if(pkt.length) | ||
193 | res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align); | ||
194 | else /* indicates that there are no remaining frames */ | ||
195 | return CODEC_OK; | ||
196 | |||
197 | if(res != rmctx.block_align) { | ||
198 | DEBUGF("codec error\n"); | ||
199 | return CODEC_ERROR; | ||
200 | } | ||
201 | |||
202 | if(datasize) | ||
203 | ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, q.samples_per_frame / rmctx.nb_channels); | ||
204 | playback_on = 1; | ||
205 | elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i; | ||
206 | ci->set_elapsed(elapsed); | ||
207 | rmctx.frame_number++; | ||
208 | } | ||
209 | packet_count -= rmctx.audio_pkt_cnt; | ||
210 | rmctx.audio_pkt_cnt = 0; | ||
211 | ci->advance_buffer(consumed); | ||
212 | } | ||
213 | |||
214 | return CODEC_OK; | ||
215 | } | ||