Alexandre Lision | 7c6f4a6 | 2013-09-05 13:27:01 -0400 | [diff] [blame] | 1 | /* |
| 2 | ** Copyright (C) 1999-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 | #include "sfconfig.h" |
| 20 | |
| 21 | #include <stdio.h> |
| 22 | #include <string.h> |
| 23 | #include <ctype.h> |
| 24 | #include <stdarg.h> |
| 25 | |
| 26 | #include "sndfile.h" |
| 27 | #include "sfendian.h" |
| 28 | #include "common.h" |
| 29 | |
| 30 | |
| 31 | /*------------------------------------------------------------------------------ |
| 32 | * Macros to handle big/little endian issues. |
| 33 | */ |
| 34 | |
| 35 | #define FORM_MARKER (MAKE_MARKER ('F', 'O', 'R', 'M')) |
| 36 | #define SVX8_MARKER (MAKE_MARKER ('8', 'S', 'V', 'X')) |
| 37 | #define SV16_MARKER (MAKE_MARKER ('1', '6', 'S', 'V')) |
| 38 | #define VHDR_MARKER (MAKE_MARKER ('V', 'H', 'D', 'R')) |
| 39 | #define BODY_MARKER (MAKE_MARKER ('B', 'O', 'D', 'Y')) |
| 40 | |
| 41 | #define ATAK_MARKER (MAKE_MARKER ('A', 'T', 'A', 'K')) |
| 42 | #define RLSE_MARKER (MAKE_MARKER ('R', 'L', 'S', 'E')) |
| 43 | |
| 44 | #define c_MARKER (MAKE_MARKER ('(', 'c', ')', ' ')) |
| 45 | #define NAME_MARKER (MAKE_MARKER ('N', 'A', 'M', 'E')) |
| 46 | #define AUTH_MARKER (MAKE_MARKER ('A', 'U', 'T', 'H')) |
| 47 | #define ANNO_MARKER (MAKE_MARKER ('A', 'N', 'N', 'O')) |
| 48 | #define CHAN_MARKER (MAKE_MARKER ('C', 'H', 'A', 'N')) |
| 49 | |
| 50 | /*------------------------------------------------------------------------------ |
| 51 | * Typedefs for file chunks. |
| 52 | */ |
| 53 | |
| 54 | typedef struct |
| 55 | { unsigned int oneShotHiSamples, repeatHiSamples, samplesPerHiCycle ; |
| 56 | unsigned short samplesPerSec ; |
| 57 | unsigned char octave, compression ; |
| 58 | unsigned int volume ; |
| 59 | } VHDR_CHUNK ; |
| 60 | |
| 61 | enum { |
| 62 | HAVE_FORM = 0x01, |
| 63 | |
| 64 | HAVE_SVX = 0x02, |
| 65 | HAVE_VHDR = 0x04, |
| 66 | HAVE_BODY = 0x08 |
| 67 | } ; |
| 68 | |
| 69 | /*------------------------------------------------------------------------------ |
| 70 | * Private static functions. |
| 71 | */ |
| 72 | |
| 73 | static int svx_close (SF_PRIVATE *psf) ; |
| 74 | static int svx_write_header (SF_PRIVATE *psf, int calc_length) ; |
| 75 | static int svx_read_header (SF_PRIVATE *psf) ; |
| 76 | |
| 77 | /*------------------------------------------------------------------------------ |
| 78 | ** Public function. |
| 79 | */ |
| 80 | |
| 81 | int |
| 82 | svx_open (SF_PRIVATE *psf) |
| 83 | { int error ; |
| 84 | |
| 85 | if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0)) |
| 86 | { if ((error = svx_read_header (psf))) |
| 87 | return error ; |
| 88 | |
| 89 | psf->endian = SF_ENDIAN_BIG ; /* All SVX files are big endian. */ |
| 90 | |
| 91 | psf->blockwidth = psf->sf.channels * psf->bytewidth ; |
| 92 | if (psf->blockwidth) |
| 93 | psf->sf.frames = psf->datalength / psf->blockwidth ; |
| 94 | |
| 95 | psf_fseek (psf, psf->dataoffset, SEEK_SET) ; |
| 96 | } ; |
| 97 | |
| 98 | if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR) |
| 99 | { if (psf->is_pipe) |
| 100 | return SFE_NO_PIPE_WRITE ; |
| 101 | |
| 102 | if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_SVX) |
| 103 | return SFE_BAD_OPEN_FORMAT ; |
| 104 | |
| 105 | psf->endian = SF_ENDIAN (psf->sf.format) ; |
| 106 | |
| 107 | if (psf->endian == SF_ENDIAN_LITTLE || (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU)) |
| 108 | return SFE_BAD_ENDIAN ; |
| 109 | |
| 110 | psf->endian = SF_ENDIAN_BIG ; /* All SVX files are big endian. */ |
| 111 | |
| 112 | error = svx_write_header (psf, SF_FALSE) ; |
| 113 | if (error) |
| 114 | return error ; |
| 115 | |
| 116 | psf->write_header = svx_write_header ; |
| 117 | } ; |
| 118 | |
| 119 | psf->container_close = svx_close ; |
| 120 | |
| 121 | if ((error = pcm_init (psf))) |
| 122 | return error ; |
| 123 | |
| 124 | return 0 ; |
| 125 | } /* svx_open */ |
| 126 | |
| 127 | /*------------------------------------------------------------------------------ |
| 128 | */ |
| 129 | |
| 130 | static int |
| 131 | svx_read_header (SF_PRIVATE *psf) |
| 132 | { VHDR_CHUNK vhdr ; |
| 133 | unsigned int FORMsize, vhdrsize, dword, marker ; |
| 134 | int filetype = 0, parsestage = 0, done = 0 ; |
| 135 | int bytecount = 0, channels ; |
| 136 | |
| 137 | if (psf->filelength > SF_PLATFORM_S64 (0xffffffff)) |
| 138 | psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ; |
| 139 | |
| 140 | memset (&vhdr, 0, sizeof (vhdr)) ; |
| 141 | psf_binheader_readf (psf, "p", 0) ; |
| 142 | |
| 143 | /* Set default number of channels. Modify later if necessary */ |
| 144 | psf->sf.channels = 1 ; |
| 145 | |
| 146 | psf->sf.format = SF_FORMAT_SVX ; |
| 147 | |
| 148 | while (! done) |
| 149 | { psf_binheader_readf (psf, "m", &marker) ; |
| 150 | switch (marker) |
| 151 | { case FORM_MARKER : |
| 152 | if (parsestage) |
| 153 | return SFE_SVX_NO_FORM ; |
| 154 | |
| 155 | psf_binheader_readf (psf, "E4", &FORMsize) ; |
| 156 | |
| 157 | if (FORMsize != psf->filelength - 2 * sizeof (dword)) |
| 158 | { dword = psf->filelength - 2 * sizeof (dword) ; |
| 159 | psf_log_printf (psf, "FORM : %d (should be %d)\n", FORMsize, dword) ; |
| 160 | FORMsize = dword ; |
| 161 | } |
| 162 | else |
| 163 | psf_log_printf (psf, "FORM : %d\n", FORMsize) ; |
| 164 | parsestage |= HAVE_FORM ; |
| 165 | break ; |
| 166 | |
| 167 | case SVX8_MARKER : |
| 168 | case SV16_MARKER : |
| 169 | if (! (parsestage & HAVE_FORM)) |
| 170 | return SFE_SVX_NO_FORM ; |
| 171 | filetype = marker ; |
| 172 | psf_log_printf (psf, " %M\n", marker) ; |
| 173 | parsestage |= HAVE_SVX ; |
| 174 | break ; |
| 175 | |
| 176 | case VHDR_MARKER : |
| 177 | if (! (parsestage & (HAVE_FORM | HAVE_SVX))) |
| 178 | return SFE_SVX_NO_FORM ; |
| 179 | |
| 180 | psf_binheader_readf (psf, "E4", &vhdrsize) ; |
| 181 | |
| 182 | psf_log_printf (psf, " VHDR : %d\n", vhdrsize) ; |
| 183 | |
| 184 | psf_binheader_readf (psf, "E4442114", &(vhdr.oneShotHiSamples), &(vhdr.repeatHiSamples), |
| 185 | &(vhdr.samplesPerHiCycle), &(vhdr.samplesPerSec), &(vhdr.octave), &(vhdr.compression), |
| 186 | &(vhdr.volume)) ; |
| 187 | |
| 188 | psf_log_printf (psf, " OneShotHiSamples : %d\n", vhdr.oneShotHiSamples) ; |
| 189 | psf_log_printf (psf, " RepeatHiSamples : %d\n", vhdr.repeatHiSamples) ; |
| 190 | psf_log_printf (psf, " samplesPerHiCycle : %d\n", vhdr.samplesPerHiCycle) ; |
| 191 | psf_log_printf (psf, " Sample Rate : %d\n", vhdr.samplesPerSec) ; |
| 192 | psf_log_printf (psf, " Octave : %d\n", vhdr.octave) ; |
| 193 | |
| 194 | psf_log_printf (psf, " Compression : %d => ", vhdr.compression) ; |
| 195 | |
| 196 | switch (vhdr.compression) |
| 197 | { case 0 : psf_log_printf (psf, "None.\n") ; |
| 198 | break ; |
| 199 | case 1 : psf_log_printf (psf, "Fibonacci delta\n") ; |
| 200 | break ; |
| 201 | case 2 : psf_log_printf (psf, "Exponential delta\n") ; |
| 202 | break ; |
| 203 | } ; |
| 204 | |
| 205 | psf_log_printf (psf, " Volume : %d\n", vhdr.volume) ; |
| 206 | |
| 207 | psf->sf.samplerate = vhdr.samplesPerSec ; |
| 208 | |
| 209 | if (filetype == SVX8_MARKER) |
| 210 | { psf->sf.format |= SF_FORMAT_PCM_S8 ; |
| 211 | psf->bytewidth = 1 ; |
| 212 | } |
| 213 | else if (filetype == SV16_MARKER) |
| 214 | { psf->sf.format |= SF_FORMAT_PCM_16 ; |
| 215 | psf->bytewidth = 2 ; |
| 216 | } ; |
| 217 | |
| 218 | parsestage |= HAVE_VHDR ; |
| 219 | break ; |
| 220 | |
| 221 | case BODY_MARKER : |
| 222 | if (! (parsestage & HAVE_VHDR)) |
| 223 | return SFE_SVX_NO_BODY ; |
| 224 | |
| 225 | psf_binheader_readf (psf, "E4", &dword) ; |
| 226 | psf->datalength = dword ; |
| 227 | |
| 228 | psf->dataoffset = psf_ftell (psf) ; |
| 229 | if (psf->dataoffset < 0) |
| 230 | return SFE_SVX_NO_BODY ; |
| 231 | |
| 232 | if (psf->datalength > psf->filelength - psf->dataoffset) |
| 233 | { psf_log_printf (psf, " BODY : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ; |
| 234 | psf->datalength = psf->filelength - psf->dataoffset ; |
| 235 | } |
| 236 | else |
| 237 | psf_log_printf (psf, " BODY : %D\n", psf->datalength) ; |
| 238 | |
| 239 | parsestage |= HAVE_BODY ; |
| 240 | |
| 241 | if (! psf->sf.seekable) |
| 242 | break ; |
| 243 | |
| 244 | psf_fseek (psf, psf->datalength, SEEK_CUR) ; |
| 245 | break ; |
| 246 | |
| 247 | case NAME_MARKER : |
| 248 | if (! (parsestage & HAVE_SVX)) |
| 249 | return SFE_SVX_NO_FORM ; |
| 250 | |
| 251 | psf_binheader_readf (psf, "E4", &dword) ; |
| 252 | |
| 253 | psf_log_printf (psf, " %M : %d\n", marker, dword) ; |
| 254 | |
| 255 | if (strlen (psf->file.name.c) != dword) |
| 256 | { if (dword > sizeof (psf->file.name.c) - 1) |
| 257 | return SFE_SVX_BAD_NAME_LENGTH ; |
| 258 | |
| 259 | psf_binheader_readf (psf, "b", psf->file.name.c, dword) ; |
| 260 | psf->file.name.c [dword] = 0 ; |
| 261 | } |
| 262 | else |
| 263 | psf_binheader_readf (psf, "j", dword) ; |
| 264 | break ; |
| 265 | |
| 266 | case ANNO_MARKER : |
| 267 | if (! (parsestage & HAVE_SVX)) |
| 268 | return SFE_SVX_NO_FORM ; |
| 269 | |
| 270 | psf_binheader_readf (psf, "E4", &dword) ; |
| 271 | |
| 272 | psf_log_printf (psf, " %M : %d\n", marker, dword) ; |
| 273 | |
| 274 | psf_binheader_readf (psf, "j", dword) ; |
| 275 | break ; |
| 276 | |
| 277 | case CHAN_MARKER : |
| 278 | if (! (parsestage & HAVE_SVX)) |
| 279 | return SFE_SVX_NO_FORM ; |
| 280 | |
| 281 | psf_binheader_readf (psf, "E4", &dword) ; |
| 282 | |
| 283 | psf_log_printf (psf, " %M : %d\n", marker, dword) ; |
| 284 | |
| 285 | bytecount += psf_binheader_readf (psf, "E4", &channels) ; |
| 286 | |
| 287 | if (channels == 2 || channels == 4) |
| 288 | psf_log_printf (psf, " Channels : %d => mono\n", channels) ; |
| 289 | else if (channels == 6) |
| 290 | { psf->sf.channels = 2 ; |
| 291 | psf_log_printf (psf, " Channels : %d => stereo\n", channels) ; |
| 292 | } |
| 293 | else |
| 294 | psf_log_printf (psf, " Channels : %d *** assuming mono\n", channels) ; |
| 295 | |
| 296 | psf_binheader_readf (psf, "j", dword - bytecount) ; |
| 297 | break ; |
| 298 | |
| 299 | |
| 300 | case AUTH_MARKER : |
| 301 | case c_MARKER : |
| 302 | if (! (parsestage & HAVE_SVX)) |
| 303 | return SFE_SVX_NO_FORM ; |
| 304 | |
| 305 | psf_binheader_readf (psf, "E4", &dword) ; |
| 306 | |
| 307 | psf_log_printf (psf, " %M : %d\n", marker, dword) ; |
| 308 | |
| 309 | psf_binheader_readf (psf, "j", dword) ; |
| 310 | break ; |
| 311 | |
| 312 | default : |
| 313 | if (psf_isprint ((marker >> 24) & 0xFF) && psf_isprint ((marker >> 16) & 0xFF) |
| 314 | && psf_isprint ((marker >> 8) & 0xFF) && psf_isprint (marker & 0xFF)) |
| 315 | { psf_binheader_readf (psf, "E4", &dword) ; |
| 316 | |
| 317 | psf_log_printf (psf, "%M : %d (unknown marker)\n", marker, dword) ; |
| 318 | |
| 319 | psf_binheader_readf (psf, "j", dword) ; |
| 320 | break ; |
| 321 | } ; |
| 322 | if ((dword = psf_ftell (psf)) & 0x03) |
| 323 | { psf_log_printf (psf, " Unknown chunk marker at position %d. Resynching.\n", dword - 4) ; |
| 324 | |
| 325 | psf_binheader_readf (psf, "j", -3) ; |
| 326 | break ; |
| 327 | } ; |
| 328 | psf_log_printf (psf, "*** Unknown chunk marker : %X. Exiting parser.\n", marker) ; |
| 329 | done = 1 ; |
| 330 | } ; /* switch (marker) */ |
| 331 | |
| 332 | if (! psf->sf.seekable && (parsestage & HAVE_BODY)) |
| 333 | break ; |
| 334 | |
| 335 | if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (dword)) |
| 336 | break ; |
| 337 | } ; /* while (1) */ |
| 338 | |
| 339 | if (vhdr.compression) |
| 340 | return SFE_SVX_BAD_COMP ; |
| 341 | |
| 342 | if (psf->dataoffset <= 0) |
| 343 | return SFE_SVX_NO_DATA ; |
| 344 | |
| 345 | return 0 ; |
| 346 | } /* svx_read_header */ |
| 347 | |
| 348 | static int |
| 349 | svx_close (SF_PRIVATE *psf) |
| 350 | { |
| 351 | if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR) |
| 352 | svx_write_header (psf, SF_TRUE) ; |
| 353 | |
| 354 | return 0 ; |
| 355 | } /* svx_close */ |
| 356 | |
| 357 | static int |
| 358 | svx_write_header (SF_PRIVATE *psf, int calc_length) |
| 359 | { static char annotation [] = "libsndfile by Erik de Castro Lopo\0\0\0" ; |
| 360 | sf_count_t current ; |
| 361 | |
| 362 | current = psf_ftell (psf) ; |
| 363 | |
| 364 | if (calc_length) |
| 365 | { psf->filelength = psf_get_filelen (psf) ; |
| 366 | |
| 367 | psf->datalength = psf->filelength - psf->dataoffset ; |
| 368 | |
| 369 | if (psf->dataend) |
| 370 | psf->datalength -= psf->filelength - psf->dataend ; |
| 371 | |
| 372 | psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; |
| 373 | } ; |
| 374 | |
| 375 | psf->header [0] = 0 ; |
| 376 | psf->headindex = 0 ; |
| 377 | psf_fseek (psf, 0, SEEK_SET) ; |
| 378 | |
| 379 | /* FORM marker and FORM size. */ |
| 380 | psf_binheader_writef (psf, "Etm8", FORM_MARKER, (psf->filelength < 8) ? |
| 381 | psf->filelength * 0 : psf->filelength - 8) ; |
| 382 | |
| 383 | psf_binheader_writef (psf, "m", (psf->bytewidth == 1) ? SVX8_MARKER : SV16_MARKER) ; |
| 384 | |
| 385 | /* VHDR chunk. */ |
| 386 | psf_binheader_writef (psf, "Em4", VHDR_MARKER, sizeof (VHDR_CHUNK)) ; |
| 387 | /* VHDR : oneShotHiSamples, repeatHiSamples, samplesPerHiCycle */ |
| 388 | psf_binheader_writef (psf, "E444", psf->sf.frames, 0, 0) ; |
| 389 | /* VHDR : samplesPerSec, octave, compression */ |
| 390 | psf_binheader_writef (psf, "E211", psf->sf.samplerate, 1, 0) ; |
| 391 | /* VHDR : volume */ |
| 392 | psf_binheader_writef (psf, "E4", (psf->bytewidth == 1) ? 0xFF : 0xFFFF) ; |
| 393 | |
| 394 | if (psf->sf.channels == 2) |
| 395 | psf_binheader_writef (psf, "Em44", CHAN_MARKER, 4, 6) ; |
| 396 | |
| 397 | /* Filename and annotation strings. */ |
| 398 | psf_binheader_writef (psf, "Emsms", NAME_MARKER, psf->file.name.c, ANNO_MARKER, annotation) ; |
| 399 | |
| 400 | /* BODY marker and size. */ |
| 401 | psf_binheader_writef (psf, "Etm8", BODY_MARKER, (psf->datalength < 0) ? |
| 402 | psf->datalength * 0 : psf->datalength) ; |
| 403 | |
| 404 | psf_fwrite (psf->header, psf->headindex, 1, psf) ; |
| 405 | |
| 406 | if (psf->error) |
| 407 | return psf->error ; |
| 408 | |
| 409 | psf->dataoffset = psf->headindex ; |
| 410 | |
| 411 | if (current > 0) |
| 412 | psf_fseek (psf, current, SEEK_SET) ; |
| 413 | |
| 414 | return psf->error ; |
| 415 | } /* svx_write_header */ |
| 416 | |