source: src/io/sink_vorbis.c @ b8fa393

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

[sink_vorbis] fix include order

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