From 2e17b8d10e09239eb6561a47f2807e0a06d2b5c4 Mon Sep 17 00:00:00 2001 From: Ryan Ashley Date: Tue, 23 Oct 2018 15:39:49 -0400 Subject: [PATCH 1/4] initial commit of API --- magnolia/data/logging_settings/logging.conf | 16 ++- magnolia/python/api/api.py | 114 ++++++++++++++++++++ 2 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 magnolia/python/api/api.py diff --git a/magnolia/data/logging_settings/logging.conf b/magnolia/data/logging_settings/logging.conf index 21fedbb..badd55a 100644 --- a/magnolia/data/logging_settings/logging.conf +++ b/magnolia/data/logging_settings/logging.conf @@ -1,8 +1,8 @@ [loggers] -keys=root,preprocessing,partitioning,model +keys=root,preprocessing,partitioning,model,api [handlers] -keys=console_handler,verbose_console_handler,preprocessing_file_handler,partitioning_file_handler,model_file_handler +keys=console_handler,verbose_console_handler,preprocessing_file_handler,partitioning_file_handler,model_file_handler, api_file_handler [formatters] keys=brief_formatter,verbose_formatter @@ -30,6 +30,12 @@ handlers=verbose_console_handler,model_file_handler qualname=model propagate=1 +[logger_api] +level=INFO +handlers=verbose_console_handler,api_file_handler +qualname=api +propagate=1 + [handler_console_handler] class=StreamHandler @@ -61,6 +67,12 @@ level=DEBUG formatter=verbose_formatter args=('model.log', 'w', 10000000, 5) +[handler_api_file_handler] +class=handlers.RotatingFileHandler +level=DEBUG +formatter=verbose_formatter +args=('api.log', 'w', 10000000, 5) + [formatter_brief_formatter] format=%(levelname)s | %(funcName)s | %(asctime)s | %(message)s diff --git a/magnolia/python/api/api.py b/magnolia/python/api/api.py new file mode 100644 index 0000000..59efec9 --- /dev/null +++ b/magnolia/python/api/api.py @@ -0,0 +1,114 @@ +import json +import tempfile +import logging.config +from flask import Flask, request, jsonify +from flask_cors import CORS +import librosa + +from preprocessing.preprocessing import normalize_waveform +from preprocessing.preprocessing import preprocess_waveform +from preprocessing.preprocessing import undo_preprocessing +from utils.postprocessing import convert_preprocessing_parameters +from utils.clustering_utils import chimera_clustering_separate, chimera_mask +from models import make_model + +UPLOAD_DIR = './uploads' +CONVERTED_DIR = './converted' +MODEL_DIR = './model' +MODEL_DEVICE = '/cpu:0' +ALLOWED_EXTENSIONS = set(['wav', 'mp3']) +SAMPLING_RATE = 10000 + +app = Flask(__name__) +CORS(app) +app.config['UPLOAD_DIR'] = UPLOAD_DIR +app.config['CONVERTED_DIR'] = UPLOAD_DIR +app.config['MODEL_DIR'] = MODEL_DIR + +def valid_file(filename): + return '.' in filename and \ + filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +def save_temp_file(request): + if 'file' not in request.files: + flash('No file part') + return '' + file = request.files['file'] + # if user does not select file, browser also + # submit a empty part without filename + if file.filename == '': + flash('No selected file') + return '' + if file and valid_file(file.filename): + tf = tempfile.NamedTemporaryFile() + filename = tf.name + temp_path = os.path.join(app.config['UPLOAD_DIR'], filename) + file.save(temp_path) + return file_path + + +def convert_file_to_waveform(file_path): + y,sr = librosa.load(file_path) + return y + +def preprocess_waveform(wf_batch): + # Compute STFT spectrogram + signal = normalize_waveform(np.squeeze(wf_batch)) + D, yy = preprocess_waveform(signal, SAMPLING_RATE, **settings['processing_parameters']) + app.logger.debug(D.shape) + app.logger.debug(yy.shape) + + return D + +def process_waveform(D): + frequency_dim = D.shape[0] + model_save_base = MODEL_DIR + model_location = MODEL_DEVICE + + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.1, + 'nonlinearity': 'tf.tanh', + } + model_params['F'] = frequency_dim + config = {'model_params': model_params, + 'device': model_location} + model = make_model('Chimera', config) + model.load(model_save_base) + + source_specs = chimera_mask(np.expand_dims(D,axis=0), model)[0] + return source_specs + +def postprocess_waveform(source_specs): + yy_out = undo_preprocessing(source_specs[:,:,0], mixer.sample_length_in_bits(), + preemphasis_coeff=0, + istft_args=istft_args) + + return yy_out + +def convert_back_to_wav(yy): + tf = tempfile.NamedTemporaryFile() + filename = tf.name + temp_path = os.path.join(app.config['CONVERTED_DIR'], filename) + librosa.write_wav(temp_path, yy, SAMPLING_RATE) + return url_for('api/v1/converted/' + filename) + +@app.route('api/v1/converted/', methods=['GET']) +def uploaded_file(filename): + return send_from_directory(app.config['CONVERTED_DIR'], + filename) +@app.route('/api/v1/convert', methods=['POST']) +def convert_file(): + temp_path = save_temp_file(request) + wf_batch = convert_file_to_waveform(temp_path) + D = preprocess_waveform(wf_batch) + source_specs = process_waveform(D) + yy = postprocess_waveform(source_specs) + converted_url = convert_back_to_wav(yy) + return converted_url + +if __name__ == '__main__': + app.logger = logging.getLogger('api') + app.logger.debug("logging started") + app.run(port='5001', debug=True) \ No newline at end of file From e5602b62541c843dd9ccd3f70d60eff1ea6bbaf3 Mon Sep 17 00:00:00 2001 From: Ryan Ashley Date: Wed, 24 Oct 2018 12:39:52 -0400 Subject: [PATCH 2/4] actually running --- magnolia/python/api/__init__.py | 0 magnolia/python/api/api.py | 14 +++++++------- magnolia/python/utils/clustering_utils.py | 4 ++-- magnolia/python/utils/postprocessing.py | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 magnolia/python/api/__init__.py diff --git a/magnolia/python/api/__init__.py b/magnolia/python/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/magnolia/python/api/api.py b/magnolia/python/api/api.py index 59efec9..4969056 100644 --- a/magnolia/python/api/api.py +++ b/magnolia/python/api/api.py @@ -5,12 +5,12 @@ from flask_cors import CORS import librosa -from preprocessing.preprocessing import normalize_waveform -from preprocessing.preprocessing import preprocess_waveform -from preprocessing.preprocessing import undo_preprocessing -from utils.postprocessing import convert_preprocessing_parameters -from utils.clustering_utils import chimera_clustering_separate, chimera_mask -from models import make_model +from magnolia.preprocessing.preprocessing import normalize_waveform +from magnolia.preprocessing.preprocessing import preprocess_waveform +from magnolia.preprocessing.preprocessing import undo_preprocessing +from magnolia.utils.postprocessing import convert_preprocessing_parameters +from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask +from magnolia.models import make_model UPLOAD_DIR = './uploads' CONVERTED_DIR = './converted' @@ -94,7 +94,7 @@ def convert_back_to_wav(yy): librosa.write_wav(temp_path, yy, SAMPLING_RATE) return url_for('api/v1/converted/' + filename) -@app.route('api/v1/converted/', methods=['GET']) +@app.route('/api/v1/converted/', methods=['GET']) def uploaded_file(filename): return send_from_directory(app.config['CONVERTED_DIR'], filename) diff --git a/magnolia/python/utils/clustering_utils.py b/magnolia/python/utils/clustering_utils.py index 8a2512e..5559480 100644 --- a/magnolia/python/utils/clustering_utils.py +++ b/magnolia/python/utils/clustering_utils.py @@ -9,8 +9,8 @@ from sklearn.mixture import BayesianGaussianMixture, GaussianMixture from sklearn.decomposition import PCA -from ..features.spectral_features import istft -from ..features.preprocessing import make_stft_features, \ +from magnolia.preprocessing.spectral_features import istft +from magnolia.preprocessing.preprocessing import make_stft_features, \ undo_preemphasis from magnolia.utils.training import preprocess_l41_batch, preprocess_chimera_batch, preprocess_l41_regression_batch diff --git a/magnolia/python/utils/postprocessing.py b/magnolia/python/utils/postprocessing.py index 3b9367e..99e68a9 100644 --- a/magnolia/python/utils/postprocessing.py +++ b/magnolia/python/utils/postprocessing.py @@ -1,6 +1,6 @@ import numpy as np -from ..features.preprocessing import undo_preemphasis -from ..features.spectral_features import istft +from magnolia.preprocessing.preprocessing import undo_preemphasis +from magnolia.preprocessing.spectral_features import istft def convert_preprocessing_parameters(params): From 6bc5aea9d5dafbee3b1523160cedc0073e90821c Mon Sep 17 00:00:00 2001 From: Ryan Ashley Date: Thu, 25 Oct 2018 14:44:12 -0400 Subject: [PATCH 3/4] fully functional --- magnolia/python/api/api.py | 50 ++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/magnolia/python/api/api.py b/magnolia/python/api/api.py index 4969056..b507fb6 100644 --- a/magnolia/python/api/api.py +++ b/magnolia/python/api/api.py @@ -1,9 +1,11 @@ import json +import os import tempfile import logging.config -from flask import Flask, request, jsonify +from flask import Flask, request, jsonify, url_for from flask_cors import CORS import librosa +import numpy as np from magnolia.preprocessing.preprocessing import normalize_waveform from magnolia.preprocessing.preprocessing import preprocess_waveform @@ -19,10 +21,22 @@ ALLOWED_EXTENSIONS = set(['wav', 'mp3']) SAMPLING_RATE = 10000 +settings={ + "compression": "gzip", + "compression_opts": 0, + "processing_parameters": { + "target_sample_rate": 10000, + "preemphasis_coeff": 0.0, + "stft_args": { + "n_fft": 512 + } + } +} + app = Flask(__name__) CORS(app) app.config['UPLOAD_DIR'] = UPLOAD_DIR -app.config['CONVERTED_DIR'] = UPLOAD_DIR +app.config['CONVERTED_DIR'] = CONVERTED_DIR app.config['MODEL_DIR'] = MODEL_DIR def valid_file(filename): @@ -41,23 +55,23 @@ def save_temp_file(request): return '' if file and valid_file(file.filename): tf = tempfile.NamedTemporaryFile() - filename = tf.name + filename = os.path.basename(tf.name) temp_path = os.path.join(app.config['UPLOAD_DIR'], filename) file.save(temp_path) - return file_path + return temp_path def convert_file_to_waveform(file_path): y,sr = librosa.load(file_path) - return y + duration = librosa.get_duration(y=y, sr=sr) + return y, duration -def preprocess_waveform(wf_batch): +def prepare_waveform(wf_batch): # Compute STFT spectrogram signal = normalize_waveform(np.squeeze(wf_batch)) - D, yy = preprocess_waveform(signal, SAMPLING_RATE, **settings['processing_parameters']) + D,yy = preprocess_waveform(signal, SAMPLING_RATE, **settings['processing_parameters']) app.logger.debug(D.shape) app.logger.debug(yy.shape) - return D def process_waveform(D): @@ -80,8 +94,10 @@ def process_waveform(D): source_specs = chimera_mask(np.expand_dims(D,axis=0), model)[0] return source_specs -def postprocess_waveform(source_specs): - yy_out = undo_preprocessing(source_specs[:,:,0], mixer.sample_length_in_bits(), +def postprocess_waveform(source_specs, sample_length_in_bits): + istft_args={'hop_length': 256} + print(sample_length_in_bits) + yy_out = undo_preprocessing(source_specs[:,:,0], sample_length_in_bits, preemphasis_coeff=0, istft_args=istft_args) @@ -89,10 +105,11 @@ def postprocess_waveform(source_specs): def convert_back_to_wav(yy): tf = tempfile.NamedTemporaryFile() - filename = tf.name + filename = os.path.basename(tf.name) temp_path = os.path.join(app.config['CONVERTED_DIR'], filename) - librosa.write_wav(temp_path, yy, SAMPLING_RATE) - return url_for('api/v1/converted/' + filename) + print(temp_path) + librosa.output.write_wav(temp_path, yy, SAMPLING_RATE) + return url_for('uploaded_file', filename=filename) @app.route('/api/v1/converted/', methods=['GET']) def uploaded_file(filename): @@ -101,10 +118,11 @@ def uploaded_file(filename): @app.route('/api/v1/convert', methods=['POST']) def convert_file(): temp_path = save_temp_file(request) - wf_batch = convert_file_to_waveform(temp_path) - D = preprocess_waveform(wf_batch) + wf_batch, len_in_secs = convert_file_to_waveform(temp_path) + len_in_bits = int(SAMPLING_RATE*len_in_secs) + D = prepare_waveform(wf_batch) source_specs = process_waveform(D) - yy = postprocess_waveform(source_specs) + yy = postprocess_waveform(source_specs, len_in_bits) converted_url = convert_back_to_wav(yy) return converted_url From 1048d5d25e59f344ffc4e8fc689c785d7171ca96 Mon Sep 17 00:00:00 2001 From: Ryan Ashley Date: Mon, 29 Oct 2018 12:08:32 -0400 Subject: [PATCH 4/4] corrected, except for an issue with output pitch shifting --- .gitignore | 3 +++ magnolia/python/api/api.py | 42 +++++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 78cfc55..21d78de 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ __pycache__ magnolia.egg-info/ dist/ build/ +magnolia/python/api/model/ +magnolia/python/api/converted/ +magnolia/python/api/uploads/ \ No newline at end of file diff --git a/magnolia/python/api/api.py b/magnolia/python/api/api.py index b507fb6..68336b4 100644 --- a/magnolia/python/api/api.py +++ b/magnolia/python/api/api.py @@ -2,8 +2,8 @@ import os import tempfile import logging.config -from flask import Flask, request, jsonify, url_for -from flask_cors import CORS +from flask import Flask, request, jsonify, url_for, send_from_directory +from flask_cors import CORS, cross_origin import librosa import numpy as np @@ -18,8 +18,8 @@ CONVERTED_DIR = './converted' MODEL_DIR = './model' MODEL_DEVICE = '/cpu:0' -ALLOWED_EXTENSIONS = set(['wav', 'mp3']) -SAMPLING_RATE = 10000 +ALLOWED_EXTENSIONS = set(['wav']) +SAMPLING_RATE = 16000 settings={ "compression": "gzip", @@ -28,7 +28,8 @@ "target_sample_rate": 10000, "preemphasis_coeff": 0.0, "stft_args": { - "n_fft": 512 + "n_fft": 512, + 'hop_length': 256 } } } @@ -63,19 +64,23 @@ def save_temp_file(request): def convert_file_to_waveform(file_path): y,sr = librosa.load(file_path) + print("sampling rate: {0}".format(sr)) duration = librosa.get_duration(y=y, sr=sr) - return y, duration + print("durations: {0}".format(duration)) + print("len in bits: {0}".format(int(duration*sr))) + return y, sr, duration def prepare_waveform(wf_batch): # Compute STFT spectrogram - signal = normalize_waveform(np.squeeze(wf_batch)) - D,yy = preprocess_waveform(signal, SAMPLING_RATE, **settings['processing_parameters']) + signal = wf_batch #normalize_waveform(np.squeeze(wf_batch)) + D,yy = preprocess_waveform(wf_batch, SAMPLING_RATE, **settings['processing_parameters']) app.logger.debug(D.shape) app.logger.debug(yy.shape) return D def process_waveform(D): frequency_dim = D.shape[0] + print("D shape 1: {0}".format(D.shape[1])) model_save_base = MODEL_DIR model_location = MODEL_DEVICE @@ -92,38 +97,41 @@ def process_waveform(D): model.load(model_save_base) source_specs = chimera_mask(np.expand_dims(D,axis=0), model)[0] + print(source_specs[0].shape) return source_specs def postprocess_waveform(source_specs, sample_length_in_bits): - istft_args={'hop_length': 256} - print(sample_length_in_bits) + istft_args={'hop_length': 256, 'win_length': 512} yy_out = undo_preprocessing(source_specs[:,:,0], sample_length_in_bits, preemphasis_coeff=0, istft_args=istft_args) return yy_out -def convert_back_to_wav(yy): +def convert_back_to_wav(yy, save_sr): tf = tempfile.NamedTemporaryFile() filename = os.path.basename(tf.name) temp_path = os.path.join(app.config['CONVERTED_DIR'], filename) - print(temp_path) - librosa.output.write_wav(temp_path, yy, SAMPLING_RATE) + librosa.output.write_wav(temp_path, yy, save_sr) return url_for('uploaded_file', filename=filename) @app.route('/api/v1/converted/', methods=['GET']) +@cross_origin(origin='*') def uploaded_file(filename): return send_from_directory(app.config['CONVERTED_DIR'], - filename) + filename, attachment_filename=filename + '.wav', mimetype='audio/wav') + @app.route('/api/v1/convert', methods=['POST']) +@cross_origin(origin='*') def convert_file(): temp_path = save_temp_file(request) - wf_batch, len_in_secs = convert_file_to_waveform(temp_path) - len_in_bits = int(SAMPLING_RATE*len_in_secs) + wf_batch, load_sr, len_in_secs = convert_file_to_waveform(temp_path) + SAMPLING_RATE = load_sr + len_in_bits = int(16000*len_in_secs) D = prepare_waveform(wf_batch) source_specs = process_waveform(D) yy = postprocess_waveform(source_specs, len_in_bits) - converted_url = convert_back_to_wav(yy) + converted_url = convert_back_to_wav(yy, 13000) return converted_url if __name__ == '__main__':