| /* | |
| ** Copyright (C) 1999-2019 Erik de Castro Lopo <erikd@mega-nerd.com> | |
| ** | |
| ** This program is free software; you can redistribute it and/or modify | |
| ** it under the terms of the GNU Lesser General Public License as published by | |
| ** the Free Software Foundation; either version 2.1 of the License, or | |
| ** (at your option) any later version. | |
| ** | |
| ** This program is distributed in the hope that it will be useful, | |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| ** GNU Lesser General Public License for more details. | |
| ** | |
| ** You should have received a copy of the GNU Lesser General Public License | |
| ** along with this program; if not, write to the Free Software | |
| ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| */ | |
| #include <config.h> | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #if HAVE_UNISTD_H | |
| #include <unistd.h> | |
| #else | |
| #include "sf_unistd.h" | |
| #endif | |
| #include <ctype.h> | |
| #include <math.h> | |
| #include <time.h> | |
| #if HAVE_SYS_TIME_H | |
| #include <sys/time.h> | |
| #endif | |
| #include "sndfile.h" | |
| #include "sfendian.h" | |
| #include "common.h" | |
| #define INITIAL_HEADER_SIZE 256 | |
| /* Allocate and initialize the SF_PRIVATE struct. */ | |
| SF_PRIVATE * | |
| psf_allocate (void) | |
| { SF_PRIVATE * psf ; | |
| if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL) | |
| return NULL ; | |
| if ((psf->header.ptr = calloc (1, INITIAL_HEADER_SIZE)) == NULL) | |
| { free (psf) ; | |
| return NULL ; | |
| } ; | |
| psf->header.len = INITIAL_HEADER_SIZE ; | |
| return psf ; | |
| } /* psf_allocate */ | |
| static int | |
| psf_bump_header_allocation (SF_PRIVATE * psf, sf_count_t needed) | |
| { | |
| sf_count_t newlen, smallest = INITIAL_HEADER_SIZE ; | |
| void * ptr ; | |
| newlen = (needed > psf->header.len) ? 2 * SF_MAX (needed, smallest) : 2 * psf->header.len ; | |
| if (newlen > 100 * 1024) | |
| { psf_log_printf (psf, "Request for header allocation of %D denied.\n", newlen) ; | |
| return 1 ; | |
| } | |
| if ((ptr = realloc (psf->header.ptr, newlen)) == NULL) | |
| { psf_log_printf (psf, "realloc (%p, %D) failed\n", psf->header.ptr, newlen) ; | |
| psf->error = SFE_MALLOC_FAILED ; | |
| return 1 ; | |
| } ; | |
| /* Always zero-out new header memory to avoid un-initializer memory accesses. */ | |
| if (newlen > psf->header.len) | |
| memset ((char *) ptr + psf->header.len, 0, newlen - psf->header.len) ; | |
| psf->header.ptr = ptr ; | |
| psf->header.len = newlen ; | |
| return 0 ; | |
| } /* psf_bump_header_allocation */ | |
| /*----------------------------------------------------------------------------------------------- | |
| ** psf_log_printf allows libsndfile internal functions to print to an internal parselog which | |
| ** can later be displayed. | |
| ** The format specifiers are as for printf but without the field width and other modifiers. | |
| ** Printing is performed to the parselog char array of the SF_PRIVATE struct. | |
| ** Printing is done in such a way as to guarantee that the log never overflows the end of the | |
| ** parselog array. | |
| */ | |
| static inline void | |
| log_putchar (SF_PRIVATE *psf, char ch) | |
| { if (psf->parselog.indx < SIGNED_SIZEOF (psf->parselog.buf) - 1) | |
| { psf->parselog.buf [psf->parselog.indx++] = ch ; | |
| psf->parselog.buf [psf->parselog.indx] = 0 ; | |
| } ; | |
| return ; | |
| } /* log_putchar */ | |
| void | |
| psf_log_printf (SF_PRIVATE *psf, const char *format, ...) | |
| { va_list ap ; | |
| uint32_t u, tens ; | |
| int d, shift, width, width_specifier, left_align, slen, precision ; | |
| char c, *strptr, istr [5], lead_char, sign_char ; | |
| va_start (ap, format) ; | |
| while ((c = *format++)) | |
| { if (c != '%') | |
| { log_putchar (psf, c) ; | |
| continue ; | |
| } ; | |
| if (format [0] == '%') /* Handle %% */ | |
| { log_putchar (psf, '%') ; | |
| format ++ ; | |
| continue ; | |
| } ; | |
| sign_char = 0 ; | |
| left_align = SF_FALSE ; | |
| while (1) | |
| { switch (format [0]) | |
| { case ' ' : | |
| case '+' : | |
| sign_char = format [0] ; | |
| format ++ ; | |
| continue ; | |
| case '-' : | |
| left_align = SF_TRUE ; | |
| format ++ ; | |
| continue ; | |
| default : break ; | |
| } ; | |
| break ; | |
| } ; | |
| if (format [0] == 0) | |
| break ; | |
| lead_char = ' ' ; | |
| if (format [0] == '0') | |
| lead_char = '0' ; | |
| width_specifier = 0 ; | |
| while ((c = *format++) && isdigit (c)) | |
| width_specifier = width_specifier * 10 + (c - '0') ; | |
| precision = 0 ; | |
| if (c == '.') | |
| { while ((c = *format++) && isdigit (c)) | |
| precision = precision * 10 + (c - '0') ; | |
| } ; | |
| switch (c) | |
| { case 0 : /* NULL character. */ | |
| va_end (ap) ; | |
| return ; | |
| case 's': /* string */ | |
| strptr = va_arg (ap, char *) ; | |
| if (strptr == NULL) | |
| break ; | |
| if (precision > 0) | |
| slen = strnlen (strptr, precision) ; | |
| else | |
| slen = strlen (strptr) ; | |
| width_specifier = width_specifier >= slen ? width_specifier - slen : 0 ; | |
| if (left_align == SF_FALSE) | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, ' ') ; | |
| while (slen--) | |
| log_putchar (psf, *strptr++) ; | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, ' ') ; | |
| break ; | |
| case 'd': /* int */ | |
| d = va_arg (ap, int) ; | |
| if (d < 0) | |
| { sign_char = '-' ; | |
| if (lead_char != '0' && left_align == SF_FALSE) | |
| width_specifier -- ; | |
| u = - ((unsigned) d) ; | |
| } | |
| else | |
| { u = (unsigned) d ; | |
| } | |
| tens = 1 ; | |
| width = 1 ; | |
| while (u / tens >= 10) | |
| { tens *= 10 ; | |
| width ++ ; | |
| } ; | |
| width_specifier -= width ; | |
| if (sign_char == ' ') | |
| { log_putchar (psf, ' ') ; | |
| width_specifier -- ; | |
| } ; | |
| if (left_align == SF_FALSE && lead_char != '0') | |
| { if (sign_char == '+') | |
| width_specifier -- ; | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, lead_char) ; | |
| } ; | |
| if (sign_char == '+' || sign_char == '-') | |
| { log_putchar (psf, sign_char) ; | |
| width_specifier -- ; | |
| } ; | |
| if (left_align == SF_FALSE) | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, lead_char) ; | |
| while (tens > 0) | |
| { log_putchar (psf, '0' + u / tens) ; | |
| u %= tens ; | |
| tens /= 10 ; | |
| } ; | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, lead_char) ; | |
| break ; | |
| case 'D': /* sf_count_t */ | |
| { sf_count_t D ; | |
| uint64_t U, Tens ; | |
| D = va_arg (ap, sf_count_t) ; | |
| if (D == 0) | |
| { while (-- width_specifier > 0) | |
| log_putchar (psf, lead_char) ; | |
| log_putchar (psf, '0') ; | |
| break ; | |
| } | |
| else | |
| { if (D < 0) | |
| { log_putchar (psf, '-') ; | |
| U = - ((uint64_t) D) ; | |
| } | |
| else | |
| { U = (uint64_t) D ; | |
| } | |
| } | |
| Tens = 1 ; | |
| width = 1 ; | |
| while (U / Tens >= 10) | |
| { Tens *= 10 ; | |
| width ++ ; | |
| } ; | |
| while (width_specifier > width) | |
| { log_putchar (psf, lead_char) ; | |
| width_specifier-- ; | |
| } ; | |
| while (Tens > 0) | |
| { log_putchar (psf, '0' + U / Tens) ; | |
| U %= Tens ; | |
| Tens /= 10 ; | |
| } ; | |
| } ; | |
| break ; | |
| case 'u': /* unsigned int */ | |
| u = va_arg (ap, unsigned int) ; | |
| tens = 1 ; | |
| width = 1 ; | |
| while (u / tens >= 10) | |
| { tens *= 10 ; | |
| width ++ ; | |
| } ; | |
| width_specifier -= width ; | |
| if (sign_char == ' ') | |
| { log_putchar (psf, ' ') ; | |
| width_specifier -- ; | |
| } ; | |
| if (left_align == SF_FALSE && lead_char != '0') | |
| { if (sign_char == '+') | |
| width_specifier -- ; | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, lead_char) ; | |
| } ; | |
| if (sign_char == '+' || sign_char == '-') | |
| { log_putchar (psf, sign_char) ; | |
| width_specifier -- ; | |
| } ; | |
| if (left_align == SF_FALSE) | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, lead_char) ; | |
| while (tens > 0) | |
| { log_putchar (psf, '0' + u / tens) ; | |
| u %= tens ; | |
| tens /= 10 ; | |
| } ; | |
| while (width_specifier -- > 0) | |
| log_putchar (psf, lead_char) ; | |
| break ; | |
| case 'c': /* char */ | |
| c = va_arg (ap, int) & 0xFF ; | |
| log_putchar (psf, c) ; | |
| break ; | |
| case 'x': /* hex */ | |
| case 'X': /* hex */ | |
| d = va_arg (ap, int) ; | |
| if (d == 0) | |
| { while (--width_specifier > 0) | |
| log_putchar (psf, lead_char) ; | |
| log_putchar (psf, '0') ; | |
| break ; | |
| } ; | |
| shift = 28 ; | |
| width = (width_specifier < 8) ? 8 : width_specifier ; | |
| while (! ((((uint32_t) 0xF) << shift) & d)) | |
| { shift -= 4 ; | |
| width -- ; | |
| } ; | |
| while (width > 0 && width_specifier > width) | |
| { log_putchar (psf, lead_char) ; | |
| width_specifier-- ; | |
| } ; | |
| while (shift >= 0) | |
| { c = (d >> shift) & 0xF ; | |
| log_putchar (psf, (c > 9) ? c + 'A' - 10 : c + '0') ; | |
| shift -= 4 ; | |
| } ; | |
| break ; | |
| case 'M': /* int2str */ | |
| d = va_arg (ap, int) ; | |
| if (CPU_IS_LITTLE_ENDIAN) | |
| { istr [0] = d & 0xFF ; | |
| istr [1] = (d >> 8) & 0xFF ; | |
| istr [2] = (d >> 16) & 0xFF ; | |
| istr [3] = (d >> 24) & 0xFF ; | |
| } | |
| else | |
| { istr [3] = d & 0xFF ; | |
| istr [2] = (d >> 8) & 0xFF ; | |
| istr [1] = (d >> 16) & 0xFF ; | |
| istr [0] = (d >> 24) & 0xFF ; | |
| } ; | |
| istr [4] = 0 ; | |
| strptr = istr ; | |
| while (*strptr) | |
| { c = *strptr++ ; | |
| log_putchar (psf, psf_isprint (c) ? c : '.') ; | |
| } ; | |
| break ; | |
| default : | |
| log_putchar (psf, '*') ; | |
| log_putchar (psf, c) ; | |
| log_putchar (psf, '*') ; | |
| break ; | |
| } /* switch */ | |
| } /* while */ | |
| va_end (ap) ; | |
| return ; | |
| } /* psf_log_printf */ | |
| /*----------------------------------------------------------------------------------------------- | |
| ** ASCII header printf functions. | |
| ** Some formats (ie NIST) use ascii text in their headers. | |
| ** Format specifiers are the same as the standard printf specifiers (uses vsnprintf). | |
| ** If this generates a compile error on any system, the author should be notified | |
| ** so an alternative vsnprintf can be provided. | |
| */ | |
| void | |
| psf_asciiheader_printf (SF_PRIVATE *psf, const char *format, ...) | |
| { va_list argptr ; | |
| int maxlen ; | |
| char *start ; | |
| if (! format) | |
| return ; | |
| maxlen = strlen ((char*) psf->header.ptr) ; | |
| start = ((char*) psf->header.ptr) + maxlen ; | |
| maxlen = psf->header.len - maxlen ; | |
| va_start (argptr, format) ; | |
| vsnprintf (start, maxlen, format, argptr) ; | |
| va_end (argptr) ; | |
| /* Make sure the string is properly terminated. */ | |
| start [maxlen - 1] = 0 ; | |
| psf->header.indx = strlen ((char*) psf->header.ptr) ; | |
| return ; | |
| } /* psf_asciiheader_printf */ | |
| /*----------------------------------------------------------------------------------------------- | |
| ** Binary header writing functions. Returns number of bytes written. | |
| ** | |
| ** Format specifiers for psf_binheader_writef are as follows | |
| ** m - marker - four bytes - no endian manipulation | |
| ** | |
| ** e - all following numerical values will be little endian | |
| ** E - all following numerical values will be big endian | |
| ** | |
| ** t - all following O types will be truncated to 4 bytes | |
| ** T - switch off truncation of all following O types | |
| ** | |
| ** 1 - single byte value | |
| ** 2 - two byte value | |
| ** 3 - three byte value | |
| ** 4 - four byte value | |
| ** 8 - eight byte value (sometimes written as 4 bytes) | |
| ** | |
| ** s - string preceded by a four byte length | |
| ** S - string including null terminator | |
| ** p - a Pascal string | |
| ** | |
| ** f - floating point data | |
| ** d - double precision floating point data | |
| ** h - 16 binary bytes value | |
| ** | |
| ** b - binary data (see below) | |
| ** z - zero bytes (ses below) | |
| ** j - jump forwards or backwards | |
| ** | |
| ** To write a word followed by an int (both little endian) use: | |
| ** psf_binheader_writef ("e24", wordval, longval) ; | |
| ** | |
| ** To write binary data use: | |
| ** psf_binheader_writef ("b", &bindata, sizeof (bindata)) ; | |
| ** | |
| ** To write N zero bytes use: | |
| ** NOTE: due to platform issues (ie x86-64) you should cast the | |
| ** argument to size_t or ensure the variable type is size_t. | |
| ** psf_binheader_writef ("z", N) ; | |
| */ | |
| /* These macros may seem a bit messy but do prevent problems with processors which | |
| ** seg. fault when asked to write an int or short to a non-int/short aligned address. | |
| */ | |
| static inline void | |
| header_put_byte (SF_PRIVATE *psf, char x) | |
| { psf->header.ptr [psf->header.indx++] = x ; | |
| } /* header_put_byte */ | |
| #if (CPU_IS_BIG_ENDIAN == 1) | |
| static inline void | |
| header_put_marker (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = (x >> 24) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 16) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = x ; | |
| } /* header_put_marker */ | |
| #elif (CPU_IS_LITTLE_ENDIAN == 1) | |
| static inline void | |
| header_put_marker (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = x ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 16) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 24) ; | |
| } /* header_put_marker */ | |
| #else | |
| # error "Cannot determine endian-ness of processor." | |
| #endif | |
| static inline void | |
| header_put_be_short (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = x ; | |
| } /* header_put_be_short */ | |
| static inline void | |
| header_put_le_short (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = x ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| } /* header_put_le_short */ | |
| static inline void | |
| header_put_be_3byte (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = (x >> 16) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = x ; | |
| } /* header_put_be_3byte */ | |
| static inline void | |
| header_put_le_3byte (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = x ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 16) ; | |
| } /* header_put_le_3byte */ | |
| static inline void | |
| header_put_be_int (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = (x >> 24) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 16) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = x ; | |
| } /* header_put_be_int */ | |
| static inline void | |
| header_put_le_int (SF_PRIVATE *psf, int x) | |
| { psf->header.ptr [psf->header.indx++] = x ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 16) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 24) ; | |
| } /* header_put_le_int */ | |
| static inline void | |
| header_put_be_8byte (SF_PRIVATE *psf, sf_count_t x) | |
| { psf->header.ptr [psf->header.indx++] = (x >> 56) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 48) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 40) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 32) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 24) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 16) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) x ; | |
| } /* header_put_be_8byte */ | |
| static inline void | |
| header_put_le_8byte (SF_PRIVATE *psf, sf_count_t x) | |
| { psf->header.ptr [psf->header.indx++] = (unsigned char) x ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 8) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 16) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 24) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 32) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 40) ; | |
| psf->header.ptr [psf->header.indx++] = (unsigned char) (x >> 48) ; | |
| psf->header.ptr [psf->header.indx++] = (x >> 56) ; | |
| } /* header_put_le_8byte */ | |
| int | |
| psf_binheader_writef (SF_PRIVATE *psf, const char *format, ...) | |
| { va_list argptr ; | |
| sf_count_t countdata ; | |
| unsigned long longdata ; | |
| unsigned int data ; | |
| float floatdata ; | |
| double doubledata ; | |
| void *bindata ; | |
| size_t size ; | |
| char c, *strptr ; | |
| int count = 0, trunc_8to4 = SF_FALSE ; | |
| if (! format) | |
| return psf_ftell (psf) ; | |
| va_start (argptr, format) ; | |
| while ((c = *format++)) | |
| { | |
| if (psf->header.indx + 16 >= psf->header.len && psf_bump_header_allocation (psf, 16)) | |
| break ; | |
| switch (c) | |
| { case ' ' : /* Do nothing. Just used to space out format string. */ | |
| break ; | |
| case 'e' : /* All conversions are now from LE to host. */ | |
| psf->rwf_endian = SF_ENDIAN_LITTLE ; | |
| break ; | |
| case 'E' : /* All conversions are now from BE to host. */ | |
| psf->rwf_endian = SF_ENDIAN_BIG ; | |
| break ; | |
| case 't' : /* All 8 byte values now get written as 4 bytes. */ | |
| trunc_8to4 = SF_TRUE ; | |
| break ; | |
| case 'T' : /* All 8 byte values now get written as 8 bytes. */ | |
| trunc_8to4 = SF_FALSE ; | |
| break ; | |
| case 'm' : | |
| data = va_arg (argptr, unsigned int) ; | |
| header_put_marker (psf, data) ; | |
| count += 4 ; | |
| break ; | |
| case '1' : | |
| data = va_arg (argptr, unsigned int) ; | |
| header_put_byte (psf, data) ; | |
| count += 1 ; | |
| break ; | |
| case '2' : | |
| data = va_arg (argptr, unsigned int) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| { header_put_be_short (psf, data) ; | |
| } | |
| else | |
| { header_put_le_short (psf, data) ; | |
| } ; | |
| count += 2 ; | |
| break ; | |
| case '3' : /* tribyte */ | |
| data = va_arg (argptr, unsigned int) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| { header_put_be_3byte (psf, data) ; | |
| } | |
| else | |
| { header_put_le_3byte (psf, data) ; | |
| } ; | |
| count += 3 ; | |
| break ; | |
| case '4' : | |
| data = va_arg (argptr, unsigned int) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| { header_put_be_int (psf, data) ; | |
| } | |
| else | |
| { header_put_le_int (psf, data) ; | |
| } ; | |
| count += 4 ; | |
| break ; | |
| case '8' : | |
| countdata = va_arg (argptr, sf_count_t) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG && trunc_8to4 == SF_FALSE) | |
| { header_put_be_8byte (psf, countdata) ; | |
| count += 8 ; | |
| } | |
| else if (psf->rwf_endian == SF_ENDIAN_LITTLE && trunc_8to4 == SF_FALSE) | |
| { header_put_le_8byte (psf, countdata) ; | |
| count += 8 ; | |
| } | |
| else if (psf->rwf_endian == SF_ENDIAN_BIG && trunc_8to4 == SF_TRUE) | |
| { longdata = countdata & 0xFFFFFFFF ; | |
| header_put_be_int (psf, longdata) ; | |
| count += 4 ; | |
| } | |
| else if (psf->rwf_endian == SF_ENDIAN_LITTLE && trunc_8to4 == SF_TRUE) | |
| { longdata = countdata & 0xFFFFFFFF ; | |
| header_put_le_int (psf, longdata) ; | |
| count += 4 ; | |
| } | |
| break ; | |
| case 'f' : | |
| /* Floats are passed as doubles. Is this always true? */ | |
| floatdata = (float) va_arg (argptr, double) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| float32_be_write (floatdata, psf->header.ptr + psf->header.indx) ; | |
| else | |
| float32_le_write (floatdata, psf->header.ptr + psf->header.indx) ; | |
| psf->header.indx += 4 ; | |
| count += 4 ; | |
| break ; | |
| case 'd' : | |
| doubledata = va_arg (argptr, double) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| double64_be_write (doubledata, psf->header.ptr + psf->header.indx) ; | |
| else | |
| double64_le_write (doubledata, psf->header.ptr + psf->header.indx) ; | |
| psf->header.indx += 8 ; | |
| count += 8 ; | |
| break ; | |
| case 's' : | |
| /* Write a C string (guaranteed to have a zero terminator). */ | |
| strptr = va_arg (argptr, char *) ; | |
| size = strlen (strptr) + 1 ; | |
| if (psf->header.indx + 4 + (sf_count_t) size + (sf_count_t) (size & 1) > psf->header.len && psf_bump_header_allocation (psf, 4 + size + (size & 1))) | |
| break ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| header_put_be_int (psf, size + (size & 1)) ; | |
| else | |
| header_put_le_int (psf, size + (size & 1)) ; | |
| memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ; | |
| size += (size & 1) ; | |
| psf->header.indx += size ; | |
| psf->header.ptr [psf->header.indx - 1] = 0 ; | |
| count += 4 + size ; | |
| break ; | |
| case 'S' : | |
| /* | |
| ** Write an AIFF style string (no zero terminator but possibly | |
| ** an extra pad byte if the string length is odd). | |
| */ | |
| strptr = va_arg (argptr, char *) ; | |
| size = strlen (strptr) ; | |
| if (psf->header.indx + 4 + (sf_count_t) size + (sf_count_t) (size & 1) > psf->header.len && psf_bump_header_allocation (psf, 4 + size + (size & 1))) | |
| break ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| header_put_be_int (psf, size) ; | |
| else | |
| header_put_le_int (psf, size) ; | |
| memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size + (size & 1)) ; | |
| size += (size & 1) ; | |
| psf->header.indx += size ; | |
| count += 4 + size ; | |
| break ; | |
| case 'p' : | |
| /* Write a PASCAL string (as used by AIFF files). | |
| */ | |
| strptr = va_arg (argptr, char *) ; | |
| size = strlen (strptr) ; | |
| size = (size & 1) ? size : size + 1 ; | |
| size = (size > 254) ? 254 : size ; | |
| if (psf->header.indx + 1 + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, 1 + size)) | |
| break ; | |
| header_put_byte (psf, size) ; | |
| memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ; | |
| psf->header.indx += size ; | |
| count += 1 + size ; | |
| break ; | |
| case 'b' : | |
| bindata = va_arg (argptr, void *) ; | |
| size = va_arg (argptr, size_t) ; | |
| if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size)) | |
| break ; | |
| memcpy (&(psf->header.ptr [psf->header.indx]), bindata, size) ; | |
| psf->header.indx += size ; | |
| count += size ; | |
| break ; | |
| case 'z' : | |
| size = va_arg (argptr, size_t) ; | |
| if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size)) | |
| break ; | |
| count += size ; | |
| while (size) | |
| { psf->header.ptr [psf->header.indx] = 0 ; | |
| psf->header.indx ++ ; | |
| size -- ; | |
| } ; | |
| break ; | |
| case 'h' : | |
| bindata = va_arg (argptr, void *) ; | |
| memcpy (&(psf->header.ptr [psf->header.indx]), bindata, 16) ; | |
| psf->header.indx += 16 ; | |
| count += 16 ; | |
| break ; | |
| case 'j' : /* Jump forwards/backwards by specified amount. */ | |
| size = va_arg (argptr, size_t) ; | |
| if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size)) | |
| break ; | |
| psf->header.indx += size ; | |
| count += size ; | |
| break ; | |
| case 'o' : /* Jump to specified offset. */ | |
| size = va_arg (argptr, size_t) ; | |
| if ((sf_count_t) size >= psf->header.len && psf_bump_header_allocation (psf, size)) | |
| break ; | |
| psf->header.indx = size ; | |
| break ; | |
| default : | |
| psf_log_printf (psf, "*** Invalid format specifier `%c'\n", c) ; | |
| psf->error = SFE_INTERNAL ; | |
| break ; | |
| } ; | |
| } ; | |
| va_end (argptr) ; | |
| return count ; | |
| } /* psf_binheader_writef */ | |
| /*----------------------------------------------------------------------------------------------- | |
| ** Binary header reading functions. Returns number of bytes read. | |
| ** | |
| ** Format specifiers are the same as for header write function above with the following | |
| ** additions: | |
| ** | |
| ** p - jump a given number of position from start of file. | |
| ** | |
| ** If format is NULL, psf_binheader_readf returns the current offset. | |
| */ | |
| #if (CPU_IS_BIG_ENDIAN == 1) | |
| #define GET_MARKER(ptr) ( (((uint32_t) (ptr) [0]) << 24) | ((ptr) [1] << 16) | \ | |
| ((ptr) [2] << 8) | ((ptr) [3])) | |
| #elif (CPU_IS_LITTLE_ENDIAN == 1) | |
| #define GET_MARKER(ptr) ( ((ptr) [0]) | ((ptr) [1] << 8) | \ | |
| ((ptr) [2] << 16) | (((uint32_t) (ptr) [3]) << 24)) | |
| #else | |
| # error "Cannot determine endian-ness of processor." | |
| #endif | |
| #define GET_LE_SHORT(ptr) (((ptr) [1] << 8) | ((ptr) [0])) | |
| #define GET_BE_SHORT(ptr) (((ptr) [0] << 8) | ((ptr) [1])) | |
| #define GET_LE_3BYTE(ptr) ( ((ptr) [2] << 16) | ((ptr) [1] << 8) | ((ptr) [0])) | |
| #define GET_BE_3BYTE(ptr) ( ((ptr) [0] << 16) | ((ptr) [1] << 8) | ((ptr) [2])) | |
| #define GET_LE_INT(ptr) ( ((ptr) [3] << 24) | ((ptr) [2] << 16) | \ | |
| ((ptr) [1] << 8) | ((ptr) [0])) | |
| #define GET_BE_INT(ptr) ( ((ptr) [0] << 24) | ((ptr) [1] << 16) | \ | |
| ((ptr) [2] << 8) | ((ptr) [3])) | |
| #define GET_LE_8BYTE(ptr) ( (((sf_count_t) (ptr) [7]) << 56) | (((sf_count_t) (ptr) [6]) << 48) | \ | |
| (((sf_count_t) (ptr) [5]) << 40) | (((sf_count_t) (ptr) [4]) << 32) | \ | |
| (((sf_count_t) (ptr) [3]) << 24) | (((sf_count_t) (ptr) [2]) << 16) | \ | |
| (((sf_count_t) (ptr) [1]) << 8) | ((ptr) [0])) | |
| #define GET_BE_8BYTE(ptr) ( (((sf_count_t) (ptr) [0]) << 56) | (((sf_count_t) (ptr) [1]) << 48) | \ | |
| (((sf_count_t) (ptr) [2]) << 40) | (((sf_count_t) (ptr) [3]) << 32) | \ | |
| (((sf_count_t) (ptr) [4]) << 24) | (((sf_count_t) (ptr) [5]) << 16) | \ | |
| (((sf_count_t) (ptr) [6]) << 8) | ((ptr) [7])) | |
| static int | |
| header_read (SF_PRIVATE *psf, void *ptr, int bytes) | |
| { int count = 0 ; | |
| if (psf->header.indx + bytes >= psf->header.len && psf_bump_header_allocation (psf, bytes)) | |
| return count ; | |
| if (psf->header.indx + bytes > psf->header.end) | |
| { count = psf_fread (psf->header.ptr + psf->header.end, 1, bytes - (psf->header.end - psf->header.indx), psf) ; | |
| if (count != bytes - (int) (psf->header.end - psf->header.indx)) | |
| { psf_log_printf (psf, "Error : psf_fread returned short count.\n") ; | |
| return count ; | |
| } ; | |
| psf->header.end += count ; | |
| } ; | |
| memcpy (ptr, psf->header.ptr + psf->header.indx, bytes) ; | |
| psf->header.indx += bytes ; | |
| return bytes ; | |
| } /* header_read */ | |
| static void | |
| header_seek (SF_PRIVATE *psf, sf_count_t position, int whence) | |
| { | |
| switch (whence) | |
| { case SEEK_SET : | |
| if (psf->header.indx + position >= psf->header.len) | |
| psf_bump_header_allocation (psf, position) ; | |
| if (position > psf->header.len) | |
| { /* Too much header to cache so just seek instead. */ | |
| psf->header.indx = psf->header.end = 0 ; | |
| psf_fseek (psf, position, whence) ; | |
| return ; | |
| } ; | |
| if (position > psf->header.end) | |
| psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, position - psf->header.end, psf) ; | |
| psf->header.indx = position ; | |
| break ; | |
| case SEEK_CUR : | |
| if (psf->header.indx + position >= psf->header.len) | |
| psf_bump_header_allocation (psf, position) ; | |
| if (psf->header.indx + position < 0) | |
| break ; | |
| if (psf->header.indx >= psf->header.len) | |
| { psf_fseek (psf, position, whence) ; | |
| return ; | |
| } ; | |
| if (psf->header.indx + position <= psf->header.end) | |
| { psf->header.indx += position ; | |
| break ; | |
| } ; | |
| if (psf->header.indx + position > psf->header.len) | |
| { /* Need to jump this without caching it. */ | |
| position -= (psf->header.end - psf->header.indx) ; | |
| psf->header.indx = psf->header.end ; | |
| if (psf->is_pipe) | |
| { | |
| /* seeking is not supported on pipe input, so we read instead */ | |
| size_t skip = position ; | |
| while (skip) | |
| { char junk [16 * 1024] ; | |
| size_t to_skip = SF_MIN (skip, sizeof (junk)) ; | |
| psf_fread (junk, 1, to_skip, psf) ; | |
| skip -= to_skip ; | |
| } | |
| } | |
| else | |
| { psf_fseek (psf, position, SEEK_CUR) ; | |
| } | |
| break ; | |
| } ; | |
| psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, position - (psf->header.end - psf->header.indx), psf) ; | |
| psf->header.indx = psf->header.end ; | |
| break ; | |
| case SEEK_END : | |
| default : | |
| psf_log_printf (psf, "Bad whence param in header_seek().\n") ; | |
| break ; | |
| } ; | |
| return ; | |
| } /* header_seek */ | |
| static int | |
| header_gets (SF_PRIVATE *psf, char *ptr, int bufsize) | |
| { int k ; | |
| if (psf->header.indx + bufsize >= psf->header.len && psf_bump_header_allocation (psf, bufsize)) | |
| return 0 ; | |
| for (k = 0 ; k < bufsize - 1 ; k++) | |
| { if (psf->header.indx < psf->header.end) | |
| { ptr [k] = psf->header.ptr [psf->header.indx] ; | |
| psf->header.indx ++ ; | |
| } | |
| else | |
| { psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, 1, psf) ; | |
| ptr [k] = psf->header.ptr [psf->header.indx] ; | |
| psf->header.indx = psf->header.end ; | |
| } ; | |
| if (ptr [k] == '\n') | |
| break ; | |
| } ; | |
| ptr [k] = 0 ; | |
| return k ; | |
| } /* header_gets */ | |
| int | |
| psf_binheader_readf (SF_PRIVATE *psf, char const *format, ...) | |
| { va_list argptr ; | |
| sf_count_t *countptr, countdata ; | |
| unsigned char *ucptr, sixteen_bytes [16] = { 0 } ; | |
| unsigned int *intptr, intdata ; | |
| unsigned short *shortptr ; | |
| char *charptr ; | |
| float *floatptr ; | |
| double *doubleptr ; | |
| char c ; | |
| int byte_count = 0, count = 0 ; | |
| if (! format) | |
| return psf_ftell (psf) ; | |
| va_start (argptr, format) ; | |
| while ((c = *format++)) | |
| { | |
| if (psf->header.indx + 16 >= psf->header.len && psf_bump_header_allocation (psf, 16)) | |
| break ; | |
| switch (c) | |
| { case 'e' : /* All conversions are now from LE to host. */ | |
| psf->rwf_endian = SF_ENDIAN_LITTLE ; | |
| break ; | |
| case 'E' : /* All conversions are now from BE to host. */ | |
| psf->rwf_endian = SF_ENDIAN_BIG ; | |
| break ; | |
| case 'm' : /* 4 byte marker value eg 'RIFF' */ | |
| intptr = va_arg (argptr, unsigned int*) ; | |
| *intptr = 0 ; | |
| ucptr = (unsigned char*) intptr ; | |
| byte_count += header_read (psf, ucptr, sizeof (int)) ; | |
| *intptr = GET_MARKER (ucptr) ; | |
| break ; | |
| case 'h' : | |
| intptr = va_arg (argptr, unsigned int*) ; | |
| *intptr = 0 ; | |
| ucptr = (unsigned char*) intptr ; | |
| byte_count += header_read (psf, sixteen_bytes, sizeof (sixteen_bytes)) ; | |
| { int k ; | |
| intdata = 0 ; | |
| for (k = 0 ; k < 16 ; k++) | |
| intdata ^= sixteen_bytes [k] << k ; | |
| } | |
| *intptr = intdata ; | |
| break ; | |
| case '1' : | |
| charptr = va_arg (argptr, char*) ; | |
| *charptr = 0 ; | |
| byte_count += header_read (psf, charptr, sizeof (char)) ; | |
| break ; | |
| case '2' : /* 2 byte value with the current endian-ness */ | |
| shortptr = va_arg (argptr, unsigned short*) ; | |
| *shortptr = 0 ; | |
| ucptr = (unsigned char*) shortptr ; | |
| byte_count += header_read (psf, ucptr, sizeof (short)) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| *shortptr = GET_BE_SHORT (ucptr) ; | |
| else | |
| *shortptr = GET_LE_SHORT (ucptr) ; | |
| break ; | |
| case '3' : /* 3 byte value with the current endian-ness */ | |
| intptr = va_arg (argptr, unsigned int*) ; | |
| *intptr = 0 ; | |
| byte_count += header_read (psf, sixteen_bytes, 3) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| *intptr = GET_BE_3BYTE (sixteen_bytes) ; | |
| else | |
| *intptr = GET_LE_3BYTE (sixteen_bytes) ; | |
| break ; | |
| case '4' : /* 4 byte value with the current endian-ness */ | |
| intptr = va_arg (argptr, unsigned int*) ; | |
| *intptr = 0 ; | |
| ucptr = (unsigned char*) intptr ; | |
| byte_count += header_read (psf, ucptr, sizeof (int)) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| *intptr = psf_get_be32 (ucptr, 0) ; | |
| else | |
| *intptr = psf_get_le32 (ucptr, 0) ; | |
| break ; | |
| case '8' : /* 8 byte value with the current endian-ness */ | |
| countptr = va_arg (argptr, sf_count_t *) ; | |
| *countptr = 0 ; | |
| byte_count += header_read (psf, sixteen_bytes, 8) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| countdata = psf_get_be64 (sixteen_bytes, 0) ; | |
| else | |
| countdata = psf_get_le64 (sixteen_bytes, 0) ; | |
| *countptr = countdata ; | |
| break ; | |
| case 'f' : /* Float conversion */ | |
| floatptr = va_arg (argptr, float *) ; | |
| *floatptr = 0.0 ; | |
| byte_count += header_read (psf, floatptr, sizeof (float)) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| *floatptr = float32_be_read ((unsigned char*) floatptr) ; | |
| else | |
| *floatptr = float32_le_read ((unsigned char*) floatptr) ; | |
| break ; | |
| case 'd' : /* double conversion */ | |
| doubleptr = va_arg (argptr, double *) ; | |
| *doubleptr = 0.0 ; | |
| byte_count += header_read (psf, doubleptr, sizeof (double)) ; | |
| if (psf->rwf_endian == SF_ENDIAN_BIG) | |
| *doubleptr = double64_be_read ((unsigned char*) doubleptr) ; | |
| else | |
| *doubleptr = double64_le_read ((unsigned char*) doubleptr) ; | |
| break ; | |
| case 's' : | |
| psf_log_printf (psf, "Format conversion 's' not implemented yet.\n") ; | |
| /* | |
| strptr = va_arg (argptr, char *) ; | |
| size = strlen (strptr) + 1 ; | |
| size += (size & 1) ; | |
| longdata = H2LE_32 (size) ; | |
| get_int (psf, longdata) ; | |
| memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ; | |
| psf->header.indx += size ; | |
| */ | |
| break ; | |
| case 'b' : /* Raw bytes */ | |
| charptr = va_arg (argptr, char*) ; | |
| count = va_arg (argptr, size_t) ; | |
| memset (charptr, 0, count) ; | |
| byte_count += header_read (psf, charptr, count) ; | |
| break ; | |
| case 'G' : | |
| charptr = va_arg (argptr, char*) ; | |
| count = va_arg (argptr, size_t) ; | |
| memset (charptr, 0, count) ; | |
| if (psf->header.indx + count >= psf->header.len && psf_bump_header_allocation (psf, count)) | |
| break ; | |
| byte_count += header_gets (psf, charptr, count) ; | |
| break ; | |
| case 'z' : | |
| psf_log_printf (psf, "Format conversion 'z' not implemented yet.\n") ; | |
| /* | |
| size = va_arg (argptr, size_t) ; | |
| while (size) | |
| { psf->header.ptr [psf->header.indx] = 0 ; | |
| psf->header.indx ++ ; | |
| size -- ; | |
| } ; | |
| */ | |
| break ; | |
| case 'p' : /* Seek to position from start. */ | |
| count = va_arg (argptr, size_t) ; | |
| header_seek (psf, count, SEEK_SET) ; | |
| byte_count = count ; | |
| break ; | |
| case 'j' : /* Seek to position from current position. */ | |
| count = va_arg (argptr, size_t) ; | |
| header_seek (psf, count, SEEK_CUR) ; | |
| byte_count += count ; | |
| break ; | |
| case '!' : /* Clear buffer, forcing re-read. */ | |
| psf->header.end = psf->header.indx = 0 ; | |
| break ; | |
| default : | |
| psf_log_printf (psf, "*** Invalid format specifier `%c'\n", c) ; | |
| psf->error = SFE_INTERNAL ; | |
| break ; | |
| } ; | |
| } ; | |
| va_end (argptr) ; | |
| return byte_count ; | |
| } /* psf_binheader_readf */ | |
| /*----------------------------------------------------------------------------------------------- | |
| */ | |
| sf_count_t | |
| psf_default_seek (SF_PRIVATE *psf, int UNUSED (mode), sf_count_t samples_from_start) | |
| { sf_count_t position, retval ; | |
| if (! (psf->blockwidth && psf->dataoffset >= 0)) | |
| { psf->error = SFE_BAD_SEEK ; | |
| return PSF_SEEK_ERROR ; | |
| } ; | |
| if (! psf->sf.seekable) | |
| { psf->error = SFE_NOT_SEEKABLE ; | |
| return PSF_SEEK_ERROR ; | |
| } ; | |
| position = psf->dataoffset + psf->blockwidth * samples_from_start ; | |
| if ((retval = psf_fseek (psf, position, SEEK_SET)) != position) | |
| { psf->error = SFE_SEEK_FAILED ; | |
| return PSF_SEEK_ERROR ; | |
| } ; | |
| return samples_from_start ; | |
| } /* psf_default_seek */ | |
| /*----------------------------------------------------------------------------------------------- | |
| */ | |
| void | |
| psf_hexdump (const void *ptr, int len) | |
| { const char *data ; | |
| char ascii [17] ; | |
| int k, m ; | |
| if ((data = ptr) == NULL) | |
| return ; | |
| if (len <= 0) | |
| return ; | |
| puts ("") ; | |
| for (k = 0 ; k < len ; k += 16) | |
| { memset (ascii, ' ', sizeof (ascii)) ; | |
| printf ("%08X: ", k) ; | |
| for (m = 0 ; m < 16 && k + m < len ; m++) | |
| { printf (m == 8 ? " %02X " : "%02X ", data [k + m] & 0xFF) ; | |
| ascii [m] = psf_isprint (data [k + m]) ? data [k + m] : '.' ; | |
| } ; | |
| if (m <= 8) printf (" ") ; | |
| for ( ; m < 16 ; m++) printf (" ") ; | |
| ascii [16] = 0 ; | |
| printf (" %s\n", ascii) ; | |
| } ; | |
| puts ("") ; | |
| } /* psf_hexdump */ | |
| void | |
| psf_log_SF_INFO (SF_PRIVATE *psf) | |
| { psf_log_printf (psf, "---------------------------------\n") ; | |
| psf_log_printf (psf, " Sample rate : %d\n", psf->sf.samplerate) ; | |
| if (psf->sf.frames == SF_COUNT_MAX) | |
| psf_log_printf (psf, " Frames : unknown\n") ; | |
| else | |
| psf_log_printf (psf, " Frames : %D\n", psf->sf.frames) ; | |
| psf_log_printf (psf, " Channels : %d\n", psf->sf.channels) ; | |
| psf_log_printf (psf, " Format : 0x%X\n", psf->sf.format) ; | |
| psf_log_printf (psf, " Sections : %d\n", psf->sf.sections) ; | |
| psf_log_printf (psf, " Seekable : %s\n", psf->sf.seekable ? "TRUE" : "FALSE") ; | |
| psf_log_printf (psf, "---------------------------------\n") ; | |
| } /* psf_dump_SFINFO */ | |
| /*======================================================================================== | |
| */ | |
| int | |
| psf_isprint (int ch) | |
| { return (ch >= ' ' && ch <= '~') ; | |
| } /* psf_isprint */ | |
| void | |
| psf_strlcat (char *dest, size_t n, const char *src) | |
| { strncat (dest, src, n - strlen (dest) - 1) ; | |
| dest [n - 1] = 0 ; | |
| } /* psf_strlcat */ | |
| void | |
| psf_strlcpy (char *dest, size_t n, const char *src) | |
| { strncpy (dest, src, n - 1) ; | |
| dest [n - 1] = 0 ; | |
| } /* psf_strlcpy */ | |
| /*======================================================================================== | |
| */ | |
| void * | |
| psf_memdup (const void *src, size_t n) | |
| { if (src == NULL) | |
| return NULL ; | |
| void * mem = calloc (1, n & 3 ? n + 4 - (n & 3) : n) ; | |
| if (mem != NULL) | |
| memcpy (mem, src, n) ; | |
| return mem ; | |
| } /* psf_memdup */ | |
| void* | |
| psf_memset (void *s, int c, sf_count_t len) | |
| { char *ptr ; | |
| int setcount ; | |
| ptr = (char *) s ; | |
| while (len > 0) | |
| { setcount = (len > 0x10000000) ? 0x10000000 : (int) len ; | |
| memset (ptr, c, setcount) ; | |
| ptr += setcount ; | |
| len -= setcount ; | |
| } ; | |
| return s ; | |
| } /* psf_memset */ | |
| /* | |
| ** Clang refuses to do sizeof (SF_CUES_VAR (cue_count)) so we have to manually | |
| ** bodgy something up instead. | |
| */ | |
| #ifdef _MSC_VER | |
| typedef SF_CUES_VAR (0) SF_CUES_0 ; | |
| #else | |
| typedef SF_CUES_VAR () SF_CUES_0 ; | |
| #endif | |
| /* calculate size of SF_CUES struct given number of cues */ | |
| #define SF_CUES_VAR_SIZE(count) (sizeof (SF_CUES_0) + count * sizeof (SF_CUE_POINT)) | |
| /* calculate number of cues in SF_CUES struct given data size */ | |
| #define SF_CUES_COUNT(datasize) (((datasize) - sizeof (uint32_t)) / sizeof (SF_CUE_POINT)) | |
| SF_CUES * | |
| psf_cues_alloc (uint32_t cue_count) | |
| { SF_CUES *pcues = calloc (1, SF_CUES_VAR_SIZE (cue_count)) ; | |
| if (pcues) | |
| { pcues->cue_count = cue_count ; | |
| } ; | |
| return pcues ; | |
| } /* psf_cues_alloc */ | |
| SF_CUES * | |
| psf_cues_dup (const void * ptr, size_t datasize) | |
| { const SF_CUES *pcues = ptr ; | |
| SF_CUES *pnew = NULL ; | |
| if (pcues->cue_count <= SF_CUES_COUNT (datasize)) | |
| { /* check that passed-in datasize is consistent with cue_count in passed-in SF_CUES struct */ | |
| pnew = psf_cues_alloc (pcues->cue_count) ; | |
| memcpy (pnew, pcues, SF_CUES_VAR_SIZE (pcues->cue_count)) ; | |
| } | |
| return pnew ; | |
| } /* psf_cues_dup */ | |
| void | |
| psf_get_cues (SF_PRIVATE * psf, void * data, size_t datasize) | |
| { | |
| if (psf->cues) | |
| { uint32_t cue_count = SF_CUES_COUNT (datasize) ; | |
| cue_count = SF_MIN (cue_count, psf->cues->cue_count) ; | |
| memcpy (data, psf->cues, SF_CUES_VAR_SIZE (cue_count)) ; | |
| ((SF_CUES*) data)->cue_count = cue_count ; | |
| } ; | |
| return ; | |
| } /* psf_get_cues */ | |
| SF_INSTRUMENT * | |
| psf_instrument_alloc (void) | |
| { SF_INSTRUMENT *instr ; | |
| instr = calloc (1, sizeof (SF_INSTRUMENT)) ; | |
| if (instr == NULL) | |
| return NULL ; | |
| /* Set non-zero default values. */ | |
| instr->basenote = -1 ; | |
| instr->velocity_lo = -1 ; | |
| instr->velocity_hi = -1 ; | |
| instr->key_lo = -1 ; | |
| instr->key_hi = -1 ; | |
| return instr ; | |
| } /* psf_instrument_alloc */ | |
| void | |
| psf_sanitize_string (char * cptr, int len) | |
| { | |
| do | |
| { | |
| len -- ; | |
| cptr [len] = psf_isprint (cptr [len]) ? cptr [len] : '.' ; | |
| } | |
| while (len > 0) ; | |
| } /* psf_sanitize_string */ | |
| void | |
| psf_get_date_str (char *str, int maxlen) | |
| { time_t current ; | |
| struct tm timedata, *tmptr ; | |
| time (¤t) ; | |
| #if defined (HAVE_GMTIME_R) | |
| /* If the re-entrant version is available, use it. */ | |
| tmptr = gmtime_r (¤t, &timedata) ; | |
| #elif defined (HAVE_GMTIME) | |
| /* Otherwise use the standard one and copy the data to local storage. */ | |
| tmptr = gmtime (¤t) ; | |
| memcpy (&timedata, tmptr, sizeof (timedata)) ; | |
| #else | |
| tmptr = NULL ; | |
| #endif | |
| if (tmptr) | |
| snprintf (str, maxlen, "%4d-%02d-%02d %02d:%02d:%02d UTC", | |
| 1900 + timedata.tm_year, timedata.tm_mon, timedata.tm_mday, | |
| timedata.tm_hour, timedata.tm_min, timedata.tm_sec) ; | |
| else | |
| snprintf (str, maxlen, "Unknown date") ; | |
| return ; | |
| } /* psf_get_date_str */ | |
| int | |
| subformat_to_bytewidth (int format) | |
| { | |
| switch (format) | |
| { case SF_FORMAT_PCM_U8 : | |
| case SF_FORMAT_PCM_S8 : | |
| return 1 ; | |
| case SF_FORMAT_PCM_16 : | |
| return 2 ; | |
| case SF_FORMAT_PCM_24 : | |
| return 3 ; | |
| case SF_FORMAT_PCM_32 : | |
| case SF_FORMAT_FLOAT : | |
| return 4 ; | |
| case SF_FORMAT_DOUBLE : | |
| return 8 ; | |
| } ; | |
| return 0 ; | |
| } /* subformat_to_bytewidth */ | |
| int | |
| s_bitwidth_to_subformat (int bits) | |
| { static int array [] = | |
| { SF_FORMAT_PCM_S8, SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_PCM_32 | |
| } ; | |
| if (bits < 8 || bits > 32) | |
| return 0 ; | |
| return array [((bits + 7) / 8) - 1] ; | |
| } /* bitwidth_to_subformat */ | |
| int | |
| u_bitwidth_to_subformat (int bits) | |
| { static int array [] = | |
| { SF_FORMAT_PCM_U8, SF_FORMAT_PCM_16, SF_FORMAT_PCM_24, SF_FORMAT_PCM_32 | |
| } ; | |
| if (bits < 8 || bits > 32) | |
| return 0 ; | |
| return array [((bits + 7) / 8) - 1] ; | |
| } /* bitwidth_to_subformat */ | |
| /* | |
| ** psf_rand_int32 : Not crypto quality, but more than adequate for things | |
| ** like stream serial numbers in Ogg files or the unique_id field of the | |
| ** SF_PRIVATE struct. | |
| */ | |
| int32_t | |
| psf_rand_int32 (void) | |
| { static uint64_t value = 0 ; | |
| int k, count ; | |
| if (value == 0) | |
| { | |
| #if HAVE_GETTIMEOFDAY | |
| struct timeval tv ; | |
| gettimeofday (&tv, NULL) ; | |
| value = tv.tv_sec + tv.tv_usec ; | |
| #else | |
| value = time (NULL) ; | |
| #endif | |
| } ; | |
| count = 4 + (value & 7) ; | |
| for (k = 0 ; k < count ; k++) | |
| value = (11117 * value + 211231) & 0x7fffffff ; | |
| return (int32_t) value ; | |
| } /* psf_rand_int32 */ | |
| void | |
| append_snprintf (char * dest, size_t maxlen, const char * fmt, ...) | |
| { size_t len = strlen (dest) ; | |
| if (len < maxlen) | |
| { va_list ap ; | |
| va_start (ap, fmt) ; | |
| vsnprintf (dest + len, maxlen - len, fmt, ap) ; | |
| va_end (ap) ; | |
| } ; | |
| return ; | |
| } /* append_snprintf */ | |
| void | |
| psf_strlcpy_crlf (char *dest, const char *src, size_t destmax, size_t srcmax) | |
| { /* Must be minus 2 so it can still expand a single trailing '\n' or '\r'. */ | |
| char * destend = dest + destmax - 2 ; | |
| const char * srcend = src + srcmax ; | |
| while (dest < destend && src < srcend) | |
| { if ((src [0] == '\r' && src [1] == '\n') || (src [0] == '\n' && src [1] == '\r')) | |
| { *dest++ = '\r' ; | |
| *dest++ = '\n' ; | |
| src += 2 ; | |
| continue ; | |
| } ; | |
| if (src [0] == '\r') | |
| { *dest++ = '\r' ; | |
| *dest++ = '\n' ; | |
| src += 1 ; | |
| continue ; | |
| } ; | |
| if (src [0] == '\n') | |
| { *dest++ = '\r' ; | |
| *dest++ = '\n' ; | |
| src += 1 ; | |
| continue ; | |
| } ; | |
| *dest++ = *src++ ; | |
| } ; | |
| /* Make sure dest is terminated. */ | |
| *dest = 0 ; | |
| } /* psf_strlcpy_crlf */ | |
| sf_count_t | |
| psf_decode_frame_count (SF_PRIVATE *psf) | |
| { sf_count_t count, readlen, total = 0 ; | |
| BUF_UNION ubuf ; | |
| /* If we're reading from a pipe or the file is too long, just return SF_COUNT_MAX. */ | |
| if (psf_is_pipe (psf) || psf->datalength > 0x1000000) | |
| return SF_COUNT_MAX ; | |
| psf_fseek (psf, psf->dataoffset, SEEK_SET) ; | |
| readlen = ARRAY_LEN (ubuf.ibuf) / psf->sf.channels ; | |
| readlen *= psf->sf.channels ; | |
| while ((count = psf->read_int (psf, ubuf.ibuf, readlen)) > 0) | |
| total += count ; | |
| psf_fseek (psf, psf->dataoffset, SEEK_SET) ; | |
| return total / psf->sf.channels ; | |
| } /* psf_decode_frame_count */ | |
| /*============================================================================== | |
| */ | |
| #define CASE_NAME(x) case x : return #x ; break ; | |
| const char * | |
| str_of_major_format (int format) | |
| { switch (SF_CONTAINER (format)) | |
| { CASE_NAME (SF_FORMAT_WAV) ; | |
| CASE_NAME (SF_FORMAT_AIFF) ; | |
| CASE_NAME (SF_FORMAT_AU) ; | |
| CASE_NAME (SF_FORMAT_RAW) ; | |
| CASE_NAME (SF_FORMAT_PAF) ; | |
| CASE_NAME (SF_FORMAT_SVX) ; | |
| CASE_NAME (SF_FORMAT_NIST) ; | |
| CASE_NAME (SF_FORMAT_VOC) ; | |
| CASE_NAME (SF_FORMAT_IRCAM) ; | |
| CASE_NAME (SF_FORMAT_W64) ; | |
| CASE_NAME (SF_FORMAT_MAT4) ; | |
| CASE_NAME (SF_FORMAT_MAT5) ; | |
| CASE_NAME (SF_FORMAT_PVF) ; | |
| CASE_NAME (SF_FORMAT_XI) ; | |
| CASE_NAME (SF_FORMAT_HTK) ; | |
| CASE_NAME (SF_FORMAT_SDS) ; | |
| CASE_NAME (SF_FORMAT_AVR) ; | |
| CASE_NAME (SF_FORMAT_WAVEX) ; | |
| CASE_NAME (SF_FORMAT_SD2) ; | |
| CASE_NAME (SF_FORMAT_FLAC) ; | |
| CASE_NAME (SF_FORMAT_CAF) ; | |
| CASE_NAME (SF_FORMAT_WVE) ; | |
| CASE_NAME (SF_FORMAT_OGG) ; | |
| CASE_NAME (SF_FORMAT_MPEG) ; | |
| default : | |
| break ; | |
| } ; | |
| return "BAD_MAJOR_FORMAT" ; | |
| } /* str_of_major_format */ | |
| const char * | |
| str_of_minor_format (int format) | |
| { switch (SF_CODEC (format)) | |
| { CASE_NAME (SF_FORMAT_PCM_S8) ; | |
| CASE_NAME (SF_FORMAT_PCM_16) ; | |
| CASE_NAME (SF_FORMAT_PCM_24) ; | |
| CASE_NAME (SF_FORMAT_PCM_32) ; | |
| CASE_NAME (SF_FORMAT_PCM_U8) ; | |
| CASE_NAME (SF_FORMAT_FLOAT) ; | |
| CASE_NAME (SF_FORMAT_DOUBLE) ; | |
| CASE_NAME (SF_FORMAT_ULAW) ; | |
| CASE_NAME (SF_FORMAT_ALAW) ; | |
| CASE_NAME (SF_FORMAT_IMA_ADPCM) ; | |
| CASE_NAME (SF_FORMAT_MS_ADPCM) ; | |
| CASE_NAME (SF_FORMAT_GSM610) ; | |
| CASE_NAME (SF_FORMAT_VOX_ADPCM) ; | |
| CASE_NAME (SF_FORMAT_NMS_ADPCM_16) ; | |
| CASE_NAME (SF_FORMAT_NMS_ADPCM_24) ; | |
| CASE_NAME (SF_FORMAT_NMS_ADPCM_32) ; | |
| CASE_NAME (SF_FORMAT_G721_32) ; | |
| CASE_NAME (SF_FORMAT_G723_24) ; | |
| CASE_NAME (SF_FORMAT_G723_40) ; | |
| CASE_NAME (SF_FORMAT_DWVW_12) ; | |
| CASE_NAME (SF_FORMAT_DWVW_16) ; | |
| CASE_NAME (SF_FORMAT_DWVW_24) ; | |
| CASE_NAME (SF_FORMAT_DWVW_N) ; | |
| CASE_NAME (SF_FORMAT_DPCM_8) ; | |
| CASE_NAME (SF_FORMAT_DPCM_16) ; | |
| CASE_NAME (SF_FORMAT_VORBIS) ; | |
| CASE_NAME (SF_FORMAT_MPEG_LAYER_I) ; | |
| CASE_NAME (SF_FORMAT_MPEG_LAYER_II) ; | |
| CASE_NAME (SF_FORMAT_MPEG_LAYER_III) ; | |
| default : | |
| break ; | |
| } ; | |
| return "BAD_MINOR_FORMAT" ; | |
| } /* str_of_minor_format */ | |
| const char * | |
| str_of_open_mode (int mode) | |
| { switch (mode) | |
| { CASE_NAME (SFM_READ) ; | |
| CASE_NAME (SFM_WRITE) ; | |
| CASE_NAME (SFM_RDWR) ; | |
| default : | |
| break ; | |
| } ; | |
| return "BAD_MODE" ; | |
| } /* str_of_open_mode */ | |
| const char * | |
| str_of_endianness (int end) | |
| { switch (end) | |
| { CASE_NAME (SF_ENDIAN_BIG) ; | |
| CASE_NAME (SF_ENDIAN_LITTLE) ; | |
| CASE_NAME (SF_ENDIAN_CPU) ; | |
| default : | |
| break ; | |
| } ; | |
| /* Zero length string for SF_ENDIAN_FILE. */ | |
| return "" ; | |
| } /* str_of_endianness */ | |
| /*============================================================================== | |
| */ | |
| void | |
| psf_f2s_array (const float *src, short *dest, int count, int normalize) | |
| { float normfact ; | |
| normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| dest [i] = psf_lrintf (src [i] * normfact) ; | |
| return ; | |
| } /* psf_f2s_array */ | |
| void | |
| psf_f2s_clip_array (const float *src, short *dest, int count, int normalize) | |
| { float normfact, scaled_value ; | |
| normfact = normalize ? (1.0 * 0x8000) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| { scaled_value = src [i] * normfact ; | |
| if (scaled_value >= (1.0 * 0x7FFF)) | |
| { dest [i] = 0x7FFF ; | |
| continue ; | |
| } ; | |
| if (scaled_value <= (-8.0 * 0x1000)) | |
| { dest [i] = -0x7FFF - 1 ; | |
| continue ; | |
| } ; | |
| dest [i] = psf_lrintf (scaled_value) ; | |
| } ; | |
| return ; | |
| } /* psf_f2s_clip_array */ | |
| void | |
| psf_d2s_array (const double *src, short *dest, int count, int normalize) | |
| { double normfact ; | |
| normfact = normalize ? (1.0 * 0x7FFF) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| dest [i] = psf_lrint (src [i] * normfact) ; | |
| return ; | |
| } /* psf_f2s_array */ | |
| void | |
| psf_d2s_clip_array (const double *src, short *dest, int count, int normalize) | |
| { double normfact, scaled_value ; | |
| normfact = normalize ? (1.0 * 0x8000) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| { scaled_value = src [i] * normfact ; | |
| if (scaled_value >= (1.0 * 0x7FFF)) | |
| { dest [i] = 0x7FFF ; | |
| continue ; | |
| } ; | |
| if (scaled_value <= (-8.0 * 0x1000)) | |
| { dest [i] = -0x7FFF - 1 ; | |
| continue ; | |
| } ; | |
| dest [i] = psf_lrint (scaled_value) ; | |
| } ; | |
| return ; | |
| } /* psf_d2s_clip_array */ | |
| void | |
| psf_f2i_array (const float *src, int *dest, int count, int normalize) | |
| { float normfact ; | |
| normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| dest [i] = psf_lrintf (src [i] * normfact) ; | |
| return ; | |
| } /* psf_f2i_array */ | |
| void | |
| psf_f2i_clip_array (const float *src, int *dest, int count, int normalize) | |
| { float normfact, scaled_value ; | |
| normfact = normalize ? (8.0 * 0x10000000) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| { scaled_value = src [i] * normfact ; | |
| #if CPU_CLIPS_POSITIVE == 0 | |
| if (scaled_value >= (1.0 * 0x7FFFFFFF)) | |
| { dest [i] = 0x7FFFFFFF ; | |
| continue ; | |
| } ; | |
| #endif | |
| #if CPU_CLIPS_NEGATIVE == 0 | |
| if (scaled_value <= (-8.0 * 0x10000000)) | |
| { dest [i] = 0x80000000 ; | |
| continue ; | |
| } ; | |
| #endif | |
| dest [i] = psf_lrintf (scaled_value) ; | |
| } ; | |
| return ; | |
| } /* psf_f2i_clip_array */ | |
| void | |
| psf_d2i_array (const double *src, int *dest, int count, int normalize) | |
| { double normfact ; | |
| normfact = normalize ? (1.0 * 0x7FFFFFFF) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| dest [i] = psf_lrint (src [i] * normfact) ; | |
| return ; | |
| } /* psf_f2i_array */ | |
| void | |
| psf_d2i_clip_array (const double *src, int *dest, int count, int normalize) | |
| { double normfact, scaled_value ; | |
| normfact = normalize ? (8.0 * 0x10000000) : 1.0 ; | |
| for (int i = 0 ; i < count ; i++) | |
| { scaled_value = src [i] * normfact ; | |
| #if CPU_CLIPS_POSITIVE == 0 | |
| if (scaled_value >= (1.0 * 0x7FFFFFFF)) | |
| { dest [i] = 0x7FFFFFFF ; | |
| continue ; | |
| } ; | |
| #endif | |
| #if CPU_CLIPS_NEGATIVE == 0 | |
| if (scaled_value <= (-8.0 * 0x10000000)) | |
| { dest [i] = 0x80000000 ; | |
| continue ; | |
| } ; | |
| #endif | |
| dest [i] = psf_lrint (scaled_value) ; | |
| } ; | |
| return ; | |
| } /* psf_d2i_clip_array */ | |
| FILE * | |
| psf_open_tmpfile (char * fname, size_t fnamelen) | |
| { const char * tmpdir ; | |
| FILE * file ; | |
| if (OS_IS_WIN32) | |
| tmpdir = getenv ("TEMP") ; | |
| else | |
| { tmpdir = getenv ("TMPDIR") ; | |
| tmpdir = tmpdir == NULL ? "/tmp" : tmpdir ; | |
| } ; | |
| if (tmpdir && access (tmpdir, R_OK | W_OK | X_OK) == 0) | |
| { snprintf (fname, fnamelen, "%s/%x%x-alac.tmp", tmpdir, psf_rand_int32 (), psf_rand_int32 ()) ; | |
| if ((file = fopen (fname, "wb+")) != NULL) | |
| return file ; | |
| } ; | |
| snprintf (fname, fnamelen, "%x%x-alac.tmp", psf_rand_int32 (), psf_rand_int32 ()) ; | |
| if ((file = fopen (fname, "wb+")) != NULL) | |
| return file ; | |
| memset (fname, 0, fnamelen) ; | |
| return NULL ; | |
| } /* psf_open_tmpfile */ |