kfr

Fast, modern C++ DSP framework, FFT, Sample Rate Conversion, FIR/IIR/Biquad Filters (SSE, AVX, AVX-512, ARM NEON)
Log | Files | Refs | README

commit afd39ce67cdf54297dfbb71e3dc2c6bf8c9b7dbc
parent a229a6dcabfb83dde41b5c0cb764a1f4d777084d
Author: d.levin256@gmail.com <d.levin256@gmail.com>
Date:   Wed,  5 Dec 2018 19:14:19 +0000

sample conversion/interleaving/deinterleaving

Diffstat:
Minclude/kfr/base.hpp | 1+
Ainclude/kfr/base/conversion.hpp | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minclude/kfr/base/types.hpp | 25++++++++++++++++++++-----
Msources.cmake | 1+
Mtests/base_test.cpp | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 200 insertions(+), 5 deletions(-)

diff --git a/include/kfr/base.hpp b/include/kfr/base.hpp @@ -31,6 +31,7 @@ #include "base/compiletime.hpp" #include "base/complex.hpp" #include "base/constants.hpp" +#include "base/conversion.hpp" #include "base/digitreverse.hpp" #include "base/expression.hpp" #include "base/filter.hpp" diff --git a/include/kfr/base/conversion.hpp b/include/kfr/base/conversion.hpp @@ -0,0 +1,125 @@ +/** @addtogroup math + * @{ + */ +/* + Copyright (C) 2016 D Levin (https://www.kfrlib.com) + This file is part of KFR + + KFR is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KFR is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KFR. + + If GPL is not suitable for your project, you must purchase a commercial license to use KFR. + Buying a commercial license is mandatory as soon as you develop commercial activities without + disclosing the source code of your own applications. + See https://www.kfrlib.com for details. + */ +#pragma once + +#include "types.hpp" +#include "vec.hpp" + +namespace kfr +{ + +template <typename T> +struct audio_sample_traits; + +template <> +struct audio_sample_traits<i8> +{ + constexpr static f32 scale = 127.f; +}; + +template <> +struct audio_sample_traits<i16> +{ + constexpr static f32 scale = 32767.f; +}; + +template <> +struct audio_sample_traits<i24> +{ + constexpr static f32 scale = 8388607.f; +}; + +template <> +struct audio_sample_traits<i32> +{ + constexpr static f64 scale = 2147483647.0; +}; + +template <> +struct audio_sample_traits<i64> +{ + constexpr static f64 scale = 9223372036854775807.0; +}; + +template <> +struct audio_sample_traits<f32> +{ + constexpr static f32 scale = 1; +}; + +template <> +struct audio_sample_traits<f64> +{ + constexpr static f64 scale = 1; +}; + +template <typename Tout, typename Tin, typename Tout_traits = audio_sample_traits<Tout>, + typename Tin_traits = audio_sample_traits<Tin>, KFR_ENABLE_IF(is_same<Tin, Tout>::value)> +inline Tout convert_sample(const Tin& in) +{ + return in; +} + +template <typename Tout, typename Tin, typename Tout_traits = audio_sample_traits<Tout>, + typename Tin_traits = audio_sample_traits<Tin>, KFR_ENABLE_IF(!is_same<Tin, Tout>::value)> +inline Tout convert_sample(const Tin& in) +{ + constexpr auto scale = Tout_traits::scale / Tin_traits::scale; + return cast<Tout>(in * scale); +} + +template <typename Tout, typename Tin, typename Tout_traits = audio_sample_traits<Tout>, + typename Tin_traits = audio_sample_traits<Tin>> +void deinterleave(Tout* out[], const Tin* in, size_t channels, size_t size) +{ + for (size_t i = 0; i < size; ++i) + { + for (size_t ch = 0; ch < channels; ++ch) + out[ch][i] = convert_sample<Tout, Tin, Tout_traits, Tin_traits>(in[i * channels + ch]); + } +} + +template <typename Tout, typename Tin, typename Tout_traits = audio_sample_traits<Tout>, + typename Tin_traits = audio_sample_traits<Tin>> +void interleave(Tout* out, const Tin* in[], size_t channels, size_t size) +{ + for (size_t i = 0; i < size; ++i) + { + for (size_t ch = 0; ch < channels; ++ch) + out[i * channels + ch] = convert_sample<Tout, Tin, Tout_traits, Tin_traits>(in[ch][i]); + } +} + +template <typename Tout, typename Tin, typename Tout_traits = audio_sample_traits<Tout>, + typename Tin_traits = audio_sample_traits<Tin>> +void convert(Tout* out, const Tin* in, size_t size) +{ + for (size_t i = 0; i < size; ++i) + { + out[i] = convert_sample<Tout, Tin, Tout_traits, Tin_traits>(in[i]); + } +} +} // namespace kfr diff --git a/include/kfr/base/types.hpp b/include/kfr/base/types.hpp @@ -127,6 +127,21 @@ struct u24 struct i24 { u8 raw[3]; + + i24(i32 x) + { + raw[0] = x & 0xFF; + raw[1] = (x >> 8) & 0xFF; + raw[2] = (x >> 16) & 0xFF; + } + + i32 as_int() const + { + return static_cast<i32>(raw[0]) | static_cast<i32>(raw[1] << 8) | + (static_cast<i32>(raw[2] << 24) >> 8); + } + + operator int() const { return as_int(); } }; struct f16 @@ -232,7 +247,7 @@ using get_third = cometa::fn_get_third; ///@copybrief cometa::returns template <typename T> using returns = cometa::fn_returns<T>; -} +} // namespace fn template <typename T> using ftype = @@ -269,7 +284,7 @@ struct flt_type_impl<double> { using type = double; }; -} +} // namespace internal template <typename T> using flt_type = typename internal::flt_type_impl<T>::type; @@ -327,7 +342,7 @@ __attribute__((__packed__, __may_alias__)) // ; CMT_PRAGMA_GNU(GCC diagnostic pop) -} +} // namespace internal /// @brief Fills a value with zeros template <typename T1> @@ -351,7 +366,7 @@ CMT_INLINE void block_process_impl(size_t& i, size_t size, Fn&& fn) for (; i < size / width * width; i += width) fn(i, csize_t<width>()); } -} +} // namespace internal template <size_t... widths, typename Fn> CMT_INLINE void block_process(size_t size, csizes_t<widths...>, Fn&& fn) @@ -409,6 +424,6 @@ constexpr cunaligned_t cunaligned{}; #else #define KFR_I_CE #endif -} +} // namespace kfr CMT_PRAGMA_GNU(GCC diagnostic pop) diff --git a/sources.cmake b/sources.cmake @@ -24,6 +24,7 @@ set( ${PROJECT_SOURCE_DIR}/include/kfr/base/compiletime.hpp ${PROJECT_SOURCE_DIR}/include/kfr/base/complex.hpp ${PROJECT_SOURCE_DIR}/include/kfr/base/constants.hpp + ${PROJECT_SOURCE_DIR}/include/kfr/base/conversion.hpp ${PROJECT_SOURCE_DIR}/include/kfr/base/digitreverse.hpp ${PROJECT_SOURCE_DIR}/include/kfr/base/expression.hpp ${PROJECT_SOURCE_DIR}/include/kfr/base/filter.hpp diff --git a/tests/base_test.cpp b/tests/base_test.cpp @@ -377,6 +377,59 @@ TEST(test_stat) } } +TEST(sample_conversion) +{ + CHECK(convert_sample<float>(static_cast<i8>(-127)) == -1.f); + CHECK(convert_sample<float>(static_cast<i8>(0)) == 0.f); + CHECK(convert_sample<float>(static_cast<i8>(127)) == 1.f); + + CHECK(convert_sample<float>(static_cast<i16>(-32767)) == -1.f); + CHECK(convert_sample<float>(static_cast<i16>(0)) == 0.f); + CHECK(convert_sample<float>(static_cast<i16>(32767)) == 1.f); + + CHECK(convert_sample<float>(static_cast<i24>(-8388607)) == -1.f); + CHECK(convert_sample<float>(static_cast<i24>(0)) == 0.f); + CHECK(convert_sample<float>(static_cast<i24>(8388607)) == 1.f); + + CHECK(convert_sample<float>(static_cast<i32>(-2147483647)) == -1.f); + CHECK(convert_sample<float>(static_cast<i32>(0)) == 0.f); + CHECK(convert_sample<float>(static_cast<i32>(2147483647)) == 1.f); + + CHECK(convert_sample<i8>(-1.f) == -127); + CHECK(convert_sample<i8>(0.f) == 0); + CHECK(convert_sample<i8>(1.f) == 127); + + CHECK(convert_sample<i16>(-1.f) == -32767); + CHECK(convert_sample<i16>(0.f) == 0); + CHECK(convert_sample<i16>(1.f) == 32767); + + CHECK(convert_sample<i24>(-1.f) == -8388607); + CHECK(convert_sample<i24>(0.f) == 0); + CHECK(convert_sample<i24>(1.f) == 8388607); + + CHECK(convert_sample<i32>(-1.f) == -2147483647); + CHECK(convert_sample<i32>(0.f) == 0); + CHECK(convert_sample<i32>(1.f) == 2147483647); +} + +TEST(sample_interleave_deinterleave) +{ + const size_t size = 50; + univector2d<float> in; + in.push_back(truncate(counter() * 3.f + 0.f, size)); + in.push_back(truncate(counter() * 3.f + 1.f, size)); + in.push_back(truncate(counter() * 3.f + 2.f, size)); + univector<float> out(size * 3); + interleave(out.data(), (const float*[]){ in[0].data(), in[1].data(), in[2].data() }, 3, size); + CHECK(maxof(out - render(counter() * 1.f, out.size())) == 0); + + deinterleave((float*[]){ in[0].data(), in[1].data(), in[2].data() }, out.data(), 3, size); + + CHECK(maxof(in[0] - render(counter() * 3.f + 0.f, size)) == 0); + CHECK(maxof(in[1] - render(counter() * 3.f + 1.f, size)) == 0); + CHECK(maxof(in[2] - render(counter() * 3.f + 2.f, size)) == 0); +} + #ifndef KFR_NO_MAIN int main() {