message: Finished benchmarking tests for Duet, added the rest of the necessary benchmark files.

diff:
+f795b9929c6ddab5130048eb54ce93feda9b212d
\ No newline at end of file

diff:
+85f8d9e53a5f8a2b8d5261a2cbd32c3915580de2
\ No newline at end of file

diff:
@@ -10,8 +10,8 @@ import scipy.io
import os
import warnings
-class DuetUnitTests(unittest.TestCase):
+class DuetUnitTests(unittest.TestCase):
def setUp(self):
path = os.path.join('..', 'Input', 'dev1_female3_inst_mix.wav')
self.signal = nussl.AudioSignal(path)
@@ -29,16 +29,16 @@ class DuetUnitTests(unittest.TestCase):
benchmark_dict[key] = value
return benchmark_dict
- # def test_duet_final_outputs(self):
- # #Test final outputs
- # source_estimates_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_source_estimates.npy')
- # atn_delay_est_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_atn_delay_est.npy')
- # benchmark_source_estimates = np.load(source_estimates_path)
- # benchmark_atn_delay_est = np.load(atn_delay_est_path)
- #
- # duet_source_estimates, duet_atn_delay_est = self.duet.run()
- # assert benchmark_source_estimates == duet_source_estimates
- # assert benchmark_atn_delay_est == duet_atn_delay_est
+ def test_duet_final_outputs(self):
+ #Test final outputs
+ source_estimates_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_source_estimates.npy')
+ atn_delay_est_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_atn_delay_est.npy')
+ benchmark_source_estimates = np.load(source_estimates_path)
+ benchmark_atn_delay_est = np.load(atn_delay_est_path)
+
+ duet_source_estimates, duet_atn_delay_est = self.duet.run()
+ assert np.all(benchmark_source_estimates == duet_source_estimates)
+ assert np.all(benchmark_atn_delay_est == duet_atn_delay_est)
def test_compute_spectrogram_1_channel(self):
# Test with one channel, should throw value error
@@ -57,7 +57,6 @@ class DuetUnitTests(unittest.TestCase):
assert np.all(np.array(self.benchmark_dict['benchmark_stft_ch1']) == duet_sft1)
assert np.all(np.array(self.benchmark_dict['benchmark_wmat']) == duet_wmat)
-
def test_compute_atn_delay(self):
# Use the same stfts for comparing the two functions' outputs
duet = nussl.Duet(self.signal, 3)
@@ -106,8 +105,10 @@ class DuetUnitTests(unittest.TestCase):
benchmark_hist = np.load(hist_path)
benchmark_peak_indices = np.load(peak_indices_path)
- duet_peak_indices = nussl.utils.find_peak_indices(benchmark_hist, self.duet.num_sources, threshold=self.duet.peak_threshold,
- min_dist=[self.duet.attenuation_min_distance, self.duet.delay_min_distance])
+ duet_peak_indices = nussl.utils.find_peak_indices(benchmark_hist, self.duet.num_sources,
+ threshold=self.duet.peak_threshold,
+ min_dist=[self.duet.attenuation_min_distance,
+ self.duet.delay_min_distance])
# original DUET returned peaks as [[28 21 44] [24 25 24]] instead of [(28, 24), (21,25), (44,24)]
# Convert current peaks being tested to original format
@@ -118,17 +119,85 @@ class DuetUnitTests(unittest.TestCase):
assert np.all(benchmark_peak_indices == duet_peak_indices)
def test_convert_peaks(self):
- pass
+ duet = nussl.Duet(self.signal, 3)
+ duet.stft_ch0 = self.benchmark_dict['benchmark_stft_ch0']
+ duet.stft_ch1 = self.benchmark_dict['benchmark_stft_ch1']
+ duet.frequency_matrix = self.benchmark_dict['benchmark_wmat']
+ duet.symmetric_atn = self.benchmark_dict['benchmark_sym_atn']
+ duet.delay = self.benchmark_dict['benchmark_delay']
+ duet.atn_bins = self.benchmark_dict['benchmark_atn_bins']
+ duet.delay_bins = self.benchmark_dict['benchmark_delay_bins']
+ duet.peak_indices = self.benchmark_dict['benchmark_peak_indices']
+
+ delay_peak_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_delay_peak.npy')
+ atn_peak_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_atn_peak.npy')
+ atn_delay_est_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_atn_delay_est.npy')
+ benchmark_delay_peak = np.load(delay_peak_path)
+ benchmark_atn_peak = np.load(atn_peak_path)
+ benchmark_atn_delay_est = np.load(atn_delay_est_path)
+
+ duet.peak_indices = zip(duet.peak_indices[0], duet.peak_indices[1])
+ delay_peak, atn_delay_est, atn_peak = duet.convert_peaks(duet.atn_bins, duet.delay_bins)
+
+ assert np.all(benchmark_delay_peak == delay_peak)
+ assert np.all(benchmark_atn_delay_est == atn_delay_est)
+ assert np.all(benchmark_atn_peak == atn_peak)
+
+ def test_compute_masks(self):
+ duet = nussl.Duet(self.signal, 3)
+ duet.stft_ch0 = self.benchmark_dict['benchmark_stft_ch0']
+ duet.stft_ch1 = self.benchmark_dict['benchmark_stft_ch1']
+ duet.frequency_matrix = self.benchmark_dict['benchmark_wmat']
+ duet.symmetric_atn = self.benchmark_dict['benchmark_sym_atn']
+ duet.delay = self.benchmark_dict['benchmark_delay']
+ duet.atn_bins = self.benchmark_dict['benchmark_atn_bins']
+ duet.delay_bins = self.benchmark_dict['benchmark_delay_bins']
+ duet.peak_indices = self.benchmark_dict['benchmark_peak_indices']
+ duet.delay_peak = self.benchmark_dict['benchmark_delay_peak']
+ duet.atn_peak = self.benchmark_dict['benchmark_atn_peak']
+ duet.num_frequency_bins = self.benchmark_dict['benchmark_frequency_bins']
+ duet.num_time_bins = self.benchmark_dict['benchmark_num_time_bins']
+
+ best_ind_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_best_ind.npy')
+ mask_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_mask.npy')
+
+ benchmark_best_ind = np.load(best_ind_path)
+ benchmark_mask = np.load(mask_path)
+
+ best_ind, mask = duet.compute_masks(duet.atn_peak, duet.delay_peak)
+
+ assert np.all(best_ind == benchmark_best_ind)
+ assert np.all(benchmark_mask == mask)
+ def test_convert_time_domain(self):
+ duet = nussl.Duet(self.signal, 3)
+ duet.stft_ch0 = self.benchmark_dict['benchmark_stft_ch0']
+ duet.stft_ch1 = self.benchmark_dict['benchmark_stft_ch1']
+ duet.frequency_matrix = self.benchmark_dict['benchmark_wmat']
+ duet.symmetric_atn = self.benchmark_dict['benchmark_sym_atn']
+ duet.delay = self.benchmark_dict['benchmark_delay']
+ duet.atn_bins = self.benchmark_dict['benchmark_atn_bins']
+ duet.delay_bins = self.benchmark_dict['benchmark_delay_bins']
+ duet.peak_indices = self.benchmark_dict['benchmark_peak_indices']
+ duet.delay_peak = self.benchmark_dict['benchmark_delay_peak']
+ duet.atn_peak = self.benchmark_dict['benchmark_atn_peak']
+ duet.num_frequency_bins = self.benchmark_dict['benchmark_frequency_bins']
+ duet.num_time_bins = self.benchmark_dict['benchmark_num_time_bins']
+ duet.best_ind = self.benchmark_dict['benchmark_best_ind']
+ duet.mask = self.benchmark_dict['benchmark_mask']
- # def test_compute_masks
+ source_estimates_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_source_estimates.npy')
+ benchmark_source_estimates = np.load(source_estimates_path)
- # def test_convert_time_domain
+ source_estimates = duet.convert_time_domain(duet.best_ind, duet.mask, duet.atn_peak, duet.delay_peak)
+
+ assert np.all(benchmark_source_estimates == source_estimates)
# test_smooth_matrix
# TODO: fix this function before writing test for it
+
if __name__ == '__main__':
unittest.main()

diff:
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from __future__ import division
+
+import unittest
+import nussl
+import numpy as np
+import scipy.io.wavfile as wav
+import scipy.io
+import os
+import warnings
+
+class DuetUnitTests(unittest.TestCase):
+
+ def setUp(self):
+ path = os.path.join('..', 'Input', 'dev1_female3_inst_mix.wav')
+ self.signal = nussl.AudioSignal(path)
+ #Call benchmarks
+ self.duet = nussl.Duet(self.signal, 3)
+
+ def load_benchmarks(self):
+ self.benchmark_dict = {}
+ directory = 'duet_reference/duet_benchmarks'
+ for filename in os.listdir(directory):
+ key = os.path.splitext(filename)[0]
+ file_path = os.path.join('duet_reference', 'duet_benchmarks', filename)
+ value = np.load(file_path)
+ self.benchmark_dict[key] = value
+
+ # def test_duet_final_outputs(self):
+ # #Test final outputs
+ # source_estimates_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_source_estimates.npy')
+ # atn_delay_est_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_atn_delay_est.npy')
+ # benchmark_source_estimates = np.load(source_estimates_path)
+ # benchmark_atn_delay_est = np.load(atn_delay_est_path)
+ #
+ # duet_source_estimates, duet_atn_delay_est = self.duet.run()
+ # assert benchmark_source_estimates == duet_source_estimates
+ # assert benchmark_atn_delay_est == duet_atn_delay_est
+
+ def test_compute_spectrogram_1_channel(self):
+ # Test with one channel, should throw value error
+ num_samples = 100 # 1 second
+ np_sin = np.sin(np.linspace(0, 100 * 2 * np.pi, num_samples)) # Freq = 100 Hz
+ signal = nussl.AudioSignal(audio_data_array=np_sin)
+ with self.assertRaises(ValueError):
+ duet = nussl.Duet(signal, 3)
+ duet._compute_spectrogram(duet.sample_rate)
+
+ def test_compute_spectrogram_wmat(self):
+ # Load Duet values to benchmark against
+ benchmark_dict = self.load_benchmarks()
+ duet_sft0, duet_sft1, duet_wmat = self.duet._compute_spectrogram(self.duet.sample_rate)
+ assert np.all(np.array(self.benchmark_dict['benchmark_stft_ch0']) == duet_sft0)
+ assert np.all(np.array(benchmark_dict['benchmark_stft_ch1']) == duet_sft1)
+ assert np.all(np.array(benchmark_dict['benchmark_wmat']) == duet_wmat)
+
+
+ def test_compute_atn_delay(self):
+ #Use the same stfts for comparing the two functions' outputs
+ duet2 = nussl.Duet(self.signal, 3)
+ duet2.stft_ch0 = self.benchmark_dict['benchmark_stft_ch0']
+ self.duet.stft_ch0 = benchmark_dict['benchmark_stft_ch0']
+ self.duet.stft_ch1 = benchmark_dict['benchmark_stft_ch1']
+ self.duet.frequency_matrix = benchmark_dict['benchmark_wmat']
+
+ symmetric_atn, delay = self.duet._compute_atn_delay()
+
+ assert np.all(np.array(benchmark_dict['benchmark_sym_atn'])== symmetric_atn)
+ assert np.all(np.array(benchmark_dict['benchmark_delay'])== delay)
+
+ def test_make_histogram(self):
+ #Use the same stfts for comparing this function's outputs
+ benchmark_dict = self.load_benchmarks()
+ self.duet.stft_ch0 = benchmark_dict['benchmark_stft_ch0']
+ self.duet.stft_ch1 = benchmark_dict['benchmark_stft_ch1']
+ self.duet.frequency_matrix = benchmark_dict['benchmark_wmat']
+ self.symmetric_atn = benchmark_dict['benchmark_sym_atn']
+ self.delay = benchmark_dict['benchmark_delay']
+
+ #Load benchmarks
+ hist_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_hist.npy')
+ atn_bins_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_atn_bins.npy')
+ delay_bins_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_delay_bins.npy')
+ benchmark_hist = np.load(hist_path)
+ benchmark_atn_bins = np.load(atn_bins_path)
+ benchmark_delay_bins = np.load(delay_bins_path)
+
+ hist, atn_bins, delay_bins = self.duet.make_histogram(self.duet.p, self.duet.q)
+
+ assert np.all(benchmark_hist == hist)
+ assert np.all(benchmark_atn_bins == atn_bins)
+ assert np.all(benchmark_delay_bins == delay_bins)
+ #
+ # def test_peak_indices(self):
+ # hist_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_hist.npy')
+ # peak_indices_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_peak_indices.npy')
+ # benchmark_hist = np.load(hist_path)
+ # benchmark_peak_indices = np.load(peak_indices_path)
+ #
+ # duet_peak_indices = nussl.utils.find_peak_indices(benchmark_hist, self.duet.num_sources, threshold=self.duet.peak_threshold,
+ # min_dist=[self.duet.attenuation_min_distance, self.duet.delay_min_distance])
+ #
+ # assert np.all(benchmark_peak_indices == duet_peak_indices)
+
+ # def test_convert_peaks
+
+ # def test_compute_masks
+
+ # def test_convert_time_domain
+
+ # test_smooth_matrix
+
+
+if __name__ == '__main__':
+ unittest.main()
+

diff:
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import nussl
+import unittest


message: Added unit tests to benchmark transformer_nmf against nimfa nmf.

change_type: MODIFY

diff:
@@ -11,7 +11,7 @@ def simple_example():
# Make two simple matrices
n = 4
a = np.arange(n ** 2).reshape((n, n))
- b = 2 * a + 3
+ b = 2. * a + 3.
# Mix them together
mixture = np.dot(b, a)
@@ -24,15 +24,19 @@ def simple_example():
nussl_nmf.max_num_iterations = n_iter
nussl_nmf.distance_measure = nussl.DistanceType.DIVERGENCE
+ # set up nimfa NMF
+ V = mixture
+ nimfa_nmf = nimfa.Nmf(V, max_iter=n_iter, rank=n_bases, update='divergence')
+
+ # calculate errors
+ errors = []
+ num_iterations = 100
+ for i in range(num_iterations):
# run NU NMF
nussl_nmf.run()
-
signals = nussl_nmf.recombine_calculated_matrices()
- # for sig in signals:
- # print sig
- V = mixture
- nimfa_nmf = nimfa.Nmf(V, max_iter=n_iter, rank=n_bases, update='divergence')
+ #run nimfa NMF
nmf_fit = nimfa_nmf()
W = nmf_fit.basis()
H = nmf_fit.coef()
@@ -40,17 +44,31 @@ def simple_example():
nimfa_est = np.dot(W, H)
nussl_est = np.dot(nussl_nmf.templates, nussl_nmf.activation_matrix)
error = np.abs(nussl_est - nimfa_est) / nimfa_est * 100
+ errors.append(error.flatten())
+
+ errors = np.array(errors).flatten()
+ mean_error = np.mean(errors)
+ std_dev_error = np.std(errors)
+ print "mean", mean_error
+ print "std", std_dev_error
+ std_check = True
+ num_over_std_dev = 0
+ for error in errors:
+ if error > (mean_error + (5 * std_dev_error)):
+ num_over_std_dev += 1
+ print num_over_std_dev
+ if (num_over_std_dev/float(num_iterations)) > .05:
+ std_check = False
+ print (num_over_std_dev / float(num_iterations))
+ print std_check
+ # error statistics
+
- print('nimfa estimate:\n%s\n\n' % nimfa_est)
- print('nussl estimate:\n%s\n\n' % nussl_est)
- print('Errors:\n%s' % error)
+ # print('nimfa estimate:\n%s\n\n' % nimfa_est)
+ # print('nussl estimate:\n%s\n\n' % nussl_est)
+ # print('Errors:\n%s' % error)
- sm = nmf_fit.summary()
- # print( np.dot(W, H))
- x = 1
- y = 2
- z = x + y
if __name__ == '__main__':
simple_example()

diff:
@@ -7,6 +7,40 @@ import numpy as np
import constants
class TransformerNMF(object):
+ """
+ This is an implementation of the Non-negative Matrix Factorization algorithm for
+ general matrix transformations. This implementation receives an input matrix and a number,
+ num_templates, which defines the number of bases vectors.
+
+ This class provides two implementations of distance measures, EUCLIDEAN and DIVERGENCE,
+ and also allows the user to define distance measure function.
+
+ References:
+ [1] Lee, Daniel D., and H. Sebastian Seung. "Algorithms for non-negative matrix factorization."
+ Advances in neural information processing systems. 2001.
+
+ :param: input_matrix: The matrix to factor into template and activation matrices
+ :type: input_matrix: 2D numpy array
+
+ :param: num_templates: The rank of the templates matrix
+ :type: num_templates: int
+
+ :param: activation_matrix: An optional seed for the activation matrix
+ :type: activation_matrix: 2D numpy array
+
+ :param: templates: An optional seed for the templates matrix
+ :type: templates: 2D numpy array
+
+ :param: distance_measure: Specifies to use euclidean or divergence distance metrics
+ :type: distance_measure: str
+
+ :param: should_update_template: Whether the template matrix should be updated for another iteration
+ :type: should_update_template: bool
+
+ :param: should_update_activation: Whether the activation matrix should be updated for another iteration
+ :type: should_update_activation: bool
+ """
+
def __init__(self, input_matrix, num_templates,
activation_matrix=None, templates=None, distance_measure=None,
should_update_template=None, should_update_activation=None):
@@ -115,6 +149,11 @@ class TransformerNMF(object):
self.templates = self._update_template_divergence()
def _update_activation_euclidean(self):
+ """
+ Computes a new activation matrix using the Lee and Seung multiplicative update algorithm
+ :return: An updated activation matrix based on euclidean distance
+ """
+
# make a new matrix to store results
activation_copy = np.empty_like(self.activation_matrix)
@@ -133,6 +172,11 @@ class TransformerNMF(object):
return activation_copy
def _update_template_euclidean(self):
+ """
+ Computes a new template matrix using the Lee and Seung multiplicative update algorithm
+ :return: An update template matrix based on euclidean distance
+ """
+
# make a new matrix to store results
template_copy = np.empty_like(self.templates)
@@ -151,6 +195,10 @@ class TransformerNMF(object):
return template_copy
def _update_activation_divergent(self):
+ """
+ Computes a new activation matrix using the Lee and Seung multiplicative update algorithm
+ :return: An update activation matrix based on divergence
+ """
# make a new matrix to store results
activation_copy = np.empty_like(self.activation_matrix)
@@ -168,6 +216,10 @@ class TransformerNMF(object):
return activation_copy
def _update_template_divergence(self):
+ """
+ Computes a new template matrix using the Lee and Seung multiplicative update algorithm
+ :return: An update template matrix based on divergence
+ """
# make a new matrix to store results
template_copy = np.empty_like(self.templates)
@@ -185,6 +237,11 @@ class TransformerNMF(object):
return template_copy
def _euclidean_distance(self):
+ """
+ Calculates the euclidean distance from the original mixture to the
+ dot product of the current template and mixture matrices
+ :return: Euclidean distance
+ """
try:
mixture = np.dot(self.templates, self.activation_matrix)
except:
@@ -198,6 +255,12 @@ class TransformerNMF(object):
return sum((self.input_matrix[index] - val) ** 2 for index, val in np.ndenumerate(mixture))
def _divergence(self):
+ def _euclidean_distance(self):
+ """
+ Calculates the divergence from the original mixture to the
+ dot product of the current template and mixture matrices
+ :return: Euclidean distance
+ """
mixture = np.dot(self.activation_matrix, self.templates)
if mixture.shape != self.input_matrix.shape:

diff:
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-import nussl
import unittest
+import numpy as np
+import nussl
+
+import nimfa
+
+class DuetUnitTests(unittest.TestCase):
+
+ def test_small_mixture_matrix(self):
+ """
+ A simple example of NMF using euclidean and divergence in nussl with a 4 by 4 matrix
+ """
+ # Make two simple matrices
+ n = 4
+ a = np.arange(n ** 2).reshape((n, n))
+ b = 2. * a + 3.
+
+ # Mix them together
+ mixture = np.dot(b, a)
+
+ # Set up NMFs and run on euclidean while calculating error
+ n_bases = 2
+ iterations = 100
+ # Set error parameters
+ num_std_deviations = 5
+ percent_over_std_dev = .05
+
+ #Run on euclidean
+ type = "euclidean"
+ errors = self.calculate_nmfs_error(mixture, n_bases, type, iterations)
+ error_check_euclidean = self.analyze_errors(errors, iterations, num_std_deviations, percent_over_std_dev)
+
+ # Run on divergence
+ type = "divergence"
+ errors = self.calculate_nmfs_error(mixture, n_bases, type, iterations)
+ error_check_divergence = self.analyze_errors(errors, iterations, num_std_deviations, percent_over_std_dev)
+
+ assert error_check_euclidean == True
+ assert error_check_divergence == True
+
+ def test_big_mixture_matrix(self):
+ """
+ A simple example of NMF using euclidean and divergence in nussl with a 10 by 10 matrix
+ """
+ # Make two simple matrices
+ n = 10
+ a = np.arange(n ** 2).reshape((n, n))
+ b = 2. * a + 3.
+
+ # Mix them together
+ mixture = np.dot(b, a)
+
+ # Set up NMFs and run on euclidean while calculating error
+ n_bases = 2
+ iterations = 100
+ # Set error parameters
+ num_std_deviations = 10
+ percent_over_std_dev = .80
+
+ # Run on euclidean
+ type = "euclidean"
+ errors = self.calculate_nmfs_error(mixture, n_bases, type, iterations)
+ error_check_euclidean = self.analyze_errors(errors, iterations, num_std_deviations, percent_over_std_dev)
+
+ # Run on divergence
+ type = "divergence"
+ errors = self.calculate_nmfs_error(mixture, n_bases, type, iterations)
+ error_check_divergence = self.analyze_errors(errors, iterations, num_std_deviations, percent_over_std_dev)
+
+ assert error_check_euclidean == True
+ assert error_check_divergence == True
+
+ def test_big_random_matrix(self):
+ V = np.random.rand(10, 10)
+ # Set up NMFs and run on euclidean while calculating error
+ n_bases = 2
+ iterations = 100
+ # Set error parameters
+ num_std_deviations = 10
+ percent_over_std_dev = .50
+
+ # Run on euclidean
+ type = "euclidean"
+ errors = self.calculate_nmfs_error(V, n_bases, type, iterations)
+ error_check_euclidean = self.analyze_errors(errors, iterations, num_std_deviations, percent_over_std_dev)
+
+ # Run on divergence
+ type = "divergence"
+ errors = self.calculate_nmfs_error(V, n_bases, type, iterations)
+ error_check_divergence = self.analyze_errors(errors, iterations, num_std_deviations, percent_over_std_dev)
+
+ assert error_check_euclidean == True
+ assert error_check_divergence == True
+
+ @staticmethod
+ def calculate_nmfs_error(mixture, n_bases, type, iterations):
+ nussl_nmf = nussl.TransformerNMF(mixture, n_bases)
+ nussl_nmf.should_use_epsilon = False
+ nussl_nmf.max_num_iterations = iterations
+ if type == "euclidean":
+ nussl_nmf.distance_measure = nussl.DistanceType.EUCLIDEAN
+ elif type == "divergence":
+ nussl_nmf.distance_measure = nussl.DistanceType.DIVERGENCE
+ else:
+ return "Use either euclidean or divergence!"
+ # set up nimfa NMF
+ V = mixture
+ nimfa_nmf = nimfa.Nmf(V, max_iter=iterations, rank=n_bases, update=type)
+
+ # calculate errors
+ errors = []
+ for i in range(iterations):
+ # run NU NMF
+ nussl_nmf.run()
+ signals = nussl_nmf.recombine_calculated_matrices()
+
+ #run nimfa NMF
+ nmf_fit = nimfa_nmf()
+ W = nmf_fit.basis()
+ H = nmf_fit.coef()
+
+ nimfa_est = np.dot(W, H)
+ nussl_est = np.dot(nussl_nmf.templates, nussl_nmf.activation_matrix)
+ error = np.abs(nussl_est - nimfa_est) / nimfa_est * 100
+ errors.append(error.flatten())
+ return errors
+
+ @staticmethod
+ def analyze_errors(errors, iterations, num_std_deviations, percent_over_std_dev):
+ # analyze mean and std deviation of errors
+ errors = np.array(errors).flatten()
+ mean_error = np.mean(errors)
+ std_dev_error = np.std(errors)
+ std_check = True
+ num_over_std_dev = 0
+
+ for error in errors:
+ if error > (mean_error + (num_std_deviations * std_dev_error)):
+ num_over_std_dev += 1
+
+ if (num_over_std_dev / float(iterations)) > percent_over_std_dev:
+ std_check = False
+
+ return std_check
+
+if __name__ == '__main__':
+ simple_example()

license: MIT License

repo: nussl/nussl


message: Added transformer and separation NMF with documentation and demo.

diff:
@@ -24,51 +24,9 @@ def simple_example():
nussl_nmf.max_num_iterations = n_iter
nussl_nmf.distance_measure = nussl.DistanceType.DIVERGENCE
- # set up nimfa NMF
- V = mixture
- nimfa_nmf = nimfa.Nmf(V, max_iter=n_iter, rank=n_bases, update='divergence')
-
- # calculate errors
- errors = []
- num_iterations = 100
- for i in range(num_iterations):
# run NU NMF
nussl_nmf.run()
signals = nussl_nmf.recombine_calculated_matrices()
- #run nimfa NMF
- nmf_fit = nimfa_nmf()
- W = nmf_fit.basis()
- H = nmf_fit.coef()
-
- nimfa_est = np.dot(W, H)
- nussl_est = np.dot(nussl_nmf.templates, nussl_nmf.activation_matrix)
- error = np.abs(nussl_est - nimfa_est) / nimfa_est * 100
- errors.append(error.flatten())
-
- errors = np.array(errors).flatten()
- mean_error = np.mean(errors)
- std_dev_error = np.std(errors)
- print "mean", mean_error
- print "std", std_dev_error
- std_check = True
- num_over_std_dev = 0
- for error in errors:
- if error > (mean_error + (5 * std_dev_error)):
- num_over_std_dev += 1
- print num_over_std_dev
- if (num_over_std_dev/float(num_iterations)) > .05:
- std_check = False
- print (num_over_std_dev / float(num_iterations))
- print std_check
- # error statistics
-
-
- # print('nimfa estimate:\n%s\n\n' % nimfa_est)
- # print('nussl estimate:\n%s\n\n' % nussl_est)
- # print('Errors:\n%s' % error)
-
-
-
if __name__ == '__main__':
simple_example()
\ No newline at end of file

diff:
@@ -21,6 +21,18 @@ class NMF(separation_base.SeparationBase):
References:
[1] Lee, Daniel D., and H. Sebastian Seung. "Algorithms for non-negative matrix factorization."
Advances in neural information processing systems. 2001.
+
+ Parameters:
+ input_matrix (np.array): The audio signal matrix to factor into template and activation matrices
+ num_templates (int): The rank of the templates matrix
+ activation_matrix (np.array): An optional seed for the activation matrix
+ templates(np.array): An optional seed for the templates matrix
+ distance_measure(str): Specifies to use euclidean or divergence distance metrics
+ should_update_template(bool): Whether the template matrix should be updated for another iteration
+ should_update_activation (bool): Whether the activation matrix should be updated for another iteration
+
+ Examples:
+ :ref:'The Transformer NMF Demo Example <transformer_nmf_demo>'
"""
def __str__(self):

diff:
@@ -19,26 +19,17 @@ class TransformerNMF(object):
[1] Lee, Daniel D., and H. Sebastian Seung. "Algorithms for non-negative matrix factorization."
Advances in neural information processing systems. 2001.
- :param: input_matrix: The matrix to factor into template and activation matrices
- :type: input_matrix: 2D numpy array
-
- :param: num_templates: The rank of the templates matrix
- :type: num_templates: int
-
- :param: activation_matrix: An optional seed for the activation matrix
- :type: activation_matrix: 2D numpy array
-
- :param: templates: An optional seed for the templates matrix
- :type: templates: 2D numpy array
-
- :param: distance_measure: Specifies to use euclidean or divergence distance metrics
- :type: distance_measure: str
-
- :param: should_update_template: Whether the template matrix should be updated for another iteration
- :type: should_update_template: bool
-
- :param: should_update_activation: Whether the activation matrix should be updated for another iteration
- :type: should_update_activation: bool
+ Parameters:
+ input_matrix (np.array): The matrix to factor into template and activation matrices
+ num_templates (int): The rank of the templates matrix
+ activation_matrix (np.array): An optional seed for the activation matrix
+ templates(np.array): An optional seed for the templates matrix
+ distance_measure(str): Specifies to use euclidean or divergence distance metrics
+ should_update_template(bool): Whether the template matrix should be updated for another iteration
+ should_update_activation (bool): Whether the activation matrix should be updated for another iteration
+
+ Examples:
+ :ref:'The Transformer NMF Demo Example <transformer_nmf_demo>'
"""
def __init__(self, input_matrix, num_templates,
@@ -91,8 +82,20 @@ class TransformerNMF(object):
No inputs. N must be set prior to calling this function.
- Returns an activation matrix (in a 2d numpy array)
- and a set of template vectors (also 2d numpy array).
+ Returns:
+ * **activation_matrix** (*np.array*) - a 2D numpy matrix containing the estimated activation matrix
+ * **templates** (*np.array*) - a 2D numpy matrix containing the estimated templates
+
+ Example:
+ ::
+ input_matrix = np.random.rand(10, 10)
+ nussl_nmf = nussl.TransformerNMF(input_matrix, num_templates=2,
+ activation_matrix=None, templates=None, distance_measure="euclidean",
+ should_update_template=None, should_update_activation=None)
+
+ nussl_nmf.run()
+ signals = nussl_nmf.recombine_calculated_matrices()
+
"""
if self.input_matrix is None or self.input_matrix.size == 0:


message: Added demo for Transformer NMF and documentation for NMF/Transformer NMF.

diff:
import time
import numpy as np
import nussl
-
import nimfa
def simple_example():
@@ -19,7 +18,7 @@ def simple_example():
# Set up NU NMF
n_bases = 2
n_iter = 100
- nussl_nmf = nussl.TransformerNMF(mixture, n_bases)
+ nussl_nmf = nussl.transformer_nmf(mixture, n_bases)
nussl_nmf.should_use_epsilon = False
nussl_nmf.max_num_iterations = n_iter
nussl_nmf.distance_measure = nussl.DistanceType.DIVERGENCE

diff:
@@ -80,8 +80,21 @@ class NMF(separation_base.SeparationBase):
No inputs. do_STFT and N must be set prior to calling this function.
- Returns an activation matrix (in a 2d numpy array)
- and a set of template vectors (also 2d numpy array).
+ Returns:
+ * **activation_matrix** (*np.array*) - a 2D numpy matrix containing the estimated activation matrix
+ * **templates** (*np.array*) - a 2D numpy matrix containing the estimated templates
+
+ Example:
+ ::
+ input_file_name = '../Input/mix1.wav'
+ signal = AudioSignal(path_to_input_file=input_file_name)
+
+ nussl_nmf = nussl.NMF(signal, num_templates=2,
+ activation_matrix=None, templates=None, distance_measure="euclidean",
+ should_update_template=None, should_update_activation=None)
+
+ nussl_nmf.run()
+ signals = nussl_nmf.recombine_calculated_matrices()
"""
if self.stft is None or self.stft.size == 0:

diff:
@@ -95,7 +95,6 @@ class TransformerNMF(object):
nussl_nmf.run()
signals = nussl_nmf.recombine_calculated_matrices()
-
"""
if self.input_matrix is None or self.input_matrix.size == 0:


message: Modified to use mask objects.

diff:
@@ -13,8 +13,12 @@ import nussl.constants
import nussl.spectral_utils
import nussl.utils
import mask_separation_base
+import masks
+from ideal_mask import IdealMask
+
class Duet(mask_separation_base.MaskSeparationBase):
+
"""Implements the Degenerate Unmixing Estimation Technique (DUET) algorithm.
The DUET algorithm was originally proposed by S.Rickard and F.Dietrich for DOA estimation
@@ -67,8 +71,10 @@ class Duet(mask_separation_base.MaskSeparationBase):
def __init__(self, input_audio_signal, num_sources,
attenuation_min=-3, attenuation_max=3, num_attenuation_bins=50,
delay_min=-3, delay_max=3, num_delay_bins=50,
- peak_threshold=0.2, attenuation_min_distance=5, delay_min_distance=5, p=1, q=0):
- super(Duet, self).__init__(input_audio_signal=input_audio_signal)
+ peak_threshold=0.2, attenuation_min_distance=5, delay_min_distance=5, p=1, q=0,
+ mask_type=mask_separation_base.MaskSeparationBase.BINARY_MASK, mask_threshold=0.5):
+ super(Duet, self).__init__(input_audio_signal=input_audio_signal, mask_type=mask_type,
+ mask_threshold=mask_threshold)
if self.audio_signal.num_channels != 2:
raise ValueError('Duet requires that the input_audio_signal has exactly 2 channels!')
@@ -85,6 +91,8 @@ class Duet(mask_separation_base.MaskSeparationBase):
self.delay_min_distance = delay_min_distance
self.p = p
self.q = q
+ self.masks = []
+ self.final_sources = []
self.stft_ch0 = None
self.stft_ch1 = None
@@ -95,7 +103,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
self.num_frequency_bins = None
self.attenuation_bins = None
self.delay_bins = None
- self.attenuation_delay_histogram = None
+ self.normalized_attenuation_delay_histogram = None
self.attenuation_delay_histogram = None
self.peak_indices = None
self.separated_sources = None
@@ -159,12 +167,15 @@ class Duet(mask_separation_base.MaskSeparationBase):
delay_peak, atn_delay_est, atn_peak = self._convert_peaks()
# compute masks for separation
- best_indices, mask = self._compute_masks(atn_peak, delay_peak)
+ self._compute_masks(atn_peak, delay_peak)
+
+
# demix with ML alignment and convert to time domain
- source_estimates = self._convert_to_time_domain(best_indices, mask, atn_peak, delay_peak)
+ source_estimates = self._convert_to_time_domain(atn_peak, delay_peak)
self.separated_sources = source_estimates
+ # self._apply_masks()
return source_estimates, atn_delay_est
def _compute_spectrogram(self, sample_rate):
@@ -188,14 +199,14 @@ class Duet(mask_separation_base.MaskSeparationBase):
time_vector = self.audio_signal.time_bins_vector
# remove dc component to avoid dividing by zero freq. in the delay estimation
- stft_ch0 = stft_ch0[1:, :]
- stft_ch1 = stft_ch1[1:, :]
+ stft_ch0 += nussl.constants.EPSILON
+ stft_ch1 += nussl.constants.EPSILON
self.num_frequency_bins = len(frequency_vector)
self.num_time_bins = len(time_vector)
# Compute the freq. matrix for later use in phase calculations
- wmat = np.array(np.tile(np.mat(frequency_vector[1:]).T, (1, self.num_time_bins))) * (2 * np.pi / sample_rate)
-
+ wmat = np.array(np.tile(np.mat(frequency_vector).T, (1, self.num_time_bins))) * (2 * np.pi / sample_rate)
+ wmat += nussl.constants.EPSILON
return stft_ch0, stft_ch1, wmat
def _compute_atn_delay(self, stft_ch0, stft_ch1, frequency_matrix):
@@ -297,20 +308,27 @@ class Duet(mask_separation_base.MaskSeparationBase):
"""
# compute masks for separation
- best_so_far = np.inf * np.ones((self.num_frequency_bins - 1, self.num_time_bins))
- best_ind = np.zeros((self.num_frequency_bins - 1, self.num_time_bins), dtype=int)
- mask = None
+ best_so_far = np.inf * np.ones((self.num_frequency_bins, self.num_time_bins))
for i in range(0, self.num_sources):
+ mask_array = np.zeros((self.num_frequency_bins, self.num_time_bins), dtype=int)
phase = np.exp(-1j * self.frequency_matrix * delay_peak[i])
score = np.abs(atn_peak[i] * phase * self.stft_ch0 - self.stft_ch1) ** 2 / (1 + atn_peak[i] ** 2)
mask = (score < best_so_far)
- best_ind[mask] = i
+ #First pass will be all zeros
+ if i == 0:
+ mask_array[mask] = 0.0
+ else:
+ mask_array[mask] = 1.0
+ background_mask = masks.BinaryMask(np.array(mask_array))
best_so_far[mask] = score[mask]
+ self.masks.append(background_mask)
+ #Compute first mask based on what the other masks left remaining
+ for i in range(1, self.num_sources):
+ self.masks[0].mask += self.masks[i].mask
+ self.masks[0].mask = abs(self.masks[0].mask - 1)
- return best_ind, mask
-
- def _convert_to_time_domain(self, best_ind, mask, atn_peak, delay_peak):
+ def _convert_to_time_domain(self, atn_peak, delay_peak):
"""Receives the attenuation and delay peaks, the mask and best indices and
applies the mask to separate the sources
@@ -326,16 +344,18 @@ class Duet(mask_separation_base.MaskSeparationBase):
"""
source_estimates = np.zeros((self.num_sources, self.audio_signal.signal_length))
for i in range(0, self.num_sources):
- mask = (best_ind == i)
- stft_sources = np.vstack([np.zeros((1, self.num_time_bins)),
- (self.stft_ch0 + atn_peak[i] * np.exp(1j * self.frequency_matrix * delay_peak[i]) * self.stft_ch1) /
- (1 + atn_peak[i] ** 2) * mask])
-
- # xi = spectral_utils.f_istft(stft_sources, L, winType, hop, fs)
- xi = nussl.spectral_utils.e_istft(stft_sources, self.stft_params.window_length,
+ mask_apply = self.masks[i]
+ mask_apply = np.squeeze(mask_apply.mask, axis=2)
+ #Apply masks to stft channels using equation provided by Rickard
+ stft_sources =((self.stft_ch0 + atn_peak[i] * np.exp(1j * self.frequency_matrix * delay_peak[i])
+ * self.stft_ch1) / (1 + atn_peak[i] ** 2))*mask_apply
+
+ # istft_sources = spectral_utils.f_istft(stft_sources, L, winType, hop, fs)
+ istft_sources = nussl.spectral_utils.e_istft(stft_sources, self.stft_params.window_length,
self.stft_params.hop_length, self.stft_params.window_type)
- source_estimates[i, :] = nussl.utils.add_mismatched_arrays(source_estimates[i, ], xi)[:self.audio_signal.signal_length]
+ source_estimates[i, :] = nussl.utils.add_mismatched_arrays(source_estimates[i, ],
+ istft_sources)[:self.audio_signal.signal_length]
# add back to the separated signal a portion of the mixture to eliminate
# most of the masking artifacts
# source_estimates=source_estimates+0.05*x[0,:]
@@ -430,8 +450,8 @@ class Duet(mask_separation_base.MaskSeparationBase):
"""
plt.close('all')
- AA = np.tile(self.attenuation_bins[1::], (self.num_delay_bins, 1)).T
- DD = np.tile(self.delay_bins[1::].T, (self.num_attenuation_bins, 1))
+ aa = np.tile(self.attenuation_bins[1::], (self.num_delay_bins, 1)).T
+ dd = np.tile(self.delay_bins[1::].T, (self.num_attenuation_bins, 1))
# save smoothed_hist (not normalized) as an option for plotting
smoothed_hist = self._smooth_matrix(self.attenuation_delay_histogram, np.array([3]))
@@ -441,7 +461,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
# plot the histogram in 2D
if not three_d_plot:
plt.figure()
- plt.pcolormesh(AA, DD, histogram_data)
+ plt.pcolormesh(aa, dd, histogram_data)
plt.xlabel(r'$\alpha$', fontsize=16)
plt.ylabel(r'$\delta$', fontsize=16)
plt.title(r'$\alpha-\delta$ Histogram')
@@ -453,7 +473,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
# plot the histogram in 3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
- ax.plot_wireframe(AA, DD, histogram_data, rstride=2, cstride=2)
+ ax.plot_wireframe(aa, dd, histogram_data, rstride=2, cstride=2)
plt.xlabel(r'$\alpha$', fontsize=16)
plt.ylabel(r'$\delta$', fontsize=16)
plt.title(r'$\alpha-\delta$ Histogram')

original_message: Modified to use mask objects.


message: Fixing stft_data bugs with convert_to_time_domain in duet.

diff:
@@ -75,6 +75,7 @@ class AudioSignal(object):
self.path_to_input_file = path_to_input_file
self._audio_data = None
+ self._stft_data = None
self.sample_rate = sample_rate
self._active_start = None
self._active_end = None
@@ -95,7 +96,9 @@ class AudioSignal(object):
self.load_audio_from_array(audio_data_array, sample_rate)
# stft data
+ if stft is not None:
self.stft_data = stft # complex spectrogram data
+
self.stft_params = spectral_utils.StftParams(self.sample_rate) if stft_params is None else stft_params
self.use_librosa_stft = config.USE_LIBROSA_STFT
@@ -220,6 +223,31 @@ class AudioSignal(object):
self.set_active_region_to_default()
+ @property
+ def stft_data(self):
+ """ (:obj:`np.ndarray`): Real-valued, uncompressed, time-domain representation of the audio.
+ 2D numpy array with shape `(n_channels, n_samples)`.
+ ``None`` by default, this can be initialized at instantiation.
+ Usually, this is expected to be floats. Some functions will convert to floats if not already.
+ """
+
+ return self._stft_data
+
+ @stft_data.setter
+ def stft_data(self, value):
+ assert isinstance(value, np.ndarray), 'Type of self.stft_data must be np.ndarray!'
+
+ if value.ndim == 1:
+ raise ValueError('Cannot support arrays with less than 2 dimensions!')
+
+ if value.ndim == 2:
+ value = np.expand_dims(value, axis=constants.STFT_CHAN_INDEX)
+
+ if value.ndim > 3:
+ raise ValueError('Cannot support arrays with more than 3 dimensions!')
+
+ self._stft_data = value
+
@property
def file_name(self):
""" (str): The name of the file wth extension, NOT the full path.
@@ -1182,7 +1210,7 @@ class AudioSignal(object):
# np.array helps with duck typing
return utils._get_axis(np.array(self.magnitude_spectrogram_data), constants.STFT_CHAN_INDEX, n)
- def to_mono(self, overwrite=False):
+ def to_mono(self, overwrite=False, remove_channels=True):
""" Converts :attr:`audio_data` to mono by averaging every sample.
Warning:
@@ -1195,11 +1223,32 @@ class AudioSignal(object):
(:obj:`np.array`): Mono-ed version of :attr:`audio_data`.
"""
+ if remove_channels:
mono = np.mean(self.audio_data, axis=constants.CHAN_INDEX)
+ else:
+ mono = np.mean(self.audio_data, axis=constants.CHAN_INDEX, keepdims=True)
if overwrite:
self.audio_data = mono
return mono
+ def stft_to_mono(self, overwrite=False):
+ """ Converts :attr:`stft_data` to mono by averaging every sample.
+
+ Warning:
+ If overwrite=True (default) this will overwrite any data in :attr:`audio_data`!
+
+ Args:
+ overwrite (bool, optional): If ``True`` this function will overwrite :attr:`stft_data`.
+
+ Returns:
+ (:obj:`np.array`): Mono-ed version of :attr:`stft_data`.
+
+ """
+ mono_stft = np.mean(self.stft_data, axis=constants.CHAN_INDEX)
+ if overwrite:
+ self.stft_data = mono_stft
+ return mono_stft
+
##################################################
# Operator overloading
##################################################

diff:
@@ -14,7 +14,6 @@ import nussl.spectral_utils
import nussl.utils
import mask_separation_base
import masks
-from ideal_mask import IdealMask
class Duet(mask_separation_base.MaskSeparationBase):
@@ -46,22 +45,23 @@ class Duet(mask_separation_base.MaskSeparationBase):
p (int): Weight the histogram with the symmetric attenuation estimator
q (int): Weight the histogram with the delay estimator
- Attributes:
- stft_ch0 (np.array):
- stft_ch1 (np.array):
- frequency_matrix (np.array):
- symmetric_atn = None
- delay = None
- num_time_bins = None
- num_frequency_bins = None
- attenuation_bins = None
- delay_bins = None
- normalized_attenuation_delay_histogram (np.array):
- attenuation_delay_histogram (np.array):
- peak_indices (np.array):
- separated_sources (np.array):
-
+ Attributes:
+ stft_ch0 (np.array): A Numpy matrix containing the stft data of channel 0.
+ stft_ch1 (np.array): A Numpy matrix containing the stft data of channel 0.
+ frequency_matrix (np.array): A Numpy matrix containing the frequencies of analysis.
+ symmetric_atn (np.array): A Numpy matrix containing the symmetric attenuation between the two channels.
+ delay (np.array): A Numpy matrix containing the delay between the two channels.
+ num_time_bins (np.array): The number of time bins for the frequency matrix and mask arrays.
+ num_frequency_bins (int): The number of frequency bins for the mask arrays.
+ attenuation_bins (int): A Numpy array containing the attenuation bins for the histogram.
+ delay_bins (np.array): A Numpy array containing the delay bins for the histogram.
+ normalized_attenuation_delay_histogram (np.array): A normalized Numpy matrix containing the
+ attenuation delay histogram, which has peaks for each source.
+ attenuation_delay_histogram (np.array): A non-normalized Numpy matrix containing the
+ attenuation delay histogram, which has peaks for each source.
+ peak_indices (np.array): A Numpy array containing the indices of the peaks for the histogram.
+ separated_sources (np.array): A Numpy array of arrays containing each separated source.
Examples:
:ref:`The DUET Demo Example <duet_demo>`
@@ -157,7 +157,6 @@ class Duet(mask_separation_base.MaskSeparationBase):
self.normalized_attenuation_delay_histogram, self.attenuation_bins, self.delay_bins = self._make_histogram()
# Find the location of peaks in the attenuation-delay plane
-
self.peak_indices = nussl.utils.find_peak_indices(self.normalized_attenuation_delay_histogram, self.num_sources,
threshold=self.peak_threshold,
min_dist=[self.attenuation_min_distance,
@@ -167,9 +166,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
delay_peak, atn_delay_est, atn_peak = self._convert_peaks()
# compute masks for separation
- self._compute_masks(atn_peak, delay_peak)
-
-
+ self._compute_masks(delay_peak, atn_peak)
# demix with ML alignment and convert to time domain
source_estimates = self._convert_to_time_domain(atn_peak, delay_peak)
@@ -209,7 +206,8 @@ class Duet(mask_separation_base.MaskSeparationBase):
wmat += nussl.constants.EPSILON
return stft_ch0, stft_ch1, wmat
- def _compute_atn_delay(self, stft_ch0, stft_ch1, frequency_matrix):
+ @staticmethod
+ def _compute_atn_delay(stft_ch0, stft_ch1, frequency_matrix):
# Calculate the symmetric attenuation (alpha) and delay (delta) for each
# time-freq. point
inter_channel_ratio = (stft_ch1 + nussl.constants.EPSILON) / (stft_ch0 + nussl.constants.EPSILON)
@@ -254,8 +252,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
histogram, atn_bins, delay_bins = np.histogram2d(symmetric_attenuation_vector, delay_vector,
bins=np.array([self.num_attenuation_bins, self.num_delay_bins]),
range=np.array([[self.attenuation_min, self.attenuation_max],
- [self.delay_min, self.delay_max]]), normed=False,
- weights=time_frequency_weights_vector)
+ [self.delay_min, self.delay_max]]), weights=time_frequency_weights_vector)
#Save non-normalized as an option for plotting later
self.attenuation_delay_histogram = histogram
@@ -295,46 +292,40 @@ class Duet(mask_separation_base.MaskSeparationBase):
atn_peak = (symmetric_atn_peak + np.sqrt(symmetric_atn_peak ** 2 + 4)) / 2
return delay_peak, atn_delay_est, atn_peak
- def _compute_masks(self, atn_peak, delay_peak):
+ def _compute_masks(self, delay_peak, atn_peak):
"""Receives the attenuation and delay peaks and computes a mask to be applied to the signal for source
separation.
Parameters:
atn_peak (np.array): Attenuation peaks determined from histogram
delay_peak (np.array): Delay peaks determined from histogram
- Returns:
- best_ind(np.array): The indices for where the masks are applied
- mask (np.array): A binary mask used to separate the sources
"""
# compute masks for separation
best_so_far = np.inf * np.ones((self.num_frequency_bins, self.num_time_bins))
-
for i in range(0, self.num_sources):
- mask_array = np.zeros((self.num_frequency_bins, self.num_time_bins), dtype=int)
- phase = np.exp(-1j * self.frequency_matrix * delay_peak[i])
- score = np.abs(atn_peak[i] * phase * self.stft_ch0 - self.stft_ch1) ** 2 / (1 + atn_peak[i] ** 2)
+ mask_array = np.zeros((self.num_frequency_bins, self.num_time_bins), dtype=bool)
+ score = self._compute_mask_score(delay_peak[i], atn_peak[i])
mask = (score < best_so_far)
- #First pass will be all zeros
- if i == 0:
- mask_array[mask] = 0.0
- else:
- mask_array[mask] = 1.0
+ mask_array[mask] = True
background_mask = masks.BinaryMask(np.array(mask_array))
- best_so_far[mask] = score[mask]
self.masks.append(background_mask)
+ self.masks[0].mask = np.logical_or(self.masks[i].mask, self.masks[0].mask)
+ best_so_far[mask] = score[mask]
+
#Compute first mask based on what the other masks left remaining
- for i in range(1, self.num_sources):
- self.masks[0].mask += self.masks[i].mask
- self.masks[0].mask = abs(self.masks[0].mask - 1)
+ self.masks[0].mask = np.logical_not(self.masks[0].mask)
+
+ def _compute_mask_score(self, delay_peak_val, atn_peak_val):
+ phase = np.exp(-1j * self.frequency_matrix * delay_peak_val)
+ score = np.abs(atn_peak_val * phase * self.stft_ch0 - self.stft_ch1) ** 2 / (1 + atn_peak_val ** 2)
+ return score
def _convert_to_time_domain(self, atn_peak, delay_peak):
"""Receives the attenuation and delay peaks, the mask and best indices and
applies the mask to separate the sources
Parameters:
- best_ind (np.array): The indices for where the masks are applied
- mask (np.array): binary mask used to separate the sources
atn_peak (np.array): Attenuation peaks determined from histogram
delay_peak (np.array): Delay peaks determined from histogram
@@ -343,22 +334,14 @@ class Duet(mask_separation_base.MaskSeparationBase):
"""
source_estimates = np.zeros((self.num_sources, self.audio_signal.signal_length))
- for i in range(0, self.num_sources):
- mask_apply = self.masks[i]
- mask_apply = np.squeeze(mask_apply.mask, axis=2)
+ for i in range(self.num_sources):
#Apply masks to stft channels using equation provided by Rickard
stft_sources = ((self.stft_ch0 + atn_peak[i] * np.exp(1j * self.frequency_matrix * delay_peak[i])
- * self.stft_ch1) / (1 + atn_peak[i] ** 2))*mask_apply
-
- # istft_sources = spectral_utils.f_istft(stft_sources, L, winType, hop, fs)
- istft_sources = nussl.spectral_utils.e_istft(stft_sources, self.stft_params.window_length,
- self.stft_params.hop_length, self.stft_params.window_type)
-
- source_estimates[i, :] = nussl.utils.add_mismatched_arrays(source_estimates[i, ],
- istft_sources)[:self.audio_signal.signal_length]
- # add back to the separated signal a portion of the mixture to eliminate
- # most of the masking artifacts
- # source_estimates=source_estimates+0.05*x[0,:]
+ * self.stft_ch1) / (1 + atn_peak[i] ** 2))
+ new_sig = self.audio_signal.make_copy_with_stft_data(stft_sources, verbose=False)
+ new_sig = new_sig.apply_mask(self.masks[i]) # Ask Ethan if we can have a apply_mask that doesn't return a new mask!
+ new_sig.stft_params = self.stft_params
+ source_estimates[i, :] = new_sig.istft(overwrite=True)
return source_estimates
@staticmethod

diff:
@@ -224,3 +224,9 @@ class MaskBase(object):
def __itruediv__(self, value):
return self.__idiv__(value)
+
+ def __eq__(self, other):
+ return np.all(self.mask == other.mask)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)

diff:
@@ -12,9 +12,12 @@ import warnings
class DuetUnitTests(unittest.TestCase):
+
+ # Update this if the benchmark file changes and rerun freeze_duet_values() (below)
+ path_to_benchmark_file = os.path.join('..', 'Input', 'dev1_female3_inst_mix.wav')
+
def setUp(self):
- path = os.path.join('..', 'Input', 'dev1_female3_inst_mix.wav')
- self.signal = nussl.AudioSignal(path)
+ self.signal = nussl.AudioSignal(self.path_to_benchmark_file)
# Call benchmarks\
self.benchmark_dict = self.load_benchmarks()
@@ -52,7 +55,7 @@ class DuetUnitTests(unittest.TestCase):
def test_compute_spectrogram_wmat(self):
# Load Duet values to benchmark against
duet = nussl.Duet(self.signal, 3)
- duet_sft0, duet_sft1, duet_wmat = duet._compute_spectrogram(self.duet.sample_rate)
+ duet_sft0, duet_sft1, duet_wmat = duet._compute_spectrogram(duet.sample_rate)
assert np.all(np.array(self.benchmark_dict['benchmark_stft_ch0']) == duet_sft0)
assert np.all(np.array(self.benchmark_dict['benchmark_stft_ch1']) == duet_sft1)
assert np.all(np.array(self.benchmark_dict['benchmark_wmat']) == duet_wmat)
@@ -197,7 +200,31 @@ class DuetUnitTests(unittest.TestCase):
# TODO: fix this function before writing test for it
+def freeze_duet_values():
+ path = DuetUnitTests.path_to_benchmark_file
+
+ signal = nussl.AudioSignal(path)
+ duet = nussl.Duet(signal, 3)
+
+ duet.stft_ch0, duet.stft_ch1, duet.frequency_matrix = duet._compute_spectrogram(duet.sample_rate)
+
+ duet.symmetric_atn, duet.delay = duet._compute_atn_delay(duet.stft_ch0, duet.stft_ch1, duet.frequency_matrix)
+ duet.normalized_attenuation_delay_histogram, duet.attenuation_bins, duet.delay_bins = duet._make_histogram()
+
+ duet.peak_indices = nussl.utils.find_peak_indices(duet.normalized_attenuation_delay_histogram, duet.num_sources,
+ threshold=duet.peak_threshold,
+ min_dist=[duet.attenuation_min_distance,
+ duet.delay_min_distance])
+
+ delay_peak, atn_delay_est, atn_peak = duet._convert_peaks()
+
+ duet._compute_masks(delay_peak, atn_peak)
+
+ source_estimates = duet._convert_to_time_domain(atn_peak, delay_peak)
+
+ duet.separated_sources = source_estimates
if __name__ == '__main__':
unittest.main()
+


message: Changed conditionals in to account for when audio data is None.

diff:
@@ -213,6 +213,7 @@ class AudioSignal(object):
elif not isinstance(value, np.ndarray):
raise ValueError('Type of self.audio_data must be of type np.ndarray!')
+ else:
if value.ndim > 1 and value.shape[constants.CHAN_INDEX] > value.shape[constants.LEN_INDEX]:
warnings.warn('self.audio_data is not as we expect it. Transposing signal...')
value = value.T
@@ -220,11 +221,11 @@ class AudioSignal(object):
if value.ndim > 2:
raise ValueError('self.audio_data cannot have more than 2 dimensions!')
- self._audio_data = value
-
if self._audio_data.ndim < 2:
self._audio_data = np.expand_dims(self._audio_data, axis=constants.CHAN_INDEX)
+ self._audio_data = value
+
self.set_active_region_to_default()
@property

original_message: Changed conditionals in @audio_data.setter to account for when audio data is None.


message: Duet is working now using the mask objects and with apply_mask in convert_to_time_domain.

diff:
@@ -5,7 +5,6 @@ import numpy as np
import matplotlib.pyplot as plt
# noinspection PyUnusedImport
-from mpl_toolkits.mplot3d import axes3d
from scipy import signal
import nussl.audio_signal
@@ -310,7 +309,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
mask_array[mask] = True
background_mask = masks.BinaryMask(np.array(mask_array))
self.masks.append(background_mask)
- self.masks[0].mask = np.logical_or(self.masks[i].mask, self.masks[0].mask)
+ self.masks[0].mask = np.logical_xor(self.masks[i].mask, self.masks[0].mask)
best_so_far[mask] = score[mask]
#Compute first mask based on what the other masks left remaining
@@ -341,7 +340,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
new_sig = self.audio_signal.make_copy_with_stft_data(stft_sources, verbose=False)
new_sig = new_sig.apply_mask(self.masks[i]) # Ask Ethan if we can have a apply_mask that doesn't return a new mask!
new_sig.stft_params = self.stft_params
- source_estimates[i, :] = new_sig.istft(overwrite=True)
+ source_estimates[i, :] = new_sig.istft(overwrite=True, truncate_to_length=self.audio_signal.signal_length)
return source_estimates
@staticmethod


message: Moved convert_to_time_domain under make_audio_signals and modified tests to include freeze_benchmarks functions.

diff:
import numpy as np
import matplotlib.pyplot as plt
-
-# noinspection PyUnusedImport
+from mpl_toolkits.mplot3d import axes3d
from scipy import signal
import nussl.audio_signal
@@ -105,6 +104,8 @@ class Duet(mask_separation_base.MaskSeparationBase):
self.normalized_attenuation_delay_histogram = None
self.attenuation_delay_histogram = None
self.peak_indices = None
+ self.delay_peak = None
+ self.atn_peak = None
self.separated_sources = None
def run(self):
@@ -162,17 +163,12 @@ class Duet(mask_separation_base.MaskSeparationBase):
self.delay_min_distance])
# compute delay_peak, attenuation peak, and attenuation/delay estimates
- delay_peak, atn_delay_est, atn_peak = self._convert_peaks()
+ self.delay_peak, atn_delay_est, self.atn_peak = self._convert_peaks()
# compute masks for separation
- self._compute_masks(delay_peak, atn_peak)
-
- # demix with ML alignment and convert to time domain
- source_estimates = self._convert_to_time_domain(atn_peak, delay_peak)
+ masks = self._compute_masks()
- self.separated_sources = source_estimates
- # self._apply_masks()
- return source_estimates, atn_delay_est
+ return masks
def _compute_spectrogram(self, sample_rate):
""" Creates the stfts matrices for channel 0 and 1, and computes the frequency matrix.
@@ -191,17 +187,14 @@ class Duet(mask_separation_base.MaskSeparationBase):
stft_ch0 = self.audio_signal.get_stft_channel(0)
stft_ch1 = self.audio_signal.get_stft_channel(1)
- frequency_vector = self.audio_signal.freq_vector
- time_vector = self.audio_signal.time_bins_vector
# remove dc component to avoid dividing by zero freq. in the delay estimation
stft_ch0 += nussl.constants.EPSILON
stft_ch1 += nussl.constants.EPSILON
- self.num_frequency_bins = len(frequency_vector)
- self.num_time_bins = len(time_vector)
# Compute the freq. matrix for later use in phase calculations
- wmat = np.array(np.tile(np.mat(frequency_vector).T, (1, self.num_time_bins))) * (2 * np.pi / sample_rate)
+ n_time_bins = len(self.audio_signal.time_bins_vector)
+ wmat = np.array(np.tile(np.mat(self.audio_signal.freq_vector).T, (1, n_time_bins))) * (2 * np.pi / sample_rate)
wmat += nussl.constants.EPSILON
return stft_ch0, stft_ch1, wmat
@@ -261,7 +254,6 @@ class Duet(mask_separation_base.MaskSeparationBase):
# smooth the normalized histogram - local average 3-by-3 neighboring bins
histogram = self._smooth_matrix(histogram, np.array([3]))
-
return histogram, atn_bins, delay_bins
def _convert_peaks(self):
@@ -291,7 +283,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
atn_peak = (symmetric_atn_peak + np.sqrt(symmetric_atn_peak ** 2 + 4)) / 2
return delay_peak, atn_delay_est, atn_peak
- def _compute_masks(self, delay_peak, atn_peak):
+ def _compute_masks(self):
"""Receives the attenuation and delay peaks and computes a mask to be applied to the signal for source
separation.
@@ -301,10 +293,12 @@ class Duet(mask_separation_base.MaskSeparationBase):
"""
# compute masks for separation
- best_so_far = np.inf * np.ones((self.num_frequency_bins, self.num_time_bins))
+ best_so_far = np.inf * np.ones_like(self.stft_ch0, dtype=float)
+
for i in range(0, self.num_sources):
- mask_array = np.zeros((self.num_frequency_bins, self.num_time_bins), dtype=bool)
- score = self._compute_mask_score(delay_peak[i], atn_peak[i])
+ mask_array = np.zeros_like(self.stft_ch0, dtype=bool)
+ phase = np.exp(-1j * self.frequency_matrix * self.delay_peak[i])
+ score = np.abs(self.atn_peak[i] * phase * self.stft_ch0 - self.stft_ch1) ** 2 / (1 + self.atn_peak[i] ** 2)
mask = (score < best_so_far)
mask_array[mask] = True
background_mask = masks.BinaryMask(np.array(mask_array))
@@ -314,34 +308,7 @@ class Duet(mask_separation_base.MaskSeparationBase):
#Compute first mask based on what the other masks left remaining
self.masks[0].mask = np.logical_not(self.masks[0].mask)
-
- def _compute_mask_score(self, delay_peak_val, atn_peak_val):
- phase = np.exp(-1j * self.frequency_matrix * delay_peak_val)
- score = np.abs(atn_peak_val * phase * self.stft_ch0 - self.stft_ch1) ** 2 / (1 + atn_peak_val ** 2)
- return score
-
- def _convert_to_time_domain(self, atn_peak, delay_peak):
- """Receives the attenuation and delay peaks, the mask and best indices and
- applies the mask to separate the sources
-
- Parameters:
- atn_peak (np.array): Attenuation peaks determined from histogram
- delay_peak (np.array): Delay peaks determined from histogram
-
- Returns:
- source_estimates (np.array): The arrays of the separated source signals
-
- """
- source_estimates = np.zeros((self.num_sources, self.audio_signal.signal_length))
- for i in range(self.num_sources):
- #Apply masks to stft channels using equation provided by Rickard
- stft_sources = ((self.stft_ch0 + atn_peak[i] * np.exp(1j * self.frequency_matrix * delay_peak[i])
- * self.stft_ch1) / (1 + atn_peak[i] ** 2))
- new_sig = self.audio_signal.make_copy_with_stft_data(stft_sources, verbose=False)
- new_sig = new_sig.apply_mask(self.masks[i]) # Ask Ethan if we can have a apply_mask that doesn't return a new mask!
- new_sig.stft_params = self.stft_params
- source_estimates[i, :] = new_sig.istft(overwrite=True, truncate_to_length=self.audio_signal.signal_length)
- return source_estimates
+ return self.masks
@staticmethod
def _smooth_matrix(matrix, kernel):
@@ -417,7 +384,15 @@ class Duet(mask_separation_base.MaskSeparationBase):
"""
signals = []
for i in range(self.num_sources):
- cur_signal = nussl.audio_signal.AudioSignal(audio_data_array=self.separated_sources[i],
+ # Apply masks to stft channels using equation provided by Rickard
+ stft_sources = ((self.stft_ch0 + self.atn_peak[i] * np.exp(1j * self.frequency_matrix * self.delay_peak[i])
+ * self.stft_ch1) / (1 + self.atn_peak[i] ** 2))
+ new_sig = self.audio_signal.make_copy_with_stft_data(stft_sources, verbose=False)
+ new_sig = new_sig.apply_mask(
+ self.masks[i]) # Ask Ethan if we can have a apply_mask that doesn't return a new mask!
+ new_sig.stft_params = self.stft_params
+ source_estimate = new_sig.istft(overwrite=True, truncate_to_length=self.audio_signal.signal_length)
+ cur_signal = nussl.audio_signal.AudioSignal(audio_data_array=source_estimate,
sample_rate=self.sample_rate)
signals.append(cur_signal)
return signals

change_type: DELETE

diff:
-f795b9929c6ddab5130048eb54ce93feda9b212d
\ No newline at end of file

diff:
-fb06e141efcbf878f10e2e87e0c5dd3f7371b47d
\ No newline at end of file
+cb65959e4786d049fdd174d176be42c1d0f37262
\ No newline at end of file

change_type: ADD

diff:
+9e0eae6d9663764f53b167799acec8f193f136bd
\ No newline at end of file

diff:
+ee842514db30c95b9d6513fd26924c98683cd85f
\ No newline at end of file

diff:
-85f8d9e53a5f8a2b8d5261a2cbd32c3915580de2
\ No newline at end of file

diff:
-6d0d15e720bf359d444fd0a3e4b2a10c312ab7cd
\ No newline at end of file
+f003de722b0b05fa5f3bd1c4e2a151c633330fda
\ No newline at end of file

diff:
-224318577eceffd14466c12dfe4121ceca3ca218
\ No newline at end of file
+1c194e8503fed55edb6753778e632e04abe28f4e
\ No newline at end of file

diff:
-e3ee0d75ab4abed4ba9d9a37c6275bd95711f837
\ No newline at end of file
+d8554310a7f3859c86e86cc8c62a1db5e4b4e099
\ No newline at end of file

diff:
-f8fb481cc282f9c762c8fa40a2514d9c7982e403
\ No newline at end of file
+acadabd7de1cf7149f31795774c472ed57f4ab8a
\ No newline at end of file

diff:
@@ -18,7 +18,7 @@ class DuetUnitTests(unittest.TestCase):
def setUp(self):
self.signal = nussl.AudioSignal(self.path_to_benchmark_file)
- # Call benchmarks\
+ # Call benchmarks
self.benchmark_dict = self.load_benchmarks()
def load_benchmarks(self):
@@ -33,15 +33,14 @@ class DuetUnitTests(unittest.TestCase):
def test_duet_final_outputs(self):
#Test final outputs
- source_estimates_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_source_estimates.npy')
- atn_delay_est_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_atn_delay_est.npy')
- benchmark_source_estimates = np.load(source_estimates_path)
- benchmark_atn_delay_est = np.load(atn_delay_est_path)
+ mask_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_masks.npy')
+ benchmark_mask = np.load(mask_path)
+
+ duet = nussl.Duet(self.signal, 3)
+ duet_masks = duet.run()
+ for i in range(len(duet_masks)):
+ assert np.all(benchmark_mask[i].mask == duet_masks[i].mask)
- duet = nussl.Duet(signal, 3)
- duet_source_estimates, duet_atn_delay_est = duet.run()
- assert np.all(benchmark_source_estimates == duet_source_estimates)
- assert np.all(benchmark_atn_delay_est == duet_atn_delay_est)
def test_compute_spectrogram_1_channel(self):
# Test with one channel, should throw value error
@@ -67,7 +66,7 @@ class DuetUnitTests(unittest.TestCase):
duet.stft_ch1 = self.benchmark_dict['benchmark_stft_ch1']
duet.frequency_matrix = self.benchmark_dict['benchmark_wmat']
- symmetric_atn, delay = duet._compute_atn_delay()
+ symmetric_atn, delay = duet._compute_atn_delay(duet.stft_ch0, duet.stft_ch1, duet.frequency_matrix)
assert np.all(np.array(self.benchmark_dict['benchmark_sym_atn']) == symmetric_atn)
assert np.all(np.array(self.benchmark_dict['benchmark_delay']) == delay)
@@ -89,7 +88,7 @@ class DuetUnitTests(unittest.TestCase):
benchmark_atn_bins = np.load(atn_bins_path)
benchmark_delay_bins = np.load(delay_bins_path)
- hist, atn_bins, delay_bins = duet.make_histogram(duet.p, duet.q)
+ hist, atn_bins, delay_bins = duet._make_histogram()
assert np.all(benchmark_hist == hist)
assert np.all(benchmark_atn_bins == atn_bins)
@@ -108,16 +107,10 @@ class DuetUnitTests(unittest.TestCase):
benchmark_hist = np.load(hist_path)
benchmark_peak_indices = np.load(peak_indices_path)
- duet_peak_indices = nussl.utils.find_peak_indices(benchmark_hist, self.duet.num_sources,
- threshold=self.duet.peak_threshold,
- min_dist=[self.duet.attenuation_min_distance,
- self.duet.delay_min_distance])
-
- # original DUET returned peaks as [[28 21 44] [24 25 24]] instead of [(28, 24), (21,25), (44,24)]
- # Convert current peaks being tested to original format
- atn_indices = [x[0] for x in duet_peak_indices]
- delay_indices = [x[1] for x in duet_peak_indices]
- duet_peak_indices = np.row_stack((atn_indices, delay_indices))
+ duet_peak_indices = nussl.utils.find_peak_indices(benchmark_hist, duet.num_sources,
+ threshold=duet.peak_threshold,
+ min_dist=[duet.attenuation_min_distance,
+ duet.delay_min_distance])
assert np.all(benchmark_peak_indices == duet_peak_indices)
@@ -128,7 +121,7 @@ class DuetUnitTests(unittest.TestCase):
duet.frequency_matrix = self.benchmark_dict['benchmark_wmat']
duet.symmetric_atn = self.benchmark_dict['benchmark_sym_atn']
duet.delay = self.benchmark_dict['benchmark_delay']
- duet.atn_bins = self.benchmark_dict['benchmark_atn_bins']
+ duet.attenuation_bins = self.benchmark_dict['benchmark_atn_bins']
duet.delay_bins = self.benchmark_dict['benchmark_delay_bins']
duet.peak_indices = self.benchmark_dict['benchmark_peak_indices']
@@ -139,8 +132,7 @@ class DuetUnitTests(unittest.TestCase):
benchmark_atn_peak = np.load(atn_peak_path)
benchmark_atn_delay_est = np.load(atn_delay_est_path)
- duet.peak_indices = zip(duet.peak_indices[0], duet.peak_indices[1])
- delay_peak, atn_delay_est, atn_peak = duet.convert_peaks(duet.atn_bins, duet.delay_bins)
+ delay_peak, atn_delay_est, atn_peak = duet._convert_peaks()
assert np.all(benchmark_delay_peak == delay_peak)
assert np.all(benchmark_atn_delay_est == atn_delay_est)
@@ -153,26 +145,20 @@ class DuetUnitTests(unittest.TestCase):
duet.frequency_matrix = self.benchmark_dict['benchmark_wmat']
duet.symmetric_atn = self.benchmark_dict['benchmark_sym_atn']
duet.delay = self.benchmark_dict['benchmark_delay']
- duet.atn_bins = self.benchmark_dict['benchmark_atn_bins']
+ duet.attenuation_bins = self.benchmark_dict['benchmark_atn_bins']
duet.delay_bins = self.benchmark_dict['benchmark_delay_bins']
duet.peak_indices = self.benchmark_dict['benchmark_peak_indices']
duet.delay_peak = self.benchmark_dict['benchmark_delay_peak']
duet.atn_peak = self.benchmark_dict['benchmark_atn_peak']
- duet.num_frequency_bins = self.benchmark_dict['benchmark_frequency_bins']
- duet.num_time_bins = self.benchmark_dict['benchmark_num_time_bins']
-
- best_ind_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_best_ind.npy')
- mask_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_mask.npy')
- benchmark_best_ind = np.load(best_ind_path)
+ mask_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_masks.npy')
benchmark_mask = np.load(mask_path)
- best_ind, mask = duet.compute_masks(duet.atn_peak, duet.delay_peak)
+ masks = duet._compute_masks()
+ for i in range(len(masks)):
+ assert np.all(benchmark_mask[i].mask == masks[i].mask)
- assert np.all(best_ind == benchmark_best_ind)
- assert np.all(benchmark_mask == mask)
-
- def test_convert_time_domain(self):
+ def test_make_audio_signals(self):
duet = nussl.Duet(self.signal, 3)
duet.stft_ch0 = self.benchmark_dict['benchmark_stft_ch0']
duet.stft_ch1 = self.benchmark_dict['benchmark_stft_ch1']
@@ -184,17 +170,14 @@ class DuetUnitTests(unittest.TestCase):
duet.peak_indices = self.benchmark_dict['benchmark_peak_indices']
duet.delay_peak = self.benchmark_dict['benchmark_delay_peak']
duet.atn_peak = self.benchmark_dict['benchmark_atn_peak']
- duet.num_frequency_bins = self.benchmark_dict['benchmark_frequency_bins']
- duet.num_time_bins = self.benchmark_dict['benchmark_num_time_bins']
- duet.best_ind = self.benchmark_dict['benchmark_best_ind']
- duet.mask = self.benchmark_dict['benchmark_mask']
+ duet.masks = self.benchmark_dict['benchmark_masks']
- source_estimates_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_source_estimates.npy')
- benchmark_source_estimates = np.load(source_estimates_path)
+ final_signals_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_final_signals.npy')
+ benchmark_final_signals = np.load(final_signals_path)
- source_estimates = duet.convert_to_time_domain(duet.best_ind, duet.mask, duet.atn_peak, duet.delay_peak)
+ final_signals = duet.make_audio_signals()
- assert np.all(benchmark_source_estimates == source_estimates)
+ assert np.all(benchmark_final_signals == final_signals)
# test_smooth_matrix
@@ -205,26 +188,41 @@ def freeze_duet_values():
signal = nussl.AudioSignal(path)
duet = nussl.Duet(signal, 3)
+ output_folder = os.path.abspath('duet_reference/duet_benchmarks')
duet.stft_ch0, duet.stft_ch1, duet.frequency_matrix = duet._compute_spectrogram(duet.sample_rate)
+ np.save(os.path.join(output_folder, "benchmark_stft_ch0"), duet.stft_ch0)
+ np.save(os.path.join(output_folder, "benchmark_stft_ch1"), duet.stft_ch1)
+ np.save(os.path.join(output_folder, "benchmark_wmat"), duet.frequency_matrix)
duet.symmetric_atn, duet.delay = duet._compute_atn_delay(duet.stft_ch0, duet.stft_ch1, duet.frequency_matrix)
+ np.save(os.path.join(output_folder, "benchmark_sym_atn"), duet.symmetric_atn)
+ np.save(os.path.join(output_folder, "benchmark_delay"), duet.delay)
duet.normalized_attenuation_delay_histogram, duet.attenuation_bins, duet.delay_bins = duet._make_histogram()
+ np.save(os.path.join(output_folder, "benchmark_hist"), duet.normalized_attenuation_delay_histogram)
+ np.save(os.path.join(output_folder, "benchmark_atn_bins"), duet.attenuation_bins)
+ np.save(os.path.join(output_folder, "benchmark_delay_bins"), duet.delay_bins)
duet.peak_indices = nussl.utils.find_peak_indices(duet.normalized_attenuation_delay_histogram, duet.num_sources,
threshold=duet.peak_threshold,
min_dist=[duet.attenuation_min_distance,
duet.delay_min_distance])
+ np.save(os.path.join(output_folder,"benchmark_peak_indices"), duet.peak_indices)
- delay_peak, atn_delay_est, atn_peak = duet._convert_peaks()
+ duet.delay_peak, duet.atn_delay_est, duet.atn_peak = duet._convert_peaks()
+ np.save(os.path.join(output_folder, "benchmark_delay_peak"), duet.delay_peak)
+ np.save(os.path.join(output_folder, "benchmark_atn_delay_est"), duet.atn_delay_est)
+ np.save(os.path.join(output_folder, "benchmark_atn_peak"), duet.atn_peak)
- duet._compute_masks(delay_peak, atn_peak)
+ duet.masks = duet._compute_masks()
+ np.save(os.path.join(output_folder, "benchmark_masks"), duet.masks)
- source_estimates = duet._convert_to_time_domain(atn_peak, delay_peak)
+ final_signals = duet.make_audio_signals()
+ np.save(os.path.join(output_folder, "benchmark_final_signals"), final_signals)
- duet.separated_sources = source_estimates
if __name__ == '__main__':
unittest.main()
+#freeze_duet_values()


message: Corrected issues for DUET code review

diff:
@@ -109,13 +109,15 @@ class AudioSignal(object):
# Plotting
##################################################
- def plot_time_domain(self):
- """
- Not implemented yet -- will raise and exception
- Returns:
+ # def plot_time_domain(self):
+ # """
+ # Not implemented yet -- will raise and exception
+ # Returns:
+ #
+ # """
+ # signal = self.audio_data
+ # for channel in num_channels
- """
- raise NotImplementedError('Not ready yet!')
_NAME_STEM = 'audio_signal'
@@ -231,8 +233,8 @@ class AudioSignal(object):
@property
def stft_data(self):
- """ (:obj:`np.ndarray`): Real-valued, uncompressed, time-domain representation of the audio.
- 2D numpy array with shape `(n_channels, n_samples)`.
+ """ (:obj:`np.ndarray`): Complex-valued, time-frequency representation of the audio.
+ 2D numpy array with shape `(n_frequency_bins, n_time_bins)`.
``None`` by default, this can be initialized at instantiation.
Usually, this is expected to be floats. Some functions will convert to floats if not already.
"""
@@ -1228,7 +1230,7 @@ class AudioSignal(object):
Args:
overwrite (bool, optional): If ``True`` this function will overwrite :attr:`audio_data`.
-
+ remove_channels (bool, optional): If ``True`` this function will remove the channel index axis.
Returns:
(:obj:`np.array`): Mono-ed version of :attr:`audio_data`.
@@ -1241,23 +1243,24 @@ class AudioSignal(object):
self.audio_data = mono
return mono
- def stft_to_mono(self, overwrite=False):
- """ Converts :attr:`stft_data` to mono by averaging every sample.
+ def stft_to_one_channel(self, overwrite=False):
+ """ Converts :attr:`stft_data` to a single channel by averaging every sample.
+ Shape: stft_data.shape will be (num_freq, num_time, 1) where the last axis is the channel number
Warning:
- If overwrite=True (default) this will overwrite any data in :attr:`audio_data`!
+ If overwrite=True (default) this will overwrite any data in :attr:`stft_data`!
Args:
overwrite (bool, optional): If ``True`` this function will overwrite :attr:`stft_data`.
Returns:
- (:obj:`np.array`): Mono-ed version of :attr:`stft_data`.
+ (:obj:`np.array`): Single channel version of :attr:`stft_data`.
"""
- mono_stft = np.mean(self.stft_data, axis=constants.CHAN_INDEX)
+ one_channel_stft = np.mean(self.stft_data, axis=constants.CHAN_INDEX)
if overwrite:
- self.stft_data = mono_stft
- return mono_stft
+ self.stft_data = one_channel_stft
+ return one_channel_stft
##################################################
# Operator overloading

diff:
@@ -19,7 +19,12 @@ class Duet(mask_separation_base.MaskSeparationBase):
"""Implements the Degenerate Unmixing Estimation Technique (DUET) algorithm.
The DUET algorithm was originally proposed by S.Rickard and F.Dietrich for DOA estimation
- and further developed for BSS and demixing by A.Jourjine, S.Rickard, and O. Yilmaz.
+ and further developed for BSS and demixing by A.Jourjine, S.Rickard, and O. Yilmaz. DUET extracts
+ sources using the symmetric attenuation and relative delay between two channels. The symmetric
+ attenuation is calculated from the ratio of the two channels' stft amplitudes, and the delay is the arrival delay
+ between the two sensors used to record the audio signal. These two values are clustered as peaks on a
+ histogram to determine where each source occurs. This implementation of DUET creates and returns Mask objects after
+ the run() function, which can then be applied to the original audio signal to extract each individual source.
References:
@@ -31,8 +36,8 @@ class Duet(mask_separation_base.MaskSeparationBase):
Parameters:
input_audio_signal (np.array): a 2-row Numpy matrix containing samples of the two-channel mixture.
num_sources (int): Number of sources to find.
- attenuation_min (int): Lower bound on attenuation value, used as minimum distance in utils.find_peak_indices.
- attenuation_max (int): Upper bound on attenuation, used for creating a histogram without outliers.
+ attenuation_min (int): Minimum distance in utils.find_peak_indices, change if not enough peaks are identified.
+ attenuation_max (int): Used for creating a histogram without outliers.
num_attenuation_bins (int): Number of bins for attenuation.
delay_min (int): Lower bound on delay, used as minimum distance in utils.find_peak_indices.
delay_max (int): Upper bound on delay, used for creating a histogram without outliers.
@@ -40,13 +45,14 @@ class Duet(mask_separation_base.MaskSeparationBase):
peak_threshold (float): Value in [0, 1] for peak picking.
attenuation_min_distance (int): Minimum distance between peaks wrt attenuation.
delay_min_distance (int): Minimum distance between peaks wrt delay.
- p (int): Weight the histogram with the symmetric attenuation estimator
+ p (int): Weight the histogram with the symmetric attenuation estimator.
q (int): Weight the histogram with the delay estimator
-
+ On page 8 of his paper, Rickard recommends p=1 and q=0 as a default starting point and p=.5, q=0 if one
+ source is more dominant.
Attributes:
stft_ch0 (np.array): A Numpy matrix containing the stft data of channel 0.
- stft_ch1 (np.array): A Numpy matrix containing the stft data of channel 0.
+ stft_ch1 (np.array): A Numpy matrix containing the stft data of channel 1.
frequency_matrix (np.array): A Numpy matrix containing the frequencies of analysis.
symmetric_atn (np.array): A Numpy matrix containing the symmetric attenuation between the two channels.
delay (np.array): A Numpy matrix containing the delay between the two channels.
@@ -69,10 +75,9 @@ class Duet(mask_separation_base.MaskSeparationBase):
def __init__(self, input_audio_signal, num_sources,
attenuation_min=-3, attenuation_max=3, num_attenuation_bins=50,
delay_min=-3, delay_max=3, num_delay_bins=50,
- peak_threshold=0.2, attenuation_min_distance=5, delay_min_distance=5, p=1, q=0,
- mask_type=mask_separation_base.MaskSeparationBase.BINARY_MASK, mask_threshold=0.5):
- super(Duet, self).__init__(input_audio_signal=input_audio_signal, mask_type=mask_type,
- mask_threshold=mask_threshold)
+ peak_threshold=0.2, attenuation_min_distance=5, delay_min_distance=5, p=1, q=0):
+ super(Duet, self).__init__(input_audio_signal=input_audio_signal,
+ mask_type=mask_separation_base.MaskSeparationBase.BINARY_MASK)
if self.audio_signal.num_channels != 2:
raise ValueError('Duet requires that the input_audio_signal has exactly 2 channels!')
@@ -142,6 +147,8 @@ class Duet(mask_separation_base.MaskSeparationBase):
i += 1
"""
+ self.masks =[]
+
if self.audio_signal.num_channels != 2: # double check this
raise ValueError('Cannot run Duet on audio signal without exactly 2 channels!')
@@ -188,10 +195,6 @@ class Duet(mask_separation_base.MaskSeparationBase):
stft_ch0 = self.audio_signal.get_stft_channel(0)
stft_ch1 = self.audio_signal.get_stft_channel(1)
- # remove dc component to avoid dividing by zero freq. in the delay estimation
- stft_ch0 += nussl.constants.EPSILON
- stft_ch1 += nussl.constants.EPSILON
-
# Compute the freq. matrix for later use in phase calculations
n_time_bins = len(self.audio_signal.time_bins_vector)
wmat = np.array(np.tile(np.mat(self.audio_signal.freq_vector).T, (1, n_time_bins))) * (2 * np.pi / sample_rate)
@@ -382,9 +385,11 @@ class Duet(mask_separation_base.MaskSeparationBase):
signals (List[AudioSignal]): List of AudioSignals extracted using DUET.
"""
+ if len(self.masks) == 0:
+ raise ValueError('Cannot make audio signals with no masks to apply!')
signals = []
for i in range(self.num_sources):
- # Apply masks to stft channels using equation provided by Rickard
+ # Apply masks to stft channels using equation 8.34 (pg. 11) provided by Rickard
stft_sources = ((self.stft_ch0 + self.atn_peak[i] * np.exp(1j * self.frequency_matrix * self.delay_peak[i])
* self.stft_ch1) / (1 + self.atn_peak[i] ** 2))
new_sig = self.audio_signal.make_copy_with_stft_data(stft_sources, verbose=False)
@@ -406,9 +411,10 @@ class Duet(mask_separation_base.MaskSeparationBase):
normalize (Optional[bool]): Flags whether the matrix should be normalized or not
"""
plt.close('all')
-
- aa = np.tile(self.attenuation_bins[1::], (self.num_delay_bins, 1)).T
- dd = np.tile(self.delay_bins[1::].T, (self.num_attenuation_bins, 1))
+ if self.attenuation_delay_histogram is None:
+ raise ValueError('Cannot plot with no histogram data!')
+ aa = np.tile(self.attenuation_bins[1:], (self.num_delay_bins, 1)).T
+ dd = np.tile(self.delay_bins[1:].T, (self.num_attenuation_bins, 1))
# save smoothed_hist (not normalized) as an option for plotting
smoothed_hist = self._smooth_matrix(self.attenuation_delay_histogram, np.array([3]))

diff:
-cb65959e4786d049fdd174d176be42c1d0f37262
\ No newline at end of file
+1493fba7a56219eb9361ab0b2daa39607ccbb64d
\ No newline at end of file

diff:
-9e0eae6d9663764f53b167799acec8f193f136bd
\ No newline at end of file
+9679e0e6b03d248e033187695c5b4517bb4f0a79
\ No newline at end of file

diff:
-f003de722b0b05fa5f3bd1c4e2a151c633330fda
\ No newline at end of file
+3462a19f9ecefe92aebecb786e5bc409af503a85
\ No newline at end of file

diff:
-1c194e8503fed55edb6753778e632e04abe28f4e
\ No newline at end of file
+8c97bdd8dd39c2a364bfb653c23c3713e29af927
\ No newline at end of file

diff:
-d8554310a7f3859c86e86cc8c62a1db5e4b4e099
\ No newline at end of file
+a5a852781704d287aaca51afcde7be132d1dc55d
\ No newline at end of file

diff:
@@ -31,6 +31,20 @@ class DuetUnitTests(unittest.TestCase):
benchmark_dict[key] = value
return benchmark_dict
+ def test_multiple_duet(self):
+ mask_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_masks.npy')
+ benchmark_mask = np.load(mask_path)
+ duet = nussl.Duet(self.signal, 3)
+ duet_masks = duet.run()
+ path_to_benchmark_file = os.path.join('..', 'Input', 'dev1_wdrums_inst_mix.wav')
+ duet.audio_signal = nussl.AudioSignal(path_to_benchmark_file)
+ duet_masks = duet.run()
+ path_to_benchmark_file = os.path.join('..', 'Input', 'dev1_female3_inst_mix.wav')
+ duet.audio_signal = nussl.AudioSignal(path_to_benchmark_file)
+ duet_masks = duet.run()
+ for i in range(len(duet_masks)):
+ assert np.all(benchmark_mask[i].mask == duet_masks[i].mask)
+
def test_duet_final_outputs(self):
#Test final outputs
mask_path = os.path.join('duet_reference', 'duet_benchmarks', 'benchmark_masks.npy')
@@ -188,7 +202,7 @@ def freeze_duet_values():
signal = nussl.AudioSignal(path)
duet = nussl.Duet(signal, 3)
- output_folder = os.path.abspath('duet_reference/duet_benchmarks')
+ output_folder = os.path.abspath('duet_reference/test')
duet.stft_ch0, duet.stft_ch1, duet.frequency_matrix = duet._compute_spectrogram(duet.sample_rate)
np.save(os.path.join(output_folder, "benchmark_stft_ch0"), duet.stft_ch0)

original_message: Corrected issues for DUET code review #87.


message: Added plotting function for time domain signals.

diff:
@@ -15,6 +15,7 @@ import audioread
import json
import warnings
import copy
+import matplotlib.pyplot as plt
import separation.masks.mask_base
import spectral_utils
@@ -108,16 +109,34 @@ class AudioSignal(object):
##################################################
# Plotting
##################################################
-
- # def plot_time_domain(self):
- # """
- # Not implemented yet -- will raise and exception
- # Returns:
#
- # """
- # signal = self.audio_data
- # for channel in num_channels
+ def plot_time_domain(self, x_label='time', save=False, name=None, output_path=None):
+ """
+ Parameters:
+ x_label (str): Label the x axis with time or samples
+ save (bool): Save a png of the plot to the current folder
+ name (str): The name of the audio signal. Applies to title and filename
+ output_path (str): The output path of where the plot is saved if save is True
+
+ """
+ signal = self.audio_data
+ for i in range(self.num_channels):
+ plt.subplot(self.num_channels+1, 1, i+1)
+ if x_label == 'time':
+ x_axis = np.linspace(0, len(signal[i])/self.sample_rate, len(signal[i]))
+ plt.plot(x_axis, signal[i])
+ else:
+ plt.plot(signal[i])
+ channel_num_plot = 'Channel ' + str(i)
+ plt.ylabel(channel_num_plot)
+ plot_title = name if name else 'Audio Signal'
+ plt.suptitle(plot_title)
+ if save:
+ output_name_path = output_path if output_path else ''
+ output_name_path += name if name else 'audio_signal'
+ plt.savefig(output_name_path)
+ plt.show()
_NAME_STEM = 'audio_signal'

diff:
@@ -24,6 +24,7 @@ class AudioSignalUnitTests(unittest.TestCase):
self.input_path1 = os.path.join(input_folder, 'k0140_int.wav')
self.input_path2 = os.path.join(input_folder, 'k0140.wav')
+ self.input_path3 = os.path.join(input_folder, 'dev1_female3_inst_mix.wav')
self.out_path1 = os.path.join(output_folder, 'k0140_int_output.wav')
self.out_path2 = os.path.join(output_folder, 'k0140_output.wav')
@@ -132,6 +133,10 @@ class AudioSignalUnitTests(unittest.TestCase):
a = nussl.AudioSignal()
a.load_audio_from_file(path, offset=offset, duration=duration)
+ def test_plot_time_domain(self):
+ a = nussl.AudioSignal(self.input_path3)
+ a.plot_time_domain('time',save=True, name='test_signal', output_path='../')
+
def test_write_to_file_path1(self):
a = nussl.AudioSignal(self.input_path1)
a.write_audio_to_file(self.out_path1)


message: Added time domain plotting and unit tests for various examples.

diff:
@@ -111,41 +111,84 @@ class AudioSignal(object):
##################################################
#
- def plot_time_domain(self, x_label='time', save=False, name=None, output_path=None):
+ _NAME_STEM = 'audio_signal'
+
+ def plot_time_domain(self, channel=None, x_label_time=True, save=False, name=None, output_path=None):
"""
+ Plots a graph of the time domain audio signal
Parameters:
x_label (str): Label the x axis with time or samples
save (bool): Save a png of the plot to the current folder
name (str): The name of the audio signal. Applies to title and filename
- output_path (str): The output path of where the plot is saved if save is True
+ output_path (str): The output path of where the plot is saved if save is True. Should include '/' at the end
"""
- signal = self.audio_data
+
+ if self.audio_data is None:
+ raise ValueError('Cannot plot with no audio data!')
+
+ if channel > self.num_channels-1:
+ raise ValueError('Channel selected does not exist!')
+
+ # Mono or single specific channel selected for plotting
+ if self.num_channels == 1 or channel is not None:
+ plot_channels = channel if channel else self.num_channels-1
+ if x_label_time is True:
+ plt.plot(self.time_vector, self.audio_data[plot_channels])
+ plt.xlim(self.time_vector[0], self.time_vector[-1])
+ else:
+ plt.plot(self.audio_data[plot_channels])
+ plt.xlim(0, self.signal_length)
+ channel_num_plot = 'Channel {}'.format(plot_channels)
+ plt.ylabel(channel_num_plot)
+
+ # Stereo signal plotting
+ elif self.num_channels == 2 and channel is None:
+ top_plot = abs(self.audio_data[0])
+ bottom_plot = -abs(self.audio_data[1])
+ if x_label_time is True:
+ plt.plot(self.time_vector, top_plot)
+ plt.plot(self.time_vector, bottom_plot, 'C0')
+ plt.xlim(self.time_vector[0], self.time_vector[-1])
+ else:
+ plt.plot(top_plot)
+ plt.plot(bottom_plot, 'C0')
+ plt.xlim(0, self.signal_length)
+
+ # Plotting more than 2 channels each on their own plots in a stack
+ elif self.num_channels > 2 and channel is None:
for i in range(self.num_channels):
plt.subplot(self.num_channels+1, 1, i+1)
- if x_label == 'time':
- x_axis = np.linspace(0, len(signal[i])/self.sample_rate, len(signal[i]))
- plt.plot(x_axis, signal[i])
+ if x_label_time is True:
+ plt.plot(self.time_vector, self.audio_data[i])
+ plt.xlim(self.time_vector[0], self.time_vector[-1])
else:
- plt.plot(signal[i])
- channel_num_plot = 'Channel ' + str(i)
+ plt.plot(self.audio_data[i])
+ plt.xlim(0, self.signal_length)
+ channel_num_plot = 'Channel {}'.format(i)
plt.ylabel(channel_num_plot)
- plot_title = name if name else 'Audio Signal'
- plt.suptitle(plot_title)
+
+ if name is None:
+ name = self.file_name if self.file_name is not None else self._NAME_STEM
+ else:
+ name = os.path.splitext(name)[0]
+
+ plt.suptitle(name)
+ name = name if self._check_if_valid_img_type(name) else name + '.png'
+
if save:
output_name_path = output_path if output_path else ''
- output_name_path += name if name else 'audio_signal'
+ output_name_path += name
plt.savefig(output_name_path)
- plt.show()
- _NAME_STEM = 'audio_signal'
+ plt.show()
def plot_spectrogram(self, file_name=None, ch=None):
# TODO: use self.stft_data if not None
# TODO: flatten to mono be default
# TODO: make other parameters adjustable
if file_name is None:
- name = self.file_name if self.file_name is not None else self._NAME_STEM
+ name = self.file_name if self.file_name is not None else self._NAME_STEM + '_spectrogram'
else:
name = os.path.splitext(file_name)[0]
@@ -160,7 +203,9 @@ class AudioSignal(object):
def _check_if_valid_img_type(name):
import matplotlib.pyplot as plt
fig = plt.figure()
- return any([name[len(k):] == k for k in fig.canvas.get_supported_filetypes().keys()])
+ result = any([name[len(k):] == k for k in fig.canvas.get_supported_filetypes().keys()])
+ plt.close()
+ return result
##################################################
# Properties
@@ -463,7 +508,7 @@ class AudioSignal(object):
warnings.warn('offset + duration are longer than the signal. Reading until end of signal...',
UserWarning)
- audio_input, self.sample_rate = librosa.load(input_file_path,
+ audio_input, self._sample_rate = librosa.load(input_file_path,
sr=None,
offset=offset,
duration=duration,
@@ -507,7 +552,7 @@ class AudioSignal(object):
signal = signal.astype('float') / (np.iinfo(np.dtype('int16')).max + 1.0)
self.audio_data = signal
- self.sample_rate = sample_rate
+ self._sample_rate = sample_rate
self.set_active_region_to_default()
def write_audio_to_file(self, output_file_path, sample_rate=None, verbose=False):

diff:
@@ -133,10 +133,6 @@ class AudioSignalUnitTests(unittest.TestCase):
a = nussl.AudioSignal()
a.load_audio_from_file(path, offset=offset, duration=duration)
- def test_plot_time_domain(self):
- a = nussl.AudioSignal(self.input_path3)
- a.plot_time_domain('time',save=True, name='test_signal', output_path='../')
-
def test_write_to_file_path1(self):
a = nussl.AudioSignal(self.input_path1)
a.write_audio_to_file(self.out_path1)
@@ -184,6 +180,41 @@ class AudioSignalUnitTests(unittest.TestCase):
freq = 30
sine_wave = np.sin(np.linspace(0, freq * 2 * np.pi, length))
+ def test_plot_time_domain_stereo(self):
+ # Stereo signal that should plot both channels on same plot
+ a = nussl.AudioSignal(self.input_path3)
+ a.plot_time_domain()
+
+ def test_plot_time_domain_specific_channel(self):
+ # Stereo signal that should only plot the specified channel
+ a = nussl.AudioSignal(self.input_path3)
+ a.plot_time_domain(channel=0)
+
+ def test_plot_time_domain_mono(self):
+ # Mono signal plotting
+ a = nussl.AudioSignal(self.input_path3)
+ a.to_mono(overwrite=True)
+ a.plot_time_domain()
+
+ def test_plot_time_domain_multi_channel(self):
+ # Plotting a signal with 5 channels
+ num_test_channels = 6
+ freq_multiple = 5
+ freqs = [i * freq_multiple for i in range(1, num_test_channels)]
+ test_signal = np.array([np.sin(np.linspace(0, i * 2 * np.pi, self.length)) for i in freqs[:num_test_channels]])
+ a = nussl.AudioSignal(audio_data_array = test_signal)
+ a.plot_time_domain()
+
+ def test_plot_time_domain_sample_xaxis(self):
+ # Plotting a stereo signal with sample numbers on the x axis instead of time
+ a = nussl.AudioSignal(self.input_path3)
+ a.plot_time_domain(x_label_time=False)
+
+ def test_plot_time_domain_save_to_path_and_rename(self):
+ # Plotting and saving the plot with a new name to a folder
+ a = nussl.AudioSignal(self.input_path3)
+ a.plot_time_domain(save=True, name='Test Graph', output_path='../')
+
def test_stft_istft_simple1(self):
"""
Tests to make sure stft and istft don't crash with default settings.


message: Removed plt.show() from plot time domain signal.

diff:
@@ -181,8 +181,6 @@ class AudioSignal(object):
output_name_path += name
plt.savefig(output_name_path)
- plt.show()
-
def plot_spectrogram(self, file_name=None, ch=None):
# TODO: use self.stft_data if not None
# TODO: flatten to mono be default

diff:
@@ -205,7 +205,7 @@ class AudioSignalUnitTests(unittest.TestCase):
a = nussl.AudioSignal(audio_data_array = test_signal)
a.plot_time_domain()
- def test_plot_time_domain_sample_xaxis(self):
+ def test_plot_time_domain_sample_on_xaxis(self):
# Plotting a stereo signal with sample numbers on the x axis instead of time
a = nussl.AudioSignal(self.input_path3)
a.plot_time_domain(x_label_time=False)