diff --git a/Makefile.am b/Makefile.am index 5f0291d7e..4df940cb6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -268,6 +268,7 @@ endif # Generic utility library libutil_a_SOURCES = \ + src/util/AllocatedArray.hxx \ src/util/StringUtil.cxx src/util/StringUtil.hxx \ src/util/Tokenizer.cxx src/util/Tokenizer.hxx \ src/util/UriUtil.cxx src/util/UriUtil.hxx \ diff --git a/src/encoder/LameEncoderPlugin.cxx b/src/encoder/LameEncoderPlugin.cxx index 2dbd6a0df..2e988d89a 100644 --- a/src/encoder/LameEncoderPlugin.cxx +++ b/src/encoder/LameEncoderPlugin.cxx @@ -21,6 +21,8 @@ #include "LameEncoderPlugin.hxx" #include "EncoderAPI.hxx" #include "AudioFormat.hxx" +#include "util/AllocatedArray.hxx" +#include "util/Manual.hxx" #include @@ -38,7 +40,7 @@ struct LameEncoder final { lame_global_flags *gfp; - unsigned char output_buffer[32768]; + Manual> output_buffer; size_t output_buffer_length, output_buffer_position; LameEncoder():encoder(lame_encoder_plugin) {} @@ -209,6 +211,8 @@ lame_encoder_open(Encoder *_encoder, AudioFormat &audio_format, return false; } + encoder->output_buffer.Construct(); + encoder->output_buffer_length = 0; encoder->output_buffer_position = 0; @@ -221,6 +225,7 @@ lame_encoder_close(Encoder *_encoder) LameEncoder *encoder = (LameEncoder *)_encoder; lame_close(encoder->gfp); + encoder->output_buffer.Destruct(); } static bool @@ -236,14 +241,22 @@ lame_encoder_write(Encoder *_encoder, const unsigned num_frames = length / encoder->audio_format.GetFrameSize(); + const unsigned num_samples = + length / encoder->audio_format.GetSampleSize(); + + /* worst-case formula according to LAME documentation */ + const size_t output_buffer_size = 5 * num_samples / 4 + 7200; + encoder->output_buffer->GrowDiscard(output_buffer_size); + + const auto output_buffer = encoder->output_buffer->begin(); /* this is for only 16-bit audio */ int bytes_out = lame_encode_buffer_interleaved(encoder->gfp, const_cast(src), num_frames, - encoder->output_buffer, - sizeof(encoder->output_buffer)); + output_buffer, + output_buffer_size); if (bytes_out < 0) { g_set_error(error, lame_encoder_quark(), 0, @@ -269,7 +282,8 @@ lame_encoder_read(Encoder *_encoder, void *dest, size_t length) if (length > remainning) length = remainning; - memcpy(dest, encoder->output_buffer + encoder->output_buffer_position, + memcpy(dest, + encoder->output_buffer->begin() + encoder->output_buffer_position, length); encoder->output_buffer_position += length; diff --git a/src/util/AllocatedArray.hxx b/src/util/AllocatedArray.hxx new file mode 100644 index 000000000..e3bcf8c64 --- /dev/null +++ b/src/util/AllocatedArray.hxx @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2010 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALLOCATED_ARRAY_HPP +#define ALLOCATED_ARRAY_HPP + +#include "gcc.h" + +#include +#include + +/** + * An array allocated on the heap with a length determined at runtime. + */ +template +class AllocatedArray { +public: + typedef size_t size_type; + +protected: + size_type the_size; + T *restrict data; + +public: + typedef T *iterator; + typedef const T *const_iterator; + +public: + constexpr AllocatedArray():the_size(0), data(nullptr) {} + + explicit AllocatedArray(size_type _size) + :the_size(_size), data(new T[the_size]) { + assert(size() == 0 || data != nullptr); + } + + explicit AllocatedArray(const AllocatedArray &other) + :the_size(other.size()), data(new T[the_size]) { + assert(size() == 0 || data != nullptr); + assert(other.size() == 0 || other.data != nullptr); + + std::copy(other.data, other.data + the_size, data); + } + + explicit AllocatedArray(AllocatedArray &&other) + :the_size(other.the_size), data(other.data) { + other.the_size = 0; + other.data = nullptr; + } + + ~AllocatedArray() { + delete[] data; + } + + AllocatedArray &operator=(const AllocatedArray &other) { + assert(size() == 0 || data != nullptr); + assert(other.size() == 0 || other.data != nullptr); + + if (&other == this) + return *this; + + ResizeDiscard(other.size()); + std::copy(other.begin(), other.end(), data); + return *this; + } + + AllocatedArray &operator=(AllocatedArray &&other) { + std::swap(the_size, other.the_size); + std::swap(data, other.data); + return *this; + } + + /** + * Returns true if no memory was allocated so far. + */ + constexpr bool empty() const { + return the_size == 0; + } + + /** + * Returns the number of allocated elements. + */ + constexpr size_type size() const { + return the_size; + } + + /** + * Returns one element. No bounds checking. + */ + T &operator[](size_type i) { + assert(i < size()); + + return data[i]; + } + + /** + * Returns one constant element. No bounds checking. + */ + const T &operator[](size_type i) const { + assert(i < size()); + + return data[i]; + } + + iterator begin() { + return data; + } + + constexpr const_iterator begin() const { + return data; + } + + iterator end() { + return data + the_size; + } + + constexpr const_iterator end() const { + return data + the_size; + } + + /** + * Resizes the array, discarding old data. + */ + void ResizeDiscard(size_type _size) { + if (_size == the_size) + return; + + delete[] data; + the_size = _size; + data = new T[the_size]; + + assert(size() == 0 || data != nullptr); + } + + /** + * Grows the array to the specified size, discarding old data. + * Similar to ResizeDiscard(), but will never shrink the array to + * avoid expensive heap operations. + */ + void GrowDiscard(size_type _size) { + if (_size > the_size) + ResizeDiscard(_size); + } + + /** + * Grows the array to the specified size, preserving the value of a + * range of elements, starting from the beginning. + */ + void GrowPreserve(size_type _size, size_type preserve) { + if (_size <= the_size) + return; + + T *new_data = new T[_size]; + assert(_size == 0 || new_data != nullptr); + + std::move(data, data + preserve, new_data); + + delete[] data; + data = new_data; + the_size = _size; + } +}; + +#endif