From e79456d5850c35b8646342a7e8d3cbded31be364 Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Mon, 10 Jan 2011 19:12:38 +0100 Subject: Added Vorbis encoder. --- SConstruct | 1 + encoder.cpp | 2 + encoders/vorbis_encoder.cpp | 115 ++++++++++++++++++++++++++++++++++++++++++++ encoders/vorbis_encoder.h | 31 ++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 encoders/vorbis_encoder.cpp create mode 100644 encoders/vorbis_encoder.h diff --git a/SConstruct b/SConstruct index 3e123a2..6b960cf 100644 --- a/SConstruct +++ b/SConstruct @@ -23,6 +23,7 @@ else: env.ParseConfig('pkg-config --cflags --libs libmpg123') env.ParseConfig('pkg-config --cflags --libs id3tag') +env.ParseConfig('pkg-config --cflags --libs vorbisenc') env.Program('audistd', Glob('*.cpp') + Glob('decoders/*.cpp') + Glob('encoders/*.cpp')) diff --git a/encoder.cpp b/encoder.cpp index 9d352dc..06fc6ba 100644 --- a/encoder.cpp +++ b/encoder.cpp @@ -1,5 +1,6 @@ #include "encoder.h" #include "encoders/lame_encoder.h" +#include "encoders/vorbis_encoder.h" #include #include @@ -11,6 +12,7 @@ std::map encoder_factories; void Encoder::init() { encoder_factories["lame"] = boost::factory >(); + encoder_factories["vorbis"] = boost::factory >(); } Encoder::p Encoder::get(const std::string& name, RawAudioSource::p source) { diff --git a/encoders/vorbis_encoder.cpp b/encoders/vorbis_encoder.cpp new file mode 100644 index 0000000..4256a20 --- /dev/null +++ b/encoders/vorbis_encoder.cpp @@ -0,0 +1,115 @@ +#include "vorbis_encoder.h" + +#include +#include + +VorbisEncoder::VorbisEncoder(RawAudioSource::p source_) : source(source_) { + vorbis_info_init(&vi); + vorbis_encode_init_vbr(&vi, 2, 44100, .4); + vorbis_analysis_init(&dsp, &vi); + vorbis_block_init(&dsp, &vb); + + vorbis_comment vc; + vorbis_comment_init(&vc); + // add tags and stuff here + ogg_packet op, op_comm, op_code; + vorbis_analysis_headerout(&dsp, &vc, &op, &op_comm, &op_code); + + ogg_stream_init(&os, 0); + ogg_stream_packetin(&os, &op); + ogg_stream_packetin(&os, &op_comm); + ogg_stream_packetin(&os, &op_code); + + headers_written = false; +} + +VorbisEncoder::~VorbisEncoder() { + vorbis_info_clear(&vi); + vorbis_dsp_clear(&dsp); + vorbis_block_clear(&vb); + ogg_stream_clear(&os); +} + +/** Write ogg stream pages to the buffer. + * \return Number of bytes written. + */ +std::size_t VorbisEncoder::write_pages(char *buf, std::size_t buf_size) { + std::size_t written = 0; + while(ogg_stream_pageout(&os, &og)) { + std::size_t total_len = (std::size_t)(og.header_len + og.body_len); + // TODO: Handle this somehow + if(written + total_len > buf_size) { + throw std::runtime_error("page too large"); + } + std::memcpy(buf, og.header, og.header_len); + std::memcpy(buf+og.header_len, og.body, og.body_len); + written = og.header_len + og.body_len; + } + return written; +} + +/** Read from source and write pages. + * Decodes a number of samples read from \c source, + * then calls \c write_pages to write completed pages. + * \return Number of bytes written by \c write_pages. + */ +std::size_t VorbisEncoder::decode(char *buf, std::size_t buf_size) { + const int samples_n = 1024; + + // samples_n samples, 2 channels, 2 byte per sample + char src_data[samples_n*2*2]; + int16_t *src_data_16 = (int16_t*)src_data; + + std::streamsize src_read = source->read(src_data, samples_n*2*2); + if(src_read % 4) { + throw std::runtime_error("invalid buffer size"); + } + + if(src_read == 0) { + vorbis_analysis_wrote(&dsp, 0); + } else { + int samples_read = src_read / 4; + float **buffer = vorbis_analysis_buffer(&dsp, samples_read); + int sample; + for(sample = 0; sample < samples_read; sample++) { + for(int c = 0; c < 2; c++) { + int i = sample*2+c; + if(i*2 >= src_read) break; + buffer[c][sample] = src_data_16[sample*2+c]/32768.; + } + } + vorbis_analysis_wrote(&dsp, sample); + } + + while(vorbis_analysis_blockout(&dsp, &vb) == 1) { + vorbis_analysis(&vb, NULL); + vorbis_bitrate_addblock(&vb); + ogg_packet op; + while(vorbis_bitrate_flushpacket(&dsp, &op) == 1) { + ogg_stream_packetin(&os, &op); + } + } + + return write_pages(buf, buf_size); +} + +std::size_t VorbisEncoder::read(char* buf, std::size_t buf_size) { + if(!headers_written) { + headers_written = true; + if(!ogg_stream_flush(&os, &og)) { + throw std::runtime_error("couldn't flush ogg stream header"); + } + std::memcpy(buf, og.header, og.header_len); + std::memcpy(buf+og.header_len, og.body, og.body_len); + return og.header_len + og.body_len; + } + + std::size_t written = 0; + + // run until we have a page or reached end of stream + while(written == 0 && !ogg_page_eos(&og)) { + written += decode(buf+written, buf_size); + } + + return written; +} diff --git a/encoders/vorbis_encoder.h b/encoders/vorbis_encoder.h new file mode 100644 index 0000000..c3e3728 --- /dev/null +++ b/encoders/vorbis_encoder.h @@ -0,0 +1,31 @@ +#ifndef VORBIS_ENCODER_H +#define VORBIS_ENCODER_H + +#include "encoder.h" + +#include +#include + +class VorbisEncoder : public Encoder { + private: + vorbis_info vi; + vorbis_dsp_state dsp; + vorbis_block vb; + ogg_stream_state os; + ogg_page og; + RawAudioSource::p source; + //! Set to true when ogg stream headers are written. + bool headers_written; + + protected: + std::size_t write_pages(char *buf, std::size_t buf_size); + std::size_t decode(char *buf, std::size_t buf_size); + + public: + VorbisEncoder(RawAudioSource::p source_); + virtual ~VorbisEncoder (); + + virtual std::size_t read(char* buf, std::size_t buf_size); +}; + +#endif -- cgit v1.2.3