Pythonでピアノ音楽のスペクトログラムを作ってみた

  • このエントリーをはてなブックマークに追加

時系列データのSTFT(短時間フーリエ変換)からスペクトログラム表示させる方法を学びました。ここでは活用例としてピアノ音楽のスペクトログラムを作り、画像保存する所までを紹介します。

こんにちは。wat(@watlablog)です。
本記事は作ったコードで色々遊んでみる回です。ピアノ音楽をスペクトログラムで画像的に眺めてみましょう

STFTとスペクトログラムのおさらい

計算の概要

色々な計算結果を眺めてみる前に、そもそもどんな計算をしているかをおさらいしておきましょう。

今回はピアノで録音した音声ファイル(.wav)をPythonで読み、時系列データをフレームでオーバーラップ分割しながら順次FFTをかけていきます。

この計算をSTFT(短時間フーリエ変換)と呼び、結果を時間×周波数×振幅レベルで表示させたものをスペクトログラム表示と呼びます。図解は以下です。

さらに詳しい説明と、段階を追ったコードの説明は「Pythonで音のSTFT計算を自作!スペクトログラム表示する方法」に全て書きましたので、そちらを参照下さい。

Pythonコード全文

wavファイルを読み込んでSTFT計算を行い、結果をスペクトログラム表示するPythonプログラムコードを以下に示します。

プログラムは可読性を考慮して関数ファイルとメインファイルに.pyを分けました。メインファイルでimport functionを宣言してから、メインファイルの方を実行することで同じ結果を得ることができると思います。

wav読み込み時のfilenameは実際に存在するwavファイルの名前を付けて下さい。

関数ファイル(function.py)

メインファイル(main.py)

ピアノ楽曲とスペクトログラム

本ページはサクッと紹介をしていく感じでいきます。ピアノ音楽といっても、僕が趣味で、さらに独学でやっているだけなので素人演奏です。

かなり雑だぞ。

使っているピアノはRolandF-110という当時10万円くらいの入門者用電子ピアノです。

録音機器はiPhone7で、「iRig2※Amazonリンク」という「弾いてみた」の動画とかで良く使われるインターフェースを介してwavファイルを作成しました。

録音とwavファイルの作成は以下の画像に示す「PCM録音」という無料のiPhoneアプリを使っています。同じような機器構成で、最終的にwavファイルが出力できれば本ページと同じ解析ができると思います。

では以下より様々な音楽のスペクトログラムを紹介していきます!

スケール(音階)

スケールは日本語で音階を意味します。今回はCスケール、ハ長調で単にドレミファソラシドと昇って、その後ドシラソファミレドと降りてくるように弾きました。

音声はこちら。※メディアプレーヤークリックで音が出ます。

スペクトログラムはこちら。

キレイに左右対称の図となっていますね。やはりピアノは1音1音がはっきりしているので、スペクトログラムにすると1つ1つがインパルス的な波形になっていることが読み取れます。

グリッサンド

グリッサンドとはスケールと同じようなものですが、指滑らせながら連続的に鍵盤を弾く奏法です。慣れないと痛いです。

音声はこちら。※メディアプレーヤークリックで音が出ます。

スペクトログラムはこちら。

スケールに比べて、高調波成分があまり無いですね。1音1音垂直に指で打鍵していないのが原因?電子ピアノなのであまりその辺の再現度はなさそうですが。

猫ふんじゃった

「猫ふんじゃった」はみんななぜか弾ける超有名な曲です。

曲はこんな感じ。※メディアプレーヤークリックで音が出ます。

スペクトログラムはこちら。

色々な音を弾くと、横を向いた十字架が至る所にあるという感じでもはやわけがわかりませんね…!

ショパン:幻想即興曲

ショパンの幻想即興曲も有名ですね。僕が弾いたものでも、多分聴けばわかるかと。

音声はこんな感じ。※メディアプレーヤークリックで音が出ます。

スペクトログラムはこちら。

最初の「じゃーん」の部分はG#のオクターブなので500Hz弱の所が最も強く出ていることがわかりますね。左手だけのアルペジオ部分から右手の主旋律が加わる部分も何となく層がわかる感じ。

ショパン:スケルツォ第2番

ショパンのスケルツォ第2番はあまり知っている人がいないかも知れませんね。個人的に好きな曲なのでよく弾いています(雑ですが)。

音声はこんな感じ。※メディアプレーヤークリックで音が出ます。

スペクトログラムはこちら。

この曲、冒頭部分はピアノ弾きの間で「ところてん」と呼ばれています。音のリズムを「んートコロテン♪トコロテン♪…にっぽ↑ーん、文化国家!」と歌詞を付けると丁度良くはまるんです。今回はその部分を解析してみました。

スペクトログラムの画像を保存する方法

matplotlibでスペクトログラムを作成していますが、このカラーマップの図は特に画像ファイルとして保存したくなる時があります。

音声データの解析結果を画像ファイルとして保存してあれば、後に機械学習用のデータとしても使えます。

軸を消して画像として保存

機械学習を念頭に置くために、保存する画像からはカラーバー、目盛線を含む軸を全て消して保存します。

上のメインファイルからグラフ描画部分を以下のコードに変更するだけで完了です。

実行すると「fig.png」という画像が保存されます。ここまで覚えれば、後は色々な活用方法が考えられますね。

まとめ

本ページではSTFT計算とスペクトログラム表示のおさらいを行い、様々なピアノ音楽のスペクトログラムを観察しました。

また、スペクトログラムを画像として保存することで機械学習への使い道考えました。

今回は「やってみた」系の遊び記事ですが、作ったコードは使ってなんぼ!色々遊んで技術の幅を拡げよう!

Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!

  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。

コメント

  1. アバター harrods227 より:

    とても参考になりました。
    フォルダ内にあるwavファイルをスペクトログラム画像に一括変換するにはどうすれば良いのでしょうか? ご教授いただけると嬉しいです。
    よろしくお願い致します。

    1. wat wat より:

      ご訪問ありがとうございます!
      僕の方も丁度その内容のプログラムが欲しかったので、「Pythonでフォルダ内全wavをスペクトログラムに変換してみた」という記事に記載してみました。
      この内容で回答になりますでしょうか?
      もしご不明点やその他要望がございましたら、再度コメントで教えて頂けると助かります!

      1. アバター harrods227 より:

        はい、とても適切な回答でした。まさにやりたかった内容です。
        ご対応ありがとうございました。
        よろしくお願い致します。

        1. wat wat より:

          ご返信ありがとうございます。
          解決できたようでよかったです!

  2. アバター harrods227 より:

    お久しぶりです。
    お元気ですか?
    またご教授頂きたい事があります。
    スペクトログラムの最大音圧の時の周波数と音圧を数値で得るにはどうすればいいですか?
    また、音圧の大きい順に5つまでの周波数と音圧を数値で知ることも可能ですか?
    以上、よろしくお願い致します。

    1. wat wat より:

      お久しぶりです。回答遅れて申し訳ございません。
      スペクトログラムといっても、2Dの配列なので最大値と周波数を取得することは可能です。
      配列の最大値を得るにはnp.max(fft_array)、最大値の位置を得るにはnp.argmax(fft_array)とします。

      但し、2D配列に対してnp.argmax(fft_array)を使うと平坦化された指標が得られる(4096✖️2の配列だったらサイズが1Dで8192になる)ので、divmod()等を使ってフレームサイズで割った余りに対して周波数分解能をかけると、周波数[Hz]の数値が得られると思います。

      「大きい順に5つまでの」を実現するためには、一度2D配列をソートしてから抽出するという手が考えられます。
      しかし、この方法だと分解能によっては最大値付近の点が複数抽出されるだけになりそうです。
      やりたいことが「大きい値が全体のどこに分布しているか」を調べることだとすると、もしかして想定通り動作しない可能性があります。

      FFT波形からピーク値をもってくるには、別途「ピーク検出アルゴリズム」を使うのが常套手段と思います。
      面白そうなので当ブログでもやってみようと思いますが、これには少し時間がかかりそうです。

      もし書きかけのコードがございましたらコメントやTwitterでもお気軽に相談下さい。
      よろしくお願い致します。

    2. wat wat より:

      既に解決されているかも知れませんが、こちらでもご質問を頂いた後にスペクトログラムからのピーク検出アルゴリズムを検討してみました。
      Pythonでスペクトログラムからピーク値を任意数抽出する方法」に説明を書いてみました。
      ただこちらで考えたものなので、もしかするともっと効率の良い一般的なアルゴリズムがどこかにあるかも知れません(もしそのようなリンクを探していたら教えて頂けると助かります…)。

コメントを残す

*