summaryrefslogtreecommitdiff
path: root/encoders/vorbis_encoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'encoders/vorbis_encoder.cpp')
-rw-r--r--encoders/vorbis_encoder.cpp115
1 files changed, 115 insertions, 0 deletions
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;
+}