source: src/io/sink_wavwrite.c @ 695e171

feature/autosinkfeature/cnnfeature/cnn_orgfeature/constantqfeature/crepefeature/crepe_orgfeature/pitchshiftfeature/pydocstringsfeature/timestretchfix/ffmpeg5sampler
Last change on this file since 695e171 was 4d6024f, checked in by Paul Brossier <piem@piem.org>, 10 years ago

src/io/sink_wavwrite.c: fail if samplerate is way too large

  • Property mode set to 100644
File size: 8.0 KB
Line 
1/*
2  Copyright (C) 2014 Paul Brossier <piem@aubio.org>
3
4  This file is part of aubio.
5
6  aubio is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10
11  aubio is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with aubio.  If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21
22#include "config.h"
23
24#ifdef HAVE_WAVWRITE
25
26#include "aubio_priv.h"
27#include "fvec.h"
28#include "fmat.h"
29#include "io/sink_wavwrite.h"
30
31#include <errno.h>
32
33#define MAX_CHANNELS 6
34#define MAX_SIZE 4096
35
36#define FLOAT_TO_SHORT(x) (short)(x * 32768)
37
38// swap endian of a short
39#define SWAPS(x) ((x & 0xff) << 8) | ((x & 0xff00) >> 8)
40
41// swap host to little endian
42#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
43#define HTOLES(x) SWAPS(x)
44#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
45#define HTOLES(x) x
46#else
47#ifdef HAVE_WIN_HACKS
48#define HTOLES(x) x
49#else
50#define HTOLES(x) SWAPS(htons(x))
51#endif
52#endif
53
54uint_t aubio_sink_wavwrite_open(aubio_sink_wavwrite_t *s);
55
56struct _aubio_sink_wavwrite_t {
57  char_t *path;
58  uint_t samplerate;
59  uint_t channels;
60  uint_t bitspersample;
61  uint_t total_frames_written;
62
63  FILE *fid;
64
65  uint_t max_size;
66
67  uint_t scratch_size;
68  unsigned short *scratch_data;
69};
70
71unsigned char *write_little_endian (unsigned int s, unsigned char *str, unsigned int length);
72unsigned char *write_little_endian (unsigned int s, unsigned char *str, unsigned int length) {
73  uint_t i;
74  for (i = 0; i < length; i++) {
75    str[i] = s >> (i * 8);
76  }
77  return str;
78}
79
80aubio_sink_wavwrite_t * new_aubio_sink_wavwrite(char_t * path, uint_t samplerate) {
81  aubio_sink_wavwrite_t * s = AUBIO_NEW(aubio_sink_wavwrite_t);
82
83  if (path == NULL) {
84    AUBIO_ERR("sink_wavwrite: Aborted opening null path\n");
85    goto beach;
86  }
87  if ((sint_t)samplerate < 0) {
88    AUBIO_ERR("sink_wavwrite: Can not create %s with samplerate %d\n", path, samplerate);
89    goto beach;
90  }
91
92  s->path = path;
93  s->max_size = MAX_SIZE;
94  s->bitspersample = 16;
95  s->total_frames_written = 0;
96
97  s->samplerate = 0;
98  s->channels = 0;
99
100  // negative samplerate given, abort
101  if ((sint_t)samplerate < 0) goto beach;
102  // zero samplerate given. do not open yet
103  if ((sint_t)samplerate == 0) return s;
104  // samplerate way too large, fail
105  if ((sint_t)samplerate > 192000 * 4) goto beach;
106
107  s->samplerate = samplerate;
108  s->channels = 1;
109
110  if (aubio_sink_wavwrite_open(s) != AUBIO_OK) {
111    // open failed, abort
112    goto beach;
113  }
114
115  return s;
116beach:
117  //AUBIO_ERR("sink_wavwrite: failed creating %s with samplerate %dHz\n",
118  //    s->path, s->samplerate);
119  del_aubio_sink_wavwrite(s);
120  return NULL;
121}
122
123uint_t aubio_sink_wavwrite_preset_samplerate(aubio_sink_wavwrite_t *s, uint_t samplerate)
124{
125  if ((sint_t)(samplerate) <= 0) return AUBIO_FAIL;
126  s->samplerate = samplerate;
127  // automatically open when both samplerate and channels have been set
128  if (s->samplerate != 0 && s->channels != 0) {
129    return aubio_sink_wavwrite_open(s);
130  }
131  return AUBIO_OK;
132}
133
134uint_t aubio_sink_wavwrite_preset_channels(aubio_sink_wavwrite_t *s, uint_t channels)
135{
136  if ((sint_t)(channels) <= 0) return AUBIO_FAIL;
137  s->channels = channels;
138  // automatically open when both samplerate and channels have been set
139  if (s->samplerate != 0 && s->channels != 0) {
140    return aubio_sink_wavwrite_open(s);
141  }
142  return AUBIO_OK;
143}
144
145uint_t aubio_sink_wavwrite_get_samplerate(aubio_sink_wavwrite_t *s)
146{
147  return s->samplerate;
148}
149
150uint_t aubio_sink_wavwrite_get_channels(aubio_sink_wavwrite_t *s)
151{
152  return s->channels;
153}
154
155uint_t aubio_sink_wavwrite_open(aubio_sink_wavwrite_t *s) {
156  unsigned char buf[5];
157  uint_t byterate, blockalign;
158
159  /* open output file */
160  s->fid = fopen((const char *)s->path, "wb");
161  if (!s->fid) {
162    AUBIO_ERR("sink_wavwrite: could not open %s (%s)\n", s->path, strerror(errno));
163    goto beach;
164  }
165
166  // ChunkID
167  fwrite("RIFF", 4, 1, s->fid);
168
169  // ChunkSize (0 for now, actual size will be written in _close)
170  fwrite(write_little_endian(0, buf, 4), 4, 1, s->fid);
171
172  // Format
173  fwrite("WAVE", 4, 1, s->fid);
174
175  // Subchunk1ID
176  fwrite("fmt ", 4, 1, s->fid);
177
178  // Subchunk1Size
179  fwrite(write_little_endian(16, buf, 4), 4, 1, s->fid);
180
181  // AudioFormat
182  fwrite(write_little_endian(1, buf, 2), 2, 1, s->fid);
183
184  // NumChannels
185  fwrite(write_little_endian(s->channels, buf, 2), 2, 1, s->fid);
186
187  // SampleRate
188  fwrite(write_little_endian(s->samplerate, buf, 4), 4, 1, s->fid);
189
190  // ByteRate
191  byterate = s->samplerate * s->channels * s->bitspersample / 8;
192  fwrite(write_little_endian(byterate, buf, 4), 4, 1, s->fid);
193
194  // BlockAlign
195  blockalign = s->channels * s->bitspersample / 8;
196  fwrite(write_little_endian(blockalign, buf, 2), 2, 1, s->fid);
197
198  // BitsPerSample
199  fwrite(write_little_endian(s->bitspersample, buf, 2), 2, 1, s->fid);
200
201  // Subchunk2ID
202  fwrite("data", 4, 1, s->fid);
203
204  // Subchunk1Size (0 for now, actual size will be written in _close)
205  fwrite(write_little_endian(0, buf, 4), 4, 1, s->fid);
206
207  s->scratch_size = s->max_size * s->channels;
208  /* allocate data for de/interleaving reallocated when needed. */
209  if (s->scratch_size >= MAX_SIZE * MAX_CHANNELS) {
210    AUBIO_ERR("sink_wavwrite: %d x %d exceeds SIZE maximum buffer size %d\n",
211        s->max_size, s->channels, MAX_SIZE * MAX_CHANNELS);
212    goto beach;
213  }
214  s->scratch_data = AUBIO_ARRAY(unsigned short,s->scratch_size);
215
216  return AUBIO_OK;
217
218beach:
219  return AUBIO_FAIL;
220}
221
222
223void aubio_sink_wavwrite_do(aubio_sink_wavwrite_t *s, fvec_t * write_data, uint_t write){
224  uint_t i = 0, written_frames = 0;
225
226  if (write > s->max_size) {
227    AUBIO_WRN("sink_wavwrite: trying to write %d frames to %s, "
228        "but only %d can be written at a time\n", write, s->path, s->max_size);
229    write = s->max_size;
230  }
231
232  for (i = 0; i < write; i++) {
233    s->scratch_data[i] = HTOLES(FLOAT_TO_SHORT(write_data->data[i]));
234  }
235  written_frames = fwrite(s->scratch_data, 2, write, s->fid);
236
237  if (written_frames != write) {
238    AUBIO_WRN("sink_wavwrite: trying to write %d frames to %s, "
239        "but only %d could be written\n", write, s->path, written_frames);
240  }
241  s->total_frames_written += written_frames;
242  return;
243}
244
245void aubio_sink_wavwrite_do_multi(aubio_sink_wavwrite_t *s, fmat_t * write_data, uint_t write){
246  uint_t c = 0, i = 0, written_frames = 0;
247
248  if (write > s->max_size) {
249    AUBIO_WRN("sink_wavwrite: trying to write %d frames to %s, "
250        "but only %d can be written at a time\n", write, s->path, s->max_size);
251    write = s->max_size;
252  }
253
254  for (c = 0; c < s->channels; c++) {
255    for (i = 0; i < write; i++) {
256      s->scratch_data[i * s->channels + c] = HTOLES(FLOAT_TO_SHORT(write_data->data[c][i]));
257    }
258  }
259  written_frames = fwrite(s->scratch_data, 2, write * s->channels, s->fid);
260
261  if (written_frames != write * s->channels) {
262    AUBIO_WRN("sink_wavwrite: trying to write %d frames to %s, "
263        "but only %d could be written\n", write, s->path, written_frames / s->channels);
264  }
265  s->total_frames_written += written_frames;
266  return;
267}
268
269uint_t aubio_sink_wavwrite_close(aubio_sink_wavwrite_t * s) {
270  uint_t data_size = s->total_frames_written * s->bitspersample * s->channels / 8;
271  unsigned char buf[5];
272  if (!s->fid) return AUBIO_FAIL;
273  // ChunkSize
274  fseek(s->fid, 4, SEEK_SET);
275  fwrite(write_little_endian(data_size + 36, buf, 4), 4, 1, s->fid);
276  // Subchunk2Size
277  fseek(s->fid, 40, SEEK_SET);
278  fwrite(write_little_endian(data_size, buf, 4), 4, 1, s->fid);
279  // close file
280  if (fclose(s->fid)) {
281    AUBIO_ERR("sink_wavwrite: Error closing file %s (%s)\n", s->path, strerror(errno));
282  }
283  s->fid = NULL;
284  return AUBIO_OK;
285}
286
287void del_aubio_sink_wavwrite(aubio_sink_wavwrite_t * s){
288  if (!s) return;
289  aubio_sink_wavwrite_close(s);
290  AUBIO_FREE(s->scratch_data);
291  AUBIO_FREE(s);
292}
293
294#endif /* HAVE_WAVWRITE */
Note: See TracBrowser for help on using the repository browser.