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 ********************************************************************
14 last mod: $Id: speex_format.c,v 1.3 2003/06/24 02:22:33 giles Exp $
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
;
146 private->stats
.total_time
= 0.0;
147 private->stats
.current_time
= 0.0;
148 private->stats
.instant_bitrate
= 0;
149 private->stats
.avg_bitrate
= 0;
151 fprintf(stderr
, _("Error: Out of memory.\n"));
155 /* Initialize structures */
156 ogg_sync_init(&private->oy
);
157 speex_bits_init(&private->bits
);
162 /* free(source); nope. caller frees. */
170 int speex_read (decoder_t
*decoder
, void *ptr
, int nbytes
, int *eos
,
171 audio_format_t
*audio_fmt
)
173 speex_private_t
*priv
= decoder
->private;
174 decoder_callbacks_t
*cb
= decoder
->callbacks
;
175 transport_t
*trans
= decoder
->source
->transport
;
176 int bytes_requested
= nbytes
;
178 short *out
= (short *) ptr
;
180 /* Read comments and audio info at the start of a logical bitstream */
183 ret
= read_speex_header(decoder
);
187 return 0; /* Bail out! */
190 print_speex_info(priv
->header
, cb
, decoder
->callback_arg
);
191 if (priv
->comment_packet
!= NULL
)
192 print_speex_comments(priv
->comment_packet
, priv
->comment_packet_len
,
193 cb
, decoder
->callback_arg
);
198 *audio_fmt
= decoder
->actual_fmt
;
204 /* First see if there is anything left in the output buffer and
206 if (priv
->output_left
> 0) {
207 int to_copy
= nbytes
/ (2 * audio_fmt
->channels
);
209 to_copy
*= audio_fmt
->channels
;
211 to_copy
= priv
->output_left
< to_copy
? priv
->output_left
: to_copy
;
214 for (i
= 0; i
< to_copy
; i
++)
215 out
[i
]=(ogg_int16_t
)priv
->output
[i
+priv
->output_start
];
218 priv
->output_start
+= to_copy
;
219 priv
->output_left
-= to_copy
;
221 priv
->currentsample
+= to_copy
/ audio_fmt
->channels
;
223 nbytes
-= to_copy
* 2;
224 } else if (ogg_stream_packetout(&priv
->os
, &priv
->op
) == 1) {
225 float *temp_output
= priv
->output
;
226 /* Decode some more samples */
228 /*Copy Ogg packet to Speex bitstream*/
229 speex_bits_read_from(&priv
->bits
, (char*)priv
->op
.packet
,
232 for (j
= 0;j
< priv
->frames_per_packet
; j
++) {
234 speex_decode(priv
->st
, &priv
->bits
, temp_output
);
236 if (audio_fmt
->channels
==2)
237 speex_decode_stereo(temp_output
, priv
->frame_size
, &priv
->stereo
);
239 priv
->samples_decoded
+= priv
->frame_size
;
241 /*PCM saturation (just in case)*/
242 for (i
=0;i
< priv
->frame_size
* audio_fmt
->channels
; i
++) {
243 if (temp_output
[i
]>32000.0)
244 temp_output
[i
]=32000.0;
245 else if (temp_output
[i
]<-32000.0)
246 temp_output
[i
]=-32000.0;
249 temp_output
+= priv
->frame_size
* audio_fmt
->channels
;
252 priv
->output_start
= 0;
253 priv
->output_left
= priv
->frame_size
* audio_fmt
->channels
*
254 priv
->frames_per_packet
;
255 } else if (ogg_sync_pageout(&priv
->oy
, &priv
->og
) == 1) {
256 /* Read in another ogg page */
257 ogg_stream_pagein(&priv
->os
, &priv
->og
);
258 } else if (!priv
->eof
) {
259 /* Finally, pull in some more data and try again on the next pass */
261 /*Get the ogg buffer for writing*/
262 data
= ogg_sync_buffer(&priv
->oy
, 200);
263 /*Read bitstream from input file*/
264 nb_read
= trans
->read(decoder
->source
, data
, sizeof(char), 200);
267 priv
->eof
= 1; /* We've read the end of the file */
269 ogg_sync_wrote(&priv
->oy
, nb_read
);
271 priv
->bytes_read
+= nb_read
;
278 return bytes_requested
- nbytes
;
282 int speex_seek (decoder_t
*decoder
, double offset
, int whence
)
284 speex_private_t
*priv
= decoder
->private;
290 #define AVG_FACTOR 0.7
292 decoder_stats_t
*speex_statistics (decoder_t
*decoder
)
294 speex_private_t
*priv
= decoder
->private;
295 long instant_bitrate
;
298 priv
->stats
.total_time
= (double) priv
->totalsamples
/
299 (double) decoder
->actual_fmt
.rate
;
300 priv
->stats
.current_time
= (double) priv
->currentsample
/
301 (double) decoder
->actual_fmt
.rate
;
303 /* Need this test to prevent averaging in false zeros. */
304 if ((priv
->bytes_read
- priv
->bytes_read_previous
) != 0 &&
305 (priv
->samples_decoded
- priv
->samples_decoded_previous
) != 0) {
307 instant_bitrate
= 8.0 * (priv
->bytes_read
- priv
->bytes_read_previous
)
308 * decoder
->actual_fmt
.rate
309 / (double) (priv
->samples_decoded
- priv
->samples_decoded_previous
);
311 /* A little exponential averaging to smooth things out */
312 priv
->stats
.instant_bitrate
= AVG_FACTOR
* instant_bitrate
313 + (1.0 - AVG_FACTOR
) * priv
->stats
.instant_bitrate
;
315 priv
->bytes_read_previous
= priv
->bytes_read
;
316 priv
->samples_decoded_previous
= priv
->samples_decoded
;
319 /* Don't know unless we seek the stream */
320 priv
->stats
.avg_bitrate
= 0;
323 return malloc_decoder_stats(&priv
->stats
);
327 void speex_cleanup (decoder_t
*decoder
)
329 speex_private_t
*priv
= decoder
->private;
331 free(priv
->comment_packet
);
333 free(decoder
->private);
338 format_t speex_format
= {
349 /* ------------------- Private functions -------------------- */
351 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
352 ((buf[base+2]<<16)&0xff0000)| \
353 ((buf[base+1]<<8)&0xff00)| \
356 void print_speex_info(SpeexHeader
*header
, decoder_callbacks_t
*cb
,
359 int modeID
= header
->mode
;
362 cb
->printf_metadata(callback_arg
, 2,
363 _("Ogg Speex stream: %d channel, %d Hz, %s mode (VBR)"),
366 speex_mode_list
[modeID
]->modeName
);
368 cb
->printf_metadata(callback_arg
, 2,
369 _("Ogg Speex stream: %d channel, %d Hz, %s mode"),
372 speex_mode_list
[modeID
]->modeName
);
374 cb
->printf_metadata(callback_arg
, 3,
375 _("Speex version: %s"),
376 header
->speex_version
);
381 void print_speex_comments(char *comments
, int length
,
382 decoder_callbacks_t
*cb
, void *callback_arg
)
385 int len
, i
, nb_fields
;
391 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
395 /* Parse out vendor string */
402 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
408 temp
= malloc(sizeof(char) * temp_len
);
410 strncpy(temp
, c
, len
);
413 cb
->printf_metadata(callback_arg
, 3, _("Encoded by: %s"), temp
);
416 /* Parse out user comments */
421 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
426 nb_fields
= readint(c
, 0);
429 for (i
= 0; i
< nb_fields
; i
++) {
431 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
438 cb
->printf_error(callback_arg
, WARNING
, _("Invalid/corrupted comments"));
444 if (temp_len
< len
+ 1) {
446 temp
= realloc(temp
, sizeof(char) * temp_len
);
450 strncpy(temp
, c
, len
);
453 print_vorbis_comment(temp
, cb
, callback_arg
);
462 void *process_header(ogg_packet
*op
, int *frame_size
,
463 SpeexHeader
**header
,
464 SpeexStereoState
*stereo
, decoder_callbacks_t
*cb
,
470 SpeexCallback callback
;
471 int enhance
= ENHANCE_AUDIO
;
473 *header
= speex_packet_to_header((char*)op
->packet
, op
->bytes
);
475 cb
->printf_error(callback_arg
, ERROR
, _("Cannot read header"));
478 if ((*header
)->mode
>= SPEEX_NB_MODES
) {
479 cb
->printf_error(callback_arg
, ERROR
,
480 _("Mode number %d does not (any longer) exist in this version"),
485 modeID
= (*header
)->mode
;
486 mode
= speex_mode_list
[modeID
];
488 if (mode
->bitstream_version
< (*header
)->mode_bitstream_version
) {
489 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"));
492 if (mode
->bitstream_version
> (*header
)->mode_bitstream_version
) {
493 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."));
497 st
= speex_decoder_init(mode
);
498 speex_decoder_ctl(st
, SPEEX_SET_ENH
, &enhance
);
499 speex_decoder_ctl(st
, SPEEX_GET_FRAME_SIZE
, frame_size
);
501 callback
.callback_id
= SPEEX_INBAND_STEREO
;
502 callback
.func
= speex_std_stereo_request_handler
;
503 callback
.data
= stereo
;
504 speex_decoder_ctl(st
, SPEEX_SET_HANDLER
, &callback
);
506 speex_decoder_ctl(st
, SPEEX_SET_SAMPLING_RATE
, &(*header
)->rate
);
512 int read_speex_header (decoder_t
*decoder
)
514 speex_private_t
*priv
= decoder
->private;
515 transport_t
*trans
= decoder
->source
->transport
;
516 int packet_count
= 0;
521 while (packet_count
< 2) {
522 /*Get the ogg buffer for writing*/
523 data
= ogg_sync_buffer(&priv
->oy
, 200);
525 /*Read bitstream from input file*/
526 nb_read
= trans
->read(decoder
->source
, data
, sizeof(char), 200);
527 ogg_sync_wrote(&priv
->oy
, nb_read
);
529 /*Loop for all complete pages we got (most likely only one)*/
530 while (ogg_sync_pageout(&priv
->oy
, &priv
->og
)==1) {
532 if (stream_init
== 0) {
533 ogg_stream_init(&priv
->os
, ogg_page_serialno(&priv
->og
));
537 /*Add page to the bitstream*/
538 ogg_stream_pagein(&priv
->os
, &priv
->og
);
540 /*Extract all available packets FIXME: EOS!*/
541 while (ogg_stream_packetout(&priv
->os
, &priv
->op
)==1) {
542 /*If first packet, process as Speex header*/
543 if (packet_count
==0) {
544 priv
->st
= process_header(&priv
->op
,
549 decoder
->callback_arg
);
554 decoder
->actual_fmt
.rate
= priv
->header
->rate
;
555 priv
->frames_per_packet
= priv
->header
->frames_per_packet
;
556 decoder
->actual_fmt
.channels
= priv
->header
->nb_channels
;
557 priv
->vbr
= priv
->header
->vbr
;
559 if (!priv
->frames_per_packet
)
560 priv
->frames_per_packet
=1;
563 priv
->output
= calloc(priv
->frame_size
*
564 decoder
->actual_fmt
.channels
*
565 priv
->frames_per_packet
, sizeof(float));
566 priv
->output_start
= 0;
567 priv
->output_left
= 0;
569 } else if (packet_count
==1){
570 priv
->comment_packet_len
= priv
->op
.bytes
;
571 priv
->comment_packet
= malloc(sizeof(char) *
572 priv
->comment_packet_len
);
573 memcpy(priv
->comment_packet
, priv
->op
.packet
,
574 priv
->comment_packet_len
);