blob: 2b58d16bb3a111bc54758a02429e8401dc6b6e7c [file] [log] [blame]
Glenn Kasten207ec292012-10-30 16:09:18 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <audio_utils/sndfile.h>
Glenn Kasten36c248b2012-11-12 14:42:31 -080018#include <audio_utils/primitives.h>
Glenn Kasten207ec292012-10-30 16:09:18 -070019#include <stdio.h>
20#include <string.h>
21
22struct SNDFILE_ {
Glenn Kasteneae13dd2013-01-04 11:24:55 -080023 int mode;
24 uint8_t *temp; // realloc buffer used for shrinking 16 bits to 8 bits and byte-swapping
Glenn Kasten207ec292012-10-30 16:09:18 -070025 FILE *stream;
26 size_t bytesPerFrame;
Glenn Kasteneae13dd2013-01-04 11:24:55 -080027 size_t remaining; // frames unread for SFM_READ, frames written for SFM_WRITE
Glenn Kasten207ec292012-10-30 16:09:18 -070028 SF_INFO info;
29};
30
31static unsigned little2u(unsigned char *ptr)
32{
33 return (ptr[1] << 8) + ptr[0];
34}
35
36static unsigned little4u(unsigned char *ptr)
37{
38 return (ptr[3] << 24) + (ptr[2] << 16) + (ptr[1] << 8) + ptr[0];
39}
40
41static int isLittleEndian(void)
42{
43 static const short one = 1;
44 return *((const char *) &one) == 1;
45}
46
Glenn Kasten8fdafc92013-02-12 16:23:42 -080047// "swab" conflicts with OS X <string.h>
48static void my_swab(short *ptr, size_t numToSwap)
Glenn Kasten207ec292012-10-30 16:09:18 -070049{
50 while (numToSwap > 0) {
51 *ptr = little2u((unsigned char *) ptr);
52 --numToSwap;
53 ++ptr;
54 }
55}
56
Glenn Kasteneae13dd2013-01-04 11:24:55 -080057static SNDFILE *sf_open_read(const char *path, SF_INFO *info)
Glenn Kasten207ec292012-10-30 16:09:18 -070058{
Glenn Kasten207ec292012-10-30 16:09:18 -070059 FILE *stream = fopen(path, "rb");
60 if (stream == NULL)
61 return NULL;
62 // don't attempt to parse all valid forms, just the most common one
63 unsigned char wav[44];
64 size_t actual;
65 actual = fread(wav, sizeof(char), sizeof(wav), stream);
66 if (actual != sizeof(wav))
67 return NULL;
68 for (;;) {
69 if (memcmp(wav, "RIFF", 4))
70 break;
71 unsigned riffSize = little4u(&wav[4]);
Glenn Kasteneae13dd2013-01-04 11:24:55 -080072 if (riffSize < 36)
Glenn Kasten207ec292012-10-30 16:09:18 -070073 break;
74 if (memcmp(&wav[8], "WAVEfmt ", 8))
75 break;
76 unsigned fmtsize = little4u(&wav[16]);
77 if (fmtsize != 16)
78 break;
79 unsigned format = little2u(&wav[20]);
80 if (format != 1) // PCM
81 break;
82 unsigned channels = little2u(&wav[22]);
83 if (channels != 1 && channels != 2)
84 break;
85 unsigned samplerate = little4u(&wav[24]);
86 if (samplerate == 0)
87 break;
88 // ignore byte rate
89 // ignore block alignment
90 unsigned bitsPerSample = little2u(&wav[34]);
Glenn Kasten36c248b2012-11-12 14:42:31 -080091 if (bitsPerSample != 8 && bitsPerSample != 16)
Glenn Kasten207ec292012-10-30 16:09:18 -070092 break;
93 unsigned bytesPerFrame = (bitsPerSample >> 3) * channels;
94 if (memcmp(&wav[36], "data", 4))
95 break;
96 unsigned dataSize = little4u(&wav[40]);
97 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
Glenn Kasteneae13dd2013-01-04 11:24:55 -080098 handle->mode = SFM_READ;
99 handle->temp = NULL;
Glenn Kasten207ec292012-10-30 16:09:18 -0700100 handle->stream = stream;
101 handle->bytesPerFrame = bytesPerFrame;
102 handle->remaining = dataSize / bytesPerFrame;
Glenn Kasten36c248b2012-11-12 14:42:31 -0800103 handle->info.frames = handle->remaining;
Glenn Kasten207ec292012-10-30 16:09:18 -0700104 handle->info.samplerate = samplerate;
105 handle->info.channels = channels;
Glenn Kasten36c248b2012-11-12 14:42:31 -0800106 handle->info.format = SF_FORMAT_WAV;
107 if (bitsPerSample == 8)
108 handle->info.format |= SF_FORMAT_PCM_U8;
109 else
110 handle->info.format |= SF_FORMAT_PCM_16;
Glenn Kasten207ec292012-10-30 16:09:18 -0700111 *info = handle->info;
112 return handle;
113 }
114 return NULL;
115}
116
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800117static void write4u(unsigned char *ptr, unsigned u)
118{
119 ptr[0] = u;
120 ptr[1] = u >> 8;
121 ptr[2] = u >> 16;
122 ptr[3] = u >> 24;
123}
124
125static SNDFILE *sf_open_write(const char *path, SF_INFO *info)
126{
127 if (!(
128 (info->samplerate > 0) &&
129 (info->channels == 1 || info->channels == 2) &&
130 ((info->format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) &&
131 ((info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_16 ||
132 (info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_U8)
133 )) {
134 return NULL;
135 }
136 FILE *stream = fopen(path, "w+b");
137 unsigned char wav[44];
138 memset(wav, 0, sizeof(wav));
139 memcpy(wav, "RIFF", 4);
140 wav[4] = 36; // riffSize
141 memcpy(&wav[8], "WAVEfmt ", 8);
142 wav[16] = 16; // fmtsize
143 wav[20] = 1; // format = PCM
144 wav[22] = info->channels;
145 write4u(&wav[24], info->samplerate);
146 unsigned bitsPerSample = (info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_16 ? 16 : 8;
147 unsigned blockAlignment = (bitsPerSample >> 3) * info->channels;
148 unsigned byteRate = info->samplerate * blockAlignment;
149 write4u(&wav[28], byteRate);
150 wav[32] = blockAlignment;
151 wav[34] = bitsPerSample;
152 memcpy(&wav[36], "data", 4);
153 // dataSize is initially zero
154 (void) fwrite(wav, sizeof(wav), 1, stream);
155 SNDFILE *handle = (SNDFILE *) malloc(sizeof(SNDFILE));
156 handle->mode = SFM_WRITE;
157 handle->temp = NULL;
158 handle->stream = stream;
159 handle->bytesPerFrame = blockAlignment;
160 handle->remaining = 0;
161 handle->info = *info;
162 return handle;
163}
164
165SNDFILE *sf_open(const char *path, int mode, SF_INFO *info)
166{
167 if (path == NULL || info == NULL)
168 return NULL;
169 switch (mode) {
170 case SFM_READ:
171 return sf_open_read(path, info);
172 case SFM_WRITE:
173 return sf_open_write(path, info);
174 default:
175 return NULL;
176 }
177}
178
Glenn Kasten207ec292012-10-30 16:09:18 -0700179void sf_close(SNDFILE *handle)
180{
181 if (handle == NULL)
182 return;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800183 free(handle->temp);
184 if (handle->mode == SFM_WRITE) {
185 (void) fflush(handle->stream);
186 rewind(handle->stream);
187 unsigned char wav[44];
188 (void) fread(wav, sizeof(wav), 1, handle->stream);
189 unsigned dataSize = handle->remaining * handle->bytesPerFrame;
190 write4u(&wav[4], dataSize + 36); // riffSize
191 write4u(&wav[40], dataSize); // dataSize
192 rewind(handle->stream);
193 (void) fwrite(wav, sizeof(wav), 1, handle->stream);
194 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700195 (void) fclose(handle->stream);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800196 free(handle);
Glenn Kasten207ec292012-10-30 16:09:18 -0700197}
198
Glenn Kasten36c248b2012-11-12 14:42:31 -0800199sf_count_t sf_readf_short(SNDFILE *handle, short *ptr, sf_count_t desiredFrames)
Glenn Kasten207ec292012-10-30 16:09:18 -0700200{
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800201 if (handle == NULL || handle->mode != SFM_READ || ptr == NULL || !handle->remaining ||
202 desiredFrames <= 0) {
Glenn Kasten207ec292012-10-30 16:09:18 -0700203 return 0;
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800204 }
Glenn Kasten36c248b2012-11-12 14:42:31 -0800205 if (handle->remaining < (size_t) desiredFrames)
Glenn Kasten207ec292012-10-30 16:09:18 -0700206 desiredFrames = handle->remaining;
207 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
208 // does not check for numeric overflow
209 size_t actualBytes = fread(ptr, sizeof(char), desiredBytes, handle->stream);
210 size_t actualFrames = actualBytes / handle->bytesPerFrame;
211 handle->remaining -= actualFrames;
Glenn Kasten36c248b2012-11-12 14:42:31 -0800212 switch (handle->info.format & SF_FORMAT_SUBMASK) {
213 case SF_FORMAT_PCM_U8:
214 memcpy_to_i16_from_u8(ptr, (unsigned char *) ptr, actualFrames * handle->info.channels);
215 break;
216 case SF_FORMAT_PCM_16:
217 if (!isLittleEndian())
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800218 my_swab(ptr, actualFrames * handle->info.channels);
Glenn Kasten36c248b2012-11-12 14:42:31 -0800219 break;
220 }
Glenn Kasten207ec292012-10-30 16:09:18 -0700221 return actualFrames;
222}
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800223
224sf_count_t sf_writef_short(SNDFILE *handle, const short *ptr, sf_count_t desiredFrames)
225{
226 if (handle == NULL || handle->mode != SFM_WRITE || ptr == NULL || desiredFrames <= 0)
227 return 0;
228 size_t desiredBytes = desiredFrames * handle->bytesPerFrame;
229 size_t actualBytes = 0;
230 switch (handle->info.format & SF_FORMAT_SUBMASK) {
231 case SF_FORMAT_PCM_U8:
232 handle->temp = realloc(handle->temp, desiredBytes);
233 memcpy_to_u8_from_i16(handle->temp, ptr, desiredBytes);
234 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
235 break;
236 case SF_FORMAT_PCM_16:
237 // does not check for numeric overflow
238 if (isLittleEndian()) {
239 actualBytes = fwrite(ptr, sizeof(char), desiredBytes, handle->stream);
240 } else {
241 handle->temp = realloc(handle->temp, desiredBytes);
242 memcpy(handle->temp, ptr, desiredBytes);
Glenn Kasten8fdafc92013-02-12 16:23:42 -0800243 my_swab((short *) handle->temp, desiredFrames * handle->info.channels);
Glenn Kasteneae13dd2013-01-04 11:24:55 -0800244 actualBytes = fwrite(handle->temp, sizeof(char), desiredBytes, handle->stream);
245 }
246 break;
247 }
248 size_t actualFrames = actualBytes / handle->bytesPerFrame;
249 handle->remaining += actualFrames;
250 return actualFrames;
251}