00001
00002
00003
00004
00005
00006
00007 #include "wvoggvorbis.h"
00008 #include <ogg/ogg.h>
00009 #include <vorbis/codec.h>
00010 #include <vorbis/vorbisenc.h>
00011 #include <unistd.h>
00012
00013 #define OGG_VORBIS_ENCODER_BUF_SIZE 1024 // 1k samples for Vorbis
00014 #define OGG_VORBIS_DECODER_BUF_SIZE 16384 // at least 8k for Vorbis
00015
00016
00017
00018 WvOggVorbisEncoder::WvOggVorbisEncoder(
00019 const BitrateSpec &bitratespec,
00020 int samplingrate, int channels, long serialno) :
00021 oggstream(NULL), ovinfo(NULL), ovcomment(NULL),
00022 ovdsp(NULL), ovblock(NULL), wrote_header(false)
00023 {
00024 assert(channels == 1 || ! "stereo not supported yet");
00025
00026
00027 if (serialno == RANDOM_SERIALNO)
00028 {
00029 serialno = rand();
00030 }
00031
00032
00033 int retval;
00034 oggstream = new ogg_stream_state;
00035 if ((retval = ogg_stream_init(oggstream, serialno)) != 0)
00036 {
00037 seterror("error %s during ogg_stream_init", retval);
00038 return;
00039 }
00040
00041
00042 ovinfo = new vorbis_info;
00043 vorbis_info_init(ovinfo);
00044
00045 ovcomment = new vorbis_comment;
00046 vorbis_comment_init(ovcomment);
00047
00048
00049 switch (bitratespec.mode)
00050 {
00051 case BitrateSpec::VBR_QUALITY:
00052 if ((retval = vorbis_encode_init_vbr(ovinfo, channels,
00053 samplingrate, bitratespec.quality_index)) < 0)
00054 {
00055 seterror("error %s during vorbis_encode_init_vbr", retval);
00056 return;
00057 }
00058 break;
00059
00060 case BitrateSpec::VBR_BITRATE:
00061 if ((retval = vorbis_encode_init(ovinfo, channels, samplingrate,
00062 bitratespec.max_bitrate, bitratespec.nominal_bitrate,
00063 bitratespec.min_bitrate)) < 0)
00064 {
00065 seterror("error %s during vorbis_encode_init", retval);
00066 return;
00067 }
00068 break;
00069 }
00070
00071
00072 ovdsp = new vorbis_dsp_state;
00073 if ((retval = vorbis_analysis_init(ovdsp, ovinfo)) != 0)
00074 {
00075 seterror("error %s during vorbis_analysis_init", retval);
00076 return;
00077 }
00078 ovblock = new vorbis_block;
00079 if ((retval = vorbis_block_init(ovdsp, ovblock)) != 0)
00080 {
00081 seterror("error %s during vorbis_block_init", retval);
00082 return;
00083 }
00084 }
00085
00086
00087 WvOggVorbisEncoder::~WvOggVorbisEncoder()
00088 {
00089
00090 if (ovblock)
00091 {
00092 vorbis_block_clear(ovblock);
00093 delete ovblock;
00094 }
00095 if (ovdsp)
00096 {
00097 vorbis_dsp_clear(ovdsp);
00098 delete ovdsp;
00099 }
00100
00101
00102 if (ovcomment)
00103 {
00104 vorbis_comment_clear(ovcomment);
00105 delete ovcomment;
00106 }
00107 if (ovinfo)
00108 {
00109 vorbis_info_clear(ovinfo);
00110 delete ovinfo;
00111 }
00112
00113
00114 if (oggstream)
00115 {
00116 ogg_stream_clear(oggstream);
00117 delete oggstream;
00118 }
00119 }
00120
00121 void WvOggVorbisEncoder::add_comment(WvStringParm comment)
00122 {
00123 if (! comment) return;
00124
00125 char *str = const_cast<char *>(comment.cstr());
00126 vorbis_comment_add(ovcomment, str);
00127 }
00128
00129
00130 void WvOggVorbisEncoder::add_tag(WvStringParm tag, WvStringParm value)
00131 {
00132 if (! tag || ! value) return;
00133
00134 char *tagstr = const_cast<char *>(tag.cstr());
00135 char *valuestr = const_cast<char *>(value.cstr());
00136 vorbis_comment_add_tag(ovcomment, tagstr, valuestr);
00137 }
00138
00139
00140 bool WvOggVorbisEncoder::_typedencode(IBuffer &inbuf, OBuffer &outbuf,
00141 bool flush)
00142 {
00143
00144 if (! wrote_header)
00145 {
00146 if (! write_header(outbuf))
00147 return false;
00148 wrote_header = true;
00149 }
00150
00151
00152 for (;;)
00153 {
00154
00155 size_t ovsamples = inbuf.used();
00156 if (ovsamples == 0)
00157 {
00158
00159 if (flush)
00160 if (! write_stream(outbuf, true))
00161 return false;
00162 return true;
00163 }
00164 if (ovsamples > OGG_VORBIS_ENCODER_BUF_SIZE)
00165 ovsamples = OGG_VORBIS_ENCODER_BUF_SIZE;
00166
00167 float **ovbuf = vorbis_analysis_buffer(ovdsp, ovsamples);
00168 if (ovbuf == NULL)
00169 {
00170 seterror("error allocating vorbis analysis buffer");
00171 return false;
00172 }
00173 inbuf.move(ovbuf[0], ovsamples);
00174 vorbis_analysis_wrote(ovdsp, ovsamples);
00175 process_audio(outbuf);
00176 }
00177 }
00178
00179
00180 bool WvOggVorbisEncoder::_typedfinish(OBuffer &outbuf)
00181 {
00182
00183 if (! wrote_header)
00184 {
00185 if (! write_header(outbuf))
00186 return false;
00187 wrote_header = true;
00188 }
00189
00190
00191 vorbis_analysis_wrote(ovdsp, 0);
00192 process_audio(outbuf);
00193 return true;
00194 }
00195
00196
00197 bool WvOggVorbisEncoder::write_header(OBuffer &outbuf)
00198 {
00199
00200 ogg_packet headers[3];
00201 int retval;
00202 if ((retval = vorbis_analysis_headerout(ovdsp, ovcomment,
00203 & headers[0], & headers[1], & headers[2])) != 0)
00204 {
00205 seterror("error %s during vorbis_analysis_headerout", retval);
00206 return false;
00207 }
00208
00209
00210 for (int i = 0; i < 3; ++i)
00211 ogg_stream_packetin(oggstream, & headers[i]);
00212
00213
00214 return write_stream(outbuf, true );
00215 }
00216
00217
00218 bool WvOggVorbisEncoder::write_stream(OBuffer &outbuf, bool flush)
00219 {
00220 ogg_page oggpage;
00221 for (;;)
00222 {
00223 if (flush)
00224 {
00225 int retval = ogg_stream_flush(oggstream, & oggpage);
00226 if (retval == 0)
00227 break;
00228 else if (retval < 0)
00229 {
00230 seterror("error %s during ogg_stream_flush", retval);
00231 return false;
00232 }
00233 }
00234 else
00235 {
00236 int retval = ogg_stream_pageout(oggstream, & oggpage);
00237 if (retval == 0)
00238 break;
00239 else if (retval < 0)
00240 {
00241 seterror("error %s during ogg_stream_pageout", retval);
00242 return false;
00243 }
00244 }
00245 outbuf.put(oggpage.header, oggpage.header_len);
00246 outbuf.put(oggpage.body, oggpage.body_len);
00247 }
00248 return true;
00249 }
00250
00251
00252 bool WvOggVorbisEncoder::process_audio(OBuffer &outbuf)
00253 {
00254 while (vorbis_analysis_blockout(ovdsp, ovblock) == 1)
00255 {
00256
00257 int retval = vorbis_analysis(ovblock, NULL);
00258 if (retval < 0)
00259 {
00260 seterror("error %s during vorbis_analysis", retval);
00261 return false;
00262 }
00263 retval = vorbis_bitrate_addblock(ovblock);
00264 if (retval < 0)
00265 {
00266 seterror("error %s during vorbis_bitrate_addblock", retval);
00267 return false;
00268 }
00269
00270
00271 ogg_packet oggpacket;
00272 while (vorbis_bitrate_flushpacket(ovdsp, & oggpacket) > 0)
00273 {
00274 ogg_stream_packetin(oggstream, & oggpacket);
00275 if (! write_stream(outbuf, false))
00276 return false;
00277 }
00278 }
00279 return true;
00280 }
00281
00282
00283
00284
00285
00286 WvOggVorbisDecoder::WvOggVorbisDecoder() :
00287 oggsync(NULL), oggstream(NULL), ovinfo(NULL), ovcomment(NULL),
00288 ovdsp(NULL), ovblock(NULL), need_serialno(true), need_headers(3)
00289 {
00290 int retval;
00291
00292
00293 oggsync = new ogg_sync_state;
00294 if ((retval = ogg_sync_init(oggsync)) != 0)
00295 {
00296 seterror("error %s during ogg_sync_init", retval);
00297 return;
00298 }
00299 oggpage = new ogg_page;
00300 }
00301
00302
00303 WvOggVorbisDecoder::~WvOggVorbisDecoder()
00304 {
00305
00306 if (ovblock)
00307 {
00308 vorbis_block_clear(ovblock);
00309 delete ovblock;
00310 }
00311 if (ovdsp)
00312 {
00313 vorbis_dsp_clear(ovdsp);
00314 delete ovdsp;
00315 }
00316
00317
00318 if (ovcomment)
00319 {
00320 vorbis_comment_clear(ovcomment);
00321 delete ovcomment;
00322 }
00323 if (ovinfo)
00324 {
00325 vorbis_info_clear(ovinfo);
00326 delete ovinfo;
00327 }
00328
00329
00330 if (oggstream)
00331 {
00332 ogg_stream_clear(oggstream);
00333 delete oggstream;
00334 }
00335
00336
00337 delete oggpage;
00338 ogg_sync_clear(oggsync);
00339 delete oggsync;
00340 }
00341
00342
00343 bool WvOggVorbisDecoder::isheaderok() const
00344 {
00345 return need_headers == 0;
00346 }
00347
00348
00349 bool WvOggVorbisDecoder::_typedencode(IBuffer &inbuf, OBuffer &outbuf,
00350 bool flush)
00351 {
00352 bool checkheaderok = ! isheaderok() && ! flush;
00353 for (;;)
00354 {
00355 if (oggstream)
00356 {
00357
00358 ogg_packet oggpacket;
00359 while (ogg_stream_packetout(oggstream, & oggpacket) > 0)
00360 {
00361 if (! process_packet(& oggpacket, outbuf))
00362 return false;
00363 }
00364
00365
00366 if (oggstream->e_o_s)
00367 {
00368 setfinished();
00369 return true;
00370 }
00371 }
00372
00373
00374 while (ogg_sync_pageseek(oggsync, oggpage) <= 0)
00375 {
00376
00377 size_t oggbufsize = inbuf.used();
00378 if (oggbufsize == 0)
00379 {
00380
00381 if (flush && oggsync->fill != 0)
00382 return false;
00383 return true;
00384 }
00385 if (oggbufsize > OGG_VORBIS_DECODER_BUF_SIZE)
00386 oggbufsize = OGG_VORBIS_DECODER_BUF_SIZE;
00387
00388 char *oggbuf = ogg_sync_buffer(oggsync, oggbufsize);
00389 if (oggbuf == NULL)
00390 {
00391 seterror("error allocating ogg sync buffer");
00392 return false;
00393 }
00394 inbuf.move(oggbuf, oggbufsize);
00395 ogg_sync_wrote(oggsync, oggbufsize);
00396 }
00397
00398 if (! process_page(oggpage, outbuf))
00399 return false;
00400
00401
00402
00403
00404
00405 if (checkheaderok && isheaderok())
00406 return true;
00407 }
00408 }
00409
00410
00411 bool WvOggVorbisDecoder::_typedfinish(OBuffer &outbuf)
00412 {
00413 if (! isheaderok())
00414 {
00415 seterror("failed to detect an Ogg Vorbis stream");
00416 return false;
00417 }
00418 return true;
00419 }
00420
00421
00422 bool WvOggVorbisDecoder::process_page(ogg_page *oggpage,
00423 OBuffer &outbuf)
00424 {
00425 if (need_serialno)
00426 {
00427
00428 long serialno = ogg_page_serialno(oggpage);
00429 if (! prepare_stream(serialno))
00430 return false;
00431 need_serialno = false;
00432 }
00433
00434 if (ogg_stream_pagein(oggstream, oggpage) != 0)
00435 {
00436
00437
00438 return true;
00439 }
00440 return true;
00441 }
00442
00443
00444 bool WvOggVorbisDecoder::process_packet(ogg_packet *oggpacket,
00445 OBuffer &outbuf)
00446 {
00447 if (need_headers > 0)
00448 {
00449
00450 int result = vorbis_synthesis_headerin(ovinfo,
00451 ovcomment, oggpacket);
00452 if (result != 0)
00453 {
00454 seterror("error %s reading vorbis headers "
00455 "(not a vorbis stream?)", result);
00456 return false;
00457 }
00458 if (--need_headers == 0)
00459 return prepare_dsp();
00460 }
00461 else
00462 {
00463
00464 if (vorbis_synthesis(ovblock, oggpacket) != 0)
00465 {
00466
00467 return true;
00468 }
00469 vorbis_synthesis_blockin(ovdsp, ovblock);
00470
00471
00472 for (;;) {
00473 float **pcm;
00474 long samples = vorbis_synthesis_pcmout(ovdsp, &pcm);
00475 if (samples == 0) break;
00476
00477 outbuf.put(pcm[0], samples);
00478 vorbis_synthesis_read(ovdsp, samples);
00479 }
00480 }
00481 return true;
00482 }
00483
00484
00485 bool WvOggVorbisDecoder::prepare_dsp()
00486 {
00487
00488 for (int i = 0; i < ovcomment->comments; ++i)
00489 {
00490 char *c = ovcomment->user_comments[i];
00491 if (c)
00492 comment_list.append(new WvString(c), true);
00493 }
00494
00495
00496 int retval;
00497 ovdsp = new vorbis_dsp_state;
00498 if ((retval = vorbis_synthesis_init(ovdsp, ovinfo)) != 0)
00499 {
00500 seterror("error %s during vorbis_synthesis_init", retval);
00501 return false;
00502 }
00503 ovblock = new vorbis_block;
00504 if ((retval = vorbis_block_init(ovdsp, ovblock)) != 0)
00505 {
00506 seterror("error %s during vorbis_block_init", retval);
00507 return false;
00508 }
00509 return true;
00510 }
00511
00512
00513 bool WvOggVorbisDecoder::prepare_stream(long serialno)
00514 {
00515
00516 oggstream = new ogg_stream_state;
00517 int retval;
00518 if ((retval = ogg_stream_init(oggstream, serialno)) != 0)
00519 {
00520 seterror("error %s during ogg_stream_init", retval);
00521 return false;
00522 }
00523
00524
00525 ovinfo = new vorbis_info;
00526 vorbis_info_init(ovinfo);
00527 ovcomment = new vorbis_comment;
00528 vorbis_comment_init(ovcomment);
00529 return true;
00530 }