Class: Vizcore::Analysis::BeatDetector

Inherits:
Object
  • Object
show all
Defined in:
lib/vizcore/analysis/beat_detector.rb

Overview

Detects beat onsets using short-term energy thresholding.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(history_size: 43, sensitivity: 1.35, refractory_frames: 4, min_history: 8, min_energy: 1e-6) ⇒ BeatDetector

Returns a new instance of BeatDetector.

Parameters:

  • history_size (Integer) (defaults to: 43)

    number of historical frames to keep

  • sensitivity (Float) (defaults to: 1.35)

    multiplier applied to moving average energy

  • refractory_frames (Integer) (defaults to: 4)

    minimum frames between beat events

  • min_history (Integer) (defaults to: 8)

    minimum history size before detecting beats

  • min_energy (Float) (defaults to: 1e-6)

    absolute energy floor required for beat detection



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/vizcore/analysis/beat_detector.rb', line 14

def initialize(history_size: 43, sensitivity: 1.35, refractory_frames: 4, min_history: 8, min_energy: 1e-6)
  @history_size = Integer(history_size)
  @sensitivity = Float(sensitivity)
  @refractory_frames = Integer(refractory_frames)
  @min_history = Integer(min_history)
  @min_energy = Float(min_energy)
  @energy_history = []
  @frame_index = 0
  @last_beat_frame = -@refractory_frames
  @beat_count = 0
end

Instance Attribute Details

#beat_countObject (readonly)

Returns the value of attribute beat_count.



7
8
9
# File 'lib/vizcore/analysis/beat_detector.rb', line 7

def beat_count
  @beat_count
end

Instance Method Details

#call(samples) ⇒ Hash

Returns beat flag and detector internals.

Parameters:

  • samples (Array<Numeric>)

    PCM frame samples

Returns:

  • (Hash)

    beat flag and detector internals



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/vizcore/analysis/beat_detector.rb', line 28

def call(samples)
  instant_energy = frame_energy(samples)
  average_energy = average(@energy_history)
  threshold = average_energy * @sensitivity
  enough_history = @energy_history.length >= @min_history
  refractory_ok = (@frame_index - @last_beat_frame) > @refractory_frames
  beat = enough_history && refractory_ok && instant_energy > threshold && instant_energy >= @min_energy

  if beat
    @beat_count += 1
    @last_beat_frame = @frame_index
  end

  @energy_history << instant_energy
  @energy_history.shift while @energy_history.length > @history_size
  @frame_index += 1

  {
    beat: beat,
    beat_count: @beat_count,
    instant_energy: instant_energy,
    average_energy: average_energy,
    threshold: threshold
  }
end