diff --git a/src/buffer/buffer.cpp b/src/buffer/buffer.cpp new file mode 100644 index 0000000..978c4d0 --- /dev/null +++ b/src/buffer/buffer.cpp @@ -0,0 +1,86 @@ +#include "buffer.hpp" + +/* +A Buffer consists of three parts: +an incoming vector, a rollingbuffer, and an output vector. + +Note that the RollingBuffer class does most of the work: +its `get` and `put` methods move the values in our +input and output vectors. +*/ + +Buffer::Buffer( + const char *pipe_file, + size_t buffer_size, + size_t output_size + ): + buffer_size(buffer_size), + output_size(output_size), + pipe_file(pipe_file) + { + + sample_con_rate = 5; + sample_con_rate_up_ctr = 0; + sample_con_rate_dn_ctr = 0; + + incoming.resize(buffer_size); + rolling_buffer.resize(buffer_size); + + output.resize(output_size); +} + + +void Buffer::update() { + // TODO: + // Disable and enable FIFO here to get rid of + // the difference between audio and visualization. + + int fd = open(pipe_file, O_RDONLY); + + ssize_t bytes_read = read( + fd, + incoming.data(), + sizeof(int16_t) * incoming.size() + ); + + const auto begin = incoming.begin(); + const auto end = incoming.begin() + bytes_read/sizeof(int16_t); + + // Autoscale here + + rolling_buffer.put(begin, end); + + // 44100 samples per second / fps = samples per frame + // 60 fps + // *2 if stereo + size_t requested_samples = (44100 / 60) * pow(1.1, sample_con_rate); + + size_t new_samples = rolling_buffer.get( + requested_samples, + output + ); + + if (new_samples == 0) { + printf("no new samples\n"); + return; + } + + // A crude way to adjust the amount of samples consumed from the buffer + // depending on how fast the rendering is. + if (rolling_buffer.size() > 0) { + if (++sample_con_rate_up_ctr > 8) { + sample_con_rate_up_ctr = 0; + ++sample_con_rate; + } + } else if (sample_con_rate > 0) { + if (++sample_con_rate_dn_ctr > 4) { + sample_con_rate_dn_ctr = 0; + --sample_con_rate; + } + sample_con_rate_up_ctr = 0; + } + + if (fd >= 0) { + close(fd); + } +} \ No newline at end of file diff --git a/src/buffer/buffer.hpp b/src/buffer/buffer.hpp new file mode 100644 index 0000000..4a54ab2 --- /dev/null +++ b/src/buffer/buffer.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +// For reading FIFO +#include +#include + +#include "rollingbuffer.hpp" + + +class Buffer { + public: + Buffer( + const char *pipe_file, + size_t buffer_size, + size_t output_size + ); + + void update(); + + const std::vector& get_output() const { + return output; + }; + + private: + std::vector incoming; + RollingBuffer rolling_buffer; + std::vector output; + + size_t buffer_size; + size_t output_size; + const char *pipe_file; + + size_t sample_con_rate; + size_t sample_con_rate_up_ctr; + size_t sample_con_rate_dn_ctr; +}; diff --git a/src/buffer/rollingbuffer.hpp b/src/buffer/rollingbuffer.hpp new file mode 100644 index 0000000..a677ea8 --- /dev/null +++ b/src/buffer/rollingbuffer.hpp @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include + + +/* +This file defines a simple "RollingBuffer" class, based on the +implementation of SampleBuffer in ncmpcpp. +*/ + + +template +struct RollingBuffer { + public: + typedef typename std::vector::iterator Iterator; + + RollingBuffer() : m_offset(0) { } + + void put(Iterator begin, Iterator end); + size_t get(size_t elems, std::vector &dest); + + void resize(size_t n); + void clear(); + + size_t size() const; + const std::vector &buffer() const; + + private: + size_t m_offset; + std::vector m_buffer; +}; + + + +template +void RollingBuffer::put( + RollingBuffer::Iterator begin, + RollingBuffer::Iterator end + ) { + + // How many elements to put + size_t elems = end - begin; + if (elems > m_buffer.size()) { + throw std::out_of_range("Size of the buffer is smaller than the amount of elements"); + } + + // How much space we have for new elements + size_t free_elems = m_buffer.size() - m_offset; + + // If we don't have enough free space, + // make more by moving the buffer forwards. + if (elems > free_elems) { + size_t to_remove = elems - free_elems; + std::copy( + m_buffer.begin() + to_remove, + m_buffer.end() - free_elems, + m_buffer.begin() + ); + m_offset -= to_remove; + } + + // Add new elements to buffer + std::copy(begin, end, m_buffer.begin() + m_offset); + m_offset += elems; +} + + + +template +size_t RollingBuffer::get( + size_t elems, + std::vector &dest + ) { + // elems: how many samples to get + // dest: where to put them + + + // (m_offset == 0) => we have no data + if (m_offset == 0) { + return 0; + } + + // If too many elements are requested, + // give as much as we can. + if (elems > m_offset) { + elems = m_offset; + } + + if (elems >= dest.size()) { + // If dest is smaller than request size, + // discard earlier elements to fit. + size_t elems_lost = elems - dest.size(); + std::copy(m_buffer.begin() + elems_lost, m_buffer.begin() + elems, dest.begin()); + } else { + // otherwise, copy samples to the destination buffer. + std::copy(dest.begin() + elems, dest.end(), dest.begin()); + std::copy(m_buffer.begin(), m_buffer.begin() + elems, dest.end() - elems); + } + + // Remove elements from the internal buffer. + std::copy(m_buffer.begin() + elems, m_buffer.begin() + m_offset, m_buffer.begin()); + m_offset -= elems; + + return elems; +} + + + +template +void RollingBuffer::resize(size_t n) { + m_buffer.resize(n); + clear(); +} + + + +template +void RollingBuffer::clear() { + m_offset = 0; +} + + + +template +size_t RollingBuffer::size() const { + return m_offset; +} + + + +template +const std::vector &RollingBuffer::buffer() const { + return m_buffer; +}