source: src/io/sink_flac.c @ 9630fa8

feature/autosinkfeature/cnnfeature/crepefix/ffmpeg5
Last change on this file since 9630fa8 was 9630fa8, checked in by Paul Brossier <piem@piem.org>, 5 years ago

[sink_flac] check for FLACstream_encoder_process_interleaved success, factorise

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/*
2  Copyright (C) 2018 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  This file is largely inspired by `examples/c/encode/file/main.c` in the
23  flac source package (versions 1.3.2 and later) available online at
24  https://xiph.org/flac/
25*/
26
27#include "aubio_priv.h"
28
29#ifdef HAVE_FLAC
30
31#include "io/ioutils.h"
32#include "fmat.h"
33
34#include <FLAC/metadata.h>
35#include <FLAC/stream_encoder.h>
36
37#include <string.h> // strerror
38#include <errno.h> // errno
39
40#define MAX_WRITE_SIZE 4096
41
42// swap host to little endian
43#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
44#define HTOLES(x) SWAPS(x)
45#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
46#define HTOLES(x) x
47#else
48#ifdef HAVE_WIN_HACKS
49#define HTOLES(x) x
50#else
51#define HTOLES(x) SWAPS(htons(x))
52#endif
53#endif
54
55// convert to short, taking care of endianness
56#define FLOAT_TO_SHORT(x) (HTOLES((FLAC__int32)(x * 32768)))
57
58struct _aubio_sink_flac_t {
59  uint_t samplerate;
60  uint_t channels;
61  char_t *path;
62
63  FILE *fid;            // file id
64  FLAC__StreamEncoder* encoder;
65  FLAC__int32 *buffer;
66  FLAC__StreamMetadata **metadata;
67};
68
69typedef struct _aubio_sink_flac_t aubio_sink_flac_t;
70
71uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
72    uint_t channels);
73uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
74    uint_t samplerate);
75uint_t aubio_sink_flac_open(aubio_sink_flac_t *s);
76uint_t aubio_sink_flac_close (aubio_sink_flac_t *s);
77void del_aubio_sink_flac (aubio_sink_flac_t *s);
78
79#if 0
80static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder,
81    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
82    unsigned frames_writtten, unsigned total_frames_estimate,
83    void *client_data);
84#endif
85
86aubio_sink_flac_t * new_aubio_sink_flac (const char_t *uri,
87    uint_t samplerate)
88{
89  aubio_sink_flac_t * s = AUBIO_NEW(aubio_sink_flac_t);
90
91  if (!uri) {
92    AUBIO_ERROR("sink_flac: Aborted opening null path\n");
93    goto failure;
94  }
95
96  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
97  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
98  s->path[strnlen(uri, PATH_MAX)] = '\0';
99
100  s->channels = 0;
101  s->samplerate = 0;
102
103  if ((sint_t)samplerate == 0)
104    return s;
105
106  aubio_sink_flac_preset_samplerate(s, samplerate);
107  s->channels = 1;
108
109  if (aubio_sink_flac_open(s) != AUBIO_OK)
110    goto failure;
111
112  return s;
113
114failure:
115  del_aubio_sink_flac(s);
116  return NULL;
117}
118
119void del_aubio_sink_flac (aubio_sink_flac_t *s)
120{
121  if (s->fid)
122    aubio_sink_flac_close(s);
123  if (s->buffer)
124    AUBIO_FREE(s->buffer);
125  if (s->path)
126    AUBIO_FREE(s->path);
127  AUBIO_FREE(s);
128}
129
130uint_t aubio_sink_flac_open(aubio_sink_flac_t *s)
131{
132  uint_t ret = AUBIO_FAIL;
133  FLAC__bool ok = true;
134  FLAC__StreamEncoderInitStatus init_status;
135  const unsigned comp_level = 5;
136  const unsigned bps = 16;
137
138  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
139
140  s->buffer = AUBIO_ARRAY(FLAC__int32, s->channels * MAX_WRITE_SIZE);
141  if (!s->buffer) {
142    AUBIO_ERR("sink_flac: failed allocating buffer for %s\n", s->path);
143    return AUBIO_FAIL;
144  }
145
146  s->fid = fopen((const char *)s->path, "wb");
147  if (!s->fid) {
148    AUBIO_ERR("sink_flac: failed opening %s, %s\n", s->path, strerror(errno));
149    return AUBIO_FAIL;
150  }
151
152  if((s->encoder = FLAC__stream_encoder_new()) == NULL) {
153    AUBIO_ERR("sink_flac: failed allocating encoder for %s\n", s->path);
154    goto failure;
155  }
156  ok &= FLAC__stream_encoder_set_verify(s->encoder, true);
157  ok &= FLAC__stream_encoder_set_compression_level(s->encoder, comp_level);
158  ok &= FLAC__stream_encoder_set_channels(s->encoder, s->channels);
159  ok &= FLAC__stream_encoder_set_bits_per_sample(s->encoder, bps);
160  ok &= FLAC__stream_encoder_set_sample_rate(s->encoder, s->samplerate);
161  // the total number of samples can not be estimated (streaming)
162  // it will be set by the encoder in FLAC__stream_encoder_finish
163  //ok &= FLAC__stream_encoder_set_total_samples_estimate(s->encoder, 0);
164
165  if (!ok) {
166    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
167    goto failure;
168  }
169
170  s->metadata = AUBIO_ARRAY(FLAC__StreamMetadata*, 2);
171  if (!s->metadata) {
172    AUBIO_ERR("sink_flac: failed allocating memory for %s\n", s->path);
173    goto failure;
174  }
175
176  s->metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
177  if (!s->metadata[0]) {
178    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
179    goto failure;
180  }
181
182  s->metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
183  if (!s->metadata[1]) {
184    AUBIO_ERR("sink_flac: failed allocating vorbis comment %s\n", s->path);
185    goto failure;
186  }
187
188  FLAC__StreamMetadata_VorbisComment_Entry entry;
189  ok = FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry,
190      "encoder", "aubio");
191  ok &= FLAC__metadata_object_vorbiscomment_append_comment(s->metadata[0],
192      entry, false);
193  if (!ok) {
194    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
195    goto failure;
196  }
197
198  // padding length
199  s->metadata[1]->length = 1234;
200  if (!FLAC__stream_encoder_set_metadata(s->encoder, s->metadata, 2)) {
201    AUBIO_ERR("sink_flac: failed setting metadata for %s\n", s->path);
202    goto failure;
203  }
204
205  // initialize encoder
206  init_status = FLAC__stream_encoder_init_file(s->encoder, s->path,
207      NULL, NULL);
208      //aubio_sink_flac_callback, s);
209  if (init_status == FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE) {
210    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
211       " (invalid samplerate %d)\n", s->path, s->samplerate);
212    goto failure;
213  } else if (init_status ==
214      FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS) {
215    AUBIO_ERR("sink_flac: failed initilizing encoder for %s"
216       " (invalid number of channel %d)\n", s->path, s->channels);
217    goto failure;
218  } else if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
219    AUBIO_ERR("sink_flac: failed initilizing encoder for %s (%d)\n",
220        s->path, (int)init_status);
221    goto failure;
222  }
223
224  // mark success
225  ret = AUBIO_OK;
226
227failure:
228
229  return ret;
230}
231
232uint_t aubio_sink_flac_preset_samplerate(aubio_sink_flac_t *s,
233    uint_t samplerate)
234{
235  if (aubio_io_validate_samplerate("sink_flac", s->path, samplerate))
236    return AUBIO_FAIL;
237  s->samplerate = samplerate;
238  if (s->samplerate != 0 && s->channels != 0)
239    return aubio_sink_flac_open(s);
240  return AUBIO_OK;
241}
242
243uint_t aubio_sink_flac_preset_channels(aubio_sink_flac_t *s,
244    uint_t channels)
245{
246  if (aubio_io_validate_channels("sink_flac", s->path, channels)) {
247    return AUBIO_FAIL;
248  }
249  s->channels = channels;
250  // automatically open when both samplerate and channels have been set
251  if (s->samplerate != 0 && s->channels != 0) {
252    return aubio_sink_flac_open(s);
253  }
254  return AUBIO_OK;
255}
256
257uint_t aubio_sink_flac_get_samplerate(const aubio_sink_flac_t *s)
258{
259  return s->samplerate;
260}
261
262uint_t aubio_sink_flac_get_channels(const aubio_sink_flac_t *s)
263{
264  return s->channels;
265}
266
267static void aubio_sink_write_frames(aubio_sink_flac_t *s, uint_t length)
268{
269  // send to encoder
270  if (!FLAC__stream_encoder_process_interleaved(s->encoder,
271        (const FLAC__int32*)s->buffer, length)) {
272    FLAC__StreamEncoderState state =
273      FLAC__stream_encoder_get_state(s->encoder);
274    AUBIO_WRN("sink_flac: error writing to %s (%s)\n",
275        s->path, FLAC__StreamEncoderStateString[state]);
276  }
277}
278
279void aubio_sink_flac_do(aubio_sink_flac_t *s, fvec_t *write_data,
280    uint_t write)
281{
282  uint_t c, v;
283  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
284      MAX_WRITE_SIZE, write_data->length, write);
285  // fill buffer
286  if (!write) {
287    return;
288  } else {
289    for (c = 0; c < s->channels; c++) {
290      for (v = 0; v < length; v++) {
291        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[v]);
292      }
293    }
294  }
295  // send to encoder
296  aubio_sink_write_frames(s, length);
297}
298
299void aubio_sink_flac_do_multi(aubio_sink_flac_t *s, fmat_t *write_data,
300    uint_t write)
301{
302  uint_t c, v;
303  uint_t channels = aubio_sink_validate_input_channels("sink_flac", s->path,
304      s->channels, write_data->height);
305  uint_t length = aubio_sink_validate_input_length("sink_flac", s->path,
306      MAX_WRITE_SIZE, write_data->length, write);
307  // fill buffer
308  if (!write) {
309    return;
310  } else {
311    for (c = 0; c < channels; c++) {
312      for (v = 0; v < length; v++) {
313        s->buffer[v * s->channels + c] = FLOAT_TO_SHORT(write_data->data[c][v]);
314      }
315    }
316  }
317  // send to encoder
318  aubio_sink_write_frames(s, length);
319}
320
321uint_t aubio_sink_flac_close (aubio_sink_flac_t *s)
322{
323  uint_t ret = AUBIO_OK;
324
325  if (!s->fid) return AUBIO_FAIL;
326
327  if (s->encoder) {
328    // mark the end of stream
329    if (!FLAC__stream_encoder_finish(s->encoder)) {
330      FLAC__StreamEncoderState state =
331        FLAC__stream_encoder_get_state(s->encoder);
332      AUBIO_ERR("sink_flac: Error closing encoder for %s (%s)\n",
333          s->path, FLAC__StreamEncoderStateString[state]);
334      ret &= AUBIO_FAIL;
335    }
336
337    FLAC__stream_encoder_delete(s->encoder);
338  }
339
340  if (s->metadata) {
341    // clean up metadata after stream finished
342    if (s->metadata[0])
343      FLAC__metadata_object_delete(s->metadata[0]);
344    if (s->metadata[1])
345      FLAC__metadata_object_delete(s->metadata[1]);
346    AUBIO_FREE(s->metadata);
347  }
348
349  if (s->fid && fclose(s->fid)) {
350    AUBIO_ERR("sink_flac: Error closing file %s (%s)\n",
351        s->path, strerror(errno));
352    ret &= AUBIO_FAIL;
353  }
354  s->fid = NULL;
355
356  return ret;
357}
358
359#if 0
360static void aubio_sink_flac_callback(const FLAC__StreamEncoder* encoder UNUSED,
361    FLAC__uint64 bytes_written, FLAC__uint64 samples_written,
362    unsigned frames_written, unsigned total_frames_estimate,
363    void *client_data UNUSED)
364{
365  AUBIO_WRN("sink_flac: %d bytes_written, %d samples_written,"
366      " %d/%d frames writen\n",
367      bytes_written, samples_written, frames_written, total_frames_estimate);
368}
369#endif
370
371#endif /* HAVE_FLAC */
Note: See TracBrowser for help on using the repository browser.