source: roaraudio/roard/codecfilter_vorbis.c @ 3358:7f9d211148e0

Last change on this file since 3358:7f9d211148e0 was 2816:b9e357b0dc43, checked in by phi, 15 years ago

added prethru support and updated list of codecs a bit

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