#include "vorbis_encoder.h" #include #include VorbisEncoder::VorbisEncoder(RawAudioSource::p source_) : source(source_) { vorbis_info_init(&vi); vorbis_encode_init_vbr(&vi, source->get_channels(), source->get_samplerate(), .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. * Encodes 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::encode(char *buf, std::size_t buf_size) { const int samples_n = 1024; int channels = source->get_channels(); // samples_n samples, channels, 2 byte per sample char src_data[samples_n*channels*2]; int16_t *src_data_16 = (int16_t*)src_data; std::streamsize src_read = source->read(src_data, samples_n*channels*2); if(src_read <= 0) { vorbis_analysis_wrote(&dsp, 0); } else if(src_read % 4) { throw std::runtime_error("invalid buffer size"); } 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 < channels; c++) { int i = sample*channels+c; if(i*channels >= src_read) break; buffer[c][sample] = src_data_16[sample*channels+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 = encode(buf, buf_size); } return written; } std::string VorbisEncoder::get_mime_type() { return "application/ogg"; }