Alexandre Lision | 7c6f4a6 | 2013-09-05 13:27:01 -0400 | [diff] [blame] | 1 | /* |
| 2 | ** Copyright (C) 2008-2011 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 | #endif |
| 32 | |
| 33 | #include "sndfile.h" |
| 34 | #include "sfendian.h" |
| 35 | #include "common.h" |
| 36 | |
| 37 | #if (ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_LIBS) |
| 38 | |
| 39 | #include <ogg/ogg.h> |
| 40 | |
| 41 | #include <speex/speex.h> |
| 42 | #include <speex/speex_stereo.h> |
| 43 | #include <speex/speex_header.h> |
| 44 | #include <speex/speex_callbacks.h> |
| 45 | |
| 46 | #include "ogg.h" |
| 47 | |
| 48 | #define OGG_SPX_READ_SIZE 200 |
| 49 | |
| 50 | typedef struct |
| 51 | { SpeexBits bits ; |
| 52 | |
| 53 | int32_t serialno ; |
| 54 | |
| 55 | int frame_size, granule_frame_size, nframes ; |
| 56 | int force_mode ; |
| 57 | |
| 58 | SpeexStereoState stereo ; |
| 59 | SpeexHeader header ; |
| 60 | |
| 61 | void * state ; |
| 62 | } SPX_PRIVATE ; |
| 63 | |
| 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) ; |
| 68 | |
| 69 | int |
| 70 | ogg_speex_open (SF_PRIVATE *psf) |
| 71 | { OGG_PRIVATE* odata = psf->container_data ; |
| 72 | SPX_PRIVATE* spx = calloc (1, sizeof (SPX_PRIVATE)) ; |
| 73 | int error = 0 ; |
| 74 | |
| 75 | if (odata == NULL) |
| 76 | { psf_log_printf (psf, "%s : odata is NULL???\n", __func__) ; |
| 77 | return SFE_INTERNAL ; |
| 78 | } ; |
| 79 | |
| 80 | psf->codec_data = spx ; |
| 81 | if (spx == NULL) |
| 82 | return SFE_MALLOC_FAILED ; |
| 83 | |
| 84 | if (psf->file.mode == SFM_RDWR) |
| 85 | return SFE_BAD_MODE_RW ; |
| 86 | |
| 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) ; |
| 90 | |
| 91 | if ((error = spx_read_header (psf))) |
| 92 | return error ; |
| 93 | |
| 94 | #if 0 |
| 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) ; |
| 100 | #endif |
| 101 | } ; |
| 102 | |
| 103 | psf->codec_close = spx_close ; |
| 104 | |
| 105 | if (psf->file.mode == SFM_WRITE) |
| 106 | { |
| 107 | #if 0 |
| 108 | /* Set the default spx quality here. */ |
| 109 | vdata->quality = 0.4 ; |
| 110 | |
| 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 ; |
| 116 | #endif |
| 117 | |
| 118 | psf->sf.frames = SF_COUNT_MAX ; /* Unknown really */ |
| 119 | psf->str_flags = SF_STR_ALLOW_START ; |
| 120 | } ; |
| 121 | |
| 122 | psf->bytewidth = 1 ; |
| 123 | psf->blockwidth = psf->bytewidth * psf->sf.channels ; |
| 124 | |
| 125 | #if 0 |
| 126 | psf->seek = spx_seek ; |
| 127 | psf->command = spx_command ; |
| 128 | #endif |
| 129 | |
| 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 ; |
| 133 | |
| 134 | psf->datalength = 1 ; |
| 135 | psf->dataoffset = 0 ; |
| 136 | /* End FIXME. */ |
| 137 | |
| 138 | return error ; |
| 139 | } /* ogg_speex_open */ |
| 140 | |
| 141 | #define le_short (x) (x) |
| 142 | |
| 143 | static int |
| 144 | spx_read_header (SF_PRIVATE * psf) |
| 145 | { static SpeexStereoState STEREO_INIT = SPEEX_STEREO_STATE_INIT ; |
| 146 | |
| 147 | OGG_PRIVATE* odata = psf->container_data ; |
| 148 | SPX_PRIVATE* spx = psf->codec_data ; |
| 149 | |
| 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 ; |
| 156 | char * data ; |
| 157 | int nb_read ; |
| 158 | int lookahead ; |
| 159 | |
| 160 | printf ("%s %d\n", __func__, __LINE__) ; |
| 161 | |
| 162 | psf_log_printf (psf, "Speex header\n") ; |
| 163 | odata->eos = 0 ; |
| 164 | |
| 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) ; |
| 168 | |
| 169 | /* Seek to start of stream. */ |
| 170 | psf_fseek (psf, 0, SEEK_SET) ; |
| 171 | |
| 172 | /* Initialize. */ |
| 173 | ogg_sync_init (&odata->osync) ; |
| 174 | speex_bits_init (&spx->bits) ; |
| 175 | |
| 176 | /* Set defaults. */ |
| 177 | psf->sf.channels = -1 ; |
| 178 | psf->sf.samplerate = 0 ; |
| 179 | spx->stereo = STEREO_INIT ; |
| 180 | |
| 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) ; |
| 185 | |
| 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)) ; |
| 190 | stream_init = 1 ; |
| 191 | } ; |
| 192 | |
| 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)) ; |
| 196 | } ; |
| 197 | |
| 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) ; |
| 202 | |
| 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 ; |
| 207 | } ; |
| 208 | |
| 209 | if (spx->serialno == -1 || odata->ostream.serialno != spx->serialno) |
| 210 | break ; |
| 211 | |
| 212 | if (packet_count == 0) |
| 213 | { spx->state = spx_header_read (psf, &odata->opacket, enh_enabled, force_mode) ; |
| 214 | if (! spx->state) |
| 215 | break ; |
| 216 | |
| 217 | speex_decoder_ctl (spx->state, SPEEX_GET_LOOKAHEAD, &lookahead) ; |
| 218 | if (spx->nframes == 0) |
| 219 | spx->nframes = 1 ; |
| 220 | } |
| 221 | else if (packet_count == 1) |
| 222 | { spx_print_comments ((const char*) odata->opacket.packet, odata->opacket.bytes) ; |
| 223 | } |
| 224 | else if (packet_count < 2 + spx->header.extra_headers) |
| 225 | { /* Ignore extra headers */ |
| 226 | } |
| 227 | packet_count ++ ; |
| 228 | } ; |
| 229 | } ; |
| 230 | |
| 231 | psf_log_printf (psf, "End\n") ; |
| 232 | |
| 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) ; |
| 236 | |
| 237 | return 0 ; |
| 238 | } /* spx_read_header */ |
| 239 | |
| 240 | static int |
| 241 | spx_close (SF_PRIVATE *psf) |
| 242 | { SPX_PRIVATE* spx = psf->codec_data ; |
| 243 | |
| 244 | if (spx->state) |
| 245 | speex_decoder_destroy (spx->state) ; |
| 246 | |
| 247 | if (spx) |
| 248 | speex_bits_destroy (&spx->bits) ; |
| 249 | |
| 250 | return 0 ; |
| 251 | } /* spx_close */ |
| 252 | |
| 253 | |
| 254 | |
| 255 | static void * |
| 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 ; |
| 258 | void *st ; |
| 259 | const SpeexMode *mode ; |
| 260 | SpeexHeader *tmp_header ; |
| 261 | int modeID ; |
| 262 | SpeexCallback callback ; |
| 263 | |
| 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") ; |
| 267 | return NULL ; |
| 268 | } ; |
| 269 | |
| 270 | memcpy (&spx->header, tmp_header, sizeof (spx->header)) ; |
| 271 | free (tmp_header) ; |
| 272 | tmp_header = NULL ; |
| 273 | |
| 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) ; |
| 276 | return NULL ; |
| 277 | } ; |
| 278 | |
| 279 | modeID = spx->header.mode ; |
| 280 | if (force_mode != -1) |
| 281 | modeID = force_mode ; |
| 282 | |
| 283 | mode = speex_lib_get_mode (modeID) ; |
| 284 | |
| 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) ; |
| 287 | return NULL ; |
| 288 | } ; |
| 289 | |
| 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") ; |
| 292 | return NULL ; |
| 293 | } ; |
| 294 | |
| 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") ; |
| 297 | return NULL ; |
| 298 | } ; |
| 299 | |
| 300 | st = speex_decoder_init (mode) ; |
| 301 | if (!st) |
| 302 | { psf_log_printf (psf, "Decoder initialization failed.\n") ; |
| 303 | return NULL ; |
| 304 | } ; |
| 305 | |
| 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 ; |
| 309 | |
| 310 | if (!psf->sf.samplerate) |
| 311 | psf->sf.samplerate = spx->header.rate ; |
| 312 | /* Adjust rate if --force-* options are used */ |
| 313 | if (force_mode!=-1) |
| 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) ; |
| 317 | } ; |
| 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) ; |
| 321 | } ; |
| 322 | } ; |
| 323 | |
| 324 | speex_decoder_ctl (st, SPEEX_SET_SAMPLING_RATE, &psf->sf.samplerate) ; |
| 325 | |
| 326 | spx->nframes = spx->header.frames_per_packet ; |
| 327 | |
| 328 | if (psf->sf.channels == -1) |
| 329 | psf->sf.channels = spx->header.nb_channels ; |
| 330 | |
| 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) ; |
| 337 | } ; |
| 338 | |
| 339 | spx->header.speex_version [sizeof (spx->header.speex_version) - 1] = 0 ; |
| 340 | |
| 341 | psf_log_printf (psf, " Encoder ver : %s\n Frames/packet : %d\n", |
| 342 | spx->header.speex_version, spx->header.frames_per_packet) ; |
| 343 | |
| 344 | if (spx->header.bitrate > 0) |
| 345 | psf_log_printf (psf, " Bit rate : %d\n", spx->header.bitrate) ; |
| 346 | |
| 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) ; |
| 349 | |
| 350 | psf_log_printf (psf, " Extra headers : %d\n", spx->header.extra_headers) ; |
| 351 | |
| 352 | return st ; |
| 353 | } /* spx_header_read */ |
| 354 | |
| 355 | |
| 356 | static void |
| 357 | spx_print_comments (const char *c, int length) |
| 358 | { |
| 359 | const char *end ; |
| 360 | int len, i, nb_fields ; |
| 361 | |
| 362 | printf ("%s %d\n", __func__, __LINE__) ; |
| 363 | if (length<8) |
| 364 | { fprintf (stderr, "Invalid/corrupted comments\n") ; |
| 365 | return ; |
| 366 | } |
| 367 | end = c + length ; |
| 368 | len = readint (c, 0) ; |
| 369 | c += 4 ; |
| 370 | if (len < 0 || c + len > end) |
| 371 | { fprintf (stderr, "Invalid/corrupted comments\n") ; |
| 372 | return ; |
| 373 | } |
| 374 | (void) fwrite (c, 1, len, stderr) ; |
| 375 | c += len ; |
| 376 | fprintf (stderr, "\n") ; |
| 377 | if (c + 4 > end) |
| 378 | { fprintf (stderr, "Invalid/corrupted comments\n") ; |
| 379 | return ; |
| 380 | } |
| 381 | nb_fields = readint (c, 0) ; |
| 382 | c += 4 ; |
| 383 | for (i = 0 ; i < nb_fields ; i++) |
| 384 | { if (c+4>end) |
| 385 | { fprintf (stderr, "Invalid/corrupted comments\n") ; |
| 386 | return ; |
| 387 | } ; |
| 388 | len = readint (c, 0) ; |
| 389 | c += 4 ; |
| 390 | if (len < 0 || c + len > end) |
| 391 | { fprintf (stderr, "Invalid/corrupted comments\n") ; |
| 392 | return ; |
| 393 | } |
| 394 | (void) fwrite (c, 1, len, stderr) ; |
| 395 | c += len ; |
| 396 | fprintf (stderr, "\n") ; |
| 397 | } ; |
| 398 | return ; |
| 399 | } /* spx_print_comments */ |
| 400 | |
| 401 | |
| 402 | /* |
| 403 | encoded_speex_frames = (frames_per_packet * Packets) |
| 404 | = 1 * 272 |
| 405 | = 272 |
| 406 | |
| 407 | audio_samples = encoded_speex_frames * frame_size |
| 408 | = 272 * 640 |
| 409 | = 174080 |
| 410 | |
| 411 | duration = audio_samples / rate |
| 412 | = 174080 / 44100 |
| 413 | = 3.947 |
| 414 | */ |
| 415 | |
| 416 | #else /* ENABLE_EXPERIMENTAL_CODE && HAVE_EXTERNAL_LIBS */ |
| 417 | |
| 418 | int |
| 419 | ogg_speex_open (SF_PRIVATE *psf) |
| 420 | { |
| 421 | psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Speex support.\n") ; |
| 422 | return SFE_UNIMPLEMENTED ; |
| 423 | } /* ogg_speex_open */ |
| 424 | |
| 425 | #endif |