diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm index 32ee88e9e..ca8da36d7 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm +++ b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm @@ -695,56 +695,40 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; // Generate the chrominance filter. { - simd::float3 firCoefficients[8]{}; + using Coefficients3 = std::array; + Coefficients3 firCoefficients{}; // Initial seed: a box filter for the chrominance parts and no filter at all for luminance. const auto chromaCoefficients = SignalProcessing::Box::filter( radiansPerPixel, 3.141592654f * 2.0f - ).resize(15); + ); - _chromaKernelSize = 15; - for(size_t c = 0; c < 8; ++c) { - // Bit of a fix here: if the pipeline is for composite then assume that chroma separation wasn't - // perfect and deemphasise the colour. - firCoefficients[c].y = firCoefficients[c].z = - chromaCoefficients[c] * (isSVideoOutput ? 2.0f : 1.25f); - if(fabsf(chromaCoefficients[c]) < 0.01f) { - _chromaKernelSize -= 2; + chromaCoefficients.copy_to( + firCoefficients.begin(), + firCoefficients.end(), + [&](auto destination, float value) { + destination->y = destination->z = value * (isSVideoOutput ? 2.0f : 1.25f); } - } - firCoefficients[7].x = 1.0f; + ); + _chromaKernelSize = chromaCoefficients.size(); - -// const SignalProcessing::FIRFilter sharpenFilter(15, 1368, 60.0f, 227.5f); - - - // Luminance will be very soft as a result of the separation phase; - // apply a sharpen filter to try to undo that. - // - // This is applied separately in order to partition three parts of the signal rather than two: - // - // 1) the luminance; - // 2) not the luminance: - // 2a) the chrominance; and - // 2b) some noise. - // - // There are real numerical hazards here given the low number of taps I am permitting to be used, - // so the sharpen filter below is just one that I found worked well. Since all numbers are fixed, the - // actual cutoff frequency is going to be a function of the input clock, which is a bit phoney but the - // best way to stay safe within the PCM sampling limits. + // Sharpen the luminance a touch if it was sourced through separation. if(!isSVideoOutput) { const auto sharpen = SignalProcessing::KaiserBessel::filter( 15, 1368, 60.0f, 227.5f); - const size_t offset = (15 - sharpen.size()) / 2; - for(size_t c = offset; c < 8; ++c) { - firCoefficients[c].x = sharpen[c - offset]; - } - - _chromaKernelSize = std::max(_chromaKernelSize, sharpen.size()); + chromaCoefficients.copy_to( + firCoefficients.begin(), + firCoefficients.end(), + [&](auto destination, float value) { + destination->x = value; + } + ); + } else { + firCoefficients[7].x = 1.0f; } // Convert to half-size floats. @@ -755,21 +739,23 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; // Generate the luminance separation filter and determine its required size. { - simd::float2 lumaCoefficients[8]{}; + using Coefficients2 = std::array; + Coefficients2 lumaCoefficients{}; + const auto coefficients = SignalProcessing::Box::filter( radiansPerPixel, 3.141592654f * 2.0f - ).resize(15); - _lumaKernelSize = 15; - for(size_t c = 0; c < 8; ++c) { - lumaCoefficients[c].x = coefficients[c];// * 1.15f; - lumaCoefficients[c].y = -coefficients[c]; + ); - if(fabsf(coefficients[c]) < 0.01f) { - _lumaKernelSize -= 2; + coefficients.copy_to( + lumaCoefficients.begin(), + lumaCoefficients.end(), + [&](auto destination, float value) { + destination->x = value; + destination->y = -value; } - } + ); lumaCoefficients[7].y += 1.0f; for(size_t c = 0; c < 8; ++c) { diff --git a/SignalProcessing/FIRFilter.hpp b/SignalProcessing/FIRFilter.hpp index 8662af4a0..456f3b3ee 100644 --- a/SignalProcessing/FIRFilter.hpp +++ b/SignalProcessing/FIRFilter.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -125,20 +126,38 @@ public: return coefficients_.size(); } - FIRFilter &resize(const size_t size) { - assert(size & 1); + template + void copy_to( + IteratorT begin, + IteratorT end, + const std::function &applier + ) const { + auto dest = begin; + auto src = coefficients_.begin(); - if(size >= coefficients_.size()) { - // TODO: find a faster solution than this. - const auto half_difference = (size - coefficients_.size()) / 2; - std::vector zeroes(half_difference); - coefficients_.insert(coefficients_.begin(), zeroes.begin(), zeroes.end()); - coefficients_.insert(coefficients_.end(), zeroes.begin(), zeroes.end()); - return *this; + const auto destination_size = size_t(std::distance(begin, end)); + if(destination_size <= coefficients_.size()) { + std::advance(src, (coefficients_.size() - destination_size) / 2); + } else { + std::advance(dest, (destination_size - coefficients_.size()) / 2); } -// const auto total = std::accumulate(coefficients_.begin(), coefficients_.end(), CoefficientType{}); - return *this; + auto steps = std::min(destination_size, coefficients_.size()); + while(steps--) { + applier(dest, *src); + ++dest; + ++src; + } + } + + template + void copy_to( + const IteratorT begin, + const IteratorT end + ) const { + copy_to(begin, end, [](const IteratorT target, const CoefficientType coefficient) { + *target = coefficient; + }); } private: