Module: Mfcc

Defined in:
lib/mfcc.rb,
lib/mfcc/dct.rb,
lib/mfcc/dft.rb,
lib/mfcc/mel.rb,
lib/mfcc/frame.rb,
lib/mfcc/hamming.rb,
lib/mfcc/version.rb,
lib/mfcc/compressor.rb,
lib/mfcc/preemphasis.rb

Overview

MFCCs are commonly derived as follows:

  • Take the Fourier transform of (a windowed excerpt of) a signal.

  • Map the powers of the spectrum obtained above onto the mel scale, using triangular overlapping windows.

  • Take the logs of the powers at each of the mel frequencies.

  • Take the discrete cosine transform of the list of mel log powers, as if it were a signal.

  • The MFCCs are the amplitudes of the resulting spectrum.

(en.wikipedia.org/wiki/Mel-frequency_cepstrum)

Defined Under Namespace

Modules: Compressor, Dct, Dft, Frame, Hamming, Mel, Preemphasis Classes: Calculator, FilterBanks

Constant Summary collapse

VERSION =
'0.0.1'
LOG10_ERROR_VALUE =
-0.00000001

Class Method Summary collapse

Class Method Details

.compress(data) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/mfcc/compressor.rb', line 10

def self.compress(data)
  return to_enum(:compress, data) { data.size } unless block_given?

  data.each do |d|
    v = if d == 0
          LOG10_ERROR_VALUE
        else
          Math.log10(d)
        end

    yield v
  end
end

.dct(data, order = 13, orthogonalize = true) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/mfcc/dct.rb', line 10

def self.dct(data, order = 13, orthogonalize = true)
  length = data.size

  scales = if orthogonalize
             [Math.sqrt(1.0 / (4 * length)), Math.sqrt(1.0 / (2 * length))]
           else
             [1, 1]
           end

  dct_matrix(order, length).each_with_index.map do |row, index|
    scale = index == 0 ? scales[0] : scales[1]
    scale * row.zip(data).inject(0) { |memo, (a, b)| memo + (2 * a * b) }
  end
end

.dct_matrix(n, m) ⇒ Object



25
26
27
28
29
30
31
32
# File 'lib/mfcc/dct.rb', line 25

def self.dct_matrix(n, m)
  n.times.map do |i|
    freq = (Math::PI * i) / m
    m.times.map do |j|
      Math.cos(freq * (j + 0.5))
    end
  end
end

.dft(data) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/mfcc/dft.rb', line 10

def self.dft(data)
  return to_enum(:dft, data) { data.size } unless block_given?

  n = data.size

  data = plus_imaginary(data)

  data.each_with_index do |_, k|
    sumreal = 0
    sumimag = 0

    data.each_with_index do |d, t|
      angle = 2 * Math::PI * t * k / n

      sumreal += d.real * Math.cos(angle) + d.imaginary * Math.sin(angle)
      sumimag += -1 * d.real * Math.sin(angle) + d.imaginary * Math.cos(angle)
    end

    yield Complex(sumreal, sumimag)
  end
end

.frame(data, size, step) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/mfcc/frame.rb', line 8

def self.frame(data, size, step)
  return to_enum(:frame, data, size, step) unless block_given?

  buffer = []

  data.each do |d|
    buffer.push(d)

    if buffer.size == size
      yield buffer

      buffer = buffer.slice(step..buffer.length)
    end
  end

  length = buffer.length

  (length / step.to_f).ceil.times do
    buffer.fill(0, buffer.size...size)

    yield buffer

    buffer = buffer.slice(step..buffer.length)
  end
end

.h(alpha, beta, size, i) ⇒ Object



19
20
21
# File 'lib/mfcc/hamming.rb', line 19

def self.h(alpha, beta, size, i)
  alpha - beta * Math.cos(2 * Math::PI * i / (size - 1))
end

.hamming(data, alpha = 0.46) ⇒ Object



8
9
10
11
12
13
14
15
16
17
# File 'lib/mfcc/hamming.rb', line 8

def self.hamming(data, alpha = 0.46)
  return to_enum(:hamming, data, alpha) { data.size } unless block_given?

  beta = 1 - alpha
  length = data.size

  data.each_with_index do |d, i|
    yield d * h(alpha, beta, length, i)
  end
end

.magnitude(data) ⇒ Object



45
46
47
48
49
50
51
# File 'lib/mfcc/dft.rb', line 45

def self.magnitude(data)
  return to_enum(:magnitude, data) { data.size } unless block_given?

  data.each do |d|
    yield d.respond_to?(:magnitude) ? d.magnitude : d
  end
end

.mel(data, filter_banks) ⇒ Object



12
13
14
# File 'lib/mfcc/mel.rb', line 12

def self.mel(data, filter_banks)
  filter_banks.process(data)
end

.plus_imaginary(data) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/mfcc/dft.rb', line 32

def self.plus_imaginary(data)
  return to_enum(:plus_imaginary, data) { data.size } unless block_given?

  data.each do |d|
    yield case d
          when Complex
            d
          else
            Complex(d, 0)
          end
  end
end

.preemphasis(data, emph = 0.97) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/mfcc/preemphasis.rb', line 8

def self.preemphasis(data, emph = 0.97)
  return data if emph == 0

  return to_enum(:preemphasis, data, emph) { |d, _| d.size || Float::INFINITY } unless block_given?

  prev = 0

  data.each do |y|
    yield (y - emph * prev)
    prev = y
  end
end