Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autovot support PR #10

Merged
merged 34 commits into from
Nov 15, 2018
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
64e27a1
Made initial test/setup for autovot integration
MichaelGoodale Sep 12, 2018
e799e5e
got arguments passing without error
MichaelGoodale Sep 12, 2018
2b95605
Got extgrid generation working
MichaelGoodale Sep 12, 2018
3f4830a
Added subprocess
MichaelGoodale Sep 12, 2018
fb46f83
Fixed autovot call
MichaelGoodale Sep 12, 2018
dd2efdd
got wokring xcept return
MichaelGoodale Sep 13, 2018
5071acc
Added vot min max parametres to make voiced test set runnable
MichaelGoodale Sep 14, 2018
aec9bb1
Made output work for multiple VOTs in one file
MichaelGoodale Sep 14, 2018
bce02a4
Added file with vot times
MichaelGoodale Sep 19, 2018
c0eb713
Updated data directory based on autovot submodule
MichaelGoodale Sep 19, 2018
b6efa5a
fixed attribute issue
MichaelGoodale Sep 20, 2018
4fa01d0
Made return type of autovot float
MichaelGoodale Sep 30, 2018
4c5c12c
Added actual output checker for autovot
MichaelGoodale Sep 30, 2018
de8542d
Added extra data and sorting
MichaelGoodale Oct 9, 2018
bac4ba4
fixed missing zip
MichaelGoodale Oct 9, 2018
d5ffbd1
removed superfluous prints
MichaelGoodale Oct 10, 2018
04d707d
debug copy csv
MichaelGoodale Oct 10, 2018
97014b4
changed copy point
MichaelGoodale Oct 10, 2018
ccc3658
starting reading header again
MichaelGoodale Oct 10, 2018
058c048
added window min max optons
MichaelGoodale Oct 10, 2018
e2911ef
fixed
MichaelGoodale Oct 11, 2018
8f2489e
fixed issue
MichaelGoodale Oct 11, 2018
20da029
removed unnecessary debug
MichaelGoodale Oct 11, 2018
c0472f2
Added debugging
MichaelGoodale Oct 19, 2018
62d901b
Made debug prints optional
MichaelGoodale Oct 20, 2018
94ad13c
made autoconvert to correct file format
MichaelGoodale Oct 22, 2018
aeb0b8e
Made compatible with python3.6
MichaelGoodale Oct 24, 2018
3315788
export confidence from autovot too
MichaelGoodale Oct 26, 2018
41c1487
Fixed error in __eq__ func which didn't account for properties
MichaelGoodale Oct 27, 2018
e3b90e3
Added fix for negative 0 conf stops
MichaelGoodale Nov 2, 2018
da400a5
Removed autovot path option(autovot requires a bunch of stuff on the …
MichaelGoodale Nov 5, 2018
662b210
Updated test case
MichaelGoodale Nov 7, 2018
ed1f180
Removed autovot submodule
MichaelGoodale Nov 15, 2018
91992e2
Removed autovot test directory and added new file to replace it
MichaelGoodale Nov 15, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "autovot"]
MichaelGoodale marked this conversation as resolved.
Show resolved Hide resolved
path = autovot
url = https://github.com/mlml/autovot
1 change: 1 addition & 0 deletions autovot
Submodule autovot added at 78a6f2
87 changes: 87 additions & 0 deletions conch/analysis/autovot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from .functions import BaseAnalysisFunction
import wave
import subprocess
import textgrid
import os
import tempfile


def is_autovot_friendly_file(sound_file):
rate = subprocess.run(["soxi", "-r", sound_file], encoding="UTF-8", stdout=subprocess.PIPE).stdout
if int(rate) != 16000:
return False

channels = subprocess.run(["soxi", "-c", sound_file], encoding="UTF-8", stdout=subprocess.PIPE).stdout
if int(channels) != 1:
return False
return True

def resample_for_autovot(soundfile, tmpdir):
output_file = os.path.join(tmpdir, "sound_file.wav")
subprocess.call(["sox", soundfile, "-c", "1", "-r", "16000", output_file])
return output_file


class MeasureVOTPretrained(object):
def __init__(self, classifier_to_use=None, min_vot_length=15, max_vot_length=250, window_max=30, window_min=30, debug=False):
if classifier_to_use is None:
raise ValueError("There must be a classifier to run AutoVOT")
else:
self.classifier_to_use = classifier_to_use
self.min_vot_length = min_vot_length
self.max_vot_length = max_vot_length
self.debug = debug
self.window_max = window_max
self.window_min = window_min

def __call__(self, segment):
file_path = os.path.expanduser(segment["file_path"])
begin = segment["begin"]
end = segment["end"]
vot_marks = sorted(segment["vot_marks"], key=lambda x: x[0])
grid = textgrid.TextGrid(maxTime=end)
vot_tier = textgrid.IntervalTier(name='vot', maxTime=end)
for vot_begin, vot_end, *extra_data in vot_marks:
vot_tier.add(vot_begin, vot_end, 'vot')
grid.append(vot_tier)
with tempfile.TemporaryDirectory() as tmpdirname:
grid_path = "{}/file.TextGrid".format(tmpdirname)
csv_path = "{}/file.csv".format(tmpdirname)
wav_filenames = "{}/wavs.txt".format(tmpdirname)
textgrid_filenames = "{}/textgrids.txt".format(tmpdirname)

if not is_autovot_friendly_file(file_path):
file_path = resample_for_autovot(file_path, tmpdirname)

with open(wav_filenames, 'w') as f:
f.write("{}\n".format(file_path))

with open(textgrid_filenames, 'w') as f:
f.write("{}\n".format(grid_path))

grid.write(grid_path)

if self.debug:
grid.write('/tmp/textgrid_from_conch.csv')
with open('/tmp/alt_wordlist.txt', 'w') as f:
f.write("{}\n".format('/tmp/textgrid_from_conch.csv'))
subprocess.run(["auto_vot_decode.py", wav_filenames, '/tmp/alt_wordlist.txt', self.classifier_to_use, '--vot_tier', 'vot', '--vot_mark', 'vot', "--min_vot_length", str(self.min_vot_length), "--max_vot_length", str(self.max_vot_length), "--window_max", str(self.window_max), "--window_min", str(self.window_min)])
subprocess.run(["auto_vot_decode.py", wav_filenames, textgrid_filenames, self.classifier_to_use, '--vot_tier', 'vot', '--vot_mark', 'vot', '--csv_file', csv_path, "--min_vot_length", str(self.min_vot_length), "--max_vot_length", str(self.max_vot_length), "--window_max", str(self.window_max), "--window_min", str(self.window_min)])
mmcauliffe marked this conversation as resolved.
Show resolved Hide resolved

return_list = []
with open(csv_path, "r") as f:
f.readline()
for l, (b, e, *extra_data) in zip(f, vot_marks):
_, time, vot, confidence = l.split(',')
if "neg 0\n" == confidence:
confidence = 0
return_list.append((float(time), float(vot), float(confidence), *extra_data))
return return_list

class AutoVOTAnalysisFunction(BaseAnalysisFunction):
def __init__(self, classifier_to_use=None, min_vot_length=15, max_vot_length=250, window_max=30, window_min=30, debug=False, arguments=None):
super(AutoVOTAnalysisFunction, self).__init__()
self._function = MeasureVOTPretrained(classifier_to_use=classifier_to_use, min_vot_length=min_vot_length, max_vot_length=max_vot_length, window_max=window_max, window_min=window_min, debug=debug)
self.requires_file = True
self.uses_segments = True
self.requires_segment_as_arg = True
3 changes: 3 additions & 0 deletions conch/analysis/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class BaseAnalysisFunction(object):
def __init__(self):
self._function = print
self.requires_file = False
self.requires_segment_as_arg = False
self.uses_segments = False
self.arguments = []

Expand All @@ -39,6 +40,8 @@ def __call__(self, segment):
elif isinstance(segment, str) and not self.requires_file:
signal, sr = librosa.load(safe_path(segment))
return self._function(signal, sr, *self.arguments)
elif isinstance(segment, FileSegment) and self.requires_segment_as_arg:
return self._function(segment, *self.arguments)
elif isinstance(segment, FileSegment) and self.requires_file and not self.uses_segments:
beg, end = segment.begin, segment.end
padding = segment['padding']
Expand Down
2 changes: 2 additions & 0 deletions conch/analysis/segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def __eq__(self, other):
return False
if self.channel != other.channel:
return False
if self.properties != other.properties:
return False
return True

def __lt__(self, other):
Expand Down
27 changes: 26 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ def praat_script_test_dir(test_dir):
def soundfiles_dir(test_dir):
return os.path.join(test_dir, 'soundfiles')

@pytest.fixture(scope='session')
def autovot_dir(test_dir):
return os.path.join(test_dir, 'autovot')

@pytest.fixture(scope='session')
def tts_dir(test_dir):
Expand Down Expand Up @@ -87,6 +90,29 @@ def base_filenames(soundfiles_dir):
if x.endswith('.wav')]
return filenames

@pytest.fixture(scope='session')
def autovot_filenames(autovot_dir):
filenames = [os.path.join(autovot_dir, x)
for x in os.listdir(autovot_dir)
if x.endswith('.wav')]
return filenames

@pytest.fixture(scope='session')
def autovot_markings(test_dir, autovot_dir):
vot_markings = {}
with open(os.path.join(test_dir, "vot_marks"), "r") as f:
for x in f:
k = os.path.join(autovot_dir, x.split(' ')[0])
l = x.split(' ')[1:]
v = []
while l:
v.append((float(l.pop(0)), float(l.pop(0))))
vot_markings[k] = v
return vot_markings

@pytest.fixture(scope='session')
def autovot_correct_times():
return [(0.82, 0.005, 42.0803), (0.92, 0.005, 50.5166), (0.8, 0.005, 125.03), (0.76, 0.007, 93.6872), (0.756, 0.01, 101.213), (0.78, 0.009, 108.41), (0.774, 0.005, -62.3751), (0.807, 0.016, 85.4072), (0.64, 0.008, 70.7911), (0.753, 0.005, 63.7703), (0.692, 0.005, 103.689), (0.919, 0.021, 55.0379), (0.554, 0.005, -58.1071), (0.699, 0.024, 73.1509), (0.65, 0.011, 84.4646), (0.713, 0.022, 78.342), (0.66, 0.005, 50.3185), (0.67, 0.005, 67.515), (0.73, 0.005, 76.6631), (0.973, 0.005, -62.2706), (0.88, 0.009, 89.3114), (1.044, 0.005, 108.472), (0.704, 0.005, 93.9944), (0.93, 0.005, 65.8704), (0.73, 0.005, 111.866), (0.861, 0.005, 94.9302)]

@pytest.fixture(scope='session')
def praatpath():
Expand Down Expand Up @@ -114,7 +140,6 @@ def formants_func():
window_length=0.025)
return func


@pytest.fixture(scope='session')
def pitch_func():
func = PitchTrackFunction(min_pitch=50, max_pitch=500, time_step=0.01)
Expand Down
1 change: 1 addition & 0 deletions tests/data/autovot
26 changes: 26 additions & 0 deletions tests/data/vot_marks
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
cas7D_1054_24_3.wav 0.81000 0.93000
cas7D_1054_26_1.wav 0.79000 0.87000
cas7D_1054_26_2.wav 0.85000 0.89000
cas7D_1054_26_3.wav 0.68000 0.79000
cas7D_1054_28_1.wav 0.83000 0.92000
cas7D_1054_28_2.wav 0.95000 0.98000
cas7D_1054_28_3.wav 0.74000 0.84000
cas7D_1054_30_1.wav 0.80000 0.87000
cas7D_1054_30_2.wav 0.85000 0.95000
cas7D_1054_30_3.wav 0.67000 0.74000
cas7D_1054_32_1.wav 0.78000 0.86000
cas7D_1054_32_2.wav 0.72000 0.82000
cas7D_1054_32_3.wav 0.58000 0.67000
cas7D_1054_34_1.wav 0.66000 0.74000
cas7D_1054_34_2.wav 0.59000 0.68000
cas7D_1054_34_3.wav 0.66000 0.75000
cas7D_1054_36_1.wav 0.70000 0.80000
cas7D_1054_36_2.wav 0.69000 0.80000
cas7D_1054_36_3.wav 0.76000 0.86000
cas7D_1144_32_3.wav 1.00000 1.10000
cas7D_1144_34_1.wav 1.07000 1.11000
cas7D_1144_34_2.wav 0.91000 0.98000
cas7D_1144_34_3.wav 0.96000 1.03000
cas7D_1144_36_1.wav 0.73000 0.80000
cas7D_1144_36_2.wav 0.76000 0.86000
cas7D_1144_36_3.wav 0.89000 0.98000
22 changes: 22 additions & 0 deletions tests/test_analysis_autovot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from conch.analysis.autovot import AutoVOTAnalysisFunction
import librosa
from statistics import mean
import wave
import pytest
from conch.analysis.segments import SegmentMapping
from conch import analyze_segments


def test_autovot(autovot_filenames, autovot_markings, autovot_correct_times):
mapping = SegmentMapping()
for filename in autovot_filenames:
with wave.open(filename,'r') as f:
length = f.getnframes() / float(f.getframerate())
mapping.add_file_segment(filename, 0, length, channel=0, vot_marks=autovot_markings[filename])
func = AutoVOTAnalysisFunction(classifier_to_use="autovot/experiments/models/bb_jasa.classifier", window_min=-30, window_max=30, min_vot_length=5, max_vot_length=100)
MichaelGoodale marked this conversation as resolved.
Show resolved Hide resolved
output = analyze_segments(mapping, func, multiprocessing=False)
output_l = sorted([output[x][0] for x in output], key=lambda x:x[0])
autovot_correct_times.sort(key=lambda x:x[0])
for o, truth in zip(output_l, autovot_correct_times):
assert o == truth