require 'pry'
module SPCore

# Base windowed sinc filter. Implements lowpass and highpass. A bandpass
# and bandstop filter would be implemented using two of these.
#
# Theoretical source: http://www.labbookpages.co.uk/audio/firWindowing.html
#
# @author James Tunnell
#
class SincFilter
  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 } ),
    :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 :lowpass_fir, :highpass_fir
  
  # Given a filter order, cutoff frequency, sample rate, and window class,
  # develop a FIR filter kernel that can be used for lowpass filtering.
  def initialize args
    hash_make SincFilter::ARG_SPECS, args
    
    raise ArgumentError, "cutoff_freq is greater than 0.5 * sample_rate" if @cutoff_freq > (@sample_rate / 2.0)
    
    size = @order + 1
    transition_freq = @cutoff_freq.to_f / @sample_rate
    b = TWO_PI * transition_freq
    
    # make FIR filter kernels for lowpass and highpass
    
    lowpass_kernel = Array.new(size)
    highpass_kernel = Array.new(size)
    window = @window_class.new(size)
    
    for n in 0...(@order / 2)
      c = n - (@order / 2)
      y = Math::sin(b * c) / (Math::PI * (c))
      lowpass_kernel[size - 1 - n] = lowpass_kernel[n] = y * window.data[n]
      highpass_kernel[size - 1 - n] = highpass_kernel[n] = -lowpass_kernel[n]
    end
    lowpass_kernel[@order / 2] = 2 * transition_freq * window.data[@order / 2]
    highpass_kernel[@order / 2] = (1 - 2 * transition_freq) * window.data[@order / 2]
    
    @lowpass_fir = FIR.new lowpass_kernel, @sample_rate
    @highpass_fir = FIR.new highpass_kernel, @sample_rate
  end
  
  # Process the input with the lowpass FIR.
  # @return [Array] containing the filtered input.
  def lowpass input
    return @lowpass_fir.convolve input
  end
  
  # Process the input with the highpass FIR.
  # @return [Array] containing the filtered input.
  def highpass input
    return @highpass_fir.convolve input
  end
end


end