|
@@ -16,6 +16,13 @@ logger = logging.getLogger(__name__) |
|
|
class FeatureExtractor(ABC): |
|
|
class FeatureExtractor(ABC): |
|
|
"""Feature extractor interface.""" |
|
|
"""Feature extractor interface.""" |
|
|
# TODO: #API -- decide if .features will be a member variable |
|
|
# TODO: #API -- decide if .features will be a member variable |
|
|
|
|
|
def _run_get_output(self, cmd: list, cwd:str=".") -> str: |
|
|
|
|
|
"""Run a command and return the output as a string |
|
|
|
|
|
|
|
|
|
|
|
Defined to be mocked out in tests via unittest.mock.patch |
|
|
|
|
|
""" |
|
|
|
|
|
return subprocess.run(cmd, stdout=subprocess.PIPE, cwd=cwd).stdout.decode("utf-8") |
|
|
|
|
|
|
|
|
def setup(self): |
|
|
def setup(self): |
|
|
"""Setup the feature extractor -- validate input files & config""" |
|
|
"""Setup the feature extractor -- validate input files & config""" |
|
|
|
|
|
|
|
@@ -47,8 +54,11 @@ class LaughterFeatureExtractor(FeatureExtractor): |
|
|
self.config = config |
|
|
self.config = config |
|
|
self.features = [] |
|
|
self.features = [] |
|
|
|
|
|
|
|
|
def _laughdetect(self, audio_file): |
|
|
|
|
|
"""Run laughter detection on the audio file""" |
|
|
|
|
|
|
|
|
def _laughdetect(self, audio_file) -> list: |
|
|
|
|
|
"""Run laughter detection on the audio file |
|
|
|
|
|
|
|
|
|
|
|
Returns a list of 2-tuples, each representing a laugh instance in the audio file |
|
|
|
|
|
""" |
|
|
laugh_detector_dir = "/home/robert/mounts/980data/code/laughter-detection/" |
|
|
laugh_detector_dir = "/home/robert/mounts/980data/code/laughter-detection/" |
|
|
laugh_detector_script = "segment_laughter.py" |
|
|
laugh_detector_script = "segment_laughter.py" |
|
|
# fake output for testing |
|
|
# fake output for testing |
|
@@ -57,9 +67,9 @@ class LaughterFeatureExtractor(FeatureExtractor): |
|
|
f"--input_audio_file={audio_file}"] |
|
|
f"--input_audio_file={audio_file}"] |
|
|
|
|
|
|
|
|
# run command, capture output, ignore exit status |
|
|
# run command, capture output, ignore exit status |
|
|
laugh_output = subprocess.run(laugh_detector_cmd, |
|
|
|
|
|
stdout=subprocess.PIPE, |
|
|
|
|
|
cwd=laugh_detector_dir).stdout.decode("utf-8") |
|
|
|
|
|
|
|
|
# use self._run_get_output to allow mocking in tests |
|
|
|
|
|
laugh_output = self._run_get_output(laugh_detector_cmd, laugh_detector_dir) |
|
|
|
|
|
|
|
|
# ↑ have to include cwd to keep laughter-detection imports happy |
|
|
# ↑ have to include cwd to keep laughter-detection imports happy |
|
|
# also, it isn't happy if no output dir is specified but we get laughs so it's grand |
|
|
# also, it isn't happy if no output dir is specified but we get laughs so it's grand |
|
|
|
|
|
|
|
@@ -112,6 +122,7 @@ class LaughterFeatureExtractor(FeatureExtractor): |
|
|
"""Extract laughter features for each input file""" |
|
|
"""Extract laughter features for each input file""" |
|
|
if self.input_files: |
|
|
if self.input_files: |
|
|
for file in self.input_files: |
|
|
for file in self.input_files: |
|
|
|
|
|
# adjust this call for better test mocking |
|
|
laughs = self._laughdetect(file.path) |
|
|
laughs = self._laughdetect(file.path) |
|
|
for laugh in laughs: |
|
|
for laugh in laughs: |
|
|
start, end = laugh |
|
|
start, end = laugh |
|
|