source: src/io/sink_flac.c @ 7436353

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

[io] add sink_flac

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