source: roaraudio/roard/codecfilter_vorbis.c @ 1226:c14eba752b62

Last change on this file since 1226:c14eba752b62 was 1226:c14eba752b62, checked in by phi, 15 years ago

moved code to cf_vorbis_encode_*() instaed of close/open/write...

File size: 11.0 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#endif
80
81 ROAR_DBG("cf_vorbis_open(*): info->id=%i", ROAR_STREAM(info)->id);
82
83 *inst = (CODECFILTER_USERDATA_T) self;
84
85 s->info.codec = ROAR_CODEC_DEFAULT;
86 s->info.bits  = 16;
87
88 if ( s->dir == ROAR_DIR_PLAY ) {
89  return 0;
90 } else if ( s->dir == ROAR_DIR_MONITOR || s->dir == ROAR_DIR_OUTPUT ) {
91#ifdef ROAR_HAVE_LIBVORBISENC
92  // set up the encoder here
93 if ( cf_vorbis_encode_start(self) == -1 ) {
94  free(self);
95  return -1;
96 }
97#else
98 free(self);
99 return -1;
100#endif
101 } else {
102  free(self);
103  return -1;
104 }
105
106 return 0;
107}
108
109int cf_vorbis_close(CODECFILTER_USERDATA_T   inst) {
110 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
111
112 if ( !inst )
113  return -1;
114
115 if ( self->got_it_running )
116  ov_clear(&(self->vf));
117
118#ifdef ROAR_HAVE_LIBVORBISENC
119 if ( self->encoding ) {
120  cf_vorbis_encode_end(self);
121 }
122#endif
123
124 free(inst);
125 return 0;
126}
127
128int cf_vorbis_write(CODECFILTER_USERDATA_T   inst, char * buf, int len) {
129#ifdef ROAR_HAVE_LIBVORBISENC
130 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
131 struct roar_stream * s = ROAR_STREAM(self->stream);
132 ogg_packet header;
133 ogg_packet header_comm;
134 ogg_packet header_code;
135 float ** encbuf;
136 int i, c;
137 int chans;
138 int end;
139 int16_t * data = (int16_t *) buf;
140
141 if ( ! self->opened ) {
142  vorbis_analysis_headerout(&(self->encoder.vd), &(self->encoder.vc), &header, &header_comm, &header_code);
143
144  ogg_stream_packetin(&(self->encoder.os), &header);
145  ogg_stream_packetin(&(self->encoder.os), &header_comm);
146  ogg_stream_packetin(&(self->encoder.os), &header_code);
147
148  while (ogg_stream_flush(&(self->encoder.os), &(self->encoder.og))) {
149   if ( stream_vio_s_write(self->stream, self->encoder.og.header, self->encoder.og.header_len)
150                                                                 != self->encoder.og.header_len ||
151        stream_vio_s_write(self->stream, self->encoder.og.body,   self->encoder.og.body_len  )
152                                                                 != self->encoder.og.body_len     ) {
153    free(self); // TODO: do we need addional cleanup?
154    return -1;
155   }
156  }
157  self->opened = 1;
158 } else {
159  encbuf = vorbis_analysis_buffer(&(self->encoder.vd), len /* TODO: need to lookup the menaing of this */);
160  chans  = s->info.channels;
161  end    = len/(2*chans);
162
163  if ( chans == 1 ) { // use optimized code
164   for (i = 0; i < end; i++)
165    encbuf[0][i] = data[i]/32768.0;
166
167  } else if ( chans == 2 ) { // use optimized code
168   for (i = 0; i < end; i++) {
169    encbuf[0][i] = data[2*i  ]/32768.0;
170    encbuf[1][i] = data[2*i+1]/32768.0;
171   }
172  } else { // use generic multi channel code
173   for (i = 0; i < end; i++) {
174    for (c = 0; c < chans; c++) {
175     encbuf[c][i] = data[chans*i+c]/32768.0;
176    }
177   }
178  }
179
180  vorbis_analysis_wrote(&(self->encoder.vd), i);
181
182  while ( vorbis_analysis_blockout(&(self->encoder.vd), &(self->encoder.vb)) == 1 ) {
183   vorbis_analysis(&(self->encoder.vb), &(self->encoder.op));
184   vorbis_bitrate_addblock(&(self->encoder.vb));
185
186   while ( vorbis_bitrate_flushpacket(&(self->encoder.vd), &(self->encoder.op)) ) {
187    ogg_stream_packetin(&(self->encoder.os), &(self->encoder.op));
188
189    while( ogg_stream_pageout(&(self->encoder.os), &(self->encoder.og)) ) {
190     if (
191          stream_vio_s_write(self->stream, self->encoder.og.header, self->encoder.og.header_len) == -1 ||
192          stream_vio_s_write(self->stream, self->encoder.og.body,   self->encoder.og.body_len  ) == -1   ) {
193      return -1;
194     }
195    }
196   }
197  }
198 }
199
200  return len; // we assume every thing was written (at least into our dsp anaylises buffer
201#else
202 errno = ENOSYS;
203 return -1;
204#endif
205}
206int cf_vorbis_read(CODECFILTER_USERDATA_T   inst, char * buf, int len) {
207 struct codecfilter_vorbis_inst * self = (struct codecfilter_vorbis_inst *) inst;
208 long r;
209 long todo = len;
210 long done = 0;
211
212// printf("cf_vorbis_read(inst=%p, buf=%p, len=%i) = ?\n", inst, buf, len);
213
214 self->opened++;
215 if ( self->opened == 16 ) {
216
217  //printf("cf_vorbis_read(*): opening...\n");
218  if ( ov_open_callbacks((void*)self->stream, &(self->vf), NULL, 0, _g_cf_vorbis_vfvio) < 0 ) {
219   return 0;
220  }
221  errno = EAGAIN;
222  return -1;
223 }
224
225 if ( self->opened < 16 ) {
226  errno = EAGAIN;
227  return -1;
228 }
229
230
231 self->got_it_running = 1;
232
233 while (todo) {
234  r = ov_read(&(self->vf), buf+done, todo, 0, 2, 1, &(self->current_section));
235  if ( r == OV_HOLE ) {
236   ROAR_DBG("cf_vorbis_read(*): Hole in stream");
237  } else if ( r < 1 ) {
238   break;
239  } else {
240   if ( self->last_section != self->current_section )
241    if ( cf_vorbis_update_stream(self) == -1 )
242     return 0;
243
244   self->last_section = self->current_section;
245   todo -= r;
246   done += r;
247  }
248 }
249
250//printf("ov_read(*) = %i\n", done);
251
252 if ( done == 0 ) {
253  // do some EOF handling...
254  return 0;
255 } else {
256  return len;
257 }
258}
259
260int cf_vorbis_update_stream (struct codecfilter_vorbis_inst * self) {
261 vorbis_info *vi = ov_info(&(self->vf), -1);
262 char **ptr = ov_comment(&(self->vf), -1)->user_comments;
263 char key[ROAR_META_MAX_NAMELEN] = {0}, value[LIBROAR_BUFFER_MSGDATA] = {0};
264 struct roar_stream * s = ROAR_STREAM(self->stream);
265 int type;
266 int j, h = 0;
267 float rpg_track = 0, rpg_album = 0;
268 int meta_ok;
269
270 s->info.channels = vi->channels;
271 s->info.rate     = vi->rate;
272 s->info.bits     = 16;
273 s->info.codec    = ROAR_CODEC_DEFAULT;
274
275 stream_meta_clear(s->id);
276
277 while(*ptr){
278  meta_ok = 1;
279
280   for (j = 0; (*ptr)[j] != 0 && (*ptr)[j] != '='; j++) {
281    if ( j == ROAR_META_MAX_NAMELEN ) {
282     ROAR_ERR("cf_vorbis_update_stream(*): invalid meta data on stream %i: meta data key too long", s->id);
283     meta_ok = 0;
284     j = 0;
285     break;
286    }
287    key[j] = (*ptr)[j];
288   }
289   key[j] = 0;
290
291   if ( meta_ok ) {
292    for (j++, h = 0; (*ptr)[j] != 0 && (*ptr)[j] != '='; j++) {
293     if ( h == LIBROAR_BUFFER_MSGDATA ) {
294      ROAR_ERR("update_stream(*): invalid meta data on stream %i: meta data value for key '%s' too long", s->id, key);
295      meta_ok = 0;
296      h = 0;
297      break;
298     }
299     value[h++] = (*ptr)[j];
300    }
301    value[h]   = 0;
302   }
303
304   if ( meta_ok ) {
305    type = roar_meta_inttype(key);
306    if ( type != -1 )
307     stream_meta_set(s->id, type, "", value);
308
309    ROAR_DBG("cf_vorbis_update_stream(*): Meta %-16s: %s", key, value);
310
311    if ( strcmp(key, "REPLAYGAIN_TRACK_PEAK") == 0 ) {
312     rpg_track = 1/atof(value);
313/*
314    } else if ( strcmp(key, "REPLAYGAIN_TRACK_GAIN") == 0 ) {
315     rpg_track = powf(10, atof(value)/20);
316*/
317    } else if ( strcmp(key, "REPLAYGAIN_ALBUM_PEAK") == 0 ) {
318     rpg_album = 1/atof(value);
319/*
320    } else if ( strcmp(key, "REPLAYGAIN_ALBUM_GAIN") == 0 ) {
321     rpg_album = powf(10, atof(value)/20);
322*/
323    }
324   }
325
326   ptr++;
327 }
328
329 if ( rpg_album ) {
330  self->stream->mixer.rpg_div = 2718;  // = int(exp(1)*1000)
331  self->stream->mixer.rpg_mul = (float)rpg_album*2718;
332 } else if ( rpg_track ) {
333  self->stream->mixer.rpg_div = 2718;
334  self->stream->mixer.rpg_mul = (float)rpg_track*2718;
335 }
336
337 stream_meta_finalize(s->id);
338 //printf("RPG: mul=%i, div=%i\n", self->stream->mixer.rpg_mul, self->stream->mixer.rpg_div);
339 return 0;
340}
341
342int cf_vorbis_encode_start  (struct codecfilter_vorbis_inst * self) {
343#ifdef ROAR_HAVE_LIBVORBISENC
344  memset(&(self->encoder), 0, sizeof(self->encoder));
345
346  self->encoding = 1;
347
348  vorbis_info_init(&(self->encoder.vi));
349  vorbis_comment_init(&(self->encoder.vc));
350  vorbis_comment_add_tag(&(self->encoder.vc), "SERVER", "RoarAudio");
351  vorbis_comment_add_tag(&(self->encoder.vc), "ENCODER", "RoarAudio Vorbis codecfilter");
352
353  if( vorbis_encode_init_vbr(&(self->encoder.vi), (long) ROAR_STREAM(self->stream)->info.channels,
354                                                  (long) ROAR_STREAM(self->stream)->info.rate,
355                                                  self->encoder.v_base_quality) != 0 ) {
356   ROAR_ERR("cf_vorbis_encode_start(*): Can not vorbis_encode_init_vbr(*)!");
357   vorbis_info_clear(&(self->encoder.vi)); // TODO: do we need to free vc also?
358   return -1;
359  }
360
361  vorbis_analysis_init(&(self->encoder.vd), &(self->encoder.vi));
362  vorbis_block_init(&(self->encoder.vd), &(self->encoder.vb));
363
364                                     //  "RA"<<16 + PID<<8 + Stream ID
365  ogg_stream_init(&(self->encoder.os), 0x52410000 + ((getpid() & 0xff)<<8) + (ROAR_STREAM(self->stream)->id & 0xff));
366 return 0;
367#else
368 return -1;
369#endif
370}
371
372int cf_vorbis_encode_end    (struct codecfilter_vorbis_inst * self) {
373#ifdef ROAR_HAVE_LIBVORBISENC
374 if ( self->encoding ) {
375  ogg_stream_clear(&(self->encoder.os));
376  vorbis_block_clear(&(self->encoder.vb));
377  vorbis_dsp_clear(&(self->encoder.vd));
378  vorbis_info_clear(&(self->encoder.vi));
379  self->opened = 0;
380 }
381
382 return 0;
383#else
384 return -1;
385#endif
386}
387
388#endif
389//ll
Note: See TracBrowser for help on using the repository browser.