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) {
81c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "actual %u < 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)) {
104c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            fprintf(stderr, "actual %u != %u\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) {
110c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            fprintf(stderr, "chunkSize %u > remaining %u\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) {
125c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "actual %u != 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) {
143c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "chunkSize %u < minSize %u\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) {
148c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "actual %u != %u\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]);
155c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten            if (channels != 1 && channels != 2) {
156c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten                fprintf(stderr, "channels %u != 1 or 2\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) {
218c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "partial chunk at end of RIFF, remaining %u\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) &&
247eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            (info->channels == 1 || info->channels == 2) &&
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");
254c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned char wav[58];
255eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    memset(wav, 0, sizeof(wav));
256eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    memcpy(wav, "RIFF", 4);
257eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    memcpy(&wav[8], "WAVEfmt ", 8);
258c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (sub == SF_FORMAT_FLOAT) {
259c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[4] = 50;    // riffSize
260c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[16] = 18;   // fmtSize
261c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[20] = WAVE_FORMAT_IEEE_FLOAT;
262c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    } else {
263c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[4] = 36;    // riffSize
264c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[16] = 16;   // fmtSize
265c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[20] = WAVE_FORMAT_PCM;
266c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
267eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    wav[22] = info->channels;
268eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    write4u(&wav[24], info->samplerate);
269c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned bitsPerSample;
270c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    switch (sub) {
271c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_16:
272c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 16;
273c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
274c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_U8:
275c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 8;
276c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
277c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_FLOAT:
278c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 32;
279c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
280c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:    // not reachable
281c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        bitsPerSample = 0;
282c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
283c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
284eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
285eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    unsigned byteRate = info->samplerate * blockAlignment;
286eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    write4u(&wav[28], byteRate);
287eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    wav[32] = blockAlignment;
288eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    wav[34] = bitsPerSample;
289c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (sub == SF_FORMAT_FLOAT) {
290c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy(&wav[38], "fact", 4);
291c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        wav[42] = 4;
292c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy(&wav[50], "data", 4);
293c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    } else
294c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy(&wav[36], "data", 4);
295eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    // dataSize is initially zero
296eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    (void) fwrite(wav, sizeof(wav), 1, stream);
297eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
298eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->mode = SFM_WRITE;
299eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->temp = NULL;
300eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->stream = stream;
301eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->bytesPerFrame = blockAlignment;
302eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->remaining = 0;
303eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->info = *info;
304eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    return handle;
305eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten}
306eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten
307eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn KastenSNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
308eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten{
309c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (path == NULL || info == NULL) {
310c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "path=%p info=%p\n", path, info);
311eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return NULL;
312c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
313eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    switch (mode) {
314eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SFM_READ:
315eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return sf_open_read(path, info);
316eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SFM_WRITE:
317eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return sf_open_write(path, info);
318eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    default:
319c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        fprintf(stderr, "mode=%d\n", mode);
320eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return NULL;
321eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
322eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten}
323eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten
324207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kastenvoid sf_close(SNDFILE *handle)
325207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
326207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    if (handle == NULL)
327207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        return;
328eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    free(handle->temp);
329eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    if (handle->mode == SFM_WRITE) {
330eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        (void) fflush(handle->stream);
331eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        rewind(handle->stream);
332c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        unsigned char wav[58];
333c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        size_t extra = (handle->info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT ? 14 : 0;
334c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        (void) fread(wav, 44 + extra, 1, handle->stream);
335eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        unsigned dataSize = handle->remaining * handle->bytesPerFrame;
336c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        write4u(&wav[4], dataSize + 36 + extra);    // riffSize
337c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        write4u(&wav[40 + extra], dataSize);        // dataSize
338eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        rewind(handle->stream);
339c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        (void) fwrite(wav, 44 + extra, 1, handle->stream);
340eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
341207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    (void) fclose(handle->stream);
342eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    free(handle);
343207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
344207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten
34536c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kastensf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
346207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten{
347eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
348eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            desiredFrames <= 0) {
349207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        return 0;
350eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
35136c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten    if (handle->remaining < (size_t) desiredFrames)
352207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten        desiredFrames = handle->remaining;
353207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    // does not check for numeric overflow
354c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
355c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t actualBytes;
356c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    void *temp = NULL;
357c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    unsigned format = handle->info.format & SF_FORMAT_SUBMASK;
358c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (format == SF_FORMAT_PCM_32 || format == SF_FORMAT_FLOAT) {
359c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        temp = malloc(desiredBytes);
360c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        actualBytes = fread(temp, sizeof(char), desiredBytes, handle->stream);
361c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    } else {
362c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
363c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
364207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    size_t actualFrames = actualBytes / handle->bytesPerFrame;
365207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    handle->remaining -= actualFrames;
366c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    switch (format) {
36736c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten    case SF_FORMAT_PCM_U8:
36836c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
36936c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        break;
37036c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten    case SF_FORMAT_PCM_16:
37136c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        if (!isLittleEndian())
3728fdafc906bc3abd9ce897c6c0e87598417bd4d58Glenn Kasten            my_swab(ptr, actualFrames * handle->info.channels);
37336c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten        break;
374c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_32:
375c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy_to_i16_from_i32(ptr, (const int *) temp, actualFrames * handle->info.channels);
376c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        free(temp);
377c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
378c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_FLOAT:
379c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memcpy_to_i16_from_float(ptr, (const float *) temp, actualFrames * handle->info.channels);
380c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        free(temp);
381c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
382c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:
383c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        memset(ptr, 0, actualFrames * handle->info.channels * sizeof(short));
384c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
38536c248b94fd41c0f0bb54106ce8e2b0ad67c40eeGlenn Kasten    }
386207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten    return actualFrames;
387207ec2930dd429e5d4aa58e2b9899e09aa76e22cGlenn Kasten}
388eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten
389c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kastensf_count_t sf_readf_float(SNDFILE *handle, float *ptr, sf_count_t desiredFrames)
390c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten{
391c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    return 0;
392c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten}
393c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
394eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kastensf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
395eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten{
396eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
397eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        return 0;
398eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
399eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    size_t actualBytes = 0;
400eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    switch (handle->info.format & SF_FORMAT_SUBMASK) {
401eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SF_FORMAT_PCM_U8:
402eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        handle->temp = realloc(handle->temp, desiredBytes);
403eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
404eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
405eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        break;
406eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    case SF_FORMAT_PCM_16:
407eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        // does not check for numeric overflow
408eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        if (isLittleEndian()) {
409eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
410eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        } else {
411eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            handle->temp = realloc(handle->temp, desiredBytes);
412eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            memcpy(handle->temp, ptr, desiredBytes);
4138fdafc906bc3abd9ce897c6c0e87598417bd4d58Glenn Kasten            my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
414eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten            actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
415eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        }
416eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten        break;
417c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_FLOAT:   // transcoding from short to float not yet implemented
418c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:
419c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
420c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    }
421c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t actualFrames = actualBytes / handle->bytesPerFrame;
422c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    handle->remaining += actualFrames;
423c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    return actualFrames;
424c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten}
425c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten
426c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kastensf_count_t sf_writef_float(SNDFILE *handle, const float *ptr, sf_count_t desiredFrames)
427c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten{
428c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
429c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        return 0;
430c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
431c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    size_t actualBytes = 0;
432c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    switch (handle->info.format & SF_FORMAT_SUBMASK) {
433c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_FLOAT:
434c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
435c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
436c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_U8:  // transcoding from float to byte/short not yet implemented
437c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    case SF_FORMAT_PCM_16:
438c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten    default:
439c0bd7157d2d5967a6b222a5d69b7703612f78a49Glenn Kasten        break;
440eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    }
441eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    size_t actualFrames = actualBytes / handle->bytesPerFrame;
442eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    handle->remaining += actualFrames;
443eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten    return actualFrames;
444eae13ddb9a4fdedf3895a10cdf5c0a4aefe0ff60Glenn Kasten}
445