Create a little more structure around the tools - warvox - Unnamed repository; … | |
Log | |
Files | |
Refs | |
README | |
--- | |
commit 60be9accc104b2d63ff2ab74bf586b4ce054a524 | |
parent 18b3b7c1dbb56be8aed57018765ebad24655d98b | |
Author: HD Moore <[email protected]> | |
Date: Fri, 13 Feb 2009 06:23:40 +0000 | |
Create a little more structure around the tools | |
Diffstat: | |
M bin/create_flow.rb | 91 ++---------------------------… | |
A bin/create_flowdb.rb | 40 +++++++++++++++++++++++++++++… | |
M bin/create_samples.rb | 15 ++++++--------- | |
M bin/create_sig.rb | 84 ++++-------------------------… | |
A data/test.db.bz2 | 0 | |
M lib/warvox.rb | 5 +++-- | |
A lib/warvox/audio.rb | 2 ++ | |
A lib/warvox/audio/error.rb | 7 +++++++ | |
A lib/warvox/audio/raw.rb | 129 +++++++++++++++++++++++++++++… | |
A lib/warvox/db.rb | 113 +++++++++++++++++++++++++++++… | |
10 files changed, 316 insertions(+), 170 deletions(-) | |
--- | |
diff --git a/bin/create_flow.rb b/bin/create_flow.rb | |
@@ -13,93 +13,12 @@ require 'warvox' | |
# | |
# Script | |
-# | |
- | |
-# | |
-# Parameters | |
-# | |
-lo_lim = 100 | |
-lo_min = 5 | |
-lo_cnt = 0 | |
-hi_min = 5 | |
-hi_cnt = 0 | |
- | |
-# | |
-# Input | |
# | |
-cnt = 0 | |
-inp = ARGV.shift || exit | |
-raw = File.read(inp) | |
-data = raw.unpack("s*").map {|c| c.abs} | |
- | |
-# | |
-# Granular hi/low state change list | |
-# | |
-fprint = [] | |
-state = :lo | |
-idx = 0 | |
-buff = [] | |
- | |
-while (idx < data.length) | |
- case state | |
- when :lo | |
- while(idx < data.length and data[idx] <= lo_lim) | |
- buff << data[idx] | |
- idx += 1 | |
- end | |
- | |
- # Ignore any sequence that is too small | |
- fprint << [:lo, buff.length, buff - [0]] if buff.length > lo_m… | |
- state = :hi | |
- buff = [] | |
- next | |
- when :hi | |
- while(idx < data.length and data[idx] > lo_lim) | |
- buff << data[idx] | |
- idx += 1 | |
- end | |
- | |
- # Ignore any sequence that is too small | |
- fprint << [:hi, buff.length, buff] if buff.length > hi_min | |
- state = :lo | |
- buff = [] | |
- next | |
- end | |
-end | |
- | |
- | |
-# | |
-# Merge similar blocks | |
-# | |
-final = [] | |
-prev = fprint[0] | |
-idx = 1 | |
- | |
-while(idx < fprint.length) | |
- | |
- if(fprint[idx][0] == prev[0]) | |
- prev[1] += fprint[idx][1] | |
- prev[2] += fprint[idx][2] | |
- else | |
- final << prev | |
- prev = fprint[idx] | |
- end | |
- | |
- idx += 1 | |
-end | |
-final << prev | |
- | |
- | |
-# | |
-# Process results | |
-# | |
-sig = "#{inp} " | |
-final.each do |f| | |
- sum = 0 | |
- f[2].each {|i| sum += i } | |
- avg = (sum == 0) ? 0 : sum / f[2].length | |
- sig << "#{f[0].to_s.upcase[0,1]},#{f[1]},#{avg} " | |
+def usage | |
+ $stderr.puts "#{$0} [audio.raw]" | |
+ exit | |
end | |
-puts sig | |
+raw = WarVOX::Audio::Raw.from_file(ARGV.shift || usage) | |
+puts raw.to_flow | |
diff --git a/bin/create_flowdb.rb b/bin/create_flowdb.rb | |
@@ -0,0 +1,40 @@ | |
+#!/usr/bin/env ruby | |
+################### | |
+ | |
+# | |
+# Load the library path | |
+# | |
+base = __FILE__ | |
+while File.symlink?(base) | |
+ base = File.expand_path(File.readlink(base), File.dirname(base)) | |
+end | |
+$:.unshift(File.join(File.expand_path(File.dirname(base)), '..', 'lib')) | |
+require 'warvox' | |
+ | |
+# | |
+# Script | |
+# | |
+ | |
+def usage | |
+ $stderr.puts "#{$0} [/path/to/audio/] [output.db]" | |
+ exit | |
+end | |
+ | |
+src = ARGV.shift || usage | |
+dst = ARGV.shift || usage | |
+db = File.new(dst, "w") | |
+dir = Dir.new(src) | |
+cnt = 0 | |
+ | |
+set = dir.entries.sort.grep(/\.raw$/) | |
+set.each do |ent| | |
+ next if not ent =~ /\.raw$/ | |
+ puts "[*] [#{sprintf("%.5d/%.5d", cnt+1, set.length)}] Processing #{en… | |
+ raw = WarVOX::Audio::Raw.from_file( File.join(src, ent) ) | |
+ db.write( ent.gsub('.raw', '') + " " + raw.to_flow + "\n" ) | |
+ cnt += 1 | |
+end | |
+ | |
+db.close | |
+ | |
+puts "[*] Wrote #{cnt} database entries into #{dst}" | |
diff --git a/bin/create_samples.rb b/bin/create_samples.rb | |
@@ -15,17 +15,14 @@ require 'warvox' | |
# Script | |
# | |
-=begin | |
- 8,000 samples per second | |
- 160 samples per block of data | |
-=end | |
+def usage | |
+ $stderr.puts "#{$0} [audio.raw]" | |
+ exit | |
+end | |
cnt = 0 | |
-inp = ARGV.shift || exit | |
-raw = File.read(inp) | |
-raw.unpack("v*").each do |s| | |
- val = (s > 0x7fff) ? (0x10000 - s) * -1 : s | |
+raw = WarVOX::Audio::Raw.from_file(ARGV.shift || usage) | |
+raw.samples.each do |val| | |
puts "#{cnt} #{val}" | |
cnt += 1 | |
end | |
- | |
diff --git a/bin/create_sig.rb b/bin/create_sig.rb | |
@@ -15,81 +15,19 @@ require 'warvox' | |
# Script | |
# | |
-inp = ARGV.shift() | |
-num1 = ARGV.shift() || exit | |
-num2 = ARGV.shift() || exit | |
- | |
-min_len = 800 | |
- | |
- | |
-info1 = [] | |
-info2 = [] | |
- | |
-fd = File.open(inp, "r") | |
-fd.each_line do |line| | |
- data = line.strip.split(/\s+/) | |
- name = data.shift | |
- next if name !~ /#{num1}|#{num2}/ | |
- | |
- # Bump the leading silence off | |
- data.shift if data[0] =~ /^L/ | |
- | |
- data.each do |d| | |
- s,l,a = d.split(",") | |
- next if l.to_i < min_len | |
- plot = [s, l.to_i, a.to_i] | |
- name =~ /#{num1}/ ? info1 << plot : info2 << plot | |
- end | |
- | |
- break if (info1.length > 0 and info2.length > 0) | |
-end | |
- | |
-if (not (info1.length > 0 and info2.length > 0)) | |
- $stderr.puts "error: could not find both numbers in the database" | |
+def usage | |
+ $stderr.puts "#{$0} [warvox.db] [num1] [num2] <fuzz-factor> <db-thresh… | |
exit | |
end | |
+inp = ARGV.shift() || usage | |
+num1 = ARGV.shift() || usage | |
+num2 = ARGV.shift() || usage | |
+fuzz = (ARGV.shift() || 100).to_i | |
+thresh = (ARGV.shift() || 800).to_i | |
-min_sig = 2 | |
-fuzz = 100 | |
-idx = 0 | |
-fnd = nil | |
-r = 0 | |
- | |
-while(idx < info1.length-min_sig) | |
- sig = info1[idx,info1.length] | |
- idx2 = 0 | |
- | |
- while (idx2 < info2.length) | |
- c = 0 | |
- 0.upto(sig.length-1) do |si| | |
- break if not info2[idx2+si] | |
- break if not ( | |
- sig[si][0] == info2[idx2+si][0] and | |
- info2[idx2 + si][1] > sig[si][1]-fuzz and | |
- info2[idx2 + si][1] < sig[si][1]+fuzz | |
- ) | |
- c += 1 | |
- end | |
- | |
- if (c > r) | |
- r = c | |
- fnd = sig[0, r] | |
- end | |
- idx2 += 1 | |
- end | |
- idx += 1 | |
-end | |
- | |
- | |
- | |
- | |
-version = "1.0" | |
+info1 = [] | |
+info2 = [] | |
-if(fnd) | |
- sig = "V=#{version},F=#{fuzz},S1=#{num1},S2=#{num2},L=#{r} " | |
- fnd.each do |i| | |
- sig << i.join(",") + " " | |
- end | |
- puts sig | |
-end | |
+wdb = WarVOX::DB.new(inp, thresh) | |
+puts wdb.find_sig(num1, num2, { :fuzz => fuzz }).inspect | |
diff --git a/data/test.db.bz2 b/data/test.db.bz2 | |
Binary files differ. | |
diff --git a/lib/warvox.rb b/lib/warvox.rb | |
@@ -3,6 +3,7 @@ | |
## | |
module WarVOX | |
- | |
- | |
end | |
+ | |
+require 'warvox/audio' | |
+require 'warvox/db' | |
diff --git a/lib/warvox/audio.rb b/lib/warvox/audio.rb | |
@@ -0,0 +1,2 @@ | |
+require 'warvox/audio/error' | |
+require 'warvox/audio/raw' | |
diff --git a/lib/warvox/audio/error.rb b/lib/warvox/audio/error.rb | |
@@ -0,0 +1,7 @@ | |
+module WarVOX | |
+module Audio | |
+class Error < ::RuntimeError | |
+ | |
+end | |
+end | |
+end | |
diff --git a/lib/warvox/audio/raw.rb b/lib/warvox/audio/raw.rb | |
@@ -0,0 +1,129 @@ | |
+module WarVOX | |
+module Audio | |
+class Raw | |
+ | |
+ ## | |
+ # RAW AUDIO - 8khz little-endian 16-bit signed | |
+ ## | |
+ | |
+ ## | |
+ # Static methods | |
+ ## | |
+ | |
+ def self.from_str(str) | |
+ self.class.new(str) | |
+ end | |
+ | |
+ def self.from_file(path) | |
+ if(not path) | |
+ raise Error, "No audio path specified" | |
+ end | |
+ | |
+ if(path == "-") | |
+ return self.new($stdin.read) | |
+ end | |
+ | |
+ if(not File.readable?(path)) | |
+ raise Error, "The specified audio file does not exist" | |
+ end | |
+ | |
+ self.new(File.read(path, File.size(path))) | |
+ end | |
+ | |
+ ## | |
+ # Class methods | |
+ ## | |
+ | |
+ attr_accessor :samples | |
+ | |
+ def initialize(data) | |
+ self.samples = data.unpack('v*').map do |s| | |
+ (s > 0x7fff) ? (0x10000 - s) * -1 : s | |
+ end | |
+ end | |
+ | |
+ def to_flow(opts={}) | |
+ | |
+ lo_lim = (opts[:lo_lim] || 100).to_i | |
+ lo_min = (opts[:lo_min] || 5).to_i | |
+ hi_min = (opts[:hi_min] || 5).to_i | |
+ lo_cnt = 0 | |
+ hi_cnt = 0 | |
+ | |
+ data = self.samples.map {|c| c.abs} | |
+ | |
+ # | |
+ # Granular hi/low state change list | |
+ # | |
+ fprint = [] | |
+ state = :lo | |
+ idx = 0 | |
+ buff = [] | |
+ | |
+ while (idx < data.length) | |
+ case state | |
+ when :lo | |
+ while(idx < data.length and data[idx] <= lo_li… | |
+ buff << data[idx] | |
+ idx += 1 | |
+ end | |
+ | |
+ # Ignore any sequence that is too small | |
+ fprint << [:lo, buff.length, buff - [0]] if bu… | |
+ state = :hi | |
+ buff = [] | |
+ next | |
+ when :hi | |
+ while(idx < data.length and data[idx] > lo_lim) | |
+ buff << data[idx] | |
+ idx += 1 | |
+ end | |
+ | |
+ # Ignore any sequence that is too small | |
+ fprint << [:hi, buff.length, buff] if buff.len… | |
+ state = :lo | |
+ buff = [] | |
+ next | |
+ end | |
+ end | |
+ | |
+ # | |
+ # Merge similar blocks | |
+ # | |
+ final = [] | |
+ prev = fprint[0] | |
+ idx = 1 | |
+ | |
+ while(idx < fprint.length) | |
+ | |
+ if(fprint[idx][0] == prev[0]) | |
+ prev[1] += fprint[idx][1] | |
+ prev[2] += fprint[idx][2] | |
+ else | |
+ final << prev | |
+ prev = fprint[idx] | |
+ end | |
+ | |
+ idx += 1 | |
+ end | |
+ final << prev | |
+ | |
+ # | |
+ # Process results | |
+ # | |
+ sig = "" | |
+ | |
+ final.each do |f| | |
+ sum = 0 | |
+ f[2].each {|i| sum += i } | |
+ avg = (sum == 0) ? 0 : sum / f[2].length | |
+ sig << "#{f[0].to_s.upcase[0,1]},#{f[1]},#{avg} " | |
+ end | |
+ | |
+ # Return the results | |
+ return sig | |
+ end | |
+ | |
+end | |
+end | |
+end | |
diff --git a/lib/warvox/db.rb b/lib/warvox/db.rb | |
@@ -0,0 +1,113 @@ | |
+module WarVOX | |
+class DB | |
+ | |
+ VERSION = '1.0' | |
+ | |
+ class Error < ::RuntimeError | |
+ end | |
+ | |
+ attr_accessor :path, :nums, :threshold, :version | |
+ | |
+ def initialize(path, threshold=800) | |
+ self.path = path | |
+ self.threshold = threshold | |
+ self.nums = {} | |
+ self.version = VERSION | |
+ | |
+ File.open(path, "r") do |fd| | |
+ fd.each_line do |line| | |
+ line.strip! | |
+ next if line.empty? | |
+ bits = line.split(/\s+/) | |
+ name = bits.shift | |
+ | |
+ self.nums[name] = [] | |
+ bits.each do |d| | |
+ s,l,a = d.split(',') | |
+ next if l.to_i < self.threshold | |
+ self.nums[name] << [s, l.to_i, a.to_i] | |
+ end | |
+ end | |
+ end | |
+ end | |
+ | |
+ def [](num) | |
+ self.nums[num] | |
+ end | |
+ | |
+ def []=(num,val) | |
+ self.nums[num] = val | |
+ end | |
+ | |
+ # | |
+ # Utility methods | |
+ # | |
+ | |
+ def find_sig(num1, num2, opts={}) | |
+ | |
+ fuzz = opts[:fuzz] || 100 | |
+ info1 = self[num1] | |
+ info2 = self[num2] | |
+ | |
+ # Make sure both samples exist in the database | |
+ if ( not (info1 and info2 and not (info1.empty? or info2.empty… | |
+ raise Error, "The database must contain both numbers" | |
+ end | |
+ | |
+ min_sig = 2 | |
+ idx = 0 | |
+ fnd = nil | |
+ mat = nil | |
+ r = 0 | |
+ | |
+ while(idx < info1.length-min_sig) | |
+ sig = info1[idx,info1.length] | |
+ idx2 = 0 | |
+ | |
+ while (idx2 < info2.length) | |
+ c = 0 | |
+ 0.upto(sig.length-1) do |si| | |
+ break if not info2[idx2+si] | |
+ break if not ( | |
+ sig[si][0] == info2[idx2+si][0… | |
+ info2[idx2 + si][1] > sig[si][… | |
+ info2[idx2 + si][1] < sig[si][… | |
+ ) | |
+ c += 1 | |
+ end | |
+ | |
+ if (c > r) | |
+ r = c | |
+ fnd = sig[0, r] | |
+ mat = info2[idx2, r] | |
+ end | |
+ idx2 += 1 | |
+ end | |
+ idx += 1 | |
+ end | |
+ | |
+ return nil if not fnd | |
+ | |
+ sig = [] | |
+ fnd.each_index do |i| | |
+ sig << | |
+ [ | |
+ (fnd[i][0]), | |
+ (fnd[i][1] + mat[i][1] / 2.0).to_i, | |
+ (fnd[i][2] + mat[i][2] / 2.0).to_i | |
+ ] | |
+ end | |
+ | |
+ { | |
+ :version => self.version, | |
+ :fuzz => fuzz, | |
+ :threshold => self.threshold, | |
+ :num1 => num1, | |
+ :num2 => num2, | |
+ :len => r, | |
+ :sig => sig | |
+ } | |
+ end | |
+ | |
+end | |
+end |