Skip to content
Snippets Groups Projects
Select Git revision
  • 7b51e26a0fe96d3b927e49ae41c3314770ce8418
  • my-very-good-branch default protected
  • v0.1.0
3 results

output.h

Blame
  • winSincFracDel.m 5.59 KiB
    %% Windowed-sinc fractional delay
    % --------------------------------------------------
    % Author: Achilles Kappis
    % e-mail: axilleaz@protonmail.com
    %
    % Date: 09/03/2024 (DD/MM/YYYY)
    %
    % Copyright: MIT
    % --------------------------------------------------
    % Functionality: Calculate a fractional delay of a signal with
    %                windowed-sinc FIR filter.
    % --------------------------------------------------
    % Input
    % 
    % delay [numeric]: The delay in samples. This must be a non-negative real
    %                  scalar.
    % 
    % len [numeric] (Optional): This is the length of the filter. This must be
    %                           a positive real integer. If this values is less
    %                           than the "delay" value, the filter length
    %                           equals the "delay" value rounded up.
    %                           [Default: ceil(delay)].
    % 
    % win [numeric] (Optional): A window to apply to the FIR filter. This can
    %                           be a vector in which case the same window will
    %                           be applied to the filter for all signals, or it
    %                           can be a matrix in which case each column will
    %                           contain the window corresponding to the filter
    %                           for each signal. The number of rows must be
    %                           equal to "len" and the number of columns to the
    %                           number of columns of "sig". If left empty, no
    %                           window is applied. It has to be noted that the
    %                           provided window has to be shifted by "delay"
    %                           samples.
    %                           [Default: []].
    % 
    % sig [numeric] (Optional): The signal(s) to be delayed in the time-domain.
    %                           This can be a matrix with each column being a
    %                           different signal. Only real-valued signals are
    %                           accepted. [Default: []].
    % 
    % --------------------------------------------------
    % Output
    % 
    % delFilt [numeric]: The fractional delay windowed-sinc FIR filter(s). If
    %                    multiple windows are provided then a matrix is
    %                    returned with each column corresponding to the filter
    %                    for each signal. Otherwise, this argument is a vector
    %                    with the FIR filter.
    % 
    % delSig [numeric]: The delayed signal(s). The length each signal is equal
    %                   to that of the original. If the delay is longer than
    %                   the original signal vector, a zeroed vector is returned
    %                   (with possible artefacts from the filtering process).
    %                   Only returned if a signal is provided, otherwise a
    %                   zeroed-out vector is returned.
    % --------------------------------------------------
    % Notes
    % 
    % - The function uses time-domain convolution, which can be quite slow for
    %   long signals. If the "delSig" is not asked for this calculation is
    %   omitted so, when the signals are not needed (or want to perform
    %   frequency-domain convolution manually), use only one output argument.
    % --------------------------------------------------
    function [delFilt, delSig] = winSincFracDel(delay, len, win, sig)
        % ====================================================
        % Check for number of arguments
        % ====================================================
        narginchk(1, 4);
        nargoutchk(0, 2);
    
        % ====================================================
        % Validate input arguments
        % ====================================================
        % Validate mandatory arguments
        validateattributes(delay, "numeric", {'scalar', 'real', 'nonnegative', 'nonnan', 'nonempty', 'finite'}, mfilename, "Delay in samples", 1);
    
        % Validate optional arguments
        if nargin > 1 && ~isempty(len)
            validateattributes(len, "numeric", {'scalar', 'integer', 'real', 'finite', 'nonnan', 'nonnegative'}, mfilename, "Filter length", 2);
    
            if len < delay
                warning("winSincFracDel(): The length of the filter cannot be less than the delay, setting equal to 'delay',");
                len = ceil(delay);
            end
        else
            len = ceil(delay);
        end
    
        if nargin > 2 && ~isempty(win)
            validateattributes(win, "numeric", {'nonnan', '2d', 'real', 'finite', 'nrows', len}, mfilename, "Window(s) to be applied", 3);
        else
            win = ones(len, 1);
        end
    
        if nargin > 3 && ~isempty(sig)
            validateattributes(sig, "numeric", {'2d', 'nonempty', 'real'}, mfilename, "Signal(s) to be delayed", 4);
    
            if size(win, 2) == 1
                win = repmat(win, 1, size(sig, 2));
            else
                if size(win, 2) ~= size(sig, 2)
                    error("Number of windows must match the number of signals provided");
                end
            end
        else
            sig = zeros(len, 1);
        end
    
        % ====================================================
        % Generate FIR filter
        % ====================================================
        delFilt = -floor((len - 1)/2):floor(len/2); delFilt = delFilt.'; % Filter sample indices
        delFilt = sinc(delFilt - mod(delay, floor(delay))); % Calculate the sinc for the fractional part of the delay
        delFilt = repmat(delFilt, 1, size(sig, 2)) .* win; % Replicate filter and apply window function
    
    
        % ====================================================
        % Filter the signals
        % ====================================================
        if nargout > 1
            for idx = size(sig, 2):-1:1
                delSig(:, idx) = conv(sig, delFilt, "same");
                delSig = [zeros(floor(delay), size(delSig, 2)); delSig(1:end - floor(delay), :)];
            end
        end
    end