source: roaraudio/roard/codecfilter_vorbis.c @ 4822:f9c32c6a8451

Last change on this file since 4822:f9c32c6a8451 was 4822:f9c32c6a8451, checked in by phi, 13 years ago

use roar_mm_*()

File size: 15.3 KB
Line 
1//codecfilter_vorbis.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2008-2011
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#define ROAR_REQUIRE_LIBVORBISFILE
27
28#include "roard.h"
29
30#ifdef ROAR_HAVE_LIBVORBISFILE
31
32#define FIFAC ((float)((uint64_t)1<<(ROAR_VORBIS_BITS-1)))
33
34int _g_cf_vorbis_vfvio_return_err (void) {
35 return -1;
36}
37
38ov_callbacks _g_cf_vorbis_vfvio = {
39  .read_func  = cf_vorbis_vfvio_read,
40  .seek_func  = (int    (*)(void *, ogg_int64_t, int      )) _g_cf_vorbis_vfvio_return_err,
41  .close_func = (int    (*)(void *                        )) _g_cf_vorbis_vfvio_return_err,
42  .tell_func  = (long   (*)(void *                        )) _g_cf_vorbis_vfvio_return_err
43};
44
45size_t cf_vorbis_vfvio_read (void *ptr, size_t size, size_t nmemb, void *datasource) {
46 ssize_t r;
47
48 r = stream_vio_s_read(ROAR_STREAM_SERVER(datasource), ptr, size*nmemb);
49
50 ROAR_DBG("cf_vorbis_vfvio_read(ptr=%p, size=%lu, nmemb=%lu, datasource=%p): r=%i", ptr, size, nmemb, datasource, r);
51
52 if ( r == -1 )
53  return 0;
54
55 if ( r > 0 )
56  errno = 0;
57
58 r /= size;
59 
60 ROAR_DBG("cf_vorbis_vfvio_read(ptr=%p, size=%lu, nmemb=%lu, datasource=%p) = %i", ptr, size, nmemb, datasource, r);
61 return r;
62}
63
64int cf_vorbis_open(CODECFILTER_USERDATA_T * inst, int codec,
65                                            struct roar_stream_server * info,
66                                            struct roar_codecfilter   * filter) {
67 struct codecfilter_vorbis_inst * self = roar_mm_malloc(sizeof(struct codecfilter_vorbis_inst));
68 struct roar_stream * s = ROAR_STREAM(info);
69
70 if ( !self )
71  return -1;
72
73 self->current_section      = -1;
74 self->last_section         = -1;
75 self->opened               =  0;
76 self->got_it_running       =  0;
77 self->stream               = info;
78// self->outlen               = ROAR_OUTPUT_BUFFER_SAMPLES * s->info.channels * s->info.bits / 8; // optimal size
79#ifdef ROAR_HAVE_LIBVORBISENC
80 self->encoding               = 0;
81 self->encoder.v_base_quality = 0.3;
82 self->encoder.srn            = -1;
83#endif
84
85 ROAR_DBG("cf_vorbis_open(*): info->id=%i", ROAR_STREAM(info)->id);
86
87 *inst = (CODECFILTER_USERDATA_T) self;
88
89#if ROAR_CODEC_DEFAULT == ROAR_CODEC_PCM_S_LE
90 self->bigendianp = 0;
91 s->info.codec = ROAR_CODEC_DEFAULT;
92#else
93 self->bigendianp = 1;
94 s->info.codec = ROAR_CODEC_PCM_S_BE; // force because ROAR_CODEC_DEFAULT can be something non-BE, too.
95#endif
96
97 s->info.bits  = 16;
98
99 if ( s->dir == ROAR_DIR_PLAY ) {
100  return 0;
101 } else if ( s->dir == ROAR_DIR_MONITOR || s->dir == ROAR_DIR_OUTPUT ) {
102#ifdef ROAR_HAVE_LIBVORBISENC
103  // set up the encoder here
104// this is delayed to the write function
105/*
106 if ( cf_vorbis_encode_start(self) == -1 ) {
107  roar_mm_free(self);
108  return -1;
109 }
110*/
111 s->info.bits  = ROAR_VORBIS_BITS;
112#else
113 roar_mm_free(self);
114 return -1;
115#endif
116 } else {
117  roar_mm_free(self);
118  return -1;
119 }
120
121 return 0;
122}
123
124int cf_vorbis_close(CODECFILTER_USERDATA_T   inst) {
125 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
126
127 if ( self == NULL )
128  return -1;
129
130 if ( self->got_it_running )
131  ov_clear(&(self->vf));
132
133#ifdef ROAR_HAVE_LIBVORBISENC
134 if ( self->encoding ) {
135  cf_vorbis_encode_end(self);
136 }
137#endif
138
139 roar_mm_free(self);
140 return 0;
141}
142
143int cf_vorbis_write(CODECFILTER_USERDATA_T   inst, char * buf, int len) {
144#ifdef ROAR_HAVE_LIBVORBISENC
145 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
146 struct roar_stream * s = ROAR_STREAM(self->stream);
147 ogg_packet header;
148 ogg_packet header_comm;
149 ogg_packet header_code;
150 float ** encbuf;
151 int i, c;
152 int chans;
153 int end;
154 int sid;
155 void * prethrubuf;
156#if ROAR_VORBIS_BITS == 8
157 int8_t  * data = (int8_t  *) buf;
158#elif ROAR_VORBIS_BITS == 16
159 int16_t * data = (int16_t *) buf;
160#elif ROAR_VORBIS_BITS == 32
161 int32_t * data = (int32_t *) buf;
162#else
163#error value of ROAR_VORBIS_BITS not supported
164#endif
165
166 if ( ! self->opened ) {
167  if ( !self->encoding ) {
168   if ( cf_vorbis_encode_start(self) == -1 ) {
169    return -1;
170   }
171  }
172
173  sid = ROAR_STREAM(self->stream)->id;
174
175  vorbis_analysis_headerout(&(self->encoder.vd), &(self->encoder.vc), &header, &header_comm, &header_code);
176
177  ogg_stream_packetin(&(self->encoder.os), &header);
178  ogg_stream_packetin(&(self->encoder.os), &header_comm);
179  ogg_stream_packetin(&(self->encoder.os), &header_code);
180
181  stream_prethru_destroy(sid);
182
183  while (ogg_stream_flush(&(self->encoder.os), &(self->encoder.og))) {
184   if ( stream_vio_s_write(self->stream, self->encoder.og.header, self->encoder.og.header_len)
185                                                                 != self->encoder.og.header_len ||
186        stream_vio_s_write(self->stream, self->encoder.og.body,   self->encoder.og.body_len  )
187                                                                 != self->encoder.og.body_len     ) {
188    roar_mm_free(self); // TODO: do we need addional cleanup?
189    return -1;
190   }
191   // we ignore errors at the moment...
192   if ( stream_prethru_add_data(sid, &prethrubuf, self->encoder.og.header_len + self->encoder.og.body_len) != -1 ) {
193    memcpy(prethrubuf,                               self->encoder.og.header, self->encoder.og.header_len);
194    memcpy(prethrubuf + self->encoder.og.header_len, self->encoder.og.body,   self->encoder.og.body_len  );
195   }
196  }
197  self->opened = 1;
198 } else {
199  encbuf = vorbis_analysis_buffer(&(self->encoder.vd), len /* TODO: need to lookup the menaing of this */);
200  chans  = s->info.channels;
201  end    = len*8/(ROAR_VORBIS_BITS*chans);
202
203  if ( chans == 1 ) { // use optimized code
204   for (i = 0; i < end; i++)
205    encbuf[0][i] = data[i]/FIFAC;
206
207  } else if ( chans == 2 ) { // use optimized code
208   for (i = 0; i < end; i++) {
209    encbuf[0][i] = data[2*i  ]/FIFAC;
210    encbuf[1][i] = data[2*i+1]/FIFAC;
211   }
212  } else { // use generic multi channel code
213   for (i = 0; i < end; i++) {
214    for (c = 0; c < chans; c++) {
215     encbuf[c][i] = data[chans*i+c]/FIFAC;
216    }
217   }
218  }
219
220  vorbis_analysis_wrote(&(self->encoder.vd), i);
221
222  if ( cf_vorbis_encode_flushout(self) == -1 )
223   return -1;
224 }
225
226  return len; // we assume every thing was written (at least into our dsp anaylises buffer
227#else
228 errno = ENOSYS;
229 return -1;
230#endif
231}
232int cf_vorbis_read(CODECFILTER_USERDATA_T   inst, char * buf, int len) {
233 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
234 long r;
235 long todo = len;
236 long done = 0;
237
238// printf("cf_vorbis_read(inst=%p, buf=%p, len=%i) = ?\n", inst, buf, len);
239
240 self->opened++;
241 if ( self->opened == 16 ) {
242
243  //printf("cf_vorbis_read(*): opening...\n");
244  if ( ov_open_callbacks((void*)self->stream, &(self->vf), NULL, 0, _g_cf_vorbis_vfvio) < 0 ) {
245   return 0;
246  }
247  errno = EAGAIN;
248  return -1;
249 }
250
251 if ( self->opened < 16 ) {
252  errno = EAGAIN;
253  return -1;
254 }
255
256
257 self->got_it_running = 1;
258
259 while (todo) {
260  r = ov_read(&(self->vf), buf+done, todo, self->bigendianp, 2, 1, &(self->current_section));
261  if ( r == OV_HOLE ) {
262   ROAR_DBG("cf_vorbis_read(*): Hole in stream");
263  } else if ( r < 1 ) {
264   break;
265  } else {
266   if ( self->last_section != self->current_section )
267    if ( cf_vorbis_update_stream(self) == -1 )
268     return 0;
269
270   self->last_section = self->current_section;
271   todo -= r;
272   done += r;
273  }
274 }
275
276//printf("ov_read(*) = %i\n", done);
277
278 if ( done == 0 ) {
279  // do some EOF handling...
280  return 0;
281 } else {
282  return len;
283 }
284}
285
286int cf_vorbis_update_stream (struct codecfilter_vorbis_inst * self) {
287#ifdef ROAR_SUPPORT_META
288 vorbis_info *vi = ov_info(&(self->vf), -1);
289 char **ptr = ov_comment(&(self->vf), -1)->user_comments;
290 char key[ROAR_META_MAX_NAMELEN] = {0}, value[LIBROAR_BUFFER_MSGDATA] = {0};
291 struct roar_stream * s = ROAR_STREAM(self->stream);
292 int type;
293 int j, h = 0;
294 float rpg_track = 0, rpg_album = 0;
295 int meta_ok;
296
297 s->info.channels = vi->channels;
298 s->info.rate     = vi->rate;
299 s->info.bits     = 16;
300#if ROAR_CODEC_DEFAULT == ROAR_CODEC_PCM_S_LE
301 s->info.codec    = ROAR_CODEC_DEFAULT;
302#else
303 s->info.codec    = ROAR_CODEC_PCM_S_BE;
304#endif
305
306 stream_meta_clear(s->id);
307
308 while(*ptr){
309  meta_ok = 1;
310
311   for (j = 0; (*ptr)[j] != 0 && (*ptr)[j] != '='; j++) {
312    if ( j == ROAR_META_MAX_NAMELEN ) {
313     ROAR_ERR("cf_vorbis_update_stream(*): invalid meta data on stream %i: meta data key too long", s->id);
314     meta_ok = 0;
315     j = 0;
316     break;
317    }
318    key[j] = (*ptr)[j];
319   }
320   key[j] = 0;
321
322   if ( meta_ok ) {
323    for (j++, h = 0; (*ptr)[j] != 0 && (*ptr)[j] != '='; j++) {
324     if ( h == LIBROAR_BUFFER_MSGDATA ) {
325      ROAR_ERR("update_stream(*): invalid meta data on stream %i: meta data value for key '%s' too long", s->id, key);
326      meta_ok = 0;
327      h = 0;
328      break;
329     }
330     value[h++] = (*ptr)[j];
331    }
332    value[h]   = 0;
333   }
334
335   if ( meta_ok ) {
336    type = roar_meta_inttype(key);
337    if ( type != -1 )
338     stream_meta_set(s->id, type, "", value);
339
340    ROAR_DBG("cf_vorbis_update_stream(*): Meta %-16s: %s", key, value);
341
342    if ( strcmp(key, "REPLAYGAIN_TRACK_PEAK") == 0 ) {
343     rpg_track = 1/atof(value);
344/*
345    } else if ( strcmp(key, "REPLAYGAIN_TRACK_GAIN") == 0 ) {
346     rpg_track = powf(10, atof(value)/20);
347*/
348    } else if ( strcmp(key, "REPLAYGAIN_ALBUM_PEAK") == 0 ) {
349     rpg_album = 1/atof(value);
350/*
351    } else if ( strcmp(key, "REPLAYGAIN_ALBUM_GAIN") == 0 ) {
352     rpg_album = powf(10, atof(value)/20);
353*/
354    }
355   }
356
357   ptr++;
358 }
359
360 if ( rpg_album ) {
361  self->stream->mixer.rpg_div = 2718;  // = int(exp(1)*1000)
362  self->stream->mixer.rpg_mul = (float)rpg_album*2718;
363 } else if ( rpg_track ) {
364  self->stream->mixer.rpg_div = 2718;
365  self->stream->mixer.rpg_mul = (float)rpg_track*2718;
366 }
367
368 stream_meta_finalize(s->id);
369#endif
370 //printf("RPG: mul=%i, div=%i\n", self->stream->mixer.rpg_mul, self->stream->mixer.rpg_div);
371 return 0;
372}
373
374int cf_vorbis_encode_start  (struct codecfilter_vorbis_inst * self) {
375#ifdef ROAR_HAVE_LIBVORBISENC
376 int srn = self->encoder.srn; // this value is allrady inited...
377#ifdef ROAR_SUPPORT_META
378 int len = 0;
379 int i;
380 int types[ROAR_META_MAX_PER_STREAM];
381#endif
382 int sid = ROAR_STREAM(self->stream)->id;
383 float v_base_quality = self->encoder.v_base_quality;
384 char val[LIBROAR_BUFFER_MSGDATA];
385
386  val[LIBROAR_BUFFER_MSGDATA-1] = 0;
387
388  memset(&(self->encoder), 0, sizeof(self->encoder));
389
390  self->encoding = 1;
391  self->encoder.srn = srn + 1;
392  self->encoder.v_base_quality = v_base_quality;
393
394  vorbis_info_init(&(self->encoder.vi));
395  vorbis_comment_init(&(self->encoder.vc));
396  vorbis_comment_add_tag(&(self->encoder.vc), "SERVER", "RoarAudio");
397  vorbis_comment_add_tag(&(self->encoder.vc), "ENCODER", "RoarAudio Vorbis codecfilter");
398
399#ifdef ROAR_SUPPORT_META
400  if ( (len = stream_meta_list(sid, types, ROAR_META_MAX_PER_STREAM)) != -1 ) {
401   for (i = 0; i < len; i++) {
402//int stream_meta_get     (int id, int type, char * name, char * val, size_t len);
403    if ( stream_meta_get(sid, types[i], NULL, val, LIBROAR_BUFFER_MSGDATA-1) == 0 )
404     vorbis_comment_add_tag(&(self->encoder.vc), roar_meta_strtype(types[i]), val);
405   }
406  }
407#endif
408
409  ROAR_DBG("cf_vorbis_encode_start(*): q=%f", v_base_quality);
410
411  if( vorbis_encode_init_vbr(&(self->encoder.vi), (long) ROAR_STREAM(self->stream)->info.channels,
412                                                  (long) ROAR_STREAM(self->stream)->info.rate,
413                                                  v_base_quality) != 0 ) {
414   ROAR_ERR("cf_vorbis_encode_start(*): Can not vorbis_encode_init_vbr(*)!");
415   vorbis_info_clear(&(self->encoder.vi)); // TODO: do we need to free vc also?
416   return -1;
417  }
418
419  vorbis_analysis_init(&(self->encoder.vd), &(self->encoder.vi));
420  vorbis_block_init(&(self->encoder.vd), &(self->encoder.vb));
421
422  ROAR_DBG("cf_vorbis_encode_start(*): srn=%i", self->encoder.srn);
423                                     //  "RA"<<16 + PID<<8 + Stream ID
424  ogg_stream_init(&(self->encoder.os),
425                   (((0x5241 + self->encoder.srn) & 0xffff)<<16) +
426                   ( (getpid()                    & 0x00ff)<< 8) +
427                   (  sid                         & 0x00ff));
428 return 0;
429#else
430 return -1;
431#endif
432}
433
434int cf_vorbis_encode_end    (struct codecfilter_vorbis_inst * self) {
435#ifdef ROAR_HAVE_LIBVORBISENC
436 if ( self->encoding ) {
437  // try to flush up to an EOS page...
438  vorbis_analysis_buffer(&(self->encoder.vd), 2*ROAR_MAX_CHANNELS);
439  vorbis_analysis_wrote(&(self->encoder.vd), 0);
440  cf_vorbis_encode_flushout(self);
441
442  // clean up...
443  ogg_stream_clear(&(self->encoder.os));
444  vorbis_block_clear(&(self->encoder.vb));
445  vorbis_dsp_clear(&(self->encoder.vd));
446  vorbis_info_clear(&(self->encoder.vi));
447  self->opened = 0;
448 }
449
450 return 0;
451#else
452 return -1;
453#endif
454}
455
456int cf_vorbis_encode_flushout(struct codecfilter_vorbis_inst * self) {
457#ifdef ROAR_HAVE_LIBVORBISENC
458 while ( vorbis_analysis_blockout(&(self->encoder.vd), &(self->encoder.vb)) == 1 ) {
459  vorbis_analysis(&(self->encoder.vb), &(self->encoder.op));
460  vorbis_bitrate_addblock(&(self->encoder.vb));
461
462  while ( vorbis_bitrate_flushpacket(&(self->encoder.vd), &(self->encoder.op)) ) {
463   ogg_stream_packetin(&(self->encoder.os), &(self->encoder.op));
464
465   while( ogg_stream_pageout(&(self->encoder.os), &(self->encoder.og)) ) {
466    if (
467         stream_vio_s_write(self->stream, self->encoder.og.header, self->encoder.og.header_len) == -1 ||
468         stream_vio_s_write(self->stream, self->encoder.og.body,   self->encoder.og.body_len  ) == -1   ) {
469     return -1;
470    }
471   }
472  }
473 }
474
475 return 0;
476#else
477 return -1;
478#endif
479}
480
481int cf_vorbis_ctl(CODECFILTER_USERDATA_T   inst, int cmd, void * data) {
482 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
483 int_least32_t type = cmd & ROAR_STREAM_CTL_TYPEMASK;
484
485 cmd -= type;
486
487 switch (cmd) {
488  case ROAR_CODECFILTER_CTL2CMD(ROAR_CODECFILTER_CTL_META_UPDATE):
489    if ( type != ROAR_STREAM_CTL_TYPE_VOID )
490     return -1;
491
492    ROAR_DBG("cf_vorbis_ctl(*): stoping stream...");
493    if ( cf_vorbis_encode_end(self) == -1 )
494     return -1;
495    ROAR_DBG("cf_vorbis_ctl(*): restarting stream...");
496    if ( cf_vorbis_encode_start(self) == -1 )
497     return -1;
498
499    return 0;
500   break;
501  case ROAR_CODECFILTER_CTL2CMD(ROAR_CODECFILTER_CTL_SET_Q):
502    if ( type != ROAR_STREAM_CTL_TYPE_FLOAT )
503     return -1;
504
505    ROAR_DBG("cf_vorbis_ctl(*): setting quality to q=%f", *(float*)data);
506
507    self->encoder.v_base_quality = *(float*)data / 10;
508
509    if ( self->encoding ) {
510     ROAR_DBG("cf_vorbis_ctl(*): we are allready encoding, restart...");
511     ROAR_DBG("cf_vorbis_ctl(*): stoping stream...");
512     if ( cf_vorbis_encode_end(self) == -1 )
513      return -1;
514     ROAR_DBG("cf_vorbis_ctl(*): restarting stream...");
515     if ( cf_vorbis_encode_start(self) == -1 )
516      return -1;
517    }
518
519    return 0;
520   break;
521  default:
522    ROAR_DBG("cf_vorbis_ctl(*): Unknown command: cmd=0x%.8x, type=0x%.8x, pcmd=0x%.8x",
523                    cmd, type, ROAR_CODECFILTER_CTL2CMD(cmd));
524    return -1;
525 }
526
527 return -1;
528}
529
530#endif
531//ll
Note: See TracBrowser for help on using the repository browser.