module SPCore

# Extended windowed sinc filter. Implements bandpass and bandstop using
# two SincFilterBase objects.
#
# Theoretical source: http://www.labbookpages.co.uk/audio/firWindowing.html
#
# @author James Tunnell
#
class DualSincFilter
  include Hashmake::HashMakeable
  
  # Use to process hashed args in #initialize.
  ARG_SPECS = {
    :order => arg_spec(:reqd => true, :type => Fixnum, :validator => ->(a){ a % 2 == 0 } ),
    :sample_rate => arg_spec(:reqd => true, :type => Fixnum, :validator => ->(a){ a > 0.0 } ),
    :left_cutoff_freq => arg_spec(:reqd => true, :type => Numeric, :validator => ->(a){ a > 0.0 } ),
    :right_cutoff_freq => arg_spec(:reqd => true, :type => Numeric, :validator => ->(a){ a > 0.0 } ),
    :window_class => arg_spec(:reqd => false, :type => Class, :default => BlackmanWindow ),
  }
  
  attr_reader :bandpass_fir, :bandstop_fir, :left_filter, :right_filter
  
  # Given a filter order, 2 cutoff frequencies, sample rate, and window class,
  # develop a FIR filter kernel that can be used for lowpass filtering.
  def initialize args
    hash_make DualSincFilter::ARG_SPECS, args
    
    raise ArgumentError, "left_cutoff_freq is greater than 0.5 * sample_rate" if @left_cutoff_freq > (@sample_rate * 0.5)
    raise ArgumentError, "right_cutoff_freq is greater than 0.5 * sample_rate" if @right_cutoff_freq > (@sample_rate * 0.5)
    raise ArgumentError, "left_cutoff_freq is not less than right_cutoff_freq" unless @left_cutoff_freq < @right_cutoff_freq 
    
    @left_filter = SincFilter.new(
      :sample_rate => @sample_rate,
      :order => @order,
      :cutoff_freq => @left_cutoff_freq,
      :window_class => @window_class,
    )

    @right_filter = SincFilter.new(
      :sample_rate => @sample_rate,
      :order => @order,
      :cutoff_freq => @right_cutoff_freq,
      :window_class => @window_class,
    )
    
    size = @order + 1

    # make FIR filter kernels for bandpass and bandstop
    
    bandpass_kernel = Array.new(size)
    bandstop_kernel = Array.new(size)
    window = @window_class.new(size)
    
    for n in 0...(@order / 2)
      bandpass_kernel[size - 1 - n] = bandpass_kernel[n] = @right_filter.lowpass_fir.kernel[n] + @left_filter.highpass_fir.kernel[n]
      bandstop_kernel[size - 1 - n] = bandstop_kernel[n] = @left_filter.lowpass_fir.kernel[n] + @right_filter.highpass_fir.kernel[n]
    end

    left_transition_freq = @left_cutoff_freq.to_f / @sample_rate
    right_transition_freq = @right_cutoff_freq.to_f / @sample_rate
    bw_times_two = 2.0 * (right_transition_freq - left_transition_freq)
    window_center_val = window.data[@order / 2]
    
    bandpass_kernel[@order / 2] = bw_times_two * window_center_val
    bandstop_kernel[@order / 2] = (1.0 - bw_times_two) * window_center_val
    
    @bandpass_fir = FIR.new bandpass_kernel, @sample_rate
    @bandstop_fir = FIR.new bandstop_kernel, @sample_rate
  end

  # Process the input with the bandpass FIR.
  # @return [Array] containing the filtered input.  
  def bandpass input
    return @bandpass_fir.convolve input
  end
  
  # Process the input with the bandstop FIR.
  # @return [Array] containing the filtered input.
  def bandstop input
    return @bandstop_fir.convolve input
  end
end
end