aboutsummaryrefslogtreecommitdiffstats
path: root/src/earworm_support.py
diff options
context:
space:
mode:
authorBrian Jordan2012-04-26 01:23:32 -0400
committerBrian Jordan2012-04-26 01:23:32 -0400
commit3a5c1b91af4dde0e7270d21aca356133e5f4d8d0 (patch)
treee1aa04d9388d43d595f6c4e9cbc8fee7450f4ad1 /src/earworm_support.py
parent0553e3ffa31633ec8b5c42fc5dbacb8b3b3bf448 (diff)
downloadvideo-tuneup-stewardess-3a5c1b91af4dde0e7270d21aca356133e5f4d8d0.tar.bz2
add earworm, outline of stewardess.rb approach
Diffstat (limited to 'src/earworm_support.py')
-rw-r--r--src/earworm_support.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/earworm_support.py b/src/earworm_support.py
new file mode 100644
index 0000000..390a1c4
--- /dev/null
+++ b/src/earworm_support.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+"""
+earworm_support.py
+
+Created by Tristan Jehan and Jason Sundram.
+"""
+
+import numpy as np
+from copy import deepcopy
+from utils import rows
+
+FUSION_INTERVAL = .06 # This is what we use in the analyzer
+AVG_PEAK_OFFSET = 0.025 # Estimated time between onset and peak of segment.
+
+
+def evaluate_distance(mat1, mat2):
+ return np.linalg.norm(mat1.flatten() - mat2.flatten())
+
+def timbre_whiten(mat):
+ if rows(mat) < 2: return mat
+ m = np.zeros((rows(mat), 12), dtype=np.float32)
+ m[:,0] = mat[:,0] - np.mean(mat[:,0],0)
+ m[:,0] = m[:,0] / np.std(m[:,0],0)
+ m[:,1:] = mat[:,1:] - np.mean(mat[:,1:].flatten(),0)
+ m[:,1:] = m[:,1:] / np.std(m[:,1:].flatten(),0) # use this!
+ return m
+
+
+def get_central(analysis, member='segments'):
+ """ Returns a tuple:
+ 1) copy of the members (e.g. segments) between end_of_fade_in and start_of_fade_out.
+ 2) the index of the first retained member.
+ """
+ def central(s):
+ return analysis.end_of_fade_in <= s.start and (s.start + s.duration) < analysis.start_of_fade_out
+
+ members = getattr(analysis, member)
+ ret = filter(central, members[:])
+ index = members.index(ret[0]) if ret else 0
+
+ return ret, index
+
+
+def get_mean_offset(segments, markers):
+ if segments == markers:
+ return 0
+
+ index = 0
+ offsets = []
+ try:
+ for marker in markers:
+ while segments[index].start < marker.start + FUSION_INTERVAL:
+ offset = abs(marker.start - segments[index].start)
+ if offset < FUSION_INTERVAL:
+ offsets.append(offset)
+ index += 1
+ except IndexError, e:
+ pass
+
+ return np.average(offsets) if offsets else AVG_PEAK_OFFSET
+
+
+def resample_features(data, rate='tatums', feature='timbre'):
+ """
+ Resample segment features to a given rate within fade boundaries.
+ @param data: analysis object.
+ @param rate: one of the following: segments, tatums, beats, bars.
+ @param feature: either timbre or pitch.
+ @return A dictionary including a numpy matrix of size len(rate) x 12, a rate, and an index
+ """
+ ret = {'rate': rate, 'index': 0, 'cursor': 0, 'matrix': np.zeros((1, 12), dtype=np.float32)}
+ segments, ind = get_central(data.analysis, 'segments')
+ markers, ret['index'] = get_central(data.analysis, rate)
+
+ if len(segments) < 2 or len(markers) < 2:
+ return ret
+
+ # Find the optimal attack offset
+ meanOffset = get_mean_offset(segments, markers)
+ # Make a copy for local use
+ tmp_markers = deepcopy(markers)
+
+ # Apply the offset
+ for m in tmp_markers:
+ m.start -= meanOffset
+ if m.start < 0: m.start = 0
+
+ # Allocate output matrix, give it alias mat for convenience.
+ mat = ret['matrix'] = np.zeros((len(tmp_markers)-1, 12), dtype=np.float32)
+
+ # Find the index of the segment that corresponds to the first marker
+ f = lambda x: tmp_markers[0].start < x.start + x.duration
+ index = (i for i,x in enumerate(segments) if f(x)).next()
+
+ # Do the resampling
+ try:
+ for (i, m) in enumerate(tmp_markers):
+ while segments[index].start + segments[index].duration < m.start + m.duration:
+ dur = segments[index].duration
+ if segments[index].start < m.start:
+ dur -= m.start - segments[index].start
+
+ C = min(dur / m.duration, 1)
+
+ mat[i, 0:12] += C * np.array(getattr(segments[index], feature))
+ index += 1
+
+ C = min( (m.duration + m.start - segments[index].start) / m.duration, 1)
+ mat[i, 0:12] += C * np.array(getattr(segments[index], feature))
+ except IndexError, e:
+ pass # avoid breaking with index > len(segments)
+
+ return ret \ No newline at end of file