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;
}
Beispiel #2
0
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;
    }