static SNDFILE *sf_open_read(const char *path, SF_INFO *info) { FILE *stream = fopen(path, "rb"); if (stream == NULL) return NULL; // don't attempt to parse all valid forms, just the most common one unsigned char wav[44]; size_t actual; actual = fread(wav, sizeof(char), sizeof(wav), stream); if (actual != sizeof(wav)) return NULL; for (;;) { if (memcmp(wav, "RIFF", 4)) break; unsigned riffSize = little4u(&wav[4]); if (riffSize < 36) break; if (memcmp(&wav[8], "WAVEfmt ", 8)) break; unsigned fmtsize = little4u(&wav[16]); if (fmtsize != 16) break; unsigned format = little2u(&wav[20]); if (format != 1) // PCM break; unsigned channels = little2u(&wav[22]); if (channels != 1 && channels != 2) break; unsigned samplerate = little4u(&wav[24]); if (samplerate == 0) break; // ignore byte rate // ignore block alignment unsigned bitsPerSample = little2u(&wav[34]); if (bitsPerSample != 8 && bitsPerSample != 16) break; unsigned bytesPerFrame = (bitsPerSample >> 3) * channels; if (memcmp(&wav[36], "data", 4)) break; unsigned dataSize = little4u(&wav[40]); SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE)); handle->mode = SFM_READ; handle->temp = NULL; handle->stream = stream; handle->bytesPerFrame = bytesPerFrame; handle->remaining = dataSize / bytesPerFrame; handle->info.frames = handle->remaining; handle->info.samplerate = samplerate; handle->info.channels = channels; handle->info.format = SF_FORMAT_WAV; if (bitsPerSample == 8) handle->info.format |= SF_FORMAT_PCM_U8; else handle->info.format |= SF_FORMAT_PCM_16; *info = handle->info; return handle; } return NULL; }
// "swab" conflicts with OS X <string.h> static void my_swab(short *ptr, size_t numToSwap) { while (numToSwap > 0) { *ptr = little2u((unsigned char *) ptr); --numToSwap; ++ptr; } }
static SNDFILE *sf_open_read(const char *path, SF_INFO *info) { FILE *stream = fopen(path, "rb"); if (stream == NULL) { #ifdef HAVE_STDERR fprintf(stderr, "fopen %s failed errno %d\n", path, errno); #endif return NULL; } SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE)); handle->mode = SFM_READ; handle->temp = NULL; handle->stream = stream; handle->info.format = SF_FORMAT_WAV; // don't attempt to parse all valid forms, just the most common ones unsigned char wav[12]; size_t actual; actual = fread(wav, sizeof(char), sizeof(wav), stream); if (actual < 12) { #ifdef HAVE_STDERR fprintf(stderr, "actual %zu < 44\n", actual); #endif goto close; } if (memcmp(wav, "RIFF", 4)) { #ifdef HAVE_STDERR fprintf(stderr, "wav != RIFF\n"); #endif goto close; } unsigned riffSize = little4u(&wav[4]); if (riffSize < 4) { #ifdef HAVE_STDERR fprintf(stderr, "riffSize %u < 4\n", riffSize); #endif goto close; } if (memcmp(&wav[8], "WAVE", 4)) { #ifdef HAVE_STDERR fprintf(stderr, "missing WAVE\n"); #endif goto close; } size_t remaining = riffSize - 4; int hadFmt = 0; int hadData = 0; long dataTell = 0L; while (remaining >= 8) { unsigned char chunk[8]; actual = fread(chunk, sizeof(char), sizeof(chunk), stream); if (actual != sizeof(chunk)) { #ifdef HAVE_STDERR fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk)); #endif goto close; } remaining -= 8; unsigned chunkSize = little4u(&chunk[4]); if (chunkSize > remaining) { #ifdef HAVE_STDERR fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining); #endif goto close; } if (!memcmp(&chunk[0], "fmt ", 4)) { if (hadFmt) { #ifdef HAVE_STDERR fprintf(stderr, "multiple fmt\n"); #endif goto close; } if (chunkSize < 2) { #ifdef HAVE_STDERR fprintf(stderr, "chunkSize %u < 2\n", chunkSize); #endif goto close; } unsigned char fmt[40]; actual = fread(fmt, sizeof(char), 2, stream); if (actual != 2) { #ifdef HAVE_STDERR fprintf(stderr, "actual %zu != 2\n", actual); #endif goto close; } unsigned format = little2u(&fmt[0]); size_t minSize = 0; switch (format) { case WAVE_FORMAT_PCM: case WAVE_FORMAT_IEEE_FLOAT: minSize = 16; break; case WAVE_FORMAT_EXTENSIBLE: minSize = 40; break; default: #ifdef HAVE_STDERR fprintf(stderr, "unsupported format %u\n", format); #endif goto close; } if (chunkSize < minSize) { #ifdef HAVE_STDERR fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize); #endif goto close; } actual = fread(&fmt[2], sizeof(char), minSize - 2, stream); if (actual != minSize - 2) { #ifdef HAVE_STDERR fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16); #endif goto close; } if (chunkSize > minSize) { fseek(stream, (long) (chunkSize - minSize), SEEK_CUR); } unsigned channels = little2u(&fmt[2]); if ((channels < 1) || (channels > FCC_8)) { #ifdef HAVE_STDERR fprintf(stderr, "unsupported channels %u\n", channels); #endif goto close; } unsigned samplerate = little4u(&fmt[4]); if (samplerate == 0) { #ifdef HAVE_STDERR fprintf(stderr, "samplerate %u == 0\n", samplerate); #endif goto close; } // ignore byte rate // ignore block alignment unsigned bitsPerSample = little2u(&fmt[14]); if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 24 && bitsPerSample != 32) { #ifdef HAVE_STDERR fprintf(stderr, "bitsPerSample %u != 8 or 16 or 24 or 32\n", bitsPerSample); #endif goto close; } unsigned bytesPerFrame = (bitsPerSample >> 3) * channels; handle->bytesPerFrame = bytesPerFrame; handle->info.samplerate = samplerate; handle->info.channels = channels; switch (bitsPerSample) { case 8: handle->info.format |= SF_FORMAT_PCM_U8; break; case 16: handle->info.format |= SF_FORMAT_PCM_16; break; case 24: handle->info.format |= SF_FORMAT_PCM_24; break; case 32: if (format == WAVE_FORMAT_IEEE_FLOAT) handle->info.format |= SF_FORMAT_FLOAT; else handle->info.format |= SF_FORMAT_PCM_32; break; } hadFmt = 1; } else if (!memcmp(&chunk[0], "data", 4)) { if (!hadFmt) { #ifdef HAVE_STDERR fprintf(stderr, "data not preceded by fmt\n"); #endif goto close; } if (hadData) { #ifdef HAVE_STDERR fprintf(stderr, "multiple data\n"); #endif goto close; } handle->remaining = chunkSize / handle->bytesPerFrame; handle->info.frames = handle->remaining; dataTell = ftell(stream); if (chunkSize > 0) { fseek(stream, (long) chunkSize, SEEK_CUR); } hadData = 1; } else if (!memcmp(&chunk[0], "fact", 4)) { // ignore fact if (chunkSize > 0) { fseek(stream, (long) chunkSize, SEEK_CUR); } } else { // ignore unknown chunk #ifdef HAVE_STDERR fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n", chunk[0], chunk[1], chunk[2], chunk[3]); #endif if (chunkSize > 0) { fseek(stream, (long) chunkSize, SEEK_CUR); } } remaining -= chunkSize; }