1207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten/*
2207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * Copyright (C) 2012 The Android Open Source Project
3207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten *
4207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * Licensed under the Apache License, Version 2.0 (the "License");
5207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * you may not use this file except in compliance with the License.
6207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * You may obtain a copy of the License at
7207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten *
8207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten *      http://www.apache.org/licenses/LICENSE-2.0
9207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten *
10207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * Unless required by applicable law or agreed to in writing, software
11207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * distributed under the License is distributed on an "AS IS" BASIS,
12207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * See the License for the specific language governing permissions and
14207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten * limitations under the License.
15207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten */
16207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
17207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten#include <audio_utils/sndfile.h>
1836c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten#include <audio_utils/primitives.h>
19207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten#include <stdio.h>
20207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten#include <string.h>
21c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten#include <errno.h>
22c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
23c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten#define WAVE_FORMAT_PCM         1
24c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten#define WAVE_FORMAT_IEEE_FLOAT  3
25c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten#define WAVE_FORMAT_EXTENSIBLE  0xFFFE
26207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
27207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kastenstruct SNDFILE_ {
28eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    int mode;
29eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    uint8_t *temp;  // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
30207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    FILE *stream;
31207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    size_t bytesPerFrame;
32eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    size_t remaining;   // frames unread for SFM_READ, frames written for SFM_WRITE
33207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    SF_INFO info;
34207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten};
35207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
36207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kastenstatic unsigned little2u(unsigned char *ptr)
37207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
38207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    return (ptr[1] << 8) + ptr[0];
39207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
40207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
41207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kastenstatic unsigned little4u(unsigned char *ptr)
42207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
43207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
44207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
45207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
46207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kastenstatic int isLittleEndian(void)
47207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
48207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    static const short one = 1;
49207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    return *((const char *) &one) == 1;
50207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
51207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
528fdafc906bc3abd9ce897c6c0e87598417bd4d58Glenn Kasten// "swab" conflicts with OS X <string.h>
538fdafc906bc3abd9ce897c6c0e87598417bd4d58Glenn Kastenstatic void my_swab(short *ptr, size_t numToSwap)
54207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
55207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    while (numToSwap > 0) {
56207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        *ptr = little2u((unsigned char *) ptr);
57207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        --numToSwap;
58207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        ++ptr;
59207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    }
60207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
61207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
62eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kastenstatic SNDFILE *sf_open_read(const char *path, SF_INFO *info)
63207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
64207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    FILE *stream = fopen(path, "rb");
65c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (stream == NULL) {
66c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
67207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        return NULL;
68c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
69c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
70c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
71c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    handle->mode = SFM_READ;
72c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    handle->temp = NULL;
73c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    handle->stream = stream;
74c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    handle->info.format = SF_FORMAT_WAV;
75c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
76c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    // don't attempt to parse all valid forms, just the most common ones
77c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned char wav[12];
78207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    size_t actual;
79207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    actual = fread(wav, sizeof(char), sizeof(wav), stream);
80c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (actual < 12) {
8196d859ca87493056f13b1b653f49ffe7bf9bcd2fKévin PETIT        fprintf(stderr, "actual %zu < 44\n", actual);
82c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        goto close;
83c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
84c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (memcmp(wav, "RIFF", 4)) {
85c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "wav != RIFF\n");
86c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        goto close;
87c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
88c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned riffSize = little4u(&wav[4]);
89c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (riffSize < 4) {
90c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "riffSize %u < 4\n", riffSize);
91c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        goto close;
92c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
93c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (memcmp(&wav[8], "WAVE", 4)) {
94c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "missing WAVE\n");
95c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        goto close;
96c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
97c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t remaining = riffSize - 4;
98c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    int hadFmt = 0;
99c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    int hadData = 0;
100c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    while (remaining >= 8) {
101c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        unsigned char chunk[8];
102c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        actual = fread(chunk, sizeof(char), sizeof(chunk), stream);
103c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        if (actual != sizeof(chunk)) {
10496d859ca87493056f13b1b653f49ffe7bf9bcd2fKévin PETIT            fprintf(stderr, "actual %zu != %zu\n", actual, sizeof(chunk));
105c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            goto close;
106c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        }
107c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        remaining -= 8;
108c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        unsigned chunkSize = little4u(&chunk[4]);
109c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        if (chunkSize > remaining) {
11096d859ca87493056f13b1b653f49ffe7bf9bcd2fKévin PETIT            fprintf(stderr, "chunkSize %u > remaining %zu\n", chunkSize, remaining);
111c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            goto close;
112c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        }
113c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        if (!memcmp(&chunk[0], "fmt ", 4)) {
114c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (hadFmt) {
115c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "multiple fmt\n");
116c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
117c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
118c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (chunkSize < 2) {
119c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "chunkSize %u < 2\n", chunkSize);
120c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
121c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
122c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            unsigned char fmt[40];
123c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            actual = fread(fmt, sizeof(char), 2, stream);
124c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (actual != 2) {
12596d859ca87493056f13b1b653f49ffe7bf9bcd2fKévin PETIT                fprintf(stderr, "actual %zu != 2\n", actual);
126c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
127c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
128c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            unsigned format = little2u(&fmt[0]);
129c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            size_t minSize = 0;
130c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            switch (format) {
131c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            case WAVE_FORMAT_PCM:
132c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            case WAVE_FORMAT_IEEE_FLOAT:
133c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                minSize = 16;
134c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                break;
135c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            case WAVE_FORMAT_EXTENSIBLE:
136c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                minSize = 40;
137c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                break;
138c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            default:
139c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "unsupported format %u\n", format);
140c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
141c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
142c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (chunkSize < minSize) {
14396d859ca87493056f13b1b653f49ffe7bf9bcd2fKévin PETIT                fprintf(stderr, "chunkSize %u < minSize %zu\n", chunkSize, minSize);
144c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
145c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
146c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            actual = fread(&fmt[2], sizeof(char), minSize - 2, stream);
147c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (actual != minSize - 2) {
14896d859ca87493056f13b1b653f49ffe7bf9bcd2fKévin PETIT                fprintf(stderr, "actual %zu != %zu\n", actual, minSize - 16);
149c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
150c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
151c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (chunkSize > minSize) {
152c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fseek(stream, (long) (chunkSize - minSize), SEEK_CUR);
153c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
154c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            unsigned channels = little2u(&fmt[2]);
1557c1930a39b04ce8c018df5f49e3be4ccdccbd3fdGlenn Kasten            if (channels != 1 && channels != 2 && channels != 4 && channels != 6 && channels != 8) {
1567c1930a39b04ce8c018df5f49e3be4ccdccbd3fdGlenn Kasten                fprintf(stderr, "unsupported channels %u\n", channels);
157c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
158c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
159c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            unsigned samplerate = little4u(&fmt[4]);
160c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (samplerate == 0) {
161c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "samplerate %u == 0\n", samplerate);
162c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
163c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
164c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            // ignore byte rate
165c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            // ignore block alignment
166c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            unsigned bitsPerSample = little2u(&fmt[14]);
167c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 32) {
168c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "bitsPerSample %u != 8 or 16 or 32\n", bitsPerSample);
169c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
170c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
171c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
172c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            handle->bytesPerFrame = bytesPerFrame;
173c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            handle->info.samplerate = samplerate;
174c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            handle->info.channels = channels;
175c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            switch (bitsPerSample) {
176c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            case 8:
177c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                handle->info.format |= SF_FORMAT_PCM_U8;
178c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                break;
179c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            case 16:
180c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                handle->info.format |= SF_FORMAT_PCM_16;
181c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                break;
182c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            case 32:
183c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                if (format == WAVE_FORMAT_IEEE_FLOAT)
184c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                    handle->info.format |= SF_FORMAT_FLOAT;
185c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                else
186c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                    handle->info.format |= SF_FORMAT_PCM_32;
187c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                break;
188c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
189c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            hadFmt = 1;
190c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        } else if (!memcmp(&chunk[0], "data", 4)) {
191c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (!hadFmt) {
192c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "data not preceded by fmt\n");
193c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
194c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
195c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (hadData) {
196c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "multiple data\n");
197c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                goto close;
198c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
199c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            handle->remaining = chunkSize / handle->bytesPerFrame;
200c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            handle->info.frames = handle->remaining;
201c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            hadData = 1;
202c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        } else if (!memcmp(&chunk[0], "fact", 4)) {
203c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            // ignore fact
204c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (chunkSize > 0) {
205c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fseek(stream, (long) chunkSize, SEEK_CUR);
206c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
207c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        } else {
208c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            // ignore unknown chunk
209c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            fprintf(stderr, "ignoring unknown chunk %c%c%c%c\n",
210c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                    chunk[0], chunk[1], chunk[2], chunk[3]);
211c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (chunkSize > 0) {
212c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fseek(stream, (long) chunkSize, SEEK_CUR);
213c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            }
214c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        }
215c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        remaining -= chunkSize;
216c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
217c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (remaining > 0) {
21896d859ca87493056f13b1b653f49ffe7bf9bcd2fKévin PETIT        fprintf(stderr, "partial chunk at end of RIFF, remaining %zu\n", remaining);
219c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        goto close;
220207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    }
221c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (!hadData) {
222c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "missing data\n");
223c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        goto close;
224c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
225c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    *info = handle->info;
226c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    return handle;
227c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
228c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kastenclose:
229c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    free(handle);
230c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    fclose(stream);
231207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    return NULL;
232207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
233207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
234eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kastenstatic void write4u(unsigned char *ptr, unsigned u)
235eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten{
236eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    ptr[0] = u;
237eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    ptr[1] = u >> 8;
238eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    ptr[2] = u >> 16;
239eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    ptr[3] = u >> 24;
240eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten}
241eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten
242eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kastenstatic SNDFILE *sf_open_write(const char *path, SF_INFO *info)
243eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten{
244c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    int sub = info->format & SF_FORMAT_SUBMASK;
245eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    if (!(
246eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            (info->samplerate > 0) &&
2477095bc708771377cd195d426ae23bc961bf9ac30Andy Hung            (info->channels > 0 && info->channels <= 8) &&
248eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
249c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            (sub == SF_FORMAT_PCM_16 || sub == SF_FORMAT_PCM_U8 || sub == SF_FORMAT_FLOAT)
250eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten          )) {
251eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return NULL;
252eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
253eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    FILE *stream = fopen(path, "w+b");
2541fa816b623b8d912d78742b4a447693c8eb383edAndy Hung    if (stream == NULL) {
2551fa816b623b8d912d78742b4a447693c8eb383edAndy Hung        fprintf(stderr, "fopen %s failed errno %d\n", path, errno);
2561fa816b623b8d912d78742b4a447693c8eb383edAndy Hung        return NULL;
2571fa816b623b8d912d78742b4a447693c8eb383edAndy Hung    }
258c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned char wav[58];
259eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    memset(wav, 0, sizeof(wav));
260eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    memcpy(wav, "RIFF", 4);
261eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    memcpy(&wav[8], "WAVEfmt ", 8);
262c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (sub == SF_FORMAT_FLOAT) {
263c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[4] = 50;    // riffSize
264c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[16] = 18;   // fmtSize
265c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[20] = WAVE_FORMAT_IEEE_FLOAT;
266c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    } else {
267c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[4] = 36;    // riffSize
268c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[16] = 16;   // fmtSize
269c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[20] = WAVE_FORMAT_PCM;
270c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
271eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    wav[22] = info->channels;
272eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    write4u(&wav[24], info->samplerate);
273c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned bitsPerSample;
274c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    switch (sub) {
275c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_16:
276c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 16;
277c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
278c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_U8:
279c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 8;
280c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
281c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_FLOAT:
282c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 32;
283c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
284c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:    // not reachable
285c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 0;
286c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
287c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
288eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
289eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    unsigned byteRate = info->samplerate * blockAlignment;
290eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    write4u(&wav[28], byteRate);
291eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    wav[32] = blockAlignment;
292eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    wav[34] = bitsPerSample;
293c74f4b7ace224c3435fd4a2f5127aa09f448d2a5Andy Hung    size_t extra = 0;
294c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (sub == SF_FORMAT_FLOAT) {
295c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy(&wav[38], "fact", 4);
296c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[42] = 4;
297c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy(&wav[50], "data", 4);
298c74f4b7ace224c3435fd4a2f5127aa09f448d2a5Andy Hung        extra = 14;
299c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    } else
300c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy(&wav[36], "data", 4);
301eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    // dataSize is initially zero
302c74f4b7ace224c3435fd4a2f5127aa09f448d2a5Andy Hung    (void) fwrite(wav, 44 + extra, 1, stream);
303eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
304eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->mode = SFM_WRITE;
305eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->temp = NULL;
306eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->stream = stream;
307eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->bytesPerFrame = blockAlignment;
308eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->remaining = 0;
309eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->info = *info;
310eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    return handle;
311eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten}
312eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten
313eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn KastenSNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
314eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten{
315c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (path == NULL || info == NULL) {
316c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "path=%p info=%p\n", path, info);
317eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return NULL;
318c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
319eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    switch (mode) {
320eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SFM_READ:
321eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return sf_open_read(path, info);
322eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SFM_WRITE:
323eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return sf_open_write(path, info);
324eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    default:
325c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "mode=%d\n", mode);
326eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return NULL;
327eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
328eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten}
329eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten
330207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kastenvoid sf_close(SNDFILE *handle)
331207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
332207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    if (handle == NULL)
333207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        return;
334eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    free(handle->temp);
335eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    if (handle->mode == SFM_WRITE) {
336eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        (void) fflush(handle->stream);
337eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        rewind(handle->stream);
338c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        unsigned char wav[58];
339c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0;
340c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        (void) fread(wav, 44 + extra, 1, handle->stream);
341eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        unsigned dataSize = handle->remaining * handle->bytesPerFrame;
342c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        write4u(&wav[4], dataSize + 36 + extra);    // riffSize
343c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        write4u(&wav[40 + extra], dataSize);        // dataSize
344eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        rewind(handle->stream);
345c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        (void) fwrite(wav, 44 + extra, 1, handle->stream);
346eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
347207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    (void) fclose(handle->stream);
348eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    free(handle);
349207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
350207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
35136c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kastensf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
352207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
353eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
354eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            desiredFrames <= 0) {
355207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        return 0;
356eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
357c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    if (handle->remaining < (size_t) desiredFrames) {
358207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        desiredFrames = handle->remaining;
359c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    }
360207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    // does not check for numeric overflow
361c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
362c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t actualBytes;
363c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    void *temp = NULL;
364c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
365c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT) {
366c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        temp = malloc(desiredBytes);
367c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
368c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    } else {
369c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
370c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
371207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    size_t actualFrames = actualBytes / handle->bytesPerFrame;
372207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    handle->remaining -= actualFrames;
373c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    switch (format) {
37436c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten    case SF_FORMAT_PCM_U8:
37536c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
37636c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        break;
37736c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten    case SF_FORMAT_PCM_16:
37836c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        if (!isLittleEndian())
3798fdafc906bc3abd9ce897c6c0e87598417bd4d58Glenn Kasten            my_swab(ptr, actualFrames * handle->info.channels);
38036c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        break;
381c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_32:
382c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels);
383c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        free(temp);
384c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
385c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_FLOAT:
386c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels);
387c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        free(temp);
388c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
389c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:
390c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
391c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
39236c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten    }
393207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    return actualFrames;
394207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
395eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten
396c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kastensf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
397c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten{
398c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
399c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten            desiredFrames <= 0) {
400c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        return 0;
401c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    }
402c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    if (handle->remaining < (size_t) desiredFrames) {
403c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        desiredFrames = handle->remaining;
404c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    }
405c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    // does not check for numeric overflow
406c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
407c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    size_t actualBytes;
40852370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung    void *temp = NULL;
409c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
41052370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung    if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
41152370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        temp = malloc(desiredBytes);
41252370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
41352370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung    } else {
41452370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
41552370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung    }
416c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    size_t actualFrames = actualBytes / handle->bytesPerFrame;
417c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    handle->remaining -= actualFrames;
418c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    switch (format) {
419c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    case SF_FORMAT_PCM_U8:
42052370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung#if 0
42152370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        // TODO - implement
42252370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        memcpy_to_float_from_u8(ptr, (const unsigned char *) temp,
423c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten                actualFrames * handle->info.channels);
424c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten#endif
42552370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        free(temp);
42652370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        break;
427c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    case SF_FORMAT_PCM_16:
42852370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        memcpy_to_float_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
42952370169f1de6905db69ef5a16fcf1ab9ae4b1abAndy Hung        free(temp);
430c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        break;
431c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    case SF_FORMAT_PCM_32:
432c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        memcpy_to_float_from_i32(ptr, (const int *) ptr, actualFrames * handle->info.channels);
433c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        break;
434c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    case SF_FORMAT_FLOAT:
435c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        break;
436c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    default:
437c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        memset(ptr, 0, actualFrames * handle->info.channels * sizeof(float));
438c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten        break;
439c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    }
440c89cb607706c98d64d5d1b1cae645353d0c8e8feGlenn Kasten    return actualFrames;
441c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten}
442c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
4437b9378104f3709d6c07ed00fe11066860a4e9999Andy Hungsf_count_t sf_readf_int(SNDFILE *handle, int *ptr, sf_count_t desiredFrames)
4447b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung{
4457b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
4467b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung            desiredFrames <= 0) {
4477b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        return 0;
4487b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    }
4497b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    if (handle->remaining < (size_t) desiredFrames) {
4507b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        desiredFrames = handle->remaining;
4517b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    }
4527b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    // does not check for numeric overflow
4537b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
4547b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    void *temp = NULL;
4557b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
4567b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    size_t actualBytes;
4577b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    if (format == SF_FORMAT_PCM_16 || format == SF_FORMAT_PCM_U8) {
4587b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        temp = malloc(desiredBytes);
4597b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
4607b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    } else {
4617b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
4627b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    }
4637b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    size_t actualFrames = actualBytes / handle->bytesPerFrame;
4647b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    handle->remaining -= actualFrames;
4657b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    switch (format) {
4667b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    case SF_FORMAT_PCM_U8:
4677b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung#if 0
4687b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        // TODO - implement
4697b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        memcpy_to_i32_from_u8(ptr, (const unsigned char *) temp,
4707b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung                actualFrames * handle->info.channels);
4717b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung#endif
4727b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        free(temp);
4737b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        break;
4747b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    case SF_FORMAT_PCM_16:
4757b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        memcpy_to_i32_from_i16(ptr, (const short *) temp, actualFrames * handle->info.channels);
4767b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        free(temp);
4777b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        break;
4787b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    case SF_FORMAT_PCM_32:
4797b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        break;
4807b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    case SF_FORMAT_FLOAT:
4817b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        memcpy_to_i32_from_float(ptr, (const float *) ptr, actualFrames * handle->info.channels);
4827b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        break;
4837b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    default:
4847b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        memset(ptr, 0, actualFrames * handle->info.channels * sizeof(int));
4857b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung        break;
4867b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    }
4877b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung    return actualFrames;
4887b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung}
4897b9378104f3709d6c07ed00fe11066860a4e9999Andy Hung
490eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kastensf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
491eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten{
492eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
493eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return 0;
494eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
495eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    size_t actualBytes = 0;
496eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    switch (handle->info.format & SF_FORMAT_SUBMASK) {
497eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SF_FORMAT_PCM_U8:
498eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        handle->temp = realloc(handle->temp, desiredBytes);
499eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
500eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
501eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        break;
502eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SF_FORMAT_PCM_16:
503eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        // does not check for numeric overflow
504eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        if (isLittleEndian()) {
505eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
506eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        } else {
507eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            handle->temp = realloc(handle->temp, desiredBytes);
508eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            memcpy(handle->temp, ptr, desiredBytes);
5098fdafc906bc3abd9ce897c6c0e87598417bd4d58Glenn Kasten            my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
510eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
511eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        }
512eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        break;
513e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung    case SF_FORMAT_FLOAT:
514e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung        handle->temp = realloc(handle->temp, desiredBytes);
5151fa816b623b8d912d78742b4a447693c8eb383edAndy Hung        memcpy_to_float_from_i16((float *) handle->temp, ptr,
5161fa816b623b8d912d78742b4a447693c8eb383edAndy Hung                desiredFrames * handle->info.channels);
517e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung        actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
518e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung        break;
519c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:
520c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
521c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
522c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t actualFrames = actualBytes / handle->bytesPerFrame;
523c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    handle->remaining += actualFrames;
524c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    return actualFrames;
525c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten}
526c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
527c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kastensf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
528c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten{
529c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
530c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        return 0;
531c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
532c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t actualBytes = 0;
533c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    switch (handle->info.format & SF_FORMAT_SUBMASK) {
534c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_FLOAT:
535c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
536c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
537c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_16:
538e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung        handle->temp = realloc(handle->temp, desiredBytes);
5391fa816b623b8d912d78742b4a447693c8eb383edAndy Hung        memcpy_to_i16_from_float((short *) handle->temp, ptr,
5401fa816b623b8d912d78742b4a447693c8eb383edAndy Hung                desiredFrames * handle->info.channels);
541e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung        actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
542e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung        break;
543e9b333c8b17dea46125ac26f0a388f62b43a8931Andy Hung    case SF_FORMAT_PCM_U8:  // transcoding from float to byte not yet implemented
544c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:
545c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
546eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
547eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    size_t actualFrames = actualBytes / handle->bytesPerFrame;
548eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->remaining += actualFrames;
549eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    return actualFrames;
550eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten}
551