1/* 2** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com> 3** 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. 8** 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. 13** 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. 17*/ 18 19 20#include "sfconfig.h" 21 22#include <stdio.h> 23#include <fcntl.h> 24#include <string.h> 25#include <ctype.h> 26#include <time.h> 27#include <math.h> 28 29#if HAVE_UNISTD_H 30#include <unistd.h> 31#else 32#include "sf_unistd.h" 33#endif 34 35#include "sndfile.h" 36#include "sfendian.h" 37#include "common.h" 38 39#if (ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_XIPH_LIBS) 40 41#include <ogg/ogg.h> 42 43#include <speex/speex.h> 44#include <speex/speex_stereo.h> 45#include <speex/speex_header.h> 46#include <speex/speex_callbacks.h> 47 48#include "ogg.h" 49 50#define OGG_SPX_READ_SIZE 200 51 52typedef struct 53{ SpeexBits bits ; 54 55 int32_t serialno ; 56 57 int frame_size, granule_frame_size, nframes ; 58 int force_mode ; 59 60 SpeexStereoState stereo ; 61 SpeexHeader header ; 62 63 void * state ; 64} SPX_PRIVATE ; 65 66static int spx_read_header (SF_PRIVATE * psf) ; 67static int spx_close (SF_PRIVATE *psf) ; 68static void *spx_header_read (SF_PRIVATE * psf, ogg_packet *op, spx_int32_t enh_enabled, int force_mode) ; 69static void spx_print_comments (const char *comments, int length) ; 70 71int 72ogg_speex_open (SF_PRIVATE *psf) 73{ OGG_PRIVATE* odata = psf->container_data ; 74 SPX_PRIVATE* spx = calloc (1, sizeof (SPX_PRIVATE)) ; 75 int error = 0 ; 76 77 if (odata == NULL) 78 { psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ; 79 free (spx) ; 80 return SFE_INTERNAL ; 81 } ; 82 83 psf->codec_data = spx ; 84 if (spx == NULL) 85 return SFE_MALLOC_FAILED ; 86 87 if (psf->file.mode == SFM_RDWR) 88 return SFE_BAD_MODE_RW ; 89 90 if (psf->file.mode == SFM_READ) 91 { /* Call this here so it only gets called once, so no memory is leaked. */ 92 ogg_sync_init (&odata->osync) ; 93 94 if ((error = spx_read_header (psf))) 95 return error ; 96 97#if 0 98 psf->read_short = spx_read_s ; 99 psf->read_int = spx_read_i ; 100 psf->read_float = spx_read_f ; 101 psf->read_double = spx_read_d ; 102 psf->sf.frames = spx_length (psf) ; 103#endif 104 } ; 105 106 psf->codec_close = spx_close ; 107 108 if (psf->file.mode == SFM_WRITE) 109 { 110#if 0 111 /* Set the default spx quality here. */ 112 vdata->quality = 0.4 ; 113 114 psf->write_header = spx_write_header ; 115 psf->write_short = spx_write_s ; 116 psf->write_int = spx_write_i ; 117 psf->write_float = spx_write_f ; 118 psf->write_double = spx_write_d ; 119#endif 120 121 psf->sf.frames = SF_COUNT_MAX ; /* Unknown really */ 122 psf->strings.flags = SF_STR_ALLOW_START ; 123 } ; 124 125 psf->bytewidth = 1 ; 126 psf->blockwidth = psf->bytewidth * psf->sf.channels ; 127 128#if 0 129 psf->seek = spx_seek ; 130 psf->command = spx_command ; 131#endif 132 133 /* FIXME, FIXME, FIXME : Hack these here for now and correct later. */ 134 psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_SPEEX ; 135 psf->sf.sections = 1 ; 136 137 psf->datalength = 1 ; 138 psf->dataoffset = 0 ; 139 /* End FIXME. */ 140 141 return error ; 142} /* ogg_speex_open */ 143 144#define le_short (x) (x) 145 146static int 147spx_read_header (SF_PRIVATE * psf) 148{ static SpeexStereoState STEREO_INIT = SPEEX_STEREO_STATE_INIT ; 149 150 OGG_PRIVATE* odata = psf->container_data ; 151 SPX_PRIVATE* spx = psf->codec_data ; 152 153 ogg_int64_t page_granule = 0 ; 154 int stream_init = 0 ; 155 int page_nb_packets = 0 ; 156 int packet_count = 0 ; 157 int enh_enabled = 1 ; 158 int force_mode = -1 ; 159 char * data ; 160 int nb_read ; 161 int lookahead ; 162 163printf ("%s %d\n", __func__, __LINE__) ; 164 165 psf_log_printf (psf, "Speex header\n") ; 166 odata->eos = 0 ; 167 168 /* Reset ogg stuff which has already been used in src/ogg.c. */ 169 ogg_stream_reset (&odata->ostream) ; 170 ogg_sync_reset (&odata->osync) ; 171 172 /* Seek to start of stream. */ 173 psf_fseek (psf, 0, SEEK_SET) ; 174 175 /* Initialize. */ 176 ogg_sync_init (&odata->osync) ; 177 speex_bits_init (&spx->bits) ; 178 179 /* Set defaults. */ 180 psf->sf.channels = -1 ; 181 psf->sf.samplerate = 0 ; 182 spx->stereo = STEREO_INIT ; 183 184 /* Get a pointer to the ogg buffer and read data into it. */ 185 data = ogg_sync_buffer (&odata->osync, OGG_SPX_READ_SIZE) ; 186 nb_read = psf_fread (data, 1, OGG_SPX_READ_SIZE, psf) ; 187 ogg_sync_wrote (&odata->osync, nb_read) ; 188 189 /* Now we chew on Ogg packets. */ 190 while (ogg_sync_pageout (&odata->osync, &odata->opage) == 1) 191 { if (stream_init == 0) 192 { ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ; 193 stream_init = 1 ; 194 } ; 195 196 if (ogg_page_serialno (&odata->opage) != odata->ostream.serialno) 197 { /* so all streams are read. */ 198 ogg_stream_reset_serialno (&odata->ostream, ogg_page_serialno (&odata->opage)) ; 199 } ; 200 201 /*Add page to the bitstream*/ 202 ogg_stream_pagein (&odata->ostream, &odata->opage) ; 203 page_granule = ogg_page_granulepos (&odata->opage) ; 204 page_nb_packets = ogg_page_packets (&odata->opage) ; 205 206 /*Extract all available packets*/ 207 while (odata->eos == 0 && ogg_stream_packetout (&odata->ostream, &odata->opacket) == 1) 208 { if (odata->opacket.bytes >= 8 && memcmp (odata->opacket.packet, "Speex ", 8) == 0) 209 { spx->serialno = odata->ostream.serialno ; 210 } ; 211 212 if (spx->serialno == -1 || odata->ostream.serialno != spx->serialno) 213 break ; 214 215 if (packet_count == 0) 216 { spx->state = spx_header_read (psf, &odata->opacket, enh_enabled, force_mode) ; 217 if (! spx->state) 218 break ; 219 220 speex_decoder_ctl (spx->state, SPEEX_GET_LOOKAHEAD, &lookahead) ; 221 if (spx->nframes == 0) 222 spx->nframes = 1 ; 223 } 224 else if (packet_count == 1) 225 { spx_print_comments ((const char*) odata->opacket.packet, odata->opacket.bytes) ; 226 } 227 else if (packet_count < 2 + spx->header.extra_headers) 228 { /* Ignore extra headers */ 229 } 230 packet_count ++ ; 231 } ; 232 } ; 233 234 psf_log_printf (psf, "End\n") ; 235 236 psf_log_printf (psf, "packet_count %d\n", packet_count) ; 237 psf_log_printf (psf, "page_nb_packets %d\n", page_nb_packets) ; 238 psf_log_printf (psf, "page_granule %lld\n", page_granule) ; 239 240 return 0 ; 241} /* spx_read_header */ 242 243static int 244spx_close (SF_PRIVATE *psf) 245{ SPX_PRIVATE* spx = psf->codec_data ; 246 247 if (spx->state) 248 speex_decoder_destroy (spx->state) ; 249 250 if (spx) 251 speex_bits_destroy (&spx->bits) ; 252 253 return 0 ; 254} /* spx_close */ 255 256 257 258static void * 259spx_header_read (SF_PRIVATE * psf, ogg_packet *op, spx_int32_t enh_enabled, int force_mode) 260{ SPX_PRIVATE* spx = psf->codec_data ; 261 void *st ; 262 const SpeexMode *mode ; 263 SpeexHeader *tmp_header ; 264 int modeID ; 265 SpeexCallback callback ; 266 267 tmp_header = speex_packet_to_header ((char*) op->packet, op->bytes) ; 268 if (tmp_header == NULL) 269 { psf_log_printf (psf, "Cannot read Speex header\n") ; 270 return NULL ; 271 } ; 272 273 memcpy (&spx->header, tmp_header, sizeof (spx->header)) ; 274 free (tmp_header) ; 275 tmp_header = NULL ; 276 277 if (spx->header.mode >= SPEEX_NB_MODES || spx->header.mode < 0) 278 { psf_log_printf (psf, "Mode number %d does not (yet/any longer) exist in this version\n", spx->header.mode) ; 279 return NULL ; 280 } ; 281 282 modeID = spx->header.mode ; 283 if (force_mode != -1) 284 modeID = force_mode ; 285 286 mode = speex_lib_get_mode (modeID) ; 287 288 if (spx->header.speex_version_id > 1) 289 { 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 return NULL ; 291 } ; 292 293 if (mode->bitstream_version < spx->header.mode_bitstream_version) 294 { 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 return NULL ; 296 } ; 297 298 if (mode->bitstream_version > spx->header.mode_bitstream_version) 299 { 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 return NULL ; 301 } ; 302 303 st = speex_decoder_init (mode) ; 304 if (!st) 305 { psf_log_printf (psf, "Decoder initialization failed.\n") ; 306 return NULL ; 307 } ; 308 309 speex_decoder_ctl (st, SPEEX_SET_ENH, &enh_enabled) ; 310 speex_decoder_ctl (st, SPEEX_GET_FRAME_SIZE, &spx->frame_size) ; 311 spx->granule_frame_size = spx->frame_size ; 312 313 if (!psf->sf.samplerate) 314 psf->sf.samplerate = spx->header.rate ; 315 /* Adjust rate if --force-* options are used */ 316 if (force_mode != -1) 317 { if (spx->header.mode < force_mode) 318 { psf->sf.samplerate <<= (force_mode - spx->header.mode) ; 319 spx->granule_frame_size >>= (force_mode - spx->header.mode) ; 320 } ; 321 if (spx->header.mode > force_mode) 322 { psf->sf.samplerate >>= (spx->header.mode - force_mode) ; 323 spx->granule_frame_size <<= (spx->header.mode - force_mode) ; 324 } ; 325 } ; 326 327 speex_decoder_ctl (st, SPEEX_SET_SAMPLING_RATE, &psf->sf.samplerate) ; 328 329 spx->nframes = spx->header.frames_per_packet ; 330 331 if (psf->sf.channels == -1) 332 psf->sf.channels = spx->header.nb_channels ; 333 334 if (! (psf->sf.channels == 1)) 335 { psf->sf.channels = 2 ; 336 callback.callback_id = SPEEX_INBAND_STEREO ; 337 callback.func = speex_std_stereo_request_handler ; 338 callback.data = &spx->stereo ; 339 speex_decoder_ctl (st, SPEEX_SET_HANDLER, &callback) ; 340 } ; 341 342 spx->header.speex_version [sizeof (spx->header.speex_version) - 1] = 0 ; 343 344 psf_log_printf (psf, " Encoder ver : %s\n Frames/packet : %d\n", 345 spx->header.speex_version, spx->header.frames_per_packet) ; 346 347 if (spx->header.bitrate > 0) 348 psf_log_printf (psf, " Bit rate : %d\n", spx->header.bitrate) ; 349 350 psf_log_printf (psf, " Sample rate : %d\n Mode : %s\n VBR : %s\n Channels : %d\n", 351 psf->sf.samplerate, mode->modeName, (spx->header.vbr ? "yes" : "no"), psf->sf.channels) ; 352 353 psf_log_printf (psf, " Extra headers : %d\n", spx->header.extra_headers) ; 354 355 return st ; 356} /* spx_header_read */ 357 358 359static void 360spx_print_comments (const char *c, int length) 361{ 362 const char *end ; 363 int len, i, nb_fields ; 364 365printf ("%s %d\n", __func__, __LINE__) ; 366 if (length < 8) 367 { fprintf (stderr, "Invalid/corrupted comments\n") ; 368 return ; 369 } 370 end = c + length ; 371 len = readint (c, 0) ; 372 c += 4 ; 373 if (len < 0 || c + len > end) 374 { fprintf (stderr, "Invalid/corrupted comments\n") ; 375 return ; 376 } 377 (void) fwrite (c, 1, len, stderr) ; 378 c += len ; 379 fprintf (stderr, "\n") ; 380 if (c + 4 > end) 381 { fprintf (stderr, "Invalid/corrupted comments\n") ; 382 return ; 383 } 384 nb_fields = readint (c, 0) ; 385 c += 4 ; 386 for (i = 0 ; i < nb_fields ; i++) 387 { if (c + 4 > end) 388 { fprintf (stderr, "Invalid/corrupted comments\n") ; 389 return ; 390 } ; 391 len = readint (c, 0) ; 392 c += 4 ; 393 if (len < 0 || c + len > end) 394 { fprintf (stderr, "Invalid/corrupted comments\n") ; 395 return ; 396 } 397 (void) fwrite (c, 1, len, stderr) ; 398 c += len ; 399 fprintf (stderr, "\n") ; 400 } ; 401 return ; 402} /* spx_print_comments */ 403 404 405/* 406encoded_speex_frames = (frames_per_packet * Packets) 407 = 1 * 272 408 = 272 409 410audio_samples = encoded_speex_frames * frame_size 411 = 272 * 640 412 = 174080 413 414duration = audio_samples / rate 415 = 174080 / 44100 416 = 3.947 417*/ 418 419#else /* ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_XIPH_LIBS */ 420 421int 422ogg_speex_open (SF_PRIVATE *psf) 423{ 424 psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Speex support.\n") ; 425 return SFE_UNIMPLEMENTED ; 426} /* ogg_speex_open */ 427 428#endif 429