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 Bjrn Stenberg
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ***************************************************************************/
21 #include "libspeex/speex/ogg.h"
22 #include "libspeex/speex/speex.h"
23 #include "libspeex/speex/speex_callbacks.h"
24 #include "libspeex/speex/speex_header.h"
25 #include "libspeex/speex/speex_stereo.h"
26 #include "libspeex/speex/speex_config_types.h"
29 #define MAX_FRAME_SIZE 2000
30 #define CHUNKSIZE 10000 /*2kb*/
31 #define SEEK_CHUNKSIZE 7*CHUNKSIZE
35 spx_int16_t output
[MAX_FRAME_SIZE
] IBSS_ATTR
;
37 int get_more_data(spx_ogg_sync_state
*oy
)
42 buffer
= (char *)spx_ogg_sync_buffer(oy
,CHUNKSIZE
);
44 bytes
= ci
->read_filebuf(buffer
, sizeof(char)*CHUNKSIZE
);
46 spx_ogg_sync_wrote(oy
,bytes
);
51 /* The read/seek functions track absolute position within the stream */
53 static spx_int64_t
get_next_page(spx_ogg_sync_state
*oy
,spx_ogg_page
*og
,
56 spx_int64_t localoffset
= ci
->curpos
;
61 boundary
+= ci
->curpos
;
64 more
= spx_ogg_sync_pageseek(oy
,og
);
71 /* send more paramedics */
72 if(!boundary
)return(-1);
74 ret
= get_more_data(oy
);
82 /* got a page. Return the offset at the page beginning,
83 advance the internal offset past the page end */
85 spx_int64_t ret
=localoffset
;
93 static spx_int64_t
seek_backwards(spx_ogg_sync_state
*oy
, spx_ogg_page
*og
,
94 spx_int64_t wantedpos
)
97 spx_int64_t
*curoffset
=&crofs
;
98 *curoffset
=ci
->curpos
;
99 spx_int64_t begin
=*curoffset
;
100 spx_int64_t end
=begin
;
102 spx_int64_t offset
=-1;
103 spx_int64_t avgpagelen
=-1;
104 spx_int64_t lastgranule
=-1;
108 while (offset
== -1) {
110 begin
-= SEEK_CHUNKSIZE
;
117 LOGF("Can't seek that early:%d\n",begin
);
118 return -3; /* too early */
124 ci
->seek_buffer(*curoffset
);
126 spx_ogg_sync_reset(oy
);
130 while (*curoffset
< end
) {
131 ret
= get_next_page(oy
,og
,end
-*curoffset
);
134 if (lastgranule
!= -1) {
136 avgpagelen
= (spx_ogg_page_granulepos(og
)-lastgranule
);
138 avgpagelen
=((spx_ogg_page_granulepos(og
)-lastgranule
)
142 lastgranule
=spx_ogg_page_granulepos(og
);
144 if ((lastgranule
- (avgpagelen
/4)) < wantedpos
&&
145 (lastgranule
+ avgpagelen
+ (avgpagelen
/4)) > wantedpos
) {
147 /*wanted offset found Yeay!*/
149 /*LOGF("GnPagefound:%d,%d,%d,%d\n",ret,
150 lastgranule,wantedpos,avgpagelen);*/
154 } else if (lastgranule
> wantedpos
) { /*too late, seek more*/
156 LOGF("Toolate, returnanyway:%d,%d,%d,%d\n",
157 ret
,lastgranule
,wantedpos
,avgpagelen
);
161 } else{ /*if (spx_ogg_page_granulepos(&og)<wantedpos)*/
166 } else if (ret
== -3)
170 else if (*curoffset
< end
) {
171 /*this should not be possible*/
173 //LOGF("Seek:get_earlier_page:Offset:not_cached by granule:"\"%d,%d,%d,%d,%d\n",*curoffset,end,begin,wantedpos,curpos);
182 int speex_seek_page_granule(spx_int64_t pos
, spx_int64_t curpos
,
183 spx_ogg_sync_state
*oy
,
184 spx_int64_t headerssize
)
186 /* TODO: Someone may want to try to implement seek to packet,
187 instead of just to page (should be more accurate, not be any
191 spx_int64_t
*curbyteoffset
= &crofs
;
192 *curbyteoffset
= ci
->curpos
;
193 spx_int64_t curoffset
;
194 curoffset
= *curbyteoffset
;
195 spx_int64_t offset
= 0;
196 spx_ogg_page og
= {0,0,0,0};
197 spx_int64_t avgpagelen
= -1;
198 spx_int64_t lastgranule
= -1;
200 if(abs(pos
-curpos
)>10000 && headerssize
>0 && curoffset
-headerssize
>10000) {
201 /* if seeking for more that 10sec,
202 headersize is known & more than 10kb is played,
203 try to guess a place to seek from the number of
204 bytes playe for this position, this works best when
205 the bitrate is relativly constant.
208 curoffset
= (int)((((float)(*curbyteoffset
-(headerssize
)) *
209 (float)pos
)/(float)curpos
)*0.98);
214 //spx_int64_t toffset=curoffset;
216 ci
->seek_buffer(curoffset
);
218 spx_ogg_sync_reset(oy
);
220 offset
= get_next_page(oy
,&og
,-1);
222 if (offset
< 0) { /* could not find new page,use old offset */
223 LOGF("Seek/guess/fault:%d->-<-%d,%d:%d,%d,%d\n",
224 curpos
,0,pos
,offset
,0,
225 ci
->curpos
,/*stream_length*/0);
227 curoffset
= *curbyteoffset
;
229 ci
->seek_buffer(curoffset
);
231 spx_ogg_sync_reset(oy
);
233 if (spx_ogg_page_granulepos(&og
) == 0 && pos
> 5000) {
234 LOGF("SEEK/guess/fault:%d->-<-%d,%d:%d,%d,%d\n",
235 curpos
,spx_ogg_page_granulepos(&og
),pos
,
236 offset
,0,ci
->curpos
,/*stream_length*/0);
238 curoffset
= *curbyteoffset
;
240 ci
->seek_buffer(curoffset
);
242 spx_ogg_sync_reset(oy
);
245 curpos
= spx_ogg_page_granulepos(&og
);
250 /* which way do we want to seek? */
252 if (curpos
> pos
) { /* backwards */
253 offset
= seek_backwards(oy
,&og
,pos
);
256 *curbyteoffset
= curoffset
;
259 } else { /* forwards */
261 while ( (offset
= get_next_page(oy
,&og
,-1)) > 0) {
262 if (lastgranule
!= -1) {
264 avgpagelen
= (spx_ogg_page_granulepos(&og
) - lastgranule
);
266 avgpagelen
= ((spx_ogg_page_granulepos(&og
) - lastgranule
)
270 lastgranule
= spx_ogg_page_granulepos(&og
);
272 if ( ((lastgranule
- (avgpagelen
/4)) < pos
&& ( lastgranule
+
273 avgpagelen
+ (avgpagelen
/ 4)) > pos
) ||
276 /*wanted offset found Yeay!*/
278 *curbyteoffset
= offset
;
285 ci
->seek_buffer(*curbyteoffset
);
287 spx_ogg_sync_reset(oy
);
289 LOGF("Seek failed:%d\n", offset
);
294 static void *process_header(spx_ogg_packet
*op
,
300 SpeexStereoState
*stereo
,
305 const SpeexMode
*mode
;
308 SpeexCallback callback
;
310 header
= speex_packet_to_header((char*)op
->packet
, op
->bytes
);
313 DEBUGF ("Cannot read header\n");
317 if (header
->mode
>= SPEEX_NB_MODES
){
318 DEBUGF ("Mode does not exist\n");
322 modeID
= header
->mode
;
324 mode
= speex_lib_get_mode(modeID
);
326 if (header
->speex_version_id
> 1) {
327 DEBUGF("Undecodeable bitstream");
331 if (mode
->bitstream_version
< header
->mode_bitstream_version
){
332 DEBUGF("Undecodeable bitstream, newer bitstream");
336 if (mode
->bitstream_version
> header
->mode_bitstream_version
){
337 DEBUGF("Too old bitstream");
341 st
= speex_decoder_init(mode
);
343 DEBUGF("Decoder init failed");
346 speex_decoder_ctl(st
, SPEEX_SET_ENH
, &enh_enabled
);
347 speex_decoder_ctl(st
, SPEEX_GET_FRAME_SIZE
, frame_size
);
349 if (!(*channels
==1)){
350 callback
.callback_id
= SPEEX_INBAND_STEREO
;
351 callback
.func
= speex_std_stereo_request_handler
;
352 callback
.data
= stereo
;
353 speex_decoder_ctl(st
, SPEEX_SET_HANDLER
, &callback
);
356 *rate
= header
->rate
;
358 speex_decoder_ctl(st
, SPEEX_SET_SAMPLING_RATE
, rate
);
360 *nframes
= header
->frames_per_packet
;
363 *channels
= header
->nb_channels
;
365 *extra_headers
= header
->extra_headers
;
371 /* this is the codec entry point */
372 enum codec_status
codec_main(void)
377 spx_ogg_sync_state oy
;
380 spx_ogg_stream_state os
;
381 spx_int64_t page_granule
= 0, cur_granule
= 0;
385 SpeexStereoState stereo
= SPEEX_STEREO_STATE_INIT
;
387 int rate
= 0, samplerate
= 0;
388 int extra_headers
= 0;
390 int page_nb_packets
, frame_size
, packet_count
= 0;
392 int headerssize
= -1;
393 unsigned long strtoffset
= 0;
397 /* We need to flush reserver memory every track load. */
405 strtoffset
= ci
->id3
->offset
;
407 while (!*ci
->taginfo_ready
&& !ci
->stop_codec
)
410 spx_ogg_sync_init(&oy
);
411 spx_ogg_alloc_buffer(&oy
,2*CHUNKSIZE
);
413 samplerate
= ci
->id3
->frequency
;
414 codec_set_replaygain(ci
->id3
);
416 speex_bits_init(&bits
);
421 if (ci
->stop_codec
|| ci
->new_track
)
424 /*seek (seeks to the page before the position) */
426 if(samplerate
!=0&&packet_count
>1){
427 LOGF("Speex seek page:%d,%d,%d,%d\n",
428 ((spx_int64_t
)ci
->seek_time
/1000) *
429 (spx_int64_t
)samplerate
,
430 page_granule
, ci
->seek_time
,
431 (page_granule
/samplerate
)*1000, samplerate
);
433 speex_seek_page_granule(((spx_int64_t
)ci
->seek_time
/1000) *
434 (spx_int64_t
)samplerate
,
435 page_granule
, &oy
, headerssize
);
441 /*Get the ogg buffer for writing*/
442 if(get_more_data(&oy
)<1){/*read error*/
447 /* Loop for all complete pages we got (most likely only one) */
448 while (spx_ogg_sync_pageout(&oy
, &og
) == 1) {
450 if (stream_init
== 0) {
451 spx_ogg_stream_init(&os
, spx_ogg_page_serialno(&og
));
455 /* Add page to the bitstream */
456 spx_ogg_stream_pagein(&os
, &og
);
458 page_granule
= spx_ogg_page_granulepos(&og
);
459 page_nb_packets
= spx_ogg_page_packets(&og
);
461 cur_granule
= page_granule
;
463 /* Extract all available packets */
466 while (!eos
&& spx_ogg_stream_packetout(&os
, &op
)==1){
467 /* If first packet, process as Speex header */
468 if (packet_count
==0){
469 st
= process_header(&op
, enh_enabled
, &frame_size
,
470 &samplerate
, &nframes
, &channels
,
471 &stereo
, &extra_headers
);
473 speex_decoder_ctl(st
, SPEEX_GET_LOOKAHEAD
, &lookahead
);
484 ci
->id3
->frequency
= samplerate
;
485 ci
->configure(DSP_SET_FREQUENCY
, ci
->id3
->frequency
);
486 ci
->configure(DSP_SET_SAMPLE_DEPTH
, 16);
488 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_INTERLEAVED
);
489 } else if (channels
== 1) {
490 ci
->configure(DSP_SET_STEREO_MODE
, STEREO_MONO
);
493 /* Speex header in its own page, add the whole page
495 headerssize
+= og
.header_len
+og
.body_len
;
497 } else if (packet_count
<=1+extra_headers
){
498 /* add packet to headersize */
499 headerssize
+= op
.bytes
;
501 /* Ignore extra headers */
503 if (packet_count
<= 2+extra_headers
) {
505 ci
->seek_buffer(strtoffset
);
506 spx_ogg_sync_reset(&oy
);
513 if (op
.e_o_s
) /* End of stream condition */
516 /* Copy Ogg packet to Speex bitstream */
518 speex_bits_read_from(&bits
, (char*)op
.packet
, op
.bytes
);
520 for (j
= 0; j
!= nframes
; j
++){
524 ret
= speex_decode_int(st
, &bits
, output
);
532 if (speex_bits_remaining(&bits
) < 0)
536 speex_decode_stereo_int(output
, frame_size
, &stereo
);
538 int new_frame_size
= frame_size
;
540 if (new_frame_size
> 0) {
541 ci
->pcmbuf_insert(output
, NULL
, new_frame_size
);
544 cur_granule
+= new_frame_size
/ 2;
546 ci
->set_offset((long) ci
->curpos
);
548 ci
->set_elapsed((samplerate
== 0) ? 0 :
549 cur_granule
* 1000 / samplerate
);
559 if (ci
->request_next_track()) {
561 /* Clean things up for the next track */
563 speex_decoder_destroy(st
);
564 speex_bits_reset(&bits
);
566 if (stream_init
== 1)
567 spx_ogg_stream_reset(&os
);
569 spx_ogg_sync_reset(&oy
);
571 cur_granule
= stream_init
= rate
= samplerate
= headerssize
572 = packet_count
= eos
= 0;
574 stereo
.balance
= stereo
.smooth_left
= stereo
.smooth_right
= 1;
576 stereo
.reserved1
= stereo
.reserved2
= 0;
584 speex_bits_destroy(&bits
);
587 spx_ogg_stream_destroy(&os
);
589 spx_ogg_sync_destroy(&oy
);