1 /* 2 * Copyright 2003-2021 The Music Player Daemon Project 3 * http://www.musicpd.org 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 #ifndef MPD_PCM_REST_BUFFER_HXX 21 #define MPD_PCM_REST_BUFFER_HXX 22 23 #include "ChannelDefs.hxx" 24 25 #include <algorithm> 26 #include <cassert> 27 #include <cstddef> 28 29 template<typename T> struct ConstBuffer; 30 class PcmBuffer; 31 32 /** 33 * A buffer which helps with conversion classes which need to handle 34 * multiple frames at a time; it stores the rest of a previous 35 * operation in order to use it in the next call. 36 */ 37 template<typename T, unsigned n_frames> 38 class PcmRestBuffer { 39 unsigned size, capacity; 40 T data[n_frames * MAX_CHANNELS]; 41 42 public: Open(unsigned channels)43 void Open(unsigned channels) noexcept { 44 assert(audio_valid_channel_count(channels)); 45 46 capacity = n_frames * channels; 47 size = 0; 48 } 49 50 /** 51 * @return the size of one input block in #T samples 52 */ GetInputBlockSize() const53 size_t GetInputBlockSize() const noexcept { 54 return capacity; 55 } 56 GetChannelCount() const57 unsigned GetChannelCount() const noexcept { 58 return capacity / n_frames; 59 } 60 Reset()61 void Reset() noexcept { 62 assert(audio_valid_channel_count(capacity / n_frames)); 63 64 size = 0; 65 } 66 67 private: Complete(ConstBuffer<T> & src)68 ConstBuffer<T> Complete(ConstBuffer<T> &src) noexcept { 69 assert(audio_valid_channel_count(GetChannelCount())); 70 assert(src.size % GetChannelCount() == 0); 71 72 if (size == 0) 73 return nullptr; 74 75 size_t missing = capacity - size; 76 size_t n = std::min(missing, src.size); 77 std::copy_n(src.begin(), n, &data[size]); 78 src.skip_front(n); 79 size += n; 80 81 if (size < capacity) 82 return nullptr; 83 84 size = 0; 85 return {data, capacity}; 86 } 87 Append(ConstBuffer<T> src)88 void Append(ConstBuffer<T> src) noexcept { 89 assert(audio_valid_channel_count(GetChannelCount())); 90 assert(src.size % GetChannelCount() == 0); 91 assert(size + src.size < capacity); 92 93 std::copy_n(src.begin(), src.size, &data[size]); 94 size += src.size; 95 } 96 97 public: 98 /** 99 * A helper function which attempts to complete the rest 100 * buffer, allocates a destination buffer and invokes the 101 * given function for both the rest buffer and the new source 102 * buffer. In the end, it copies more remaining data to the 103 * rest buffer. 104 * 105 * @param U the destination data type 106 * @param F the inner process function 107 * @param dest_block_size how many destination samples in one block? 108 * @return the destination buffer (allocated from #buffer); 109 * may be empty 110 */ 111 template<typename U, typename F> Process(PcmBuffer & buffer,ConstBuffer<T> src,size_t dest_block_size,F && f)112 ConstBuffer<U> Process(PcmBuffer &buffer, ConstBuffer<T> src, 113 size_t dest_block_size, 114 F &&f) { 115 assert(dest_block_size % GetChannelCount() == 0); 116 117 const auto previous_rest = Complete(src); 118 assert(previous_rest.size == 0 || 119 previous_rest.size == capacity); 120 121 const size_t previous_rest_blocks = !previous_rest.empty(); 122 const size_t src_blocks = src.size / capacity; 123 const size_t next_rest_samples = src.size % capacity; 124 const size_t dest_blocks = previous_rest_blocks + src_blocks; 125 const size_t dest_samples = dest_blocks * dest_block_size; 126 127 const auto dest0 = buffer.GetT<U>(dest_samples); 128 auto dest = dest0; 129 130 if (!previous_rest.empty()) { 131 f(dest, previous_rest.data, previous_rest_blocks); 132 dest += dest_block_size; 133 } 134 135 f(dest, src.data, src_blocks); 136 137 if (next_rest_samples > 0) 138 Append({src.data + src_blocks * capacity, 139 next_rest_samples}); 140 141 return { dest0, dest_samples }; 142 } 143 }; 144 145 #endif 146