Created
September 9, 2009 15:05
-
-
Save youpy/183783 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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