source: src/io/sink_vorbis.c @ 7e93013

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

[io] add note about reference vorbis implementation

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