Skip to content

Instantly share code, notes, and snippets.

@youpy
Created September 9, 2009 15:05
Show Gist options
  • Save youpy/183783 to your computer and use it in GitHub Desktop.
Save youpy/183783 to your computer and use it in GitHub Desktop.
# Simple sound energy algorithm #1
# http://www.gamedev.net/reference/programming/features/beatdetection/
require 'rubygems'
require 'riff/reader'
file = ARGV.shift
class Wav
def initialize(riff)
@riff = riff
raise if @riff.root_chunk.signature != 'WAVE'
end
def num_channels
@nc ||= read_bytes(fmt_chunk.offset + 10, 2, 's').first
end
def sample_rate
@sr ||= read_bytes(fmt_chunk.offset + 12, 4, 'I').first
end
def bit_rate
@br ||= read_bytes(fmt_chunk.offset + 22, 2, 's').first
end
def data_speed
@ds ||= read_bytes(fmt_chunk.offset + 16, 4, 'I').first
end
def block_size
@bs ||= read_bytes(fmt_chunk.offset + 20, 2, 's').first
end
def each_sample
file = @riff.file
offset = data_chunk.offset + 8
length = data_chunk.length
pack_template = bit_rate == 16 ? 's*' : 'C*'
file.pos = offset
while file.pos < offset + length
yield read_bytes(file.pos, block_size, pack_template)
end
end
private
def fmt_chunk
find_chunk('fmt ')
end
def data_chunk
find_chunk('data')
end
def find_chunk(fourcc)
@riff.root_chunk.each do |chunk|
if chunk.fourcc == fourcc
return chunk
end
end
end
def read_bytes(offset, bytes, pack_template)
@riff.file.pos = offset
@riff.file.read(bytes).unpack(pack_template)
end
end
wav = Wav.new(Riff::Reader.open(file, 'r'))
INSTANT_ENERGY_SIZE = 1024
HISTORY_BUFFER_SIZE = 44032
SENSIBILITY = 1.3
buffer = []
history_buffer = []
i = 0
wav.each_sample do |sample|
if wav.num_channels == 2
buffer << sample[0] ** 2 + sample[1] ** 2
else
buffer << sample[0] ** 2
end
if buffer.size == INSTANT_ENERGY_SIZE
if history_buffer.size == HISTORY_BUFFER_SIZE
instant_energy = buffer.inject(0) do |total, samp|
total + samp
end
local_average_energy = history_buffer.inject(0) do |total, samp|
total + samp
end * INSTANT_ENERGY_SIZE / wav.sample_rate * SENSIBILITY
puts '%f sec(%i): %s' % [i / wav.data_speed.to_f, i, instant_energy > local_average_energy ? 'beat!!' : '']
end
history_buffer[0..0] = buffer
history_buffer.slice!(HISTORY_BUFFER_SIZE, INSTANT_ENERGY_SIZE)
buffer = []
end
i += wav.block_size
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment