source: src/io/sink_flac.c

Last change on this file was 07c76d00, checked in by Paul Brossier <piem@piem.org>, 6 years ago

[sink_flac] no declaration after statement

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