はじめに
NumPyやLibROSA、scikit-learnなどのPythonの素晴らしい信号処理・統計解析モジュールを利用してオーディオ信号処理アプリを作るため、学習コストが低いマイクロフレームワークのFlaskを使いWeb基盤を作ってみました。
実装
ユーザがアップロードした複数のオーディオファイルに対して、LibROSAで処理を行うところまで実装してみました。
今回は、各オーディオファイルを読み込み、時間サンプル数を出力して表示しています。
ファイルのアップロードは、Flask-Dropzoneを利用しています。
examples/in-formを参考にしました。
アップロードされたファイルは、タイムスタンプと乱数文字列で命名したディレクトリに格納するようにしました。
index.html
{% extends "bootstrap/base.html" %} {% block title %}Wizardcraft.Works{% endblock %} {% block content %} <form action="{{ url_for('handle_form') }}" enctype="multipart/form-data" method="post"> {{ dropzone.create() }} <input type="submit" id="submit" value="Submit and Upload"> </form> {{ dropzone.load_js() }} {{ dropzone.config() }} {% endblock %} {% block scripts %} {{ super() }} {{ dropzone.load_css() }} {{ dropzone.style('border: 2px dashed #0087F7; margin: 10%; min-height: 400px;') }} {% endblock %}
Flaskの主要部(app.py)
# -*- coding: utf-8 -*- import os import random import string from datetime import datetime from flask import Flask, render_template from flask import request, redirect, url_for from flask import current_app from flask_dropzone import Dropzone from flask_bootstrap import Bootstrap from werkzeug import secure_filename from signal_proc import signal_proc basedir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) app.config.update( UPLOADED_PATH=os.path.join(basedir, 'uploads'), # Flask-Dropzone config: DROPZONE_ALLOWED_FILE_TYPE='audio', DROPZONE_MAX_FILE_SIZE=10, # MB DROPZONE_IN_FORM=True, DROPZONE_UPLOAD_ON_CLICK=True, DROPZONE_MAX_FILES=10, DROPZONE_PARALLEL_UPLOADS=3, # set parallel amount DROPZONE_UPLOAD_MULTIPLE=True, # enable upload multiple DROPZONE_UPLOAD_BTN_ID='submit', DROPZONE_UPLOAD_ACTION='handle_upload', # UPLOADED_AUDIOFILE_DIRPATH=None, UPLOADED_AUDIOFILE_LIST=None, ) dropzone = Dropzone(app) bootstrap = Bootstrap(app) @app.route('/') def index(): return render_template('index.html') @app.route('/upload', methods=['POST']) def handle_upload(): filepath_list = [] timestamp = datetime.utcnow().strftime('%Y%m%d%H%M%S') random_str = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6)) dirname = timestamp + "_" + random_str dirpath = os.path.join(app.config['UPLOADED_PATH'], dirname) current_app.config["UPLOADED_AUDIOFILE_DIRPATH"] = dirpath try: os.mkdir(dirpath) except FileExistsError: pass print("upload", dirpath) for key, f in request.files.items(): if key.startswith('file'): filename = secure_filename(f.filename) filepath = os.path.join(dirpath, filename) f.save(filepath) filepath_list.append(filepath) current_app.config["UPLOADED_AUDIOFILE_LIST"] = filepath_list print("upload", current_app.config["UPLOADED_AUDIOFILE_LIST"]) return '', 204 @app.route('/form', methods=['POST']) def handle_form(): filepath_list = current_app.config["UPLOADED_AUDIOFILE_LIST"] print("form", filepath_list) info_list = signal_proc(filepath_list) html = '<br>'.join([info for info in info_list]) return html if __name__ == '__main__': app.run("127.0.0.1", 8080, debug=True)
オーディオ信号処理モジュール
import librosa def signal_proc(filepath_list, sr=44100, mono=True): info_list = [] for filepath in filepath_list: y, sr = librosa.core.load(filepath, sr=sr, mono=mono) info_list.append(str(len(y))) return info_list
最後に
実際に多くの人に利用される実用的なアプリを管理運用をする場合、Flaskのサーバーでは厳しいので Nginx+uWSGI+Flaskのような環境構築を行うといいようです。