2 ** Copyright (C) 2008-2011 Erik de Castro Lopo <erikd@mega-nerd.com>
4 ** This program is free software ; you can redistribute it and/or modify
5 ** it under the terms of the GNU Lesser General Public License as published by
6 ** the Free Software Foundation ; either version 2.1 of the License, or
7 ** (at your option) any later version.
9 ** This program is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY ; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ** GNU Lesser General Public License for more details.
14 ** You should have received a copy of the GNU Lesser General Public License
15 ** along with this program ; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #if (ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_LIBS)
41 #include <speex/speex.h>
42 #include <speex/speex_stereo.h>
43 #include <speex/speex_header.h>
44 #include <speex/speex_callbacks.h>
48 #define OGG_SPX_READ_SIZE 200
55 int frame_size
, granule_frame_size
, nframes
;
58 SpeexStereoState stereo
;
64 static int spx_read_header (SF_PRIVATE
* psf
) ;
65 static int spx_close (SF_PRIVATE
*psf
) ;
66 static void *spx_header_read (SF_PRIVATE
* psf
, ogg_packet
*op
, spx_int32_t enh_enabled
, int force_mode
) ;
67 static void spx_print_comments (const char *comments
, int length
) ;
70 ogg_speex_open (SF_PRIVATE
*psf
)
71 { OGG_PRIVATE
* odata
= psf
->container_data
;
72 SPX_PRIVATE
* spx
= calloc (1, sizeof (SPX_PRIVATE
)) ;
76 { psf_log_printf (psf
, "%s : odata is NULL???\n", __func__
) ;
80 psf
->codec_data
= spx
;
82 return SFE_MALLOC_FAILED
;
84 if (psf
->file
.mode
== SFM_RDWR
)
85 return SFE_BAD_MODE_RW
;
87 if (psf
->file
.mode
== SFM_READ
)
88 { /* Call this here so it only gets called once, so no memory is leaked. */
89 ogg_sync_init (&odata
->osync
) ;
91 if ((error
= spx_read_header (psf
)))
95 psf
->read_short
= spx_read_s
;
96 psf
->read_int
= spx_read_i
;
97 psf
->read_float
= spx_read_f
;
98 psf
->read_double
= spx_read_d
;
99 psf
->sf
.frames
= spx_length (psf
) ;
103 psf
->codec_close
= spx_close
;
105 if (psf
->file
.mode
== SFM_WRITE
)
108 /* Set the default spx quality here. */
109 vdata
->quality
= 0.4 ;
111 psf
->write_header
= spx_write_header
;
112 psf
->write_short
= spx_write_s
;
113 psf
->write_int
= spx_write_i
;
114 psf
->write_float
= spx_write_f
;
115 psf
->write_double
= spx_write_d
;
118 psf
->sf
.frames
= SF_COUNT_MAX
; /* Unknown really */
119 psf
->str_flags
= SF_STR_ALLOW_START
;
123 psf
->blockwidth
= psf
->bytewidth
* psf
->sf
.channels
;
126 psf
->seek
= spx_seek
;
127 psf
->command
= spx_command
;
130 /* FIXME, FIXME, FIXME : Hack these here for now and correct later. */
131 psf
->sf
.format
= SF_FORMAT_OGG
| SF_FORMAT_SPEEX
;
132 psf
->sf
.sections
= 1 ;
134 psf
->datalength
= 1 ;
135 psf
->dataoffset
= 0 ;
139 } /* ogg_speex_open */
141 #define le_short (x) (x)
144 spx_read_header (SF_PRIVATE
* psf
)
145 { static SpeexStereoState STEREO_INIT
= SPEEX_STEREO_STATE_INIT
;
147 OGG_PRIVATE
* odata
= psf
->container_data
;
148 SPX_PRIVATE
* spx
= psf
->codec_data
;
150 ogg_int64_t page_granule
= 0 ;
151 int stream_init
= 0 ;
152 int page_nb_packets
= 0 ;
153 int packet_count
= 0 ;
154 int enh_enabled
= 1 ;
155 int force_mode
= -1 ;
160 printf ("%s %d\n", __func__
, __LINE__
) ;
162 psf_log_printf (psf
, "Speex header\n") ;
165 /* Reset ogg stuff which has already been used in src/ogg.c. */
166 ogg_stream_reset (&odata
->ostream
) ;
167 ogg_sync_reset (&odata
->osync
) ;
169 /* Seek to start of stream. */
170 psf_fseek (psf
, 0, SEEK_SET
) ;
173 ogg_sync_init (&odata
->osync
) ;
174 speex_bits_init (&spx
->bits
) ;
177 psf
->sf
.channels
= -1 ;
178 psf
->sf
.samplerate
= 0 ;
179 spx
->stereo
= STEREO_INIT
;
181 /* Get a pointer to the ogg buffer and read data into it. */
182 data
= ogg_sync_buffer (&odata
->osync
, OGG_SPX_READ_SIZE
) ;
183 nb_read
= psf_fread (data
, 1, OGG_SPX_READ_SIZE
, psf
) ;
184 ogg_sync_wrote (&odata
->osync
, nb_read
) ;
186 /* Now we chew on Ogg packets. */
187 while (ogg_sync_pageout (&odata
->osync
, &odata
->opage
) == 1)
188 { if (stream_init
== 0)
189 { ogg_stream_init (&odata
->ostream
, ogg_page_serialno (&odata
->opage
)) ;
193 if (ogg_page_serialno (&odata
->opage
) != odata
->ostream
.serialno
)
194 { /* so all streams are read. */
195 ogg_stream_reset_serialno (&odata
->ostream
, ogg_page_serialno (&odata
->opage
)) ;
198 /*Add page to the bitstream*/
199 ogg_stream_pagein (&odata
->ostream
, &odata
->opage
) ;
200 page_granule
= ogg_page_granulepos (&odata
->opage
) ;
201 page_nb_packets
= ogg_page_packets (&odata
->opage
) ;
203 /*Extract all available packets*/
204 while (odata
->eos
== 0 && ogg_stream_packetout (&odata
->ostream
, &odata
->opacket
) == 1)
205 { if (odata
->opacket
.bytes
>= 8 && memcmp (odata
->opacket
.packet
, "Speex ", 8) == 0)
206 { spx
->serialno
= odata
->ostream
.serialno
;
209 if (spx
->serialno
== -1 || odata
->ostream
.serialno
!= spx
->serialno
)
212 if (packet_count
== 0)
213 { spx
->state
= spx_header_read (psf
, &odata
->opacket
, enh_enabled
, force_mode
) ;
217 speex_decoder_ctl (spx
->state
, SPEEX_GET_LOOKAHEAD
, &lookahead
) ;
218 if (spx
->nframes
== 0)
221 else if (packet_count
== 1)
222 { spx_print_comments ((const char*) odata
->opacket
.packet
, odata
->opacket
.bytes
) ;
224 else if (packet_count
< 2 + spx
->header
.extra_headers
)
225 { /* Ignore extra headers */
231 psf_log_printf (psf
, "End\n") ;
233 psf_log_printf (psf
, "packet_count %d\n", packet_count
) ;
234 psf_log_printf (psf
, "page_nb_packets %d\n", page_nb_packets
) ;
235 psf_log_printf (psf
, "page_granule %lld\n", page_granule
) ;
238 } /* spx_read_header */
241 spx_close (SF_PRIVATE
*psf
)
242 { SPX_PRIVATE
* spx
= psf
->codec_data
;
245 speex_decoder_destroy (spx
->state
) ;
248 speex_bits_destroy (&spx
->bits
) ;
256 spx_header_read (SF_PRIVATE
* psf
, ogg_packet
*op
, spx_int32_t enh_enabled
, int force_mode
)
257 { SPX_PRIVATE
* spx
= psf
->codec_data
;
259 const SpeexMode
*mode
;
260 SpeexHeader
*tmp_header
;
262 SpeexCallback callback
;
264 tmp_header
= speex_packet_to_header ((char*) op
->packet
, op
->bytes
) ;
265 if (tmp_header
== NULL
)
266 { psf_log_printf (psf
, "Cannot read Speex header\n") ;
270 memcpy (&spx
->header
, tmp_header
, sizeof (spx
->header
)) ;
274 if (spx
->header
.mode
>= SPEEX_NB_MODES
|| spx
->header
.mode
< 0)
275 { psf_log_printf (psf
, "Mode number %d does not (yet/any longer) exist in this version\n", spx
->header
.mode
) ;
279 modeID
= spx
->header
.mode
;
280 if (force_mode
!= -1)
281 modeID
= force_mode
;
283 mode
= speex_lib_get_mode (modeID
) ;
285 if (spx
->header
.speex_version_id
> 1)
286 { psf_log_printf (psf
, "This file was encoded with Speex bit-stream version %d, which I don't know how to decode\n", spx
->header
.speex_version_id
) ;
290 if (mode
->bitstream_version
< spx
->header
.mode_bitstream_version
)
291 { psf_log_printf (psf
, "The file was encoded with a newer version of Speex. You need to upgrade in order to play it.\n") ;
295 if (mode
->bitstream_version
> spx
->header
.mode_bitstream_version
)
296 { psf_log_printf (psf
, "The file was encoded with an older version of Speex. You would need to downgrade the version in order to play it.\n") ;
300 st
= speex_decoder_init (mode
) ;
302 { psf_log_printf (psf
, "Decoder initialization failed.\n") ;
306 speex_decoder_ctl (st
, SPEEX_SET_ENH
, &enh_enabled
) ;
307 speex_decoder_ctl (st
, SPEEX_GET_FRAME_SIZE
, &spx
->frame_size
) ;
308 spx
->granule_frame_size
= spx
->frame_size
;
310 if (!psf
->sf
.samplerate
)
311 psf
->sf
.samplerate
= spx
->header
.rate
;
312 /* Adjust rate if --force-* options are used */
314 { if (spx
->header
.mode
< force_mode
)
315 { psf
->sf
.samplerate
<<= (force_mode
- spx
->header
.mode
) ;
316 spx
->granule_frame_size
>>= (force_mode
- spx
->header
.mode
) ;
318 if (spx
->header
.mode
> force_mode
)
319 { psf
->sf
.samplerate
>>= (spx
->header
.mode
- force_mode
) ;
320 spx
->granule_frame_size
<<= (spx
->header
.mode
- force_mode
) ;
324 speex_decoder_ctl (st
, SPEEX_SET_SAMPLING_RATE
, &psf
->sf
.samplerate
) ;
326 spx
->nframes
= spx
->header
.frames_per_packet
;
328 if (psf
->sf
.channels
== -1)
329 psf
->sf
.channels
= spx
->header
.nb_channels
;
331 if (! (psf
->sf
.channels
== 1))
332 { psf
->sf
.channels
= 2 ;
333 callback
.callback_id
= SPEEX_INBAND_STEREO
;
334 callback
.func
= speex_std_stereo_request_handler
;
335 callback
.data
= &spx
->stereo
;
336 speex_decoder_ctl (st
, SPEEX_SET_HANDLER
, &callback
) ;
339 spx
->header
.speex_version
[sizeof (spx
->header
.speex_version
) - 1] = 0 ;
341 psf_log_printf (psf
, " Encoder ver : %s\n Frames/packet : %d\n",
342 spx
->header
.speex_version
, spx
->header
.frames_per_packet
) ;
344 if (spx
->header
.bitrate
> 0)
345 psf_log_printf (psf
, " Bit rate : %d\n", spx
->header
.bitrate
) ;
347 psf_log_printf (psf
, " Sample rate : %d\n Mode : %s\n VBR : %s\n Channels : %d\n",
348 psf
->sf
.samplerate
, mode
->modeName
, (spx
->header
.vbr
? "yes" : "no"), psf
->sf
.channels
) ;
350 psf_log_printf (psf
, " Extra headers : %d\n", spx
->header
.extra_headers
) ;
353 } /* spx_header_read */
357 spx_print_comments (const char *c
, int length
)
360 int len
, i
, nb_fields
;
362 printf ("%s %d\n", __func__
, __LINE__
) ;
364 { fprintf (stderr
, "Invalid/corrupted comments\n") ;
368 len
= readint (c
, 0) ;
370 if (len
< 0 || c
+ len
> end
)
371 { fprintf (stderr
, "Invalid/corrupted comments\n") ;
374 (void) fwrite (c
, 1, len
, stderr
) ;
376 fprintf (stderr
, "\n") ;
378 { fprintf (stderr
, "Invalid/corrupted comments\n") ;
381 nb_fields
= readint (c
, 0) ;
383 for (i
= 0 ; i
< nb_fields
; i
++)
385 { fprintf (stderr
, "Invalid/corrupted comments\n") ;
388 len
= readint (c
, 0) ;
390 if (len
< 0 || c
+ len
> end
)
391 { fprintf (stderr
, "Invalid/corrupted comments\n") ;
394 (void) fwrite (c
, 1, len
, stderr
) ;
396 fprintf (stderr
, "\n") ;
399 } /* spx_print_comments */
403 encoded_speex_frames = (frames_per_packet * Packets)
407 audio_samples = encoded_speex_frames * frame_size
411 duration = audio_samples / rate
416 #else /* ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_LIBS */
419 ogg_speex_open (SF_PRIVATE
*psf
)
421 psf_log_printf (psf
, "This version of libsndfile was compiled without Ogg/Speex support.\n") ;
422 return SFE_UNIMPLEMENTED
;
423 } /* ogg_speex_open */