diff options
Diffstat (limited to 'extern/audaspace/src/respec/JOSResampleReader.cpp')
-rw-r--r-- | extern/audaspace/src/respec/JOSResampleReader.cpp | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/extern/audaspace/src/respec/JOSResampleReader.cpp b/extern/audaspace/src/respec/JOSResampleReader.cpp new file mode 100644 index 00000000000..6753a2e8b6b --- /dev/null +++ b/extern/audaspace/src/respec/JOSResampleReader.cpp @@ -0,0 +1,383 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "respec/JOSResampleReader.h" + +#include <algorithm> +#include <cmath> +#include <cstring> + +#define RATE_MAX 256 +#define SHIFT_BITS 12 +#define double_to_fp(x) (lrint(x * double(1 << SHIFT_BITS))) +#define int_to_fp(x) (x << SHIFT_BITS) +#define fp_to_int(x) (x >> SHIFT_BITS) +#define fp_to_double(x) (x * 1.0/(1 << SHIFT_BITS)) +#define fp_rest(x) (x & ((1 << SHIFT_BITS) - 1)) +#define fp_rest_to_double(x) fp_to_double(fp_rest(x)) + +AUD_NAMESPACE_BEGIN + +JOSResampleReader::JOSResampleReader(std::shared_ptr<IReader> reader, SampleRate rate) : + ResampleReader(reader, rate), + m_channels(CHANNELS_INVALID), + m_n(0), + m_P(0), + m_cache_valid(0), + m_last_factor(0) +{ +} + +void JOSResampleReader::reset() +{ + m_cache_valid = 0; + m_n = 0; + m_P = 0; + m_last_factor = 0; +} + +void JOSResampleReader::updateBuffer(int size, double factor, int samplesize) +{ + unsigned int len; + double num_samples = double(m_len) / double(m_L); + // first calculate what length we need right now + if(factor >= 1) + len = std::ceil(num_samples); + else + len = (unsigned int)(std::ceil(num_samples / factor)); + + // then check if afterwards the length is enough for the maximum rate + if(len + size < num_samples * RATE_MAX) + len = num_samples * RATE_MAX - size; + + if(m_n > len) + { + sample_t* buf = m_buffer.getBuffer(); + len = m_n - len; + std::memmove(buf, buf + len * m_channels, (m_cache_valid - len) * samplesize); + m_n -= len; + m_cache_valid -= len; + } + + m_buffer.assureSize((m_cache_valid + size) * samplesize, true); +} + +#define RESAMPLE_METHOD(name, left, right) void JOSResampleReader::name(double target_factor, int length, sample_t* buffer)\ +{\ + sample_t* buf = m_buffer.getBuffer();\ +\ + unsigned int P, l;\ + int end, channel, i;\ + double eta, v, f_increment, factor;\ +\ + m_sums.assureSize(m_channels * sizeof(double));\ + double* sums = reinterpret_cast<double*>(m_sums.getBuffer());\ + sample_t* data;\ + const float* coeff = m_coeff;\ +\ + unsigned int P_increment;\ +\ + for(unsigned int t = 0; t < length; t++)\ + {\ + factor = (m_last_factor * (length - t - 1) + target_factor * (t + 1)) / length;\ +\ + std::memset(sums, 0, sizeof(double) * m_channels);\ +\ + if(factor >= 1)\ + {\ + P = double_to_fp(m_P * m_L);\ +\ + end = std::floor(m_len / double(m_L) - m_P) - 1;\ + if(m_n < end)\ + end = m_n;\ +\ + data = buf + (m_n - end) * m_channels;\ + l = fp_to_int(P);\ + eta = fp_rest_to_double(P);\ + l += m_L * end;\ +\ + for(i = 0; i <= end; i++)\ + {\ + v = coeff[l] + eta * (coeff[l+1] - coeff[l]);\ + l -= m_L;\ + left\ + }\ +\ + P = int_to_fp(m_L) - P;\ +\ + end = std::floor((m_len - 1) / double(m_L) + m_P) - 1;\ + if(m_cache_valid - m_n - 2 < end)\ + end = m_cache_valid - m_n - 2;\ +\ + data = buf + (m_n + 2 + end) * m_channels - 1;\ + l = fp_to_int(P);\ + eta = fp_rest_to_double(P);\ + l += m_L * end;\ +\ + for(i = 0; i <= end; i++)\ + {\ + v = coeff[l] + eta * (coeff[l+1] - coeff[l]);\ + l -= m_L;\ + right\ + }\ +\ + for(channel = 0; channel < m_channels; channel++)\ + {\ + *buffer = sums[channel];\ + buffer++;\ + }\ + }\ + else\ + {\ + f_increment = factor * m_L;\ + P_increment = double_to_fp(f_increment);\ + P = double_to_fp(m_P * f_increment);\ +\ + end = (int_to_fp(m_len) - P) / P_increment - 1;\ + if(m_n < end)\ + end = m_n;\ +\ + P += P_increment * end;\ + data = buf + (m_n - end) * m_channels;\ + l = fp_to_int(P);\ +\ + for(i = 0; i <= end; i++)\ + {\ + eta = fp_rest_to_double(P);\ + v = coeff[l] + eta * (coeff[l+1] - coeff[l]);\ + P -= P_increment;\ + l = fp_to_int(P);\ + left\ + }\ +\ + P = 0 - P;\ +\ + end = (int_to_fp(m_len) - P) / P_increment - 1;\ + if(m_cache_valid - m_n - 2 < end)\ + end = m_cache_valid - m_n - 2;\ +\ + P += P_increment * end;\ + data = buf + (m_n + 2 + end) * m_channels - 1;\ + l = fp_to_int(P);\ +\ + for(i = 0; i <= end; i++)\ + {\ + eta = fp_rest_to_double(P);\ + v = coeff[l] + eta * (coeff[l+1] - coeff[l]);\ + P -= P_increment;\ + l = fp_to_int(P);\ + right\ + }\ +\ + for(channel = 0; channel < m_channels; channel++)\ + {\ + *buffer = factor * sums[channel];\ + buffer++;\ + }\ + }\ +\ + m_P += std::fmod(1.0 / factor, 1.0);\ + m_n += std::floor(1.0 / factor);\ +\ + while(m_P >= 1.0)\ + {\ + m_P -= 1.0;\ + m_n++;\ + }\ + }\ +} + +RESAMPLE_METHOD(resample, { + channel = 0; + do + { + sums[channel] += *data * v; + channel++; + data++; + } + while(channel < m_channels); +}, { + channel = m_channels; + do + { + channel--; + sums[channel] += *data * v; + data--; + } + while(channel); +}) + +RESAMPLE_METHOD(resample_mono, { + *sums += *data * v; + data++; +}, { + *sums += *data * v; + data--; +}) + +RESAMPLE_METHOD(resample_stereo, { + sums[0] += data[0] * v; + sums[1] += data[1] * v; + data+=2; +}, { + data-=2; + sums[0] += data[1] * v; + sums[1] += data[2] * v; +}) + +void JOSResampleReader::seek(int position) +{ + position = std::floor(position * double(m_reader->getSpecs().rate) / double(m_rate)); + m_reader->seek(position); + reset(); +} + +int JOSResampleReader::getLength() const +{ + return std::floor(m_reader->getLength() * double(m_rate) / double(m_reader->getSpecs().rate)); +} + +int JOSResampleReader::getPosition() const +{ + return std::floor((m_reader->getPosition() + double(m_P)) * m_rate / m_reader->getSpecs().rate); +} + +Specs JOSResampleReader::getSpecs() const +{ + Specs specs = m_reader->getSpecs(); + specs.rate = m_rate; + return specs; +} + +void JOSResampleReader::read(int& length, bool& eos, sample_t* buffer) +{ + if(length == 0) + return; + + Specs specs = m_reader->getSpecs(); + + int samplesize = AUD_SAMPLE_SIZE(specs); + double target_factor = double(m_rate) / double(specs.rate); + eos = false; + int len; + double num_samples = double(m_len) / double(m_L); + + // check for channels changed + if(specs.channels != m_channels) + { + m_channels = specs.channels; + reset(); + + switch(m_channels) + { + case CHANNELS_MONO: + m_resample = &JOSResampleReader::resample_mono; + break; + case CHANNELS_STEREO: + m_resample = &JOSResampleReader::resample_stereo; + break; + default: + m_resample = &JOSResampleReader::resample; + break; + } + } + + if(m_last_factor == 0) + m_last_factor = target_factor; + + if(target_factor == 1 && m_last_factor == 1 && (m_P == 0)) + { + // can read directly! + + len = length - (m_cache_valid - m_n); + + updateBuffer(len, target_factor, samplesize); + sample_t* buf = m_buffer.getBuffer(); + + m_reader->read(len, eos, buf + m_cache_valid * m_channels); + m_cache_valid += len; + + length = m_cache_valid - m_n; + + if(length > 0) + { + std::memcpy(buffer, buf + m_n * m_channels, length * samplesize); + m_n += length; + } + + return; + } + + // use minimum for the following calculations + double factor = std::min(target_factor, m_last_factor); + + if(factor >= 1) + len = (int(m_n) - m_cache_valid) + int(std::ceil(length / factor)) + std::ceil(num_samples); + else + len = (int(m_n) - m_cache_valid) + int(std::ceil(length / factor) + std::ceil(num_samples / factor)); + + if(len > 0) + { + int should = len; + + updateBuffer(len, factor, samplesize); + + m_reader->read(len, eos, m_buffer.getBuffer() + m_cache_valid * m_channels); + m_cache_valid += len; + + if(len < should) + { + if(len == 0 && eos) + length = 0; + else + { + // use maximum for the following calculations + factor = std::max(target_factor, m_last_factor); + + if(eos) + { + // end of stream, let's check how many more samples we can produce + len = std::floor((m_cache_valid - m_n) * factor); + if(len < length) + length = len; + } + else + { + // not enough data available yet, so we recalculate how many samples we can calculate + if(factor >= 1) + len = std::floor((num_samples + m_cache_valid - m_n) * factor); + else + len = std::floor((num_samples * factor + m_cache_valid - m_n) * factor); + if(len < length) + length = len; + } + } + } + } + + (this->*m_resample)(target_factor, length, buffer); + + m_last_factor = target_factor; + + if(m_n > m_cache_valid) + { + m_n = m_cache_valid; + } + + eos = eos && ((m_n == m_cache_valid) || (length == 0)); +} + +AUD_NAMESPACE_END |