source: roaraudio/roard/codecfilter_vorbis.c @ 2087:30d3653999b9

Last change on this file since 2087:30d3653999b9 was 2087:30d3653999b9, checked in by phi, 15 years ago

added some debug outputs and got setting of quality for vorbis working :)

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