鼻歌から曲を生成するアプリ「HUMOR」の紹介(録音・MIDI変換編)
(note初投稿。エディタの操作むずかしい…)
曲、つくってみたいな
でも作曲ってむずかしそう…
音楽理論なんてしらないよぉ…
そんなあなたにHUMORを。
鼻歌を録音するだけで作曲できる(デスクトップ)アプリの制作に挑戦しました。
前置き
どうも、iiTAiと申します。名古屋で工業大学生しています。
時は2024年12月、所属する課外活動団体にて、ピクシブ株式会社様と団体OBの方々のご協力のもと、ハッカソンが開催されました。
私は相方と2人で、鼻歌から曲(の続き)を生成するアプリ「HUMOR」をつくり、pixivのメンターの方から、メンター賞をいただきました(はじめての受賞!うれしい!)。
当記事では、「HUMOR」の概要や背景…は省略して、MIDIデータを生成モデルに入力するよりも前、鼻歌を録音する部分と、録音データをMIDIデータに変換する部分について簡単に紹介しようと思います。
概要や背景、相方が担当した曲(の続き)を生成する部分については`(準備中)`をあわせてご覧ください。
機能について
当アプリでは、MIDIデータをトークン化し、モデルに入力することで曲の続きのMIDIデータを得ます(MIDIなら後からいろいろ操作できて便利ですね)。
MIDIデータなんてよくわからないよという方(私もそちら側ですが)も少なくないので、HUMORでは以下の機能を提供します。
任意時間(5秒以上20秒以下、1秒単位に制限)の録音
録音データのWAVファイルへの書き出し
(WAVの)音声のピッチとBPMの分析
ピッチとBPMを利用したMIDIファイルの生成
WAVファイルの書き出しと、ピッチとBPMの抽出はそれぞれ録音およびMIDIファイル生成とあわせて行うためユーザが意識することのない機能ですが、あとの説明のためにここに記します。
使用技術
プログラミング言語
Pythonのみです。機械学習をはじめ、ライブラリが豊富であることも理由のひとつですが、恥ずかしながら私も相方もプログラミングの知識に乏しいので選択の余地は正直なかったと思います。
ライブラリ等
主要なものだけ挙げます。
PyAudio
音声を録音したり再生できるライブラリ
単に音声を録音するために使用
PyWorld
音声合成システム「WORLD」のPythonラッパ
音声のピッチを抽出するために使用
Librosa
音響解析、信号処理用のライブラリ
音声のBPMを抽出するために使用
mido
MIDI操作用ライブラリ
MIDIファイルを作成するために使用
仕組みをざっくり
大まかな処理の流れ
先述の機能がそのまま処理の順番になっています。
録音とWAVファイル保存
ピッチとBPMの抽出
MIDIファイルの生成
以下、それぞれについて簡単に説明します。
録音とWAVファイルの保存
録音については愚直にPyAudioというライブラリを使いました。というよりこれ以外で録音できるPythonのライブラリを知らなかった故の消去法です。
詳細なオプションは省略しますが、簡単のため、チャンネル数は1(モノラル)としました。(サンプリング周波数は44.1kHzとしましたが、正直16kHzでよかった気がします)
録音したデータはwaveモジュールによりWAV形式で保存されます。
ピッチの抽出
ここからが本題です。録音については(設定さえすれば)他のアプリでもできますしね。
ピッチの抽出には、PyWorldで使用可能なharvestあるいはdioという手法のどちらかを使います(ユーザが選択できます)。ざっくりいうと、前者は比較的低速で高精度、後者は高速で低精度な手法になります。dioについてはstonemaskというものでピッチを補正します(某奇妙な冒険みたいですね)。
WAVファイルの読み込みにはsicpy.ioのwavfileモジュールを使い、そこで得た信号とサンプリング周波数を入力してピッチの時間軸データを取得します。
BPMの抽出
BPMの抽出にはLibrosaで提供される、beat_trackメソッドを使います。意外と正確です。
なお、ここではWAVファイルの読み込みにもLibrosaのメソッドを使用しています(scipyとの互換性は未検証)。
MIDIファイルの生成
ピッチの時間軸データ(以下、単に「ピッチ」と呼ぶ)とBPMからMIDIファイルをつくります。今回は以下のような手順でMIDIデータに変換します。なお、MIDIデータの操作にはmidoというライブラリを使用しました。
有声部の開始点を調べる
ピッチを、BPMにおける8分の1拍子ごとに分割
はじめの無声部は切り捨て
分割してできた各リストに対し、それぞれ平均値を導出
ここで得た値がそれぞれ32分音符の音高となる
Hz単位のピッチをMIDIノート番号に変換
同じピッチの音符が隣接したら連結
例えば32分音符どうしが連結すればひとつの16分音符になる
実際には、連続する同じ音高の32分音符の数を扱う
MIDIファイルの作成
テンポを設定
ピッチと長さを設定してMIDIノートを逐次追加
ノート番号が-1ならベロシティ(音量)0のノートを追加
MIDIファイルとして保存
そしてここで保存したMIDIファイルを(トークン化して)モデルに入力します。ほかにもっと良い手法が何となくあるような気がしますが、HUMORでは以上の手続きによりMIDIファイルを生成しています。
おわりに
ロクにコード書けない、音楽理論を学んでいないという割にはなんかそれっぽいことができたと思います。しかしながら、実際にMIDIファイルを生成してみると、元の鼻歌の原型をあまり保てておらず、改善の余地は大いにあると考えられます。
今後についてはPythonの記法を勉強した後、改めて音声情報処理や音楽理論の理解を深め、リファクタリングに挑戦する所存です(相方次第ですが)。
公開できたらいいなぁ…
ではまたノシ