source: roaraudio/roard/codecfilter_vorbis.c @ 745:b2996b41f084

Last change on this file since 745:b2996b41f084 was 745:b2996b41f084, checked in by phi, 16 years ago

done some cleanup

File size: 10.7 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#include "roard.h"
26
27#ifdef ROAR_HAVE_LIBVORBISFILE
28
29int _g_cf_vorbis_vfvio_return_err (void) {
30 return -1;
31}
32
33ov_callbacks _g_cf_vorbis_vfvio = {
34  .read_func  = cf_vorbis_vfvio_read,
35  .seek_func  = (int    (*)(void *, ogg_int64_t, int      )) _g_cf_vorbis_vfvio_return_err,
36  .close_func = (int    (*)(void *                        )) _g_cf_vorbis_vfvio_return_err,
37  .tell_func  = (long   (*)(void *                        )) _g_cf_vorbis_vfvio_return_err
38};
39
40size_t cf_vorbis_vfvio_read (void *ptr, size_t size, size_t nmemb, void *datasource) {
41 ssize_t r;
42 r = stream_vio_s_read(ROAR_STREAM_SERVER(datasource), ptr, size*nmemb);
43
44 ROAR_DBG("cf_vorbis_vfvio_read(ptr=%p, size=%lu, nmemb=%lu, datasource=%p): r=%i", ptr, size, nmemb, datasource, r);
45
46 errno = 0;
47
48 if ( r == -1 )
49  return 0;
50
51 if ( r > 0 )
52  errno = 0;
53
54 r /= size;
55 
56 ROAR_DBG("cf_vorbis_vfvio_read(ptr=%p, size=%lu, nmemb=%lu, datasource=%p) = %i", ptr, size, nmemb, datasource, r);
57 return r;
58}
59
60int cf_vorbis_open(CODECFILTER_USERDATA_T * inst, int codec,
61                                            struct roar_stream_server * info,
62                                            struct roar_codecfilter   * filter) {
63 struct codecfilter_vorbis_inst * self = malloc(sizeof(struct codecfilter_vorbis_inst));
64 struct roar_stream * s = ROAR_STREAM(info);
65
66 if ( !self )
67  return -1;
68
69 self->current_section      = -1;
70 self->last_section         = -1;
71 self->opened               =  0;
72 self->got_it_running       =  0;
73 self->stream               = info;
74// self->outlen               = ROAR_OUTPUT_BUFFER_SAMPLES * s->info.channels * s->info.bits / 8; // optimal size
75#ifdef ROAR_HAVE_LIBVORBISENC
76 self->encoding             = 0;
77#endif
78
79 ROAR_DBG("cf_vorbis_open(*): info->id=%i", ROAR_STREAM(info)->id);
80
81/*
82 if ( (self->in = fdopen(s->fh, "r")) == NULL ) {
83  free((void*)self);
84  return -1;
85 }
86*/
87
88 *inst = (CODECFILTER_USERDATA_T) self;
89
90 s->info.codec = ROAR_CODEC_DEFAULT;
91 s->info.bits  = 16;
92
93 if ( s->dir == ROAR_DIR_PLAY ) {
94  return 0;
95 } else if ( s->dir == ROAR_DIR_MONITOR || s->dir == ROAR_DIR_OUTPUT ) {
96#ifdef ROAR_HAVE_LIBVORBISENC
97  // set up the encoder here
98
99  memset(&(self->encoder), 0, sizeof(self->encoder));
100
101  self->encoding = 1;
102
103  vorbis_info_init(&(self->encoder.vi));
104  vorbis_comment_init(&(self->encoder.vc));
105  vorbis_comment_add_tag(&(self->encoder.vc), "SERVER", "RoarAudio");
106  vorbis_comment_add_tag(&(self->encoder.vc), "ENCODER", "RoarAudio Vorbis codecfilter");
107
108  if( vorbis_encode_init_vbr(&(self->encoder.vi), (long) s->info.channels, (long) s->info.rate,
109                                                  self->encoder.v_base_quality) != 0 ) {
110   ROAR_ERR("cf_vorbis_open(*): Can not vorbis_encode_init_vbr(*)!");
111   vorbis_info_clear(&(self->encoder.vi)); // TODO: do we need to free vc also?
112   free(self);
113   return -1;
114  }
115
116  vorbis_analysis_init(&(self->encoder.vd), &(self->encoder.vi));
117  vorbis_block_init(&(self->encoder.vd), &(self->encoder.vb));
118
119                                     //  "RA"<<16 + PID<<8 + Stream ID
120  ogg_stream_init(&(self->encoder.os), 0x52410000 + ((getpid() & 0xff)<<8) + s->id);
121
122#else
123 free(self);
124 return -1;
125#endif
126 } else {
127  free(self);
128  return -1;
129 }
130
131 return 0;
132}
133
134int cf_vorbis_close(CODECFILTER_USERDATA_T   inst) {
135 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
136
137 if ( !inst )
138  return -1;
139
140 if ( self->got_it_running )
141  ov_clear(&(self->vf));
142
143#ifdef ROAR_HAVE_LIBVORBISENC
144 if ( self->encoding ) {
145  ogg_stream_clear(&(self->encoder.os));
146  vorbis_block_clear(&(self->encoder.vb));
147  vorbis_dsp_clear(&(self->encoder.vd));
148  vorbis_info_clear(&(self->encoder.vi));
149 }
150#endif
151
152 free(inst);
153 return 0;
154}
155
156int cf_vorbis_write(CODECFILTER_USERDATA_T   inst, char * buf, int len) {
157#ifdef ROAR_HAVE_LIBVORBISENC
158 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
159 struct roar_stream * s = ROAR_STREAM(self->stream);
160 ogg_packet header;
161 ogg_packet header_comm;
162 ogg_packet header_code;
163 float ** encbuf;
164 int i, c;
165 int chans;
166 int end;
167 int16_t * data = (int16_t *) buf;
168
169 if ( ! self->opened ) {
170  vorbis_analysis_headerout(&(self->encoder.vd), &(self->encoder.vc), &header, &header_comm, &header_code);
171
172  ogg_stream_packetin(&(self->encoder.os), &header);
173  ogg_stream_packetin(&(self->encoder.os), &header_comm);
174  ogg_stream_packetin(&(self->encoder.os), &header_code);
175
176  while (ogg_stream_flush(&(self->encoder.os), &(self->encoder.og))) {
177   if ( stream_vio_s_write(self->stream, self->encoder.og.header, self->encoder.og.header_len)
178                                                                 != self->encoder.og.header_len ||
179        stream_vio_s_write(self->stream, self->encoder.og.body,   self->encoder.og.body_len  )
180                                                                 != self->encoder.og.body_len     ) {
181    free(self); // TODO: do we need addional cleanup?
182    return -1;
183   }
184  }
185  self->opened = 1;
186 } else {
187  encbuf = vorbis_analysis_buffer(&(self->encoder.vd), len /* TODO: need to lookup the menaing of this */);
188  chans  = s->info.channels;
189  end    = len/(2*chans);
190
191  if ( chans == 1 ) { // use optimized code
192   for (i = 0; i < end; i++)
193    encbuf[0][i] = data[i]/32768.0;
194
195  } else if ( chans == 2 ) { // use optimized code
196   for (i = 0; i < end; i++) {
197    encbuf[0][i] = data[2*i  ]/32768.0;
198    encbuf[1][i] = data[2*i+1]/32768.0;
199   }
200  } else { // use generic multi channel code
201   for (i = 0; i < end; i++) {
202    for (c = 0; c < chans; c++) {
203     encbuf[c][i] = data[chans*i+c]/32768.0;
204    }
205   }
206  }
207
208  vorbis_analysis_wrote(&(self->encoder.vd), i);
209
210  while ( vorbis_analysis_blockout(&(self->encoder.vd), &(self->encoder.vb)) == 1 ) {
211   vorbis_analysis(&(self->encoder.vb), &(self->encoder.op));
212   vorbis_bitrate_addblock(&(self->encoder.vb));
213
214   while ( vorbis_bitrate_flushpacket(&(self->encoder.vd), &(self->encoder.op)) ) {
215    ogg_stream_packetin(&(self->encoder.os), &(self->encoder.op));
216
217    while( ogg_stream_pageout(&(self->encoder.os), &(self->encoder.og)) ) {
218     if (
219          stream_vio_s_write(self->stream, self->encoder.og.header, self->encoder.og.header_len) == -1 ||
220          stream_vio_s_write(self->stream, self->encoder.og.body,   self->encoder.og.body_len  ) == -1   ) {
221      return -1;
222     }
223    }
224   }
225  }
226 }
227
228  return len; // we assume every thing was written (at least into our dsp anaylises buffer
229#else
230 errno = ENOSYS;
231 return -1;
232#endif
233}
234int cf_vorbis_read(CODECFILTER_USERDATA_T   inst, char * buf, int len) {
235 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
236 long r;
237 long todo = len;
238 long done = 0;
239
240// printf("cf_vorbis_read(inst=%p, buf=%p, len=%i) = ?\n", inst, buf, len);
241
242 self->opened++;
243 if ( self->opened == 16 ) {
244
245  //printf("cf_vorbis_read(*): opening...\n");
246//int ov_open_callbacks(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks);
247  if ( ov_open_callbacks((void*)self->stream, &(self->vf), NULL, 0, _g_cf_vorbis_vfvio) < 0 ) {
248//  if ( ov_open(self->in, &(self->vf), NULL, 0) < 0 ) {
249//   free((void*)self);
250   return 0;
251  }
252  errno = EAGAIN;
253  return -1;
254 }
255
256 if ( self->opened < 16 ) {
257  errno = EAGAIN;
258  return -1;
259 }
260
261
262 self->got_it_running = 1;
263
264 while (todo) {
265  r = ov_read(&(self->vf), buf+done, todo, 0, 2, 1, &(self->current_section));
266  if ( r == OV_HOLE ) {
267   ROAR_DBG("cf_vorbis_read(*): Hole in stream");
268  } else if ( r < 1 ) {
269   break;
270  } else {
271   if ( self->last_section != self->current_section )
272    if ( cf_vorbis_update_stream(self) == -1 )
273     return 0;
274
275   self->last_section = self->current_section;
276   todo -= r;
277   done += r;
278  }
279 }
280
281//printf("ov_read(*) = %i\n", done);
282
283 if ( done == 0 ) {
284  // do some EOF handling...
285  return 0;
286 } else {
287  return len;
288 }
289}
290
291int cf_vorbis_update_stream (struct codecfilter_vorbis_inst * self) {
292 vorbis_info *vi = ov_info(&(self->vf), -1);
293 char **ptr = ov_comment(&(self->vf), -1)->user_comments;
294 char key[ROAR_META_MAX_NAMELEN] = {0}, value[LIBROAR_BUFFER_MSGDATA] = {0};
295 struct roar_stream * s = ROAR_STREAM(self->stream);
296 int type;
297 int j, h = 0;
298 float rpg_track = 0, rpg_album = 0;
299 int meta_ok;
300
301 s->info.channels = vi->channels;
302 s->info.rate     = vi->rate;
303 s->info.bits     = 16;
304 s->info.codec    = ROAR_CODEC_DEFAULT;
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 //printf("RPG: mul=%i, div=%i\n", self->stream->mixer.rpg_mul, self->stream->mixer.rpg_div);
369 return 0;
370}
371
372#endif
373
374//ll
Note: See TracBrowser for help on using the repository browser.