1 /********************************************************************
3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
5 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. *
6 * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
8 * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2003 *
9 * by Stan Seibert <volsung@xiph.org> AND OTHER CONTRIBUTORS *
10 * http://www.xiph.org/ *
12 ********************************************************************
16 ********************************************************************/
27 #include <speex/speex.h>
28 #include <speex/speex_header.h>
29 #include <speex/speex_stereo.h>
30 #include <speex/speex_callbacks.h>
31 #include "transport.h"
33 #include "vorbis_comments.h"
37 /* Note that this file contains gratuitous cut and paste from speexdec.c. */
40 /* Use speex's audio enhancement feature */
41 #define ENHANCE_AUDIO 1
44 typedef struct speex_private_t
{
50 SpeexStereoState
*stereo
;
55 int comment_packet_len
;
57 float *output
; /* Has frame_size * [number of channels] * frames_per_packet
62 int frames_per_packet
;
66 int bos
; /* If we are at the beginning (before headers) of a stream */
67 int eof
; /* If we've read the end of the data source (but there still might
68 be data in the buffers */
69 /* For calculating bitrate stats */
71 long samples_decoded_previous
;
73 long bytes_read_previous
;
78 decoder_stats_t stats
;
81 /* Forward declarations */
82 format_t speex_format
;
86 /* Private functions declarations */
87 void print_speex_info(SpeexHeader
*header
, decoder_callbacks_t
*cb
,
89 void print_speex_comments(char *comments
, int length
,
90 decoder_callbacks_t
*cb
, void *callback_arg
);
91 void *process_header(ogg_packet
*op
, int *frame_size
,
93 SpeexStereoState
*stereo
, decoder_callbacks_t
*cb
,
95 int read_speex_header(decoder_t
*decoder
);
97 /* ----------------------------------------------------------- */
100 int speex_can_decode (data_source_t
*source
)
105 len
= source
->transport
->peek(source
, buf
, sizeof(char), 36);
107 if (len
>= 32 && memcmp(buf
, "OggS", 4) == 0
108 && memcmp(buf
+28, "Speex ", 8) == 0) /* 3 trailing spaces */
115 decoder_t
* speex_init (data_source_t
*source
, ogg123_options_t
*ogg123_opts
,
116 audio_format_t
*audio_fmt
,
117 decoder_callbacks_t
*callbacks
, void *callback_arg
)
120 speex_private_t
*private;
124 /* Allocate data source structures */
125 decoder
= malloc(sizeof(decoder_t
));
126 private = malloc(sizeof(speex_private_t
));
128 if (decoder
!= NULL
&& private != NULL
) {
129 decoder
->source
= source
;
130 decoder
->actual_fmt
= decoder
->request_fmt
= *audio_fmt
;
131 decoder
->format
= &speex_format
;
132 decoder
->callbacks
= callbacks
;
133 decoder
->callback_arg
= callback_arg
;
134 decoder
->private = private;
139 private->samples_decoded
= private->samples_decoded_previous
= 0;
140 private->bytes_read
= private->bytes_read_previous
= 0;
141 private->currentsample
= 0;
142 private->comment_packet
= NULL
;
143 private->comment_packet_len
= 0;
144 private->header
= NULL
;
145 private->stereo
= speex_stereo_state_init();
147 private->stats
.total_time
= 0.0;
148 private->stats
.current_time
= 0.0;
149 private->stats
.instant_bitrate
= 0;
150 private->stats
.avg_bitrate
= 0;
152 fprintf(stderr
, _("ERROR: Out of memory.\n"));
156 /* Initialize structures */
157 ogg_sync_init(&private->oy
);
158 speex_bits_init(&private->bits
);
163 /* free(source); nope. caller frees. */
171 int speex_read (decoder_t
*decoder
, void *ptr
, int nbytes
, int *eos
,
172 audio_format_t
*audio_fmt
)
174 speex_private_t
*priv
= decoder
->private;
175 decoder_callbacks_t
*cb
= decoder
->callbacks
;
176 transport_t
*trans
= decoder
->source
->transport
;
177 int bytes_requested
= nbytes
;
179 short *out
= (short *) ptr
;
181 /* Read comments and audio info at the start of a logical bitstream */
184 ret
= read_speex_header(decoder
);
188 return 0; /* Bail out! */
191 print_speex_info(priv
->header
, cb
, decoder
->callback_arg
);
192 if (priv
->comment_packet
!= NULL
)
193 print_speex_comments(priv
->comment_packet
, priv
->comment_packet_len
,
194 cb
, decoder
->callback_arg
);
199 *audio_fmt
= decoder
->actual_fmt
;
205 /* First see if there is anything left in the output buffer and
207 if (priv
->output_left
> 0) {
208 int to_copy
= nbytes
/ (2 * audio_fmt
->channels
);
210 to_copy
*= audio_fmt
->channels
;
212 to_copy
= priv
->output_left
< to_copy
? priv
->output_left
: to_copy
;
215 for (i
= 0; i
< to_copy
; i
++)
216 out
[i
]=(ogg_int16_t
)priv
->output
[i
+priv
->output_start
];
219 priv
->output_start
+= to_copy
;
220 priv
->output_left
-= to_copy
;
222 priv
->currentsample
+= to_copy
/ audio_fmt
->channels
;
224 nbytes
-= to_copy
* 2;
225 } else if (ogg_stream_packetout(&priv
->os
, &priv
->op
) == 1) {
226 float *temp_output
= priv
->output
;
227 /* Decode some more samples */
229 /*Copy Ogg packet to Speex bitstream*/
230 speex_bits_read_from(&priv
->bits
, (char*)priv
->op
.packet
,
233 for (j
= 0;j
< priv
->frames_per_packet
; j
++) {
235 speex_decode(priv
->st
, &priv
->bits
, temp_output
);
237 if (audio_fmt
->channels
==2)
238 speex_decode_stereo(temp_output
, priv
->frame_size
, priv
->stereo
);
240 priv
->samples_decoded
+= priv
->frame_size
;
242 /*PCM saturation (just in case)*/
243 for (i
=0;i
< priv
->frame_size
* audio_fmt
->channels
; i
++) {
244 if (temp_output
[i
]>32000.0)
245 temp_output
[i
]=32000.0;
246 else if (temp_output
[i
]<-32000.0)
247 temp_output
[i
]=-32000.0;
250 temp_output
+= priv
->frame_size
* audio_fmt
->channels
;
253 priv
->output_start
= 0;
254 priv
->output_left
= priv
->frame_size
* audio_fmt
->channels
*
255 priv
->frames_per_packet
;
256 } else if (ogg_sync_pageout(&priv
->oy
, &priv
->og
) == 1) {
257 /* Read in another ogg page */
258 ogg_stream_pagein(&priv
->os
, &priv
->og
);
259 } else if (!priv
->eof
) {
260 /* Finally, pull in some more data and try again on the next pass */
262 /*Get the ogg buffer for writing*/
263 data
= ogg_sync_buffer(&priv
->oy
, 200);
264 /*Read bitstream from input file*/
265 nb_read
= trans
->read(decoder
->source
, data
, sizeof(char), 200);
268 priv
->eof
= 1; /* We've read the end of the file */
270 ogg_sync_wrote(&priv
->oy
, nb_read
);
272 priv
->bytes_read
+= nb_read
;
279 return bytes_requested
- nbytes
;
283 int speex_seek (decoder_t
*decoder
, double offset
, int whence
)
285 speex_private_t
*priv
= decoder
->private;
291 #define AVG_FACTOR 0.7
293 decoder_stats_t
*speex_statistics (decoder_t
*decoder
)
295 speex_private_t
*priv
= decoder
->private;
296 long instant_bitrate
;
299 priv
->stats
.total_time
= (double) priv
->totalsamples
/
300 (double) decoder
->actual_fmt
.rate
;
301 priv
->stats
.current_time
= (double) priv
->currentsample
/
302 (double) decoder
->actual_fmt
.rate
;
304 /* Need this test to prevent averaging in false zeros. */
305 if ((priv
->bytes_read
- priv
->bytes_read_previous
) != 0 &&
306 (priv
->samples_decoded
- priv
->samples_decoded_previous
) != 0) {
308 instant_bitrate
= 8.0 * (priv
->bytes_read
- priv
->bytes_read_previous
)
309 * decoder
->actual_fmt
.rate
310 / (double) (priv
->samples_decoded
- priv
->samples_decoded_previous
);
312 /* A little exponential averaging to smooth things out */
313 priv
->stats
.instant_bitrate
= AVG_FACTOR
* instant_bitrate
314 + (1.0 - AVG_FACTOR
) * priv
->stats
.instant_bitrate
;
316 priv
->bytes_read_previous
= priv
->bytes_read
;
317 priv
->samples_decoded_previous
= priv
->samples_decoded
;
320 /* Don't know unless we seek the stream */
321 priv
->stats
.avg_bitrate
= 0;
324 return malloc_decoder_stats(&priv
->stats
);
328 void speex_cleanup (decoder_t
*decoder
)
330 speex_private_t
*priv
= decoder
->private;
332 speex_stereo_state_destroy(priv
->stereo
);
333 free(priv
->comment_packet
);
335 free(decoder
->private);
340 format_t speex_format
= {
351 /* ------------------- Private functions -------------------- */
353 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
354 ((buf[base+2]<<16)&0xff0000)| \
355 ((buf[base+1]<<8)&0xff00)| \
358 void print_speex_info(SpeexHeader
*header
, decoder_callbacks_t
*cb
,
361 int modeID
= header
->mode
;
364 cb
->printf_metadata(callback_arg
, 2,
365 _("Ogg Speex stream: %d channel, %d Hz, %s mode (VBR)"),
368 speex_mode_list
[modeID
]->modeName
);
370 cb
->printf_metadata(callback_arg
, 2,
371 _("Ogg Speex stream: %d channel, %d Hz, %s mode"),
374 speex_mode_list
[modeID
]->modeName
);
376 cb
->printf_metadata(callback_arg
, 3,
377 _("Speex version: %s"),
378 header
->speex_version
);
383 void print_speex_comments(char *comments
, int length
,
384 decoder_callbacks_t
*cb
, void *callback_arg
)
387 int len
, i
, nb_fields
;
393 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
397 /* Parse out vendor string */
404 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
410 temp
= malloc(sizeof(char) * temp_len
);
412 strncpy(temp
, c
, len
);
415 cb
->printf_metadata(callback_arg
, 3, _("Encoded by: %s"), temp
);
418 /* Parse out user comments */
423 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
428 nb_fields
= readint(c
, 0);
431 for (i
= 0; i
< nb_fields
; i
++) {
433 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
440 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
446 if (temp_len
< len
+ 1) {
448 temp
= realloc(temp
, sizeof(char) * temp_len
);
452 strncpy(temp
, c
, len
);
455 print_vorbis_comment(temp
, cb
, callback_arg
);
464 void *process_header(ogg_packet
*op
, int *frame_size
,
465 SpeexHeader
**header
,
466 SpeexStereoState
*stereo
, decoder_callbacks_t
*cb
,
472 SpeexCallback callback
;
473 int enhance
= ENHANCE_AUDIO
;
475 *header
= speex_packet_to_header((char*)op
->packet
, op
->bytes
);
477 cb
->printf_error(callback_arg
, ERROR
, _("Cannot read header"));
480 if ((*header
)->mode
>= SPEEX_NB_MODES
|| (*header
)->mode
< 0) {
481 cb
->printf_error(callback_arg
, ERROR
,
482 _("Mode number %d does not (any longer) exist in this version"),
487 modeID
= (*header
)->mode
;
488 mode
= speex_mode_list
[modeID
];
490 if (mode
->bitstream_version
< (*header
)->mode_bitstream_version
) {
491 cb
->printf_error(callback_arg
, ERROR
, _("The file was encoded with a newer version of Speex.\n You need to upgrade in order to play it.\n"));
494 if (mode
->bitstream_version
> (*header
)->mode_bitstream_version
) {
495 cb
->printf_error(callback_arg
, ERROR
, _("The file was encoded with an older version of Speex.\nYou would need to downgrade the version in order to play it."));
499 st
= speex_decoder_init(mode
);
500 speex_decoder_ctl(st
, SPEEX_SET_ENH
, &enhance
);
501 speex_decoder_ctl(st
, SPEEX_GET_FRAME_SIZE
, frame_size
);
503 callback
.callback_id
= SPEEX_INBAND_STEREO
;
504 callback
.func
= speex_std_stereo_request_handler
;
505 callback
.data
= stereo
;
506 speex_decoder_ctl(st
, SPEEX_SET_HANDLER
, &callback
);
508 speex_decoder_ctl(st
, SPEEX_SET_SAMPLING_RATE
, &(*header
)->rate
);
514 int read_speex_header (decoder_t
*decoder
)
516 speex_private_t
*priv
= decoder
->private;
517 transport_t
*trans
= decoder
->source
->transport
;
518 int packet_count
= 0;
523 while (packet_count
< 2) {
524 /*Get the ogg buffer for writing*/
525 data
= ogg_sync_buffer(&priv
->oy
, 200);
527 /*Read bitstream from input file*/
528 nb_read
= trans
->read(decoder
->source
, data
, sizeof(char), 200);
529 ogg_sync_wrote(&priv
->oy
, nb_read
);
531 /*Loop for all complete pages we got (most likely only one)*/
532 while (ogg_sync_pageout(&priv
->oy
, &priv
->og
)==1) {
534 if (stream_init
== 0) {
535 ogg_stream_init(&priv
->os
, ogg_page_serialno(&priv
->og
));
539 /*Add page to the bitstream*/
540 ogg_stream_pagein(&priv
->os
, &priv
->og
);
542 /*Extract all available packets FIXME: EOS!*/
543 while (ogg_stream_packetout(&priv
->os
, &priv
->op
)==1) {
544 /*If first packet, process as Speex header*/
545 if (packet_count
==0) {
546 priv
->st
= process_header(&priv
->op
,
551 decoder
->callback_arg
);
556 decoder
->actual_fmt
.rate
= priv
->header
->rate
;
557 priv
->frames_per_packet
= priv
->header
->frames_per_packet
;
558 decoder
->actual_fmt
.channels
= priv
->header
->nb_channels
;
559 priv
->vbr
= priv
->header
->vbr
;
561 if (!priv
->frames_per_packet
)
562 priv
->frames_per_packet
=1;
565 priv
->output
= calloc(priv
->frame_size
*
566 decoder
->actual_fmt
.channels
*
567 priv
->frames_per_packet
, sizeof(float));
568 priv
->output_start
= 0;
569 priv
->output_left
= 0;
571 } else if (packet_count
==1){
572 priv
->comment_packet_len
= priv
->op
.bytes
;
573 priv
->comment_packet
= malloc(sizeof(char) *
574 priv
->comment_packet_len
);
575 memcpy(priv
->comment_packet
, priv
->op
.packet
,
576 priv
->comment_packet_len
);