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/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/__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 new file mode 100644 index 0000000..68336b4 --- /dev/null +++ b/magnolia/python/api/api.py @@ -0,0 +1,140 @@ +import json +import os +import tempfile +import logging.config +from flask import Flask, request, jsonify, url_for, send_from_directory +from flask_cors import CORS, cross_origin +import librosa +import numpy as np + +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' +MODEL_DIR = './model' +MODEL_DEVICE = '/cpu:0' +ALLOWED_EXTENSIONS = set(['wav']) +SAMPLING_RATE = 16000 + +settings={ + "compression": "gzip", + "compression_opts": 0, + "processing_parameters": { + "target_sample_rate": 10000, + "preemphasis_coeff": 0.0, + "stft_args": { + "n_fft": 512, + 'hop_length': 256 + } + } +} + +app = Flask(__name__) +CORS(app) +app.config['UPLOAD_DIR'] = UPLOAD_DIR +app.config['CONVERTED_DIR'] = CONVERTED_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 = os.path.basename(tf.name) + temp_path = os.path.join(app.config['UPLOAD_DIR'], filename) + file.save(temp_path) + return temp_path + + +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) + 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 = 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 + + 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] + print(source_specs[0].shape) + return source_specs + +def postprocess_waveform(source_specs, 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, save_sr): + tf = tempfile.NamedTemporaryFile() + filename = os.path.basename(tf.name) + temp_path = os.path.join(app.config['CONVERTED_DIR'], filename) + 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, 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, 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, 13000) + 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 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):