From ebf318bde0a343d98980d19d77262c444837223c Mon Sep 17 00:00:00 2001 From: Leonardo Invernizzi Date: Wed, 14 Aug 2024 01:45:22 +0000 Subject: [PATCH] write_samples WIP --- .../roc_audio/vorbis_encoder.cpp | 114 +++++++++++++++--- .../roc_audio/vorbis_encoder.h | 9 +- 2 files changed, 104 insertions(+), 19 deletions(-) diff --git a/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.cpp b/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.cpp index b9eb200ef..32f0b8b27 100644 --- a/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.cpp +++ b/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.cpp @@ -8,6 +8,7 @@ #include "roc_audio/vorbis_encoder.h" #include "roc_core/panic.h" +#include namespace roc { namespace audio { @@ -18,9 +19,9 @@ VorbisEncoder::VorbisEncoder(const SampleSpec& sample_spec) , frame_size_(0) { vorbis_info_init(&vorbis_info_); - const int num_channels = static_cast(sample_spec.num_channels()); - const int sample_rate = static_cast(sample_spec.sample_rate()); - int ret = vorbis_encode_init_vbr(&vorbis_info_, num_channels, sample_rate, 0.0f); + const long num_channels = static_cast(sample_spec.num_channels()); + const long sample_rate = static_cast(sample_spec.sample_rate()); + int ret = vorbis_encode_init_vbr(&vorbis_info_, num_channels, sample_rate, 0.5f); if (ret != 0) { roc_panic("vorbis encoder: failed to initialize vorbis encoder"); } @@ -56,22 +57,16 @@ status::StatusCode VorbisEncoder::init_status() const { size_t VorbisEncoder::encoded_byte_count(size_t num_samples) const { if (!initialized_) { + roc_panic("vorbis encoder: encoder not initialized"); return 0; } - if (vorbis_info_.rate <= 0 || vorbis_info_.channels <= 0 - || vorbis_info_.bitrate_nominal <= 0) { - roc_panic("vorbis encoder: vorbis_info structure has invalid values"); - return 0; - } - - // Average number of bits used per sample, per channel. - const size_t bit_width = static_cast( - (vorbis_info_.bitrate_nominal / vorbis_info_.rate) / vorbis_info_.channels); + const size_t channels = static_cast(vorbis_info_.channels); + // const size_t sample_rate = static_cast(vorbis_info_.rate); + // const size_t bitrate_nominal = static_cast(vorbis_info_.bitrate_nominal); - const size_t encoded_bytes = (num_samples * bit_width + 7) / 8; - - return encoded_bytes; + size_t estimated_bytes = (32 * num_samples * channels) / (8); + return estimated_bytes; } void VorbisEncoder::begin_frame(void* frame_data, size_t frame_size) { @@ -84,12 +79,95 @@ void VorbisEncoder::begin_frame(void* frame_data, size_t frame_size) { } size_t VorbisEncoder::write_samples(const sample_t* samples, size_t n_samples) { - roc_panic("TODO"); - return 0; + if (!initialized_) { + roc_panic("vorbis encoder: encoder not initialized"); + return 0; + } + + if (!samples || n_samples == 0) { + return 0; + } + + buffer_samples_(samples, n_samples); + + size_t total_bytes_written = process_analysis_and_encoding_(); + + vorbis_analysis_wrote(&vorbis_dsp_, 0); + + total_bytes_written += process_analysis_and_encoding_(); + + return total_bytes_written; } void VorbisEncoder::end_frame() { - roc_panic("TODO"); + frame_data_ = NULL; + frame_size_ = 0; +} + +void VorbisEncoder::buffer_samples_(const sample_t* samples, size_t n_samples) { + const int int_n_samples = static_cast(n_samples); + + float** buffer = vorbis_analysis_buffer(&vorbis_dsp_, int_n_samples); + + for (int i = 0; i < int_n_samples; ++i) { + for (int ch = 0; ch < vorbis_info_.channels; ++ch) { + buffer[ch][i] = samples[i * vorbis_info_.channels + ch]; + } + } + + vorbis_analysis_wrote(&vorbis_dsp_, int_n_samples); +} + +size_t VorbisEncoder::process_analysis_and_encoding_() { + size_t total_bytes_written = 0; + + while (vorbis_analysis_blockout(&vorbis_dsp_, &vorbis_block_) == 1) { + vorbis_analysis(&vorbis_block_, NULL); + + vorbis_bitrate_addblock(&vorbis_block_); + + total_bytes_written += extract_and_write_packets_(); + } + + return total_bytes_written; +} + +size_t VorbisEncoder::extract_and_write_packets_() { + size_t bytes_written = 0; + + ogg_packet packet; + while (vorbis_bitrate_flushpacket(&vorbis_dsp_, &packet)) { + ogg_stream_packetin(&ogg_stream_, &packet); + + bytes_written += write_ogg_pages_(); + } + + return bytes_written; +} + +size_t VorbisEncoder::write_ogg_pages_() { + long bytes_written = 0; + + ogg_page page; + while (ogg_stream_pageout(&ogg_stream_, &page)) { + if (bytes_written + page.header_len + page.body_len + > static_cast(frame_size_)) { + roc_panic("vorbis encoder: frame buffer overflow"); + } + + write_to_frame_(page.header, page.header_len, bytes_written); + bytes_written += page.header_len; + + write_to_frame_(page.body, page.body_len, bytes_written); + bytes_written += page.body_len; + } + + return static_cast(bytes_written); +} + +void VorbisEncoder::write_to_frame_(const void* data, long size, long offset) { + const size_t casted_size = static_cast(size); + memcpy(static_cast(frame_data_) + offset, data, casted_size); } } // namespace audio diff --git a/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.h b/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.h index 33043d3d8..2331b1b08 100644 --- a/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.h +++ b/src/internal_modules/roc_audio/target_libvorbis/roc_audio/vorbis_encoder.h @@ -7,13 +7,14 @@ */ //! @file roc_audio/target_libvorbis/roc_audio/vorbis_encoder.h -//! @brief Vorbis audio decoder. +//! @brief Vorbis audio encoder. #ifndef ROC_AUDIO_VORBIS_ENCODER_H_ #define ROC_AUDIO_VORBIS_ENCODER_H_ #include "roc_audio/iframe_encoder.h" #include "roc_audio/sample_spec.h" +#include #include namespace roc { @@ -44,6 +45,12 @@ class VorbisEncoder : public IFrameEncoder { virtual void end_frame(); private: + void buffer_samples_(const sample_t* samples, size_t n_samples); + size_t process_analysis_and_encoding_(); + size_t extract_and_write_packets_(); + size_t write_ogg_pages_(); + void write_to_frame_(const void* data, long size, long offset); + bool initialized_; void* frame_data_; size_t frame_size_;