1 /**************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
9 * Copyright (C) 2006 Frederik M.J. Vestre
10 * Based on vorbis.c codec interface:
11 * Copyright (C) 2002 Björn Stenberg
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "libspeex/speex/ogg.h"
24 #include "libspeex/speex/speex.h"
25 #include "libspeex/speex/speex_callbacks.h"
26 #include "libspeex/speex/speex_header.h"
27 #include "libspeex/speex/speex_stereo.h"
28 #include "libspeex/speex/speex_config_types.h"
31 /* Room for one stereo frame of max size, 2*640 */
32 #define MAX_FRAME_SIZE 1280
33 #define CHUNKSIZE 10000 /*2kb*/
34 #define SEEK_CHUNKSIZE 7*CHUNKSIZE
38 static spx_int16_t output
[MAX_FRAME_SIZE
] IBSS_ATTR
;
40 static int get_more_data(spx_ogg_sync_state
*oy
)
45 buffer
= (char *)spx_ogg_sync_buffer(oy
,CHUNKSIZE
);
47 bytes
= ci
->read_filebuf(buffer
, sizeof(char)*CHUNKSIZE
);
49 spx_ogg_sync_wrote(oy
,bytes
);
54 /* The read/seek functions track absolute position within the stream */
56 static spx_int64_t
get_next_page(spx_ogg_sync_state
*oy
,spx_ogg_page
*og
,
59 spx_int64_t localoffset
= ci
->curpos
;
64 boundary
+= ci
->curpos
;
67 more
= spx_ogg_sync_pageseek(oy
,og
);
74 /* send more paramedics */
75 if(!boundary
)return(-1);
77 ret
= get_more_data(oy
);
85 /* got a page. Return the offset at the page beginning,
86 advance the internal offset past the page end */
88 spx_int64_t ret
=localoffset
;
96 static spx_int64_t
seek_backwards(spx_ogg_sync_state
*oy
, spx_ogg_page
*og
,
97 spx_int64_t wantedpos
)
100 spx_int64_t
*curoffset
=&crofs
;
101 *curoffset
=ci
->curpos
;
102 spx_int64_t begin
=*curoffset
;
103 spx_int64_t end
=begin
;
105 spx_int64_t offset
=-1;
106 spx_int64_t avgpagelen
=-1;
107 spx_int64_t lastgranule
=-1;
111 while (offset
== -1) {
113 begin
-= SEEK_CHUNKSIZE
;
120 LOGF("Can't seek that early:%lld\n",begin
);
121 return -3; /* too early */
127 ci
->seek_buffer(*curoffset
);
129 spx_ogg_sync_reset(oy
);
133 while (*curoffset
< end
) {
134 ret
= get_next_page(oy
,og
,end
-*curoffset
);
137 if (lastgranule
!= -1) {
139 avgpagelen
= (spx_ogg_page_granulepos(og
)-lastgranule
);
141 avgpagelen
=((spx_ogg_page_granulepos(og
)-lastgranule
)
145 lastgranule
=spx_ogg_page_granulepos(og
);
147 if ((lastgranule
- (avgpagelen
/4)) < wantedpos
&&
148 (lastgranule
+ avgpagelen
+ (avgpagelen
/4)) > wantedpos
) {
150 /*wanted offset found Yeay!*/
152 /*LOGF("GnPagefound:%d,%d,%d,%d\n",ret,
153 lastgranule,wantedpos,avgpagelen);*/
157 } else if (lastgranule
> wantedpos
) { /*too late, seek more*/
159 LOGF("Toolate, returnanyway:%lld,%lld,%lld,%lld\n",
160 ret
,lastgranule
,wantedpos
,avgpagelen
);
164 } else{ /*if (spx_ogg_page_granulepos(&og)<wantedpos)*/
169 } else if (ret
== -3)
173 else if (*curoffset
< end
) {
174 /*this should not be possible*/
176 //LOGF("Seek:get_earlier_page:Offset:not_cached by granule:"\"%d,%d,%d,%d,%d\n",*curoffset,end,begin,wantedpos,curpos);
185 static int speex_seek_page_granule(spx_int64_t pos
, spx_int64_t curpos
,
186 spx_ogg_sync_state
*oy
,
187 spx_int64_t headerssize
)
189 /* TODO: Someone may want to try to implement seek to packet,
190 instead of just to page (should be more accurate, not be any
194 spx_int64_t
*curbyteoffset
= &crofs
;
195 *curbyteoffset
= ci
->curpos
;
196 spx_int64_t curoffset
;
197 curoffset
= *curbyteoffset
;
198 spx_int64_t offset
= 0;
199 spx_ogg_page og
= {0,0,0,0};
200 spx_int64_t avgpagelen
= -1;
201 spx_int64_t lastgranule
= -1;
203 if(abs(pos
-curpos
)>10000 && headerssize
>0 && curoffset
-headerssize
>10000) {
204 /* if seeking for more that 10sec,
205 headersize is known & more than 10kb is played,
206 try to guess a place to seek from the number of
207 bytes playe for this position, this works best when
208 the bitrate is relativly constant.
211 curoffset
= (((*curbyteoffset
-headerssize
) * pos
)/curpos
)*98/100;
215 //spx_int64_t toffset=curoffset;
217 ci
->seek_buffer(curoffset
);
219 spx_ogg_sync_reset(oy
);
221 offset
= get_next_page(oy
,&og
,-1);
223 if (offset
< 0) { /* could not find new page,use old offset */
224 LOGF("Seek/guess/fault:%lld->-<-%d,%lld:%lld,%d,%ld,%d\n",
225 curpos
,0,pos
,offset
,0,
226 ci
->curpos
,/*stream_length*/0);
228 curoffset
= *curbyteoffset
;
230 ci
->seek_buffer(curoffset
);
232 spx_ogg_sync_reset(oy
);
234 if (spx_ogg_page_granulepos(&og
) == 0 && pos
> 5000) {
235 LOGF("SEEK/guess/fault:%lld->-<-%lld,%lld:%lld,%d,%ld,%d\n",
236 curpos
,spx_ogg_page_granulepos(&og
),pos
,
237 offset
,0,ci
->curpos
,/*stream_length*/0);
239 curoffset
= *curbyteoffset
;
241 ci
->seek_buffer(curoffset
);
243 spx_ogg_sync_reset(oy
);
246 curpos
= spx_ogg_page_granulepos(&og
);
251 /* which way do we want to seek? */
253 if (curpos
> pos
) { /* backwards */
254 offset
= seek_backwards(oy
,&og
,pos
);
257 *curbyteoffset
= curoffset
;
260 } else { /* forwards */
262 while ( (offset
= get_next_page(oy
,&og
,-1)) > 0) {
263 if (lastgranule
!= -1) {
265 avgpagelen
= (spx_ogg_page_granulepos(&og
) - lastgranule
);
267 avgpagelen
= ((spx_ogg_page_granulepos(&og
) - lastgranule
)
271 lastgranule
= spx_ogg_page_granulepos(&og
);
273 if ( ((lastgranule
- (avgpagelen
/4)) < pos
&& ( lastgranule
+
274 avgpagelen
+ (avgpagelen
/ 4)) > pos
) ||
277 /*wanted offset found Yeay!*/
279 *curbyteoffset
= offset
;
286 ci
->seek_buffer(*curbyteoffset
);
288 spx_ogg_sync_reset(oy
);
290 LOGF("Seek failed:%lld\n", offset
);
295 static void *process_header(spx_ogg_packet
*op
,
301 SpeexStereoState
*stereo
,
306 const SpeexMode
*mode
;
309 SpeexCallback callback
;
311 header
= speex_packet_to_header((char*)op
->packet
, op
->bytes
);
314 DEBUGF ("Cannot read header\n");
318 if (header
->mode
>= SPEEX_NB_MODES
){
319 DEBUGF ("Mode does not exist\n");
323 modeID
= header
->mode
;
325 mode
= speex_lib_get_mode(modeID
);
327 if (header
->speex_version_id
> 1) {
328 DEBUGF("Undecodeable bitstream");
332 if (mode
->bitstream_version
< header
->mode_bitstream_version
){
333 DEBUGF("Undecodeable bitstream, newer bitstream");
337 if (mode
->bitstream_version
> header
->mode_bitstream_version
){
338 DEBUGF("Too old bitstream");
342 st
= speex_decoder_init(mode
);
344 DEBUGF("Decoder init failed");
347 speex_decoder_ctl(st
, SPEEX_SET_ENH
, &enh_enabled
);
348 speex_decoder_ctl(st
, SPEEX_GET_FRAME_SIZE
, frame_size
);
350 if (header
->nb_channels
!=1){
351 callback
.callback_id
= SPEEX_INBAND_STEREO
;
352 callback
.func
= speex_std_stereo_request_handler
;
353 callback
.data
= stereo
;
354 speex_decoder_ctl(st
, SPEEX_SET_HANDLER
, &callback
);
356 *channels
= header
->nb_channels
;
359 *rate
= header
->rate
;
361 speex_decoder_ctl(st
, SPEEX_SET_SAMPLING_RATE
, rate
);
363 *nframes
= header
->frames_per_packet
;
365 *extra_headers
= header
->extra_headers
;
370 /* this is the codec entry point */
371 enum codec_status
codec_main(enum codec_entry_call_reason reason
)
378 /* this is called for each file to process */
379 enum codec_status
codec_run(void)
381 int error
= CODEC_ERROR
;
385 spx_ogg_sync_state oy
;
388 spx_ogg_stream_state os
;
389 spx_int64_t page_granule
= 0;
390 spx_int64_t cur_granule
= 0;
394 SpeexStereoState
*stereo
;
396 int samplerate
= ci
->id3
->frequency
;
397 int extra_headers
= 0;
399 /* rockbox: comment 'set but unused' variables
403 int packet_count
= 0;
406 unsigned long strtoffset
= ci
->id3
->offset
;
411 memset(&bits
, 0, sizeof(bits
));
412 memset(&oy
, 0, sizeof(oy
));
414 /* Ogg handling still uses mallocs, so reset the malloc buffer per track */
422 stereo
= speex_stereo_state_init();
423 spx_ogg_sync_init(&oy
);
424 spx_ogg_alloc_buffer(&oy
,2*CHUNKSIZE
);
426 codec_set_replaygain(ci
->id3
);
430 enum codec_command_action action
= ci
->get_command(¶m
);
432 if (action
== CODEC_ACTION_HALT
)
435 /*seek (seeks to the page before the position) */
436 if (action
== CODEC_ACTION_SEEK_TIME
) {
437 if(samplerate
!=0&&packet_count
>1){
438 LOGF("Speex seek page:%lld,%lld,%ld,%lld,%d\n",
439 ((spx_int64_t
)param
/1000) *
440 (spx_int64_t
)samplerate
,
441 page_granule
, (long)param
,
442 (page_granule
/samplerate
)*1000, samplerate
);
444 speex_seek_page_granule(((spx_int64_t
)param
/1000) *
445 (spx_int64_t
)samplerate
,
446 page_granule
, &oy
, headerssize
);
449 ci
->set_elapsed(param
);
454 /*Get the ogg buffer for writing*/
455 if(get_more_data(&oy
)<1){/*read error*/
459 /* Loop for all complete pages we got (most likely only one) */
460 while (spx_ogg_sync_pageout(&oy
, &og
) == 1) {
462 if (stream_init
== 0) {
463 spx_ogg_stream_init(&os
, spx_ogg_page_serialno(&og
));
467 /* Add page to the bitstream */
468 spx_ogg_stream_pagein(&os
, &og
);
470 page_granule
= spx_ogg_page_granulepos(&og
);
471 /* page_nb_packets = spx_ogg_page_packets(&og); */
473 cur_granule
= page_granule
;
475 /* Extract all available packets */
478 while (!eos
&& spx_ogg_stream_packetout(&os
, &op
)==1){
479 /* If first packet, process as Speex header */
480 if (packet_count
==0){
481 st
= process_header(&op
, enh_enabled
, &frame_size
,
482 &samplerate
, &nframes
, &channels
,
483 stereo
, &extra_headers
);
485 speex_decoder_ctl(st
, SPEEX_GET_LOOKAHEAD
, &lookahead
);
493 ci
->configure(DSP_SET_FREQUENCY
, ci
->id3
->frequency
);
494 ci
->configure(DSP_SET_SAMPLE_DEPTH
, 16);
496 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_INTERLEAVED
);
497 } else if (channels
== 1) {
498 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_MONO
);
501 /* Speex header in its own page, add the whole page
503 headerssize
+= og
.header_len
+og
.body_len
;
505 } else if (packet_count
<=1+extra_headers
){
506 /* add packet to headersize */
507 headerssize
+= op
.bytes
;
509 /* Ignore extra headers */
511 if (packet_count
<= 2+extra_headers
) {
513 ci
->seek_buffer(strtoffset
);
514 spx_ogg_sync_reset(&oy
);
521 if (op
.e_o_s
) /* End of stream condition */
524 /* Set Speex bitstream to point to Ogg packet */
525 speex_bits_set_bit_buffer(&bits
, (char *)op
.packet
,
527 for (j
= 0; j
!= nframes
; j
++){
531 ret
= speex_decode_int(st
, &bits
, output
);
539 if (speex_bits_remaining(&bits
) < 0)
543 speex_decode_stereo_int(output
, frame_size
, stereo
);
545 if (frame_size
> 0) {
546 spx_int16_t
*frame_start
= output
+ lookahead
;
549 frame_start
+= lookahead
;
550 ci
->pcmbuf_insert(frame_start
, NULL
,
551 frame_size
- lookahead
);
554 cur_granule
+= frame_size
/ 2;
556 ci
->set_offset((long) ci
->curpos
);
558 ci
->set_elapsed((samplerate
== 0) ? 0 :
559 cur_granule
* 1000 / samplerate
);
570 /* Clean things up for the next track */
571 speex_bits_destroy(&bits
);
574 speex_decoder_destroy(st
);
577 spx_ogg_stream_destroy(&os
);
579 spx_ogg_sync_destroy(&oy
);