summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Bergli Heier <snakebite@jvnv.net>2011-01-10 19:12:38 +0100
committerJon Bergli Heier <snakebite@jvnv.net>2011-01-10 19:12:38 +0100
commite79456d5850c35b8646342a7e8d3cbded31be364 (patch)
treeca38129c06fc72a331094123668bb9fb1a30b7a5
parentfbbb74bac8db34e1d472213d0c625413545bc48e (diff)
Added Vorbis encoder.
-rw-r--r--SConstruct1
-rw-r--r--encoder.cpp2
-rw-r--r--encoders/vorbis_encoder.cpp115
-rw-r--r--encoders/vorbis_encoder.h31
4 files changed, 149 insertions, 0 deletions
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 <boost/function.hpp>
#include <boost/functional/factory.hpp>
@@ -11,6 +12,7 @@ std::map<std::string, EncoderFactory> encoder_factories;
void Encoder::init() {
encoder_factories["lame"] = boost::factory<boost::shared_ptr<EncoderLame> >();
+ encoder_factories["vorbis"] = boost::factory<boost::shared_ptr<VorbisEncoder> >();
}
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 <stdexcept>
+#include <cstring>
+
+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 <vorbis/vorbisenc.h>
+#include <ogg/ogg.h>
+
+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