Class: Noyes::MelFilter

Inherits:
Object
  • Object
show all
Includes:
Math, NoyesFilterDSL
Defined in:
lib/ruby_impl/mel_filter.rb,
lib/common/noyes_dsl.rb

Overview

Mel filter takes an m x n matrix. The inner array becomes equal to the number of mel filter banks (nfilt). The dimensionality of the outer array remains unchanged.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Math

#dot_product, log2, max, min

Methods included from NoyesFilterDSL

#+, #|

Constructor Details

#initialize(srate, nfft, nfilt, lowerf, upperf) ⇒ MelFilter

Returns a new instance of MelFilter.


9
10
11
12
13
14
15
16
17
18
# File 'lib/ruby_impl/mel_filter.rb', line 9

def initialize srate, nfft, nfilt, lowerf, upperf
  bank_params = MelFilter.make_bank_parameters srate, nfft, nfilt, lowerf, upperf
  @indices = []
  @weights = []
  bank_params.map do |params|
    ind, weights = MelFilter.make_filter *params
    @indices << ind
    @weights << weights
  end
end

Class Method Details

.determine_bin(in_freq, step_freq) ⇒ Object


42
43
44
# File 'lib/ruby_impl/mel_filter.rb', line 42

def self.determine_bin in_freq, step_freq
  step_freq * (in_freq/step_freq).round
end

.make_bank_parameters(srate, nfft, nfilt, lowerf, upperf) ⇒ Object


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/ruby_impl/mel_filter.rb', line 45

def self.make_bank_parameters srate, nfft, nfilt, lowerf, upperf
  raise 'Number of FFT points is <= 0.' if nfft == 0
  raise 'Number of filters is <= 0.' if nfilt == 0
  srate = srate.to_f; lowerf = lowerf.to_f; upperf = upperf.to_f
  left_edge = Array.new nfilt
  right_edge = Array.new nfilt
  center_freq = Array.new nfilt
  melmax = self.to_mel upperf
  melmin = self.to_mel lowerf
  delta_freq_mel = (melmax - melmin) / (nfilt + 1.0)
  delta_freq = srate/nfft
  left_edge[0] = self.determine_bin lowerf, delta_freq
  next_edge_mel = melmin
  nfilt.times do |i|
    next_edge_mel += delta_freq_mel
    next_edge = self.to_linear next_edge_mel
    center_freq[i] = self.determine_bin next_edge, delta_freq
    right_edge[i-1] = center_freq[i] if i > 0   
    left_edge[i+1] = center_freq[i] if i < nfilt - 1 
  end

  next_edge_mel += delta_freq_mel
  next_edge = self.to_linear next_edge_mel
  right_edge[nfilt-1] = self.determine_bin next_edge, delta_freq
  fparams = Array.new nfilt
  nfilt.times do |i| 
    initial_freq_bin = self.determine_bin left_edge[i], delta_freq
    initial_freq_bin += delta_freq if initial_freq_bin < left_edge[i]
    fparams[i] = [left_edge[i], center_freq[i], right_edge[i], 
                initial_freq_bin, delta_freq]
  end
  fparams
end

.make_filter(left, center, right, init_freq, delta) ⇒ Object


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/ruby_impl/mel_filter.rb', line 78

def self.make_filter left, center, right, init_freq, delta
  raise 'delta freq has zero value' if delta == 0
  if (right - left).round == 0 || (center - left).round == 0 ||
     (right - center).round == 0
    raise 'filter boundaries too close'
  end
 
  n_elements = ((right - left)/ delta + 1).round
  raise 'number of mel elements is zero' if n_elements == 0

  weights = Array.new n_elements    
  height = 1
  left_slope = height / (center - left)
  right_slope = height / (center - right)

  index_fw = 0
  init_freq.step right, delta do |current|
    if current < center
      weights[index_fw] = left_slope * (current - left)
    else
      weights[index_fw] = height + right_slope * (current - center) 
    end
    index_fw += 1
  end  #weights.insert 0, (init_freq/delta).round

  [(init_freq/delta).round, weights]
end

.to_linear(m) ⇒ Object


38
39
40
41
# File 'lib/ruby_impl/mel_filter.rb', line 38

def self.to_linear m
  return m.map {|melfreq| self.to_linear melfreq} if m.respond_to? :map
  700.0 * (10.0**(m/2595.0) - 1.0)
end

.to_mel(f) ⇒ Object


34
35
36
37
# File 'lib/ruby_impl/mel_filter.rb', line 34

def self.to_mel f
  return f.map {|linfreq| self.to_mel linfreq} if f.respond_to? :map
  2595.0 * Math.log10(1.0 + f/700.0)
end

Instance Method Details

#<<(power_spectra) ⇒ Object


19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/ruby_impl/mel_filter.rb', line 19

def << power_spectra
  power_spectra.map do |spectrum|
    mel_bank = Array.new @indices.size
    @indices.size.times do |i|
      initial_index, weights = @indices[i], @weights[i]
      output = 0.0
      weights.size.times do |j|
        index = initial_index + j
        output += spectrum[index] * weights[j] if index < spectrum.length
      end
      mel_bank[i] = output
    end
    mel_bank
  end
end

#apply_weights(init_index, weights, spectrum) ⇒ Object


105
106
107
108
109
110
111
# File 'lib/ruby_impl/mel_filter.rb', line 105

def apply_weights init_index, weights, spectrum
  output = 0.0
  weights.size.times do |i|
    output += spectrum[i + init_index] * weights[i] 
  end
  output
end