Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

EuroPython 2018参加レポート(2)~Pythonによるサウンド生成、C APIのエミュレート、Decoratorの分類

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

 ヨーロッパ最大級のPythonのカンファレンス「EuroPython」。今回は、7月23日から29日までの間、イギリスのエディンバラにて開催された、EuroPython 2018を2回にわたってレポートします。本記事では、筆者らが特に興味を持ったセッションを紹介します。

目次

はじめに

 前回のEuroPython 2018 第1回レポートでは、最初の基調講演であるDavid Beazley氏による発表やPython3.7の新機能について紹介しました。

 本記事では前回に続き、EuroPython 2018で発表されたセッションの中から、筆者らが特に興味を持った・面白いと感じたセッションについて紹介していきたいと思います。

参考リンク

音楽理論に基づくサウンド生成

Change music in two epochs
Change music in two epochs

 このセッションでは、発表者がどのようにしてオートエンコーダーによる楽器認識モデルを構築したのかという話が主題としてありました。その中で楽器認識モデルの学習に必要な大量の訓練データを用意するために、numpyを使ってオーディオシンセサイザーを開発しました。そこで紹介された音声合成の考え方と音楽理論の話が、非常に面白かったため紹介しようと思います。

音声データの表現

 オーディオシンセサイザーの解説に入る前に、まずは音声データの表現について知る必要があります。WAVEファイルをSciPyで読み込んでみましょう。

>>> from scipy.io.wavfile import read
>>> sampling_rate, data_raw = read('../data/sine.wav')
>>> sampling_rate
44100
>>> data_raw
array([    0,  2052,  4097, ..., -6126, -4097, -2052], dtype=int16)

 SciPyで読み込むと2つ返り値がありました。1つはサンプリングレートと呼ばれ、1秒あたりの音声信号のサンプリング数です。ちなみに44100という数値はCDでも採用される一般的な値です[1]。2つめは実際にサンプリングされた音声信号の値で、符号付きの整数の配列となっています。

 音声データの中身がわかったところで、サンプリングレート44100の信号を実際に生成してみましょう。ここでは440Hzのサイン波の信号を生成してみます。

import pandas as pd
import numpy as np

generated = pd.DataFrame({'time': np.arange(0, 1, 1 / sampling_rate)}).set_index('time')
generated['sine'] = np.sin(generated.index * 440 * 2 * np.pi)

 生成される音は次のようになります。

音の強弱を調整する

 これまで生成してきた音はあまりにも機械的でした。

 現実世界では必ず音が小さくなるということを考慮して、音が時間経過に応じて小さくなるようなフィルターを用いて波形を変えてみます。

dr = 0.2
generated['decay_envelope'] = np.exp(-generated.index / dr)
generated['decay_sine'] = generated['sine'] * generated['decay_envelope']

 波形は次のように変化します。

左:通常のサイン波、右:時間経過に応じて減衰 左:通常のサイン波、右:時間経過に応じて減衰
左:通常のサイン波、右:時間経過に応じて減衰

 実際に音を聴いてみましょう。

 先ほどの音声に比べると機械的な印象が薄まり、弦楽器をはじいたような音になりました。このように音の波形を発生させてから、周波数成分や音の強弱に手を加えていくのは、減算合成(Subtractive Synthesis)とよばれる音声合成方式の基礎となる考え方です。

打楽器(スネアドラム)の音

 さきほどは弦楽器のような音を生成しましたがせっかくなので打楽器の音も生成してみましょう。まずはnp.randomで雑音を生成します。

generated['noise'] = np.random.uniform(low=-1, high=1, size=generated.index.shape)

 スペクトルを見ると、どの周波数帯域にもまんべんなく信号が存在することが確認できます。

ノイズのスペクトル
ノイズのスペクトル

 次のような音になります(耳障りな音が鳴るのでご注意ください)。

 このノイズデータをフィルターをかけて音を少し変えてみます。

generated['short_decay'] = np.exp(-generated.index / 0.05)
generated['snare_base'] = np.sin( (1 / (generated.index + 0.1) + 30 ) * 4 * np.pi) * np.exp(-generated.index / 0.4)
generated['snare'] = generated['snare_base'] + generated['noise'] * generated['short_decay']

 こうすると生成される音はスネアドラムのような音に近づきます。

音階の表現

 ここまでただ音を鳴らしてきましたが、音楽のメロディーを表現するためには音階が表現できないといけません。ピアノの調律として4オクターブ目のA(ラ)の音を440Hzに設定するA440というものがあります[2]。その周波数を基準に次の計算式で各鍵の周波数が求まります。

\[f = f_{A4}(\sqrt[12]{2})^N\]

 440Hzからこの周波数間隔で音を変化させ、音を鳴らしてみます。ソースコードは少しここに載せるには長くなるので省略しますが、興味を持った方は、発表者が公開しているJupyter Notebookmusic_generatorモジュールを読んでみてください。

 生成された音声は次のようになります。音の高さが一定の間隔で上昇しているのが分かるでしょうか。

 発表者はここで紹介した理論をもとにオーディオシンセサイザーを作成しました。それを用いて大量のラベル付された音声データを作成し、楽器識別モデルを学習させています。文字数の都合上ここではオーディオシンセサイザーの開発にテーマを絞りましたが、ここで生成した音声データでもうまく楽器識別ができるモデルを構築できたようです。興味のある方は動画をチェックしてみてください。

 また本記事の執筆中に、requestsの開発者として有名なKenneth Reitzが、「PyTheory: Music Theory for Humans」というライブラリを公開しました。本記事で解説した内容と非常に関連の高いソフトウェアです。興味を持った方はこちらもチェックしてみるといいかもしれません。

[1] 人間の可聴域は一般的に20Hzから20000Hzと言われています。観測したい信号がもつ最大周波数の2倍以上でサンプリングすれば元の信号が復元できる(サンプリング定理)ため、この44100Hzが選択されているのはリーズナブルであることがわかります。

[2] 音階を「ドラミファソラシ」と覚えている方が多いかと思います。これは実はイタリア語読みで、アルファベットの表記では「CDEFGAH」や「CDEFGAB」と書きます。同様に日本語では「イロハニホヘト」、ドイツ語では「ツェー・デー・エー・エフ・ゲー・アー・ハー(Bの時はベー)」と読まれます。余談になりますが筆者(芝田)は昔ピアノを習っていた時期があり、そのときはドイツ語読みでピアノの先生に教えられました。日本語読みは「ハ長調」や「イ短調」など曲調を表すときに使われます。


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

著者プロフィール

バックナンバー

連載:EuroPythonレポート
All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5