source: src/io/sink_vorbis.c @ 0e6ad10

feature/autosink
Last change on this file since 0e6ad10 was 0e6ad10, checked in by Paul Brossier <piem@piem.org>, 22 months ago

[io] fix error message in sink_vorbis

  • Property mode set to 100644
File size: 8.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/encoder_example.c` in the
23  libvorbis source package (versions 1.3.5 and later) available online at
24  https://xiph.org/vorbis/
25*/
26
27#include "aubio_priv.h"
28
29#ifdef HAVE_VORBISENC
30
31#include "io/ioutils.h"
32#include "fmat.h"
33
34#include <vorbis/vorbisenc.h>
35#include <string.h> // strerror
36#include <errno.h> // errno
37#include <time.h> // time
38
39#define MAX_SIZE 2048
40
41struct _aubio_sink_vorbis_t {
42  FILE *fid;            // file id
43  ogg_stream_state os;  // stream
44  ogg_page og;          // page
45  ogg_packet op;        // data packet
46  vorbis_info vi;       // vorbis bitstream settings
47  vorbis_comment vc;    // user comment
48  vorbis_dsp_state vd;  // working state
49  vorbis_block vb;      // working space
50
51  uint_t samplerate;
52  uint_t channels;
53  char_t *path;
54};
55
56typedef struct _aubio_sink_vorbis_t aubio_sink_vorbis_t;
57
58uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
59    uint_t channels);
60uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
61    uint_t samplerate);
62uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s);
63uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s);
64void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s);
65
66aubio_sink_vorbis_t * new_aubio_sink_vorbis (const char_t *uri,
67    uint_t samplerate)
68{
69  aubio_sink_vorbis_t * s = AUBIO_NEW(aubio_sink_vorbis_t);
70
71  if (!uri) {
72    AUBIO_ERROR("sink_vorbis: Aborted opening null path\n");
73    goto failure;
74  }
75
76  s->path = AUBIO_ARRAY(char_t, strnlen(uri, PATH_MAX) + 1);
77  strncpy(s->path, uri, strnlen(uri, PATH_MAX) + 1);
78  s->path[strnlen(uri, PATH_MAX)] = '\0';
79
80  s->channels = 0;
81
82  if ((sint_t)samplerate == 0)
83    return s;
84
85  aubio_sink_vorbis_preset_samplerate(s, samplerate);
86  s->channels = 1;
87
88  if (aubio_sink_vorbis_open(s) != AUBIO_OK)
89    goto failure;
90
91  return s;
92
93failure:
94  del_aubio_sink_vorbis(s);
95  return NULL;
96}
97
98void del_aubio_sink_vorbis (aubio_sink_vorbis_t *s)
99{
100  if (s->fid) aubio_sink_vorbis_close(s);
101  // clean up
102  ogg_stream_clear(&s->os);
103  vorbis_block_clear(&s->vb);
104  vorbis_dsp_clear(&s->vd);
105  vorbis_comment_clear(&s->vc);
106  vorbis_info_clear(&s->vi);
107
108  if (s->path) AUBIO_FREE(s->path);
109  AUBIO_FREE(s);
110}
111
112uint_t aubio_sink_vorbis_open(aubio_sink_vorbis_t *s)
113{
114  float quality_mode = .9;
115
116  if (s->samplerate == 0 || s->channels == 0) return AUBIO_FAIL;
117
118  s->fid = fopen((const char *)s->path, "wb");
119  if (!s->fid) {
120    AUBIO_ERR("sink_vorbis: Error opening file %s (%s)\n",
121        s->path, strerror(errno));
122    return AUBIO_FAIL;
123  }
124
125  vorbis_info_init(&s->vi);
126  if (vorbis_encode_init_vbr(&s->vi, s->channels, s->samplerate, quality_mode))
127  {
128    AUBIO_ERR("sink_vorbis: vorbis_encode_init_vbr failed\n");
129    return AUBIO_FAIL;
130  }
131
132  // add comment
133  vorbis_comment_init(&s->vc);
134  vorbis_comment_add_tag(&s->vc, "ENCODER", "aubio");
135
136  // initalise analysis and block
137  vorbis_analysis_init(&s->vd, &s->vi);
138  vorbis_block_init(&s->vd, &s->vb);
139
140  // pick randome serial number
141  srand(time(NULL));
142  ogg_stream_init(&s->os, rand());
143
144  // write header
145  {
146    int ret = 0;
147    size_t wrote;
148    ogg_packet header;
149    ogg_packet header_comm;
150    ogg_packet header_code;
151
152    vorbis_analysis_headerout(&s->vd, &s->vc, &header, &header_comm,
153        &header_code);
154
155    ogg_stream_packetin(&s->os, &header);
156    ogg_stream_packetin(&s->os, &header_comm);
157    ogg_stream_packetin(&s->os, &header_code);
158
159    // make sure audio data will start on a new page
160    while (1)
161    {
162      ret = ogg_stream_flush(&s->os, &s->og);
163      if (ret==0) break;
164      wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
165      ret = (wrote == (unsigned)s->og.header_len);
166      wrote = fwrite(s->og.body,   1, s->og.body_len,   s->fid);
167      ret &= (wrote == (unsigned)s->og.body_len);
168      if (ret == 0) {
169        AUBIO_ERR("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
170            s->path, strerror(errno));
171        return AUBIO_FAIL;
172      }
173    }
174  }
175
176  return AUBIO_OK;
177}
178
179uint_t aubio_sink_vorbis_preset_samplerate(aubio_sink_vorbis_t *s,
180    uint_t samplerate)
181{
182  if (aubio_io_validate_samplerate("sink_vorbis", s->path, samplerate))
183    return AUBIO_FAIL;
184  s->samplerate = samplerate;
185  if (/* s->samplerate != 0 && */ s->channels != 0)
186    return aubio_sink_vorbis_open(s);
187  return AUBIO_OK;
188}
189
190uint_t aubio_sink_vorbis_preset_channels(aubio_sink_vorbis_t *s,
191    uint_t channels)
192{
193  if (aubio_io_validate_channels("sink_vorbis", s->path, channels)) {
194    return AUBIO_FAIL;
195  }
196  s->channels = channels;
197  // automatically open when both samplerate and channels have been set
198  if (s->samplerate != 0 /* && s->channels != 0 */) {
199    return aubio_sink_vorbis_open(s);
200  }
201  return AUBIO_OK;
202}
203
204uint_t aubio_sink_vorbis_get_samplerate(const aubio_sink_vorbis_t *s)
205{
206  return s->samplerate;
207}
208
209uint_t aubio_sink_vorbis_get_channels(const aubio_sink_vorbis_t *s)
210{
211  return s->channels;
212}
213
214void aubio_sink_vorbis_write(aubio_sink_vorbis_t *s)
215{
216  int result;
217  size_t wrote;
218  // pre-analysis
219  while (vorbis_analysis_blockout(&s->vd, &s->vb) == 1) {
220
221    vorbis_analysis(&s->vb, NULL);
222    vorbis_bitrate_addblock(&s->vb);
223
224    while (vorbis_bitrate_flushpacket(&s->vd, &s->op))
225    {
226      ogg_stream_packetin(&s->os, &s->op);
227
228      while (1) {
229        result = ogg_stream_pageout (&s->os, &s->og);
230        if (result == 0) break;
231        wrote = fwrite(s->og.header, 1, s->og.header_len, s->fid);
232        result = (wrote == (unsigned)s->og.header_len);
233        wrote = fwrite(s->og.body, 1, s->og.body_len,     s->fid);
234        result &= (wrote == (unsigned)s->og.body_len);
235        if (result == 0) {
236          AUBIO_WRN("sink_vorbis: failed writing \'%s\' to disk (%s)\n",
237              s->path, strerror(errno));
238        }
239        if (ogg_page_eos(&s->og)) break;
240      }
241    }
242  }
243}
244
245void aubio_sink_vorbis_do(aubio_sink_vorbis_t *s, fvec_t *write_data,
246    uint_t write)
247{
248  uint_t c, v;
249  uint_t length = aubio_sink_validate_input_length("sink_vorbis", s->path,
250      MAX_SIZE, write_data->length, write);
251  float **buffer = vorbis_analysis_buffer(&s->vd, (long)length);
252  // fill buffer
253  if (!write) {
254    return;
255  } else if (!buffer) {
256    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
257    return;
258  } else {
259    for (c = 0; c < s->channels; c++) {
260      for (v = 0; v < length; v++) {
261        buffer[c][v] = write_data->data[v];
262      }
263    }
264    // tell vorbis how many frames were written
265    vorbis_analysis_wrote(&s->vd, (long)length);
266  }
267  // write to file
268  aubio_sink_vorbis_write(s);
269}
270
271void aubio_sink_vorbis_do_multi(aubio_sink_vorbis_t *s, fmat_t *write_data,
272    uint_t write)
273{
274  uint_t c, v;
275  uint_t channels = aubio_sink_validate_input_channels("sink_vorbis", s->path,
276      s->channels, write_data->height);
277  uint_t length = aubio_sink_validate_input_length("sink_vorbis", s->path,
278      MAX_SIZE, write_data->length, write);
279  float **buffer = vorbis_analysis_buffer(&s->vd, (long)length);
280  // fill buffer
281  if (!write) {
282    return;
283  } else if (!buffer) {
284    AUBIO_WRN("sink_vorbis: failed fetching buffer of size %d\n", write);
285    return;
286  } else {
287    for (c = 0; c < channels; c++) {
288      for (v = 0; v < length; v++) {
289        buffer[c][v] = write_data->data[c][v];
290      }
291    }
292    // tell vorbis how many frames were written
293    vorbis_analysis_wrote(&s->vd, (long)write);
294  }
295
296  aubio_sink_vorbis_write(s);
297}
298
299uint_t aubio_sink_vorbis_close (aubio_sink_vorbis_t *s)
300{
301  if (!s->fid) return AUBIO_FAIL;
302  //mark the end of stream
303  vorbis_analysis_wrote(&s->vd, 0);
304
305  aubio_sink_vorbis_write(s);
306
307  if (s->fid && fclose(s->fid)) {
308    AUBIO_ERR("sink_vorbis: Error closing file %s (%s)\n",
309        s->path, strerror(errno));
310    return AUBIO_FAIL;
311  }
312  s->fid = NULL;
313  return AUBIO_OK;
314}
315
316#endif /* HAVE_VORBISENC */
Note: See TracBrowser for help on using the repository browser.