Adding first draft of NMF MFCC with tests and inputs.
ADD
demos/demo_nmf_mfcc.py
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import os
+import nussl
+import numpy as np
+
+def main():
+ # Load input file
+
+
+ input_file_name = os.path.join('..', 'input', 'dev1_female3_inst_mix.wav')
+ signal = nussl.AudioSignal(path_to_input_file=input_file_name)
+
+ # Set up DUET
+ nmf_mfcc = nussl.NMF_MFCC(signal, num_sources= 3, num_templates=60, distance_measure="euclidean", num_iterations=50)
+
+ # and run
+ nmf_mfcc.run()
+ nmf_mfcc.make_audio_signals()
+
+if __name__ == '__main__':
+ main()

ADD
input/piano_and_synth_arp_chord.wav.REMOVED.git-id
+e47266ac3908483ff4e2bd656a63d03aff94fe7d
\ No newline at end of file

ADD
input/piano_and_synth_arp_chord_mono.wav.REMOVED.git-id
+3bf00a74d886d6ba178d7272dcb32d8d04159514
\ No newline at end of file

MODIFY
nussl/separation/__init__.py
nussl/separation/__init__.py
@@ -15,10 +15,12 @@ from .duet import Duet
from .projet import Projet
# Others
+from .nmf_mfcc import NMF_MFCC
from .ideal_mask import IdealMask
from .overlap_add import OverlapAdd
from .ica import ICA
from .high_low_pass_filter import HighLowPassFilter
__all__ = ['SeparationBase', 'MaskSeparationBase',
- 'Repet', 'RepetSim', 'FT2D', 'Duet', 'Projet', 'IdealMask', 'OverlapAdd', 'ICA', 'HighLowPassFilter']
+ 'Repet', 'RepetSim', 'FT2D', 'Duet', 'Projet', 'IdealMask', 'OverlapAdd',
+ 'ICA', 'HighLowPassFilter', 'NMF_MFCC']

ADD
nussl/separation/nmf_mfcc.py
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import numpy as np
+from sklearn.cluster import KMeans
+import librosa
+import nussl.audio_signal
+import nussl.constants
+import nussl.spectral_utils
+import nussl.utils
+from nussl.transformers import transformer_nmf
+from nussl.separation import separation_base
+import mask_separation_base
+import masks
+
+
+class NMF_MFCC(mask_separation_base.MaskSeparationBase):
+ def __init__(self, input_audio_signal, num_sources, num_templates, distance_measure, num_iterations):
+ super(NMF_MFCC, self).__init__(input_audio_signal=input_audio_signal,
+ mask_type=mask_separation_base.MaskSeparationBase.BINARY_MASK)
+
+ self.num_sources = num_sources
+ self.num_templates = num_templates
+ self.distance_measure = distance_measure
+ self.num_iterations = num_iterations
+ self.input_audio_signal = input_audio_signal
+ self.audio_data = input_audio_signal.audio_data
+ self.clusterer = None
+ self.signal_stft = None
+ self.templates_matrix = None
+ self.activation_matrix = None
+ self.labeled_templates = None
+ self.sources = None
+ self.masks = []
+ self.clusterer = KMeans(n_clusters=self.num_sources)
+ self.input_audio_signal.stft_params = self.stft_params
+ self.signal_stft = self.input_audio_signal.stft()
+
+ def run(self):
+ templates_stft = self.signal_stft[:, 0:self.signal_stft.shape[1]]
+ templates_stft = np.squeeze(templates_stft)
+
+ # Set up NMF and run
+ nmf = transformer_nmf.TransformerNMF(np.abs(templates_stft), self.num_templates)
+ nmf.should_use_epsilon = False
+ nmf.max_num_iterations = self.num_iterations
+ nmf.distance_measure = self.distance_measure
+ self.activation_matrix, self.templates_matrix = nmf.transform()
+
+ # Cluster the templates matrix into Mel frequencies
+ cluster_templates = librosa.feature.mfcc(S=self.templates_matrix)[1:14]
+ self.clusterer.fit_transform(cluster_templates.T)
+
+ # Retrieve cluster labels for the templates
+ self.labeled_templates = self.clusterer.labels_
+ self.sources = []
+
+ # Extract sources from signal
+ for source_index in range(self.num_sources):
+ source_indices = np.where(self.labeled_templates == source_index)[0]
+ self.extract_masks(clustered_templates=self.templates_matrix[:, source_indices],
+ signal_stft=self.signal_stft)
+ return self.masks
+
+ def extract_masks(self, clustered_templates, signal_stft):
+ """ Extracts the binary mask objects for each source.
+ Parameters:
+ clustered_templates (np.matrix): The columns from self.templates_matrix that correspond to the current
+ source being extracted
+ signal_stft (np.matrix): The stft of the input audio signal
+ """
+ new_templates_stft = np.abs(np.squeeze(signal_stft))
+
+ # Set up and run NMF using each clustered template
+ reconstruct_nmf = transformer_nmf.TransformerNMF(input_matrix=new_templates_stft, templates=clustered_templates,
+ num_components=clustered_templates.shape[1], should_update_template=False)
+ reconstruct_nmf.should_use_epsilon = False
+ reconstruct_nmf.max_num_iterations = self.num_iterations
+ reconstruct_nmf.distance_measure = self.distance_measure
+ activation_matrix, new_templates_matrix = reconstruct_nmf.transform()
+
+ # Reconstruct the signal
+ reconstructed_signal = new_templates_matrix.dot(activation_matrix)
+
+ # Mask the input signal
+ music_stft_max = np.maximum(reconstructed_signal, np.abs(new_templates_stft))
+ mask = np.divide(reconstructed_signal, music_stft_max)
+ mask = np.nan_to_num(mask)
+
+ # Create the binary mask
+ mask = np.round(mask)
+ mask_object = masks.BinaryMask(np.array(mask))
+ self.masks.append(mask_object)
+
+ def make_audio_signals(self):
+ """ Applies each mask in self.masks and returns a list of audio_signal objects for each source.
+
+ Returns:
+ self.sources (np.array): An array of audio_signal objects containing each separated source
+ """
+ for i in range(self.num_sources):
+ source_audio_signal = self.audio_signal.make_copy_with_stft_data(np.squeeze(self.signal_stft), verbose=False)
+ source_audio_signal = source_audio_signal.apply_mask(self.masks[i])
+ source_audio_signal.stft_params = self.stft_params
+ source_audio_signal.istft(overwrite=True, truncate_to_length=self.audio_signal.signal_length)
+ self.sources.append(source_audio_signal)
+ return self.sources
+
+ # # template - extracted template, residual - everything that's leftover.
+ # template = np.multiply(np.squeeze(signal_stft), mask)
+ # residual = np.multiply(np.squeeze(signal_stft), 1 - mask)
+ #
+ # return template, residual
\ No newline at end of file

ADD
tests/test_nmf_mfcc.py
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import unittest
+import numpy as np
+import nussl
+import os
+
+
+
+class NMFMFCCUnitTests(unittest.TestCase):
+
+ def test_2_sin_waves(self):
+ sr = nussl.constants.DEFAULT_SAMPLE_RATE
+ dur = 3 # seconds
+ length = dur * sr
+ sine_wave_1 = np.sin(np.linspace(0, 440 * 2 * np.pi, length))
+ sine_wave_2 = np.sin(np.linspace(0, 440 * 5 * 2 * np.pi, length))
+ signal = sine_wave_1 + sine_wave_2
+ test_audio = nussl.AudioSignal()
+ test_audio.load_audio_from_array(signal)
+
+ # Set up NMMF MFCC
+ nmf_mfcc = nussl.NMF_MFCC(test_audio, num_sources=2, num_templates=2, distance_measure="euclidean",
+ num_iterations=50)
+
+ # and run
+ nmf_mfcc.run()
+ sources = nmf_mfcc.make_audio_signals()
+
+ for i, source in enumerate(sources):
+ output_file_name = str(i) + '.wav'
+ source.write_audio_to_file(output_file_name)
+
+ def test_piano_and_synth(self):
+ # Load input file
+ input_file_name = os.path.join('..', 'input', 'piano_and_synth_arp_chord_mono.wav')
+ signal = nussl.AudioSignal(path_to_input_file=input_file_name)
+
+ # Set up NMMF MFCC
+ nmf_mfcc = nussl.NMF_MFCC(signal, num_sources=2, num_templates=6, distance_measure="euclidean",
+ num_iterations=150)
+ # and run
+ nmf_mfcc.run()
+ sources = nmf_mfcc.make_audio_signals()
+ for i, source in enumerate(sources):
+ output_file_name = str(i) + '.wav'
+ source.write_audio_to_file(output_file_name)

Adding first draft of NMF MFCC with tests and inputs.