diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/BUILD.gn b/webrtc/modules/audio_processing/test/py_quality_assessment/BUILD.gn index 383b107b7b..15b31898b0 100644 --- a/webrtc/modules/audio_processing/test/py_quality_assessment/BUILD.gn +++ b/webrtc/modules/audio_processing/test/py_quality_assessment/BUILD.gn @@ -40,6 +40,7 @@ copy("lib") { "quality_assessment/eval_scores_factory.py", "quality_assessment/eval_scores_unittest.py", "quality_assessment/evaluation.py", + "quality_assessment/exceptions.py", "quality_assessment/noise_generation.py", "quality_assessment/noise_generation_factory.py", "quality_assessment/noise_generation_unittest.py", diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/audioproc_wrapper.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/audioproc_wrapper.py index 843a84f14a..66c0c224d7 100644 --- a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/audioproc_wrapper.py +++ b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/audioproc_wrapper.py @@ -14,7 +14,7 @@ import logging import os import subprocess -from .data_access import AudioProcConfigFile +from . import data_access class AudioProcWrapper(object): @@ -57,7 +57,7 @@ class AudioProcWrapper(object): return # Load configuration. - self._config = AudioProcConfigFile.load(config_filepath) + self._config = data_access.AudioProcConfigFile.load(config_filepath) # Set remaining parametrs. self._config['-i'] = self._input_signal_filepath diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/eval_scores.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/eval_scores.py index 90b88ed44d..1488b4a126 100644 --- a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/eval_scores.py +++ b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/eval_scores.py @@ -11,8 +11,11 @@ import logging import os +import re +import subprocess from . import data_access +from . import exceptions from . import signal_processing @@ -123,12 +126,62 @@ class PolqaScore(EvaluationScore): """ NAME = 'polqa' + _BIN_FILENAME = 'PolqaOem64' def __init__(self, polqa_tool_path): EvaluationScore.__init__(self) + + # Path to the POLQA directory with binary and license files. self._polqa_tool_path = polqa_tool_path + # POLQA binary file path. + self._polqa_bin_filepath = os.path.join( + self._polqa_tool_path, self._BIN_FILENAME) + if not os.path.exists(self._polqa_bin_filepath): + logging.error('cannot find POLQA tool binary file') + raise exceptions.FileNotFoundError() + def _run(self, output_path): - # TODO(alessio): implement. - self._score = 0.0 + polqa_out_filepath = os.path.join(output_path, 'polqa.out') + if os.path.exists(polqa_out_filepath): + os.unlink(polqa_out_filepath) + + args = [ + self._polqa_bin_filepath, '-t', '-q', '-Overwrite', + '-Ref', self._reference_signal_filepath, + '-Test', self._tested_signal_filepath, + '-LC', 'NB', + '-Out', polqa_out_filepath, + ] + logging.debug(' '.join(args)) + subprocess.call(args, cwd=self._polqa_tool_path) + + # Parse POLQA tool output and extract the score. + polqa_output = self._parse_output_file(polqa_out_filepath) + self._score = float(polqa_output['PolqaScore']) + self._save_score() + + @classmethod + def _parse_output_file(cls, polqa_out_filepath): + """ + Parse the POLQA tool output formatted as a table ('-t' option). + """ + data = [] + with open(polqa_out_filepath) as f: + for line in f: + line = line.strip() + if len(line) == 0 or line.startswith('*'): + # Ignore comments. + continue + # Read fields. + data.append(re.split(r'\t+', line)) + + # Two rows expected (header and values). + assert len(data) == 2, 'Cannot parse POLQA output' + number_of_fields = len(data[0]) + assert number_of_fields == len(data[1]) + + # Build and return a dictionary with field names (header) as keys and the + # corresponding field values as values. + return {data[0][index]: data[1][index] for index in range(number_of_fields)} diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/exceptions.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/exceptions.py new file mode 100644 index 0000000000..b67a64e0c4 --- /dev/null +++ b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/exceptions.py @@ -0,0 +1,18 @@ +# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +"""Exception classes. +""" + + +class FileNotFoundError(Exception): + pass + + +class SignalProcessingException(Exception): + pass diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py index a6a8a0653c..6af6eb6f1a 100644 --- a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py +++ b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/noise_generation.py @@ -32,6 +32,7 @@ except ImportError: sys.exit(1) from . import data_access +from . import exceptions from . import signal_processing class NoiseGenerator(object): @@ -319,7 +320,7 @@ class EnvironmentalNoiseGenerator(NoiseGenerator): self._NOISE_TRACKS_PATH, noise_track_filename) if not os.path.exists(noise_track_filepath): logging.error('cannot find the <%s> noise track', noise_track_filename) - continue + raise exceptions.FileNotFoundError() noise_signal = signal_processing.SignalProcessingUtils.load_wav( noise_track_filepath) diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing.py index 76b103d091..f611eca0dc 100644 --- a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing.py +++ b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing.py @@ -8,6 +8,7 @@ import array import logging +import os import sys try: @@ -29,9 +30,7 @@ except ImportError: logging.critical('Cannot import the third-party Python package scipy') sys.exit(1) - -class SignalProcessingException(Exception): - pass +from . import exceptions class SignalProcessingUtils(object): @@ -46,6 +45,9 @@ class SignalProcessingUtils(object): Return: AudioSegment instance. """ + if not os.path.exists(filepath): + logging.error('cannot find the <%s> audio track file', filepath) + raise exceptions.FileNotFoundError() return pydub.AudioSegment.from_file( filepath, format='wav', channels=channels) @@ -175,10 +177,12 @@ class SignalProcessingUtils(object): noise_power = float(noise.dBFS) if signal_power == -np.Inf: logging.error('signal has -Inf power, cannot mix') - raise SignalProcessingException('cannot mix a signal with -Inf power') + raise exceptions.SignalProcessingException( + 'cannot mix a signal with -Inf power') if noise_power == -np.Inf: logging.error('noise has -Inf power, cannot mix') - raise SignalProcessingException('cannot mix a signal with -Inf power') + raise exceptions.SignalProcessingException( + 'cannot mix a signal with -Inf power') # Pad signal (if necessary). If noise is the shortest, the AudioSegment # overlay() method implictly pads noise. Hence, the only case to handle diff --git a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing_unittest.py b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing_unittest.py index 1765a2c515..7cb088c0d4 100644 --- a/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing_unittest.py +++ b/webrtc/modules/audio_processing/test/py_quality_assessment/quality_assessment/signal_processing_unittest.py @@ -14,6 +14,7 @@ import unittest import numpy as np import pydub +from . import exceptions from . import signal_processing @@ -65,10 +66,10 @@ class TestSignalProcessing(unittest.TestCase): signal = signal_processing.SignalProcessingUtils.generate_white_noise( silence) - with self.assertRaises(signal_processing.SignalProcessingException): + with self.assertRaises(exceptions.SignalProcessingException): _ = signal_processing.SignalProcessingUtils.mix_signals( signal, silence, 0.0) - with self.assertRaises(signal_processing.SignalProcessingException): + with self.assertRaises(exceptions.SignalProcessingException): _ = signal_processing.SignalProcessingUtils.mix_signals( silence, signal, 0.0)