クーの自由研究

マスターのかえるのクーは、弟子達の召喚術により新たな依り代を得てⅡ世として復活しました。

自由研究の準備(その11)Pythonで音をフーリエ変換(FFT)して分析します

はじめに

さて、自由研究の素材を「画像:MNIST」から「:Sound」に変更すると宣言してから幾年月。
ようやく大詰めを迎えました。いよいよ大御所にしてラスボスフーリエ変換FFTの登場です。前回のlibrosaでも内部的には大活躍でしたが、直接対決です。

おおまかな内容

音を解析し、再構築するには「フーリエ変換」(もしくは高速フーリエ変換FFT)を使うことが多いです。

フーリエ変換  F(j\omega)=\int_{-\infty}^{\infty}f(t)e^{-j\omega t}dt

離散フーリエ変換   F(j\omega)=\sum_{n=-\infty}^{\infty}f(nT_{0})e^{jnT_{0}\omega}

...
みなかったことにしましょう。
この数式の意味は分からずとも、ライブラリに準備された高速フーリエ変換を実際に使い、音を周波数の数値に変換してみます。また、元の音に戻してみます。

いきなりやってみました

元にする波形は準備(その5)でお世話になったプログラムで作成した9倍音まで含むノコギリ波です。


# coding:utf-8
import wave
import numpy as np
import scipy.fftpack
from scipy import signal
from pylab import *

if __name__ == "__main__":
# あらかじめ準備した ノコギリ波 C音(基底 261.626Hz:9倍音まで)を読み込み
wf = wave.open("Music/saw-sample-c.wav", "r")
fs = wf.getframerate() # サンプリング周波数 上のサンプルでは48K
x = wf.readframes(wf.getnframes())
x = frombuffer(x, dtype="int16") / 32768.0 # -1 - +1に正規化
wf.close()

start = 0 # サンプリングする開始位置
N = 2 ** 12 # FFTのサンプル数 2**12は4096です

# サンプリング数にあったハニング窓を準備します。
win = signal.hann(N)

# FFTを実行してスペクトラム情報を獲得します。

spectrum = scipy.fftpack.fft(x[start:start + N] * win)
freqList = scipy.fftpack.fftfreq(N, d=1.0 / fs)

# 振幅と位相をグラフ表示するために抽出します。
amplitudeSpectrum = [np.sqrt(c.real ** 2 + c.imag ** 2) for c in spectrum] # 振幅スペクトル
phaseSpectrum = [np.arctan2(int(c.imag), int(c.real)) for c in spectrum] # 位相スペクトル

# スペクトラム情報から元の波形に復元します。
resyn_sig = ifft(spectrum)

# 1.元の波形を描画
figure(figsize=(8, 8))
subplot(511) # 5行1列のグラフの1番目の位置にプロット
subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.2, hspace=0.5)
plot(range(start, start + N), x[start:start + N])
axis([start, start + N, -1.0, 1.0])
xlabel("Fig 1. Original Wave / time [sample]")
ylabel("amp")

# 2.窓関数を通した波形を描画
subplot(512) # 5行1列のグラフの2番目の位置にプロット
plot(range(start, start + N), x[start:start + N] * win)
axis([start, start + N, -1.0, 1.0])
xlabel("Fig 2. through window Wave / time [sample]")
ylabel("amp")

# 3.振幅スペクトルを描画
subplot(513)
plot(freqList, amplitudeSpectrum, linestyle='-')
axis([0, 3000, 0, 600])
xlabel("Fig 3. frequency [Hz]")
ylabel("amp spect.")

# 4.位相スペクトルを描画
subplot(514)
plot(freqList, phaseSpectrum, linestyle='-')
axis([0, 3000, -np.pi, np.pi])
xlabel("Fig 4. frequency [Hz]")
ylabel("phase spect.")

# 5.元の波形を描画
subplot(515) # 5行1列のグラフの5番目の位置にプロット
plot(range(start, start + N), resyn_sig[start:start + N])
axis([start, start + N, -1.0, 1.0])
xlabel("Fig 5. resynth Wave / time [sample]")
ylabel("amp")

show()

ちなにみ、ちょっとだけscipyのFFTのほうが、numpyのFFTよりも早いという噂がありscipyのFFTを使っています。

図1.元にする波形の のこぎり波です

あらかじめ別のpythonプログラムで生成したノコギリ波(に近い)の波形です。

少し低い ド(C) で基底の周波数は261.626Hzです。9倍音までを重ねています。

f:id:np2LKoo:20160924172808p:plain

音も貼ってみます。4096サンプルなのでとても短いです。

 

図2.窓関数を通した波形

そのまま使うと両端でクリッピングノイズが発生するので、窓関数で両端をゼロにしてやります。(正確にはサンプリングの「窓」(上記プログラムでは N )の範囲で周期関数であると仮定して計算するため、終端がゼロでないと元の信号にない成分が導出されるのでこれを防ぐためためです)

f:id:np2LKoo:20160924172857p:plain

これも音を貼ってみます。立ち上がり、立下りがクリップノイズなどがなく、スムーズに聞こえると思います。一層短く聞こえます。

 

図3.スペクトラムの振幅情報

フーリエ変換(FFT)して得たスペクトラムの振幅情報です。グラフで9倍音までちゃんと検出されていることがわかります。

f:id:np2LKoo:20160924172934p:plain

 

図4.スペクトラムの位相情報

フーリエ変換(FFT)して得たスペクトラムの位相情報です。おもいのほか重要な情報です。

f:id:np2LKoo:20160924172956p:plain

図5.スペクトラムから元に戻した波形

スペクトラム情報からFFT逆変換(scipy.ifft())を使い元の波形にしています。

f:id:np2LKoo:20160924173046p:plain

ほぼ完ぺきなくらいに復元できてます。ここまで復元できるのは技術的には当たり前なのでしょうが、感覚的にはまるで魔法のようです。元の音と違いはわからないですが、いちおう貼っておきます。

 

ちなみに、窓関数をつかわないとこうなります。

f:id:np2LKoo:20160924173118p:plain

 

フーリエ変換のまとめ

フーリエ変換は元になる波形情報からある範囲(窓)を計算し、以下を導出します

  • 周波数
  • 振幅
  • 位相

窓関数を入れて計算することが多いです。

窓関数には用途に応じいろいろな種類がありますが、正弦波、または正弦波の組み合わせの解析にはハニング窓関数がよいとのことでした。

以下がハニング窓関数です。

       w(n) = 0.5−0.5 \cos ( {\frac{2 \pi n}{M−1}} ) \ \ \ 0\  \leq \ n \ \leq \ M−1

...
みなかったことにしましょう。

ちなみにグラフはこちらです。

f:id:np2LKoo:20160924193410p:plain

ハニング関数はscipy.signal.hanning()を使えます。scipy.signal.hann()も同じです。似た名前のハミング関数scipy.signal.hamming()は似ているけど別のものなので注意しましょう。(ハミングは両端がゼロでない関数です)

ハニング(ハン窓)関数は気象学者のJulius von Hannにより名づけられました。

ハミング関数はR. W. Hammingにより名づけられました。

低域の正確な検出には多くのサンプルが必要です

いろいろな周波数で確認していると問題点がでてきました。
高域ではまったく問題ない解像度なのですが、低域から中域にかけてサンプル数が少ないため分解能がひくいのです。
たとえば、サンプリング周波数が48kサンプル数が1024の場合はfreqList=[0.0, 46.875, 93.75, ..]と約46.8Hz刻みです。これでは低域の半音検出もできません。

低音の「ド」は、C=32.703, C#=34.648 で、差は1.945Hz です。sin波の音をCかC#を特定するにはこの周波数付近では1Hzの解像度はほしいところです。
とするとサンプル数(=窓の大きさ)Nは上限(=サンプリング周波数)の48000までいってしまいました。これで低音の「ド」を解析させると、1Hz刻み
amplitudeSpectrum[30] =   90.05 (30.0Hz)
amplitudeSpectrum[31] =  474.23 (31.0Hz)
amplitudeSpectrum[32] = 4315.14 (32.0Hz)
amplitudeSpectrum[33] = 5665.88 (33.0Hz)
amplitudeSpectrum[34] = 1734.12 (34.0Hz)
なので、原音C=32.703Hzは12半音識別できます。(ちなみにB=30.868, C#=34.648)

中域はA=440.000Hz, G#=415.305Hz, A#=466.164 で14Hz程度の識別は最低必要です。例えば「窓」サイズN = 4096 (2**12) で11.72Hz刻みになります。

高域A=3520.000Hz, G#=3322.438Hz, A#=3729.310Hz で209Hz程度の識別は最低必要です。例えば「窓」サイズN = 256 (2**8) で187.5Hz刻みになります。

低音の検出の「窓」は思いっきり広くとり、中音では程々に、高音では結構狭くてもかまわない感じです。(ただ、ビブラートとかチョーキングとか、ットレスース感を出したいので精度をどれくらいにするか、各種資源や処理速度とのせめぎあいです)どのように実装に反映するかは、実験しながら決めたいです。

特定の若者の能力の発現させるため20kHz以上の音波を使うSF

があったような、なかったような。ボクは、周波数の確認をしてるときに高い周波数(14000Hz以上)がまったく聞こえないことがわかり、ショックを受けました。あ、ボクはかえるなので人間のスペックと競っても意味ないか。。。

自分はどうかな?と興味のある方は

などで確認できます。音量にはくれぐれも注意してください。(聞こえないからといって決して音量をあげないでください。)

蛇足の中の余談ですが、かえるの耳はちゃんとした内耳構造をもっていますが、哺乳類などが持つ「かたつむり管(蝸牛)」はありません。

f:id:np2LKoo:20160924165013p:plain

かえるの内耳には

  • 低音を主に受容する sacculus(10-250 Hzの音を受容)
  • 中音域を主に受容する amphibian papilla(100-1000Hzの周波数を受容)
  • 高音を主に受容する basilar papilla(1-4 KHzの周波数を受容)

という三種類の音の受容器があります。音の振動により、リンパ液に浸った有毛細胞と蓋膜が、局所的にこすれあうことで音を検出します。

やはり、音の検出についてはボクの耳のように3種類くらいフーリエ変換を使い分けて行うのがよさそうです。

 

自由研究の準備(その10)pythonライブラリのlibrosaで音楽を解析します

はじめに

LibROSAとは、音楽やオーディオ解析/分析のためのpythonパッケージです。

Brian McFee氏らにより開発され、現在も頻繁に改良されています。(2016/09時点の登録バージョンはpypi,Anaconda Cloudともに0.4.3)
開発者本人による説明(scipy2015での発表youtube)は自由研究の準備(その9)に貼りましたので参照してみてください。

MIRとは何でしょう?

LibROSAは「音楽情報検索 music information retrieval (MIR)」のための各種のモジュールを提供します。
MIRという技術/分野は日本でも注目されていますが、諸外国では音楽ビジネスや研究分野で「普通に」使われはじめている技術のようです。(この言い方は控えめすぎました。音処理に関してYoutubeSoundCloudはこの技術の「かたまり」です)

information retrieval はいわゆる「情報検索」ですが、日本語の「検索」よりも「取得」「抽出」のニュアンスが強い感じです。
音楽を条件検索するなどの「検索」以外にも、その音楽自体からいろいろな切り口の情報を取り出すオペレーションを含めて、そのように呼ばれているようです。
例えば

  • 聴く人に合った音楽を勧める(好みやシチュエーション、プレイリスト自動生成、よくある「これもどう?」購入斡旋)
  • 楽器の分離や楽器認識(ピアノの音だけ取り出すとか、シタールが使われている曲にラベル付けとか)
  • 自動採譜(好きな音楽を楽譜にしてくれるなんて素敵!)
  • 自動分類(いわゆるジャンル分け)
  • 音楽生成(なんと作っちゃうのもふくみます)
  • その他、インディースからのヒット(可能)曲発掘や売れそうなアーチスト開拓など音楽ビジネスへの応用(ビジネスしている方には、もっと「画一化」から「多様化」を誘導して欲しいなぁ。)


など、単なる「検索」以上の範囲まで含むようです。

LibROSAはどんなことができるの?

LibROSAは以下の主な機能があります。

  • 中核となる入出力機能とデジタル信号処理機能
  • 情報の表示機能(matplotlib機能を応用)
  • 特徴抽出(クロマグラム(12半音)分析や特徴量操作による楽曲データの変形)
  • オンセット特定(ハミング検索、断片検索とか、ひらたくいえばイントロあてドン)
  • ビートとテンポ(数値化や抽出)
  • スペクトログラム分離(周波数情報への分解)
  • エフェクト(元サウンドの変形)
  • 出力(エフェクト等オペレーション後の出力)
  • テンポを考慮したセグメント分け
  • 各種ユーティリティ
  • フィルタ機能

ライブラリ機能として機能毎にいくつものメソッドを持ち、機能間で相互に密接に関連しています。

インストールはどうやってやるの?

インストール

pip install librosa

で行えます。

どんな風に動くの?

それではチュートリアルを動かしてみましょう。本家はこちらです。

ビートを検出してみます

まずはチュートリアルのクイックスタートをやってみます。楽曲からビートを検出してCSV出力するサンプルです。


# ビートをトラッキング(分離)する例です。
from __future__ import print_function
import librosa

# 1. librosaで準備しているサンプル曲のファイル名を取得します。
filename = librosa.util.example_audio_file()

# 2. 波形情報を `y` へ、
# サンプリングレートを `sr` へ格納します。
y, sr = librosa.load(filename)

# 3. デフォルトの「ビートトラッカー」を実行します。
tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr)

print('Estimated tempo: {:.2f} beats per minute'.format(tempo))

# 4. ビートイベントの発生したフレーム索引をタイムスタンプ(先頭からの秒数)へ変換します。
beat_times = librosa.frames_to_time(beat_frames, sr=sr)

print('Saving output to beat_times.csv')
librosa.output.times_csv('beat_times.csv', beat_times)

Estimated tempo: 64.60 beats per minute
Saving output to beat_times.csv


と表示されました。(ボクのマシン環境のせいかubuntuでは動作し、Windowsではエラーが出ました。ubuntuでやってみたレポートとなります)。
あらかじめ準備されているサンプル曲が使われています。
example内にあるKevin_MacLeod_-_Vibe_Ace.oggが使われているようです。(どんな曲かは次のデモで貼ります)

このサンプルでは1分間に64.60 ビートであると導かれました。
beat_times.csv にはビート検出した時点の楽曲の開始からの秒数が出力されています。

中身をみてみます。


7.430
8.290
9.218
...
55.217
56.123
57.051


時間表示して音楽を聴きながらこの数字をみると、なるほどドラムがはいってくる前はビート検出されていないことがわかります。

oggファイル以外でも、audioread が読み込めるコーデックならなんでもよいようです。
手元のflacファイルでやってみましたが、OKでした。複雑なシンコペーションに惑わされることなくながらも、ほぼ正確にビート検出しています。
楽曲のスネアやバスドラムをわざと抜いているタイミングのところもビート検出しているので、他の楽器や前後のビートをみてうまくやっている感じがしています。(これは曲を貼れないのですません。お持ちの「これは無理だろう」という曲でやってみるのもおもしろいと思います。)
現在調整中なのか動かない機能(エラー)もあったので、今確認できる部分のみレポートしていきます。

Jupyter Notebookで動作するデモもあります

次はJupyter Notebookで動作するデモをやってみます。

共通部分を実行しておきます。


from __future__ import print_function
# 数学的操作をするので numpy が必要です。
import numpy as np

# グラフ表示のため、matplotlib を使います。
import matplotlib.pyplot as plt
import matplotlib.style as ms
ms.use('seaborn-muted')
%matplotlib inline

# オーディオ出力のために IPython.display を使います。
import IPython.display

# オーディオ解析にLibrosaを使います。
import librosa
# そして、表示のために display モジュールを使います。
import librosa.display

audio_path = librosa.util.example_audio_file()

# かわりに、下の行のコメントを外し貴方の好きな曲を設定してもいいですね。
# audio_path = '/path/to/your/favorite/song.mp3'

y, sr = librosa.load(audio_path)

変数 y には曲オブジェクトが設定されます。

jupyter(ipython)で曲を確認します。(下はJupyterNotebook表示のイメージです。)

f:id:np2LKoo:20160922025147j:plain

jupyter上のオーディオオブジェクトをそのまま貼れないので、かわりにsoundcloudに置いたものを張ります。(soundcloudにはoggファイルもそのままアップロードできました)

音の確認はこちらでお願いします。上のビートCSVサンプルや下のグラフを見ながら聞いてください

メルスケールのスペクトログラムを表示してみます

メルスケール(メル尺度)とはピッチ知覚の尺度です。スペクトログラムは声紋とよばれることもあり、音声、音響の分析によく使われます。横軸は時間で、縦軸は音の高さです。


# メルスケールのパワー(音圧)スペクトログラムのグラフを作って表示してみましょう。
S = librosa.feature.melspectrogram(y, sr=sr, n_mels=128)

# ログスケール(dB)に変換します。ピーク音圧を参照して使います。
log_S = librosa.logamplitude(S, ref_power=np.max)

# 新しい図を作ります。
plt.figure(figsize=(12,4))

# メルスケールのスペクトログラムを表示します。
# サンプルレートとホップ長のパラメータは時間軸の情報に使います。
librosa.display.specshow(log_S, sr=sr, x_axis='time', y_axis='mel')

# 図の上にタイトルをつけます。
plt.title('mel power spectrogram')

# カラーバーで描画します。
plt.colorbar(format='%+02.0f dB')

# 図はコンパクトに表示します。
plt.tight_layout()

f:id:np2LKoo:20160922024411j:plain最初のサンプルと同じ曲で、グラフでも最初の7秒すこしはドラムがはいっていないことがわかります。曲と一緒に聴くと、今の音は”このドットだ”というのがよくわかります。

和音とリズムを分離してみます


# effectsのhpssメソッドを使ってハーモニックとパーカッシブに分離します。
y_harmonic, y_percussive = librosa.effects.hpss(y)

# スペクトログラムはどのようになるでしょう?
# パワースペクトログラムを表示してみましょう。
S_harmonic = librosa.feature.melspectrogram(y_harmonic, sr=sr)
S_percussive = librosa.feature.melspectrogram(y_percussive, sr=sr)

# ログスケール(dB)に変換します. ピーク音圧を参照します。
log_Sh = librosa.logamplitude(S_harmonic, ref_power=np.max)
log_Sp = librosa.logamplitude(S_percussive, ref_power=np.max)

# 新しい図をつくります。
plt.figure(figsize=(12,6))

# メルスケールのスペクトログラムを表示します。
# 和音系です

plt.subplot(2,1,1)
librosa.display.specshow(log_Sh, sr=sr, y_axis='mel')
plt.title('mel power spectrogram (Harmonic)')
plt.colorbar(format='%+02.0f dB')

# リズム系です
plt.subplot(2,1,2)
librosa.display.specshow(log_Sp, sr=sr, x_axis='time', y_axis='mel')
plt.title('mel power spectrogram (Percussive)')
plt.colorbar(format='%+02.0f dB')

# 図をコンパクトに表示します。
plt.tight_layout()

librosa.effects.hpss()一発で和音とリズムを分離できます。グラフはこんな感じです。

f:id:np2LKoo:20160922032837p:plain

和音系の抽出音はこんな感じです。

ビブラフォンとベースのアタック音が一部パーカッション系にいっているのでま~るい感じになっています。利用者観点からすると、もう少し和音系からスネアとハイハットの高音部の分離がきれいにできればいいなと思います(ぜいたく)。

パーカッション系の抽出音はこんな感じです。

とてもうまく分離できているともいます。さすがに音が重なっているところは難しいですね。でも開発視点からすると、ものすごく頑張って作ってあると思います。

ソースを見て勉強させて頂きます。

音を再合成してみます

特定の周波数付近(高、中、低)を3つ再合成した例です。(長くなるのでソースは本家#Resynthesize. デモ参照してください。本家は高、中、低をそれぞれ個別に作成しています)

 

他のライブラリと共同作業してみましょう

mir_evalというライブラリを使用します。mir_evalは2014年に開催された第15回国際音楽情報検索(MIR)カンファレンスで公表されたようです。 craffelさんがメインでbmcfeeさんがサブ(コントリビュータ)で、お二方は知り合いのようです。インストール

pip install mir_eval

で行えます。

import mir_eval

# mir_evalの機能をつかって 最初に作ったbeat_times からクリック音を作ります
y_click = mir_eval.sonify.clicks(beat_times, sr, length=len(y))
# 元の音とクリック音を重ねてならしてみましょう
IPython.display.Audio(data=y + y_click, rate=sr)
# ついでにsaveしておきます
librosa.output.write_wav('click.wav', y + y_click, sr)

 最初迷って、スネアがはいるといい感じにリズムがとれて、スネアがぬけると途端に不整脈になるあたりがとても愛らしいです。

ほかにもいろいろありますので、興味のあるかたは是非本家チュートリアルをしてみてください。

世界の中心へ愛を叫んだけもの蛙(ボク)

自由研究の本編にいく予告に反し、librosaの内容を確認してました。サウンドで本編実験するにはあと、「(高速)フーリエ変換」(FFT)の確認が必要なので、まだ準備は続きます。

「王様の耳はロバの耳」という童話があります。最近ブログを書いていると、なぜかこの童話を思い浮かべます。「このブログは誰にも見てもらえていない」という思いが、「穴に向かって大声で何かを叫び、本当は誰かに聞いて欲しい」というところにつながっているのかもしれません。ボクの場合は古井戸の入り口から底へ「クー!」と叫ぶ感じでしょうか。このブログの唯一にして最大の読者はボク自身なので、まずはボクを満足させられるようがんばります。(誰もみてくれないのでいじけ気味です) 

(追記)読者登録していただけたのでボク自身が「唯一の読者」ではなくなりました。ばんざ~い。

自由研究の準備(その9)PythonでのMIDI操作(SMF編)

おおまかな内容

pythonでSMF(Standard MIDI File)を扱うライブラリを確認します。python-midiとpretty-midiを両方入れるのがトレンドのようです。pretty-midipython-midiに依存していますのでpretty-midiだけ入れて使うことはできません。

Midi系ライブラリの導入

ようやくubuntuの環境ができたので、python-midipretty-midiをいれます。

vishnubobさん怖そうな方です。

git clone git://github.com/vishnubob/python-midi

craffelさんクールな感じのする方です。

git clone git://github.com/craffel/pretty-midi

このあたりについては

様が詳しいです。参照させていただきました。それぞれをセットアップします。

cd python-midi
python setup.py install
cd ..
cd pretty-midi
python setup.py install

(追記)なお、残念ながら、python-midiは現在3系未対応です。2016/09末時点ではvishnubobさんによれば「やるつもりあるけどボランティア活動にも興味があり、オープンソース作業に集中して時間とれるまで本件(Python3対応)は保留だべ」とのことでした。

以下はpython2.7系での確認です。(追記ここまで)


まずはpython-midiのテストプログラムを実行します。

cd python-midi/tests
python tests.py

しばらくすると mary.mid  ができるので、デスクトップからメディアプレーヤで聞いてみます。
グルーブ感に問題はありそうですが、きちんと音がなります。

次はexamplesをやってみましょう。
python-midi/examples/ へcdして

python example_1.py

してみます。examplesはpython2系で実行しないとエラーがでる模様です。

midi.Pattern(format=1, resolution=220, tracks=\
[midi.Track(\
  [midi.NoteOnEvent(tick=0, channel=0, data=[43, 20]),
   midi.NoteOffEvent(tick=100, channel=0, data=[43, 0]),
   midi.EndOfTrackEvent(tick=1, data=[])])])<

と表示されました。example.mid ができているので聞いてみます。
う~んきこえません。
ソースをちょっとさわってみます

velocity=20->velocity=120

on = midi.NoteOnEvent(tick=0, velocity=120, pitch=midi.G_3)

tick=100->tick=960

off = midi.NoteOffEvent(tick=960, pitch=midi.G_3)

にしてみます。音が小さく、短いようなので調整しました。

example.mid ができているので聞いてみます。ピアノみたいな音が聞こえました。

次は

python example_2.py

してみます。

midi.Pattern(format=1, resolution=220, tracks=\
[midi.Track(\
  [midi.NoteOnEvent(tick=0, channel=0, data=[43, 120]),
   midi.NoteOffEvent(tick=960, channel=0, data=[43, 0]),
   midi.EndOfTrackEvent(tick=1, data=[])])])

が表示されるだけでした。midiファイルの中の表示するサンプルのようです。

python-midi/scripts/ にcdして、

python mididumphw.py

してみます。

Traceback (most recent call last):
  File "mididumphw.py", line 5, in <module>
    import midi.sequencer as sequencer
ImportError: No module named sequencer


Linux系でALSAがはいっていればシーケンサ使えるべ」と書いてあったので、期待していましたが何かが整っていないようです。シーケンサがつかえませんでした
ubuntu StudioなのでもちろんALSAははいっています。(そのためにStudioにしたのですから)
やっぱりJackを使っていないので、オーディオ系はpulseAudio->VMWareサウンドエミュレーション->USBAudio(Quad-capture)で、Jack/ALSAが介在していないのかなぁ。
残念ながらシーケンサは今度にして、先へすすみます。

((追記)SMF対応といえば、SMF読み込んで、シーケンサオブジェクトにしていろいろ操作できることだろ!と思われた方ごめんなさい。ボクもそれを期待してましたが、ボクのマシン環境ではできませんでした。「Python2系 + linux + ALSA がちゃんと動作する環境」 であればシーケンスオブジェクトが使えるはずです)

pretty-midi/examples/ へcdして

python chord_catalog.py


all_chords.mid ができたので聞いてみます。
あ、あんなところにUFOが・・・
すみません、取り乱しました。

もの凄いチュートリアルって結局どうよ?

さて、ここからがpretty-midiチュートリアルが凄いのでは?という本題です。
import djitw とimport librosa が必要なのですが、これがwindowsだとどうしてもうまくいきませんでした。以下もubuntsuでやります。

githubからとってきます

git clone https://github.com/craffel/djitw

djitw へ cdして

python setup.py install

します


librosa は

pip install librosa

します。(setup.py install からpip install へ訂正:setup.py install でもできます)

(追記)youtubeにScipy2015での開発者本人による紹介があったので英語のできる方はどうぞ。

ちなみにボクにはさっぱりわかりませんが、デモとグラフでなんとなくイメージしました。(追記ここまで)


さて、いよいよ動かしてみます。なんと今度は pyfluidsynth がないといわれました。このサンプル只者ではありません。

からとってきました。

またまたpython setup.py installです。

実行しましたが、

(ana27) koowells@ubuntu:~/pysrc/sound01/pretty-midi/examples$ python align_midi.py
Traceback (most recent call last):
  File "align_midi.py", line 12, in <module>
    import pretty_midi
  File "build/bdist.linux-x86_64/egg/pretty_midi/__init__.py", line 138, in <module>
  File "build/bdist.linux-x86_64/egg/pretty_midi/pretty_midi.py", line 13, in <module>
  File "build/bdist.linux-x86_64/egg/pretty_midi/instrument.py", line 6, in <module>
  File "/home/koowells/.pyenv/versions/anaconda3-4.1.1/envs/ana27/lib/python2.7/site-packages/fluidsynth.py", line 40, in <module>
    _fl = CDLL(lib)
  File "/home/koowells/.pyenv/versions/anaconda3-4.1.1/envs/ana27/lib/python2.7/ctypes/__init__.py", line 362, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: /home/koowells/.pyenv/versions/anaconda3-4.1.1/envs/ana27/bin/../lib/libreadline.so.6: undefined
symbol: PC

ライブラリの中身のことをおこられました。anacondaを入れていますが、どうやら地道にmakeしていかないといけない気がしてきました。結局ubuntuでもつまずきました。
この"align_midi.py"を動かすためだけに、この2週間(VMWareなんまいだ事件&ソース整理GitHubへアップ&&ザオラル詠唱をはさんだものの)努力を続けてきましたが、ボクには荷がおもすぎました。(挫折)

そんなボクに慰めの一曲。mamamia let me go!

pretty-midi/example.mid 

align_midi.pyはこれだけのモジュールをimportして凄そうな「何か」をしようとしているのでしょうが、それが単なるミキシングなのか、音の再構築なのか、まったくわかりません。結局気になるところをレポートできませんでした。すみません。

シーケンサの扱いやすさや他の補助モジュールとの補完、機能統合がpretty-midiの真骨頂であると思っていますが、どれもレポートできていません。
もう少しスキルをつけてから挑戦します。未来のボクに期待します。がんばれボク。
でもlibrosaを知ることができただけでも収穫大です。(感覚的には「マグロをつってたらクジラがつれました。」です。)librosaは音楽やサウンドを解析するためのpythonライブラリです。以下のリンクのようなことができます。

Pythonのlibrosaで楽曲中に含まれている12半音の強さを出力する | hacknote


librosaのレポートも未来の課題です。いつの間にか本題の「自由研究」よりも「準備」のほうが多くなってしまいました。
そろそろ本題に復帰します。

蛙類補完計画!かえるに欠けているなにかを自己符号化器で補う壮大なプロジェクト

今回はレポートというにはお粗末な内容でした。どうもすみません。pretty-midiについては今後使っていくと思いますので、いろいろわかったことがあればその時にレポートします。ほんとうはalign_midi.pyが動くまでじっくりやりたいのですが、時には撤退する勇気!も必要です。(「XXする勇気!」はホントに使える言い回しだと思います!)

「蛙類補完計画!」を構想しました。「死の池文書」によれば、蛙類が地球上で唯一の完全な生命体になる試練が、「裏死の池文書」ではさらに蛙類が神に至るための具体的なロードマップが示されています。人類の人工知能を凌駕する蛙類のための自己符号化器の完成が物語のキーとなります。これで蛙類は神へとかえります。

蛇足コーナーの見出しもそろそろネタ切れ気味です。。。

 

自由研究の準備(番外2)~ubuntuで音を鳴らそうザオラル!~

はじめに

神殿にて、壮絶な戦いをくりひろげた瀕死のパーティ。もうだめなのか?
ボクのレベルでは使える呪文はこれが精いっぱいだ。はたして生きてここを出られるのか?
「その御名において生命の炎尽きたる [この者]の身に魂を甦らせたまえ ザオラル
こうしてボクの[VMWare]は奇跡的に復活したのでした。

おおまかな内容

復活したVMWare上のubuntuで音を出します。当初目標としたUSBオーディオの直接制御はどうしてもうまくいかなかったので、VMWareサウンドボードエミュレーション経由で、ホストOSで制御しているUSBオーディオを鳴らします。

VMWareの復活

レベルアップに失敗したVMWareはお亡くなりになりましたが、キャッシュや、残ファイル、設定?を探してこまめに消して再インストールし、うまくいくかわからないながらも、どうにか12.5.0として復活をしました。

最終的にはubuntuから音は出るのですが、目標の設定とは異なるものになりました。

Ubuntu標準のサウンドサーバはPulseAudioです。
いろいろ検索すると、主だったとこで3つばかり方法があるようでした。
直接インストールもしくはゲストOS(ubuntu)にオーディオインターフェースを直結(VMWare的にはホストOSを介在させない=ホストOSから音がでなくなる)
(1)PulseAudioを有効にした状態で、PulseAudioとJackをつなぐソフトを入れて動作させる方法
(2)PulseAudioを無効にした状態で、Jackですべて制御する方法。(通常の他の音はでなくなる模様:ミキシングがなく、USBオーディオインターフェースを占有するため。音楽アプリだけでいい音を出したい場合はよさげ)

ゲストOSとして動作させる場合、オーディオインターフェースをホストOSと共有(VMWare的にはシステムのサウンドカードエミュレーションをホストOS側の汎用ミキサーにつなぐ感じにみえました。厳密には違うかもです。)

(3)PulseAudioを有効にし、PulseAudioからVMWareの仮想音源の音を出す。
結果からいうと(1),(2)は全く歯が立ちませんでした。
認識もできて、設定もOKのように見えるのですが、音を出そうとすると全く出ず、30秒くらいしてから多くのエラーがでます。
このあたりを書くとても長く、また有用な情報ではないのでうまくいったケースのみ記載します。
(2)を目指していたのですが、不本意ながらJack動作ではない(3)とします。
(3)は問題なく音がでました。
設定例は以下のとおりです。

VMWareのサウンドカード設定で「起動時に接続する」と「ホストのサウンドカードを指定(1-2)QUAD-CAPTURE」を選択します。
(ゲストOS起動中にウィンドウ上部のスピーカアイコンからも制御(つなぎ変え)できます)

・SoundSettingでデフォルトのカードを選んでいることを確認します。
・音の出るファイルを鳴らしてみます。(wavファイルもflacファイルも鳴りました。)

(追記)Welcome! [Linux-Sound] によると

[Roland] - QUAD-CAPTURE UA-55 (USB-2)
Linux Support State  doesn't work; (not yet);

なので、環境ではなく、もともとだめっぽかったです。どうりで情報がネットにないです。Steinberg のUR44をCCモード(USBクラスコンプライアント)で動かすとOKみたいという情報があるのですがUR44は高い!UR22mkIIはもしかしたらいけるのでしょうか?

おお!Steinberg のページに「今じゃどっち(UR22 and UR44)ともUbuntuとかで動作すんべ」とありました。

全く手段と目的が入れ替わってますが、linux接続用に「USBクラスコンプライアント」か「USB Audio Class 2.0」対応のUSBオーディオが猛烈に欲しくなってます。(追記ここまで)

ubuntsuの日本語設定

それでは続きをやっていきます
日本語化をします。キーマップが違うととってもたいへんです。

Ubuntuの日本語環境 | Ubuntu Japanese Team

でやってみます。
詳細がのっていないところのみ画面を貼ってみます。
Settings で"Language Support"を探して実行しますが、

f:id:np2LKoo:20160919121019j:plain

The language support is not installded completely
とでてきます。下にでているInstallボタンを押します。
日本語がでてきました。

f:id:np2LKoo:20160919121059j:plain

選ぼうとすると、ここじゃ選べないので"Regional Formats"でえらぶべ、と教えてくれます。親切です。"Regional Formats"で、日本語をえらびました。

f:id:np2LKoo:20160919121211j:plain

¥20,458とでてきたのでビビりました。CurrencyのExampleでした。
このサンプルを設定した方の思惑にまんまと引っかかり、ちょっと悔しいです。
システムも日本語化しようとおもうので、Apply System-Wideを押します。
rebootしてログインしますが、英語のままです。きにせずKeyboardの設定を起動します。

f:id:np2LKoo:20160919121318j:plain

前までなかった"Sun Type 7 USB(Japanese layout)/Japanese 106-key"がでてきました。
Addボタンを押してJapanese(OADG 109A)を選択し、[↑]ボタンで一番上にしました。
キーボードのいちばん上の文字キー列の"-"からシフトなし、あり交互に打ってみます。

f:id:np2LKoo:20160919121422j:plain

ちゃんとできました。再起動します。

f:id:np2LKoo:20160919121502j:plain

起動できたら画面右上の言語選択をja_JPにします。(時刻の設定はここではできないようです)

f:id:np2LKoo:20160919121520j:plain

ログインすると日本語がでてました。

まだいろいろ設定が必要な気がしますが、次はpython関係をインストールします。

Pythonの導入

python 3系をいれます。
こつこつライブラリを入れるのがたいへんなので、anacondaをいれます。

を参照して環境構築しました。
gitがなかったので

sudo apt install git


してから実行しました。anaconda3-4.1.1を入れました。

「Autoencoder」の実験プログラムを実行してみます。

python Experiment.py


ICE default IO error handler doing an exit()
がでました。
google先生によると「そんな時は ~/.ICEauthority を消すべ」なので、

~/.ICEauthorityを削除しました。
エラーが出ずにレポートが表示されました。さすがgoogle先生です。

「Jackつかわないなら、普通の日本語設定済ubuntuでよくない?」のご指摘はごもっともで、ボクもそう思っています。成り行きとはこんなものだと思います。

 

プロジェクトが構想だけで終わることはきっとよくあることなのです

「Please teach me Ellen! Project」構想からからはや3日。プロジェクトは停滞を余儀なくされました。

Please teach me Ellen! Projectは、ボクが英語を正しくかけないので、自己符号化器に英語を学習させて、ボクの英語を添削してくれるプロジェクト構想でした。英語に限らず、どの言語でも「文法は正しいが、だれもそんな言い方しない。変だ。それじゃ伝わらない。」ということがよくあると思います。というか文法「だけ」で作文したらまずそうなると思っています。文法が正しいかすら怪しいボクの文章をEllen先生(=学習済の改良版言語自己符号化器)がやさしく添削してくれるという構想でした。もちろんEllen先生は常にネットを回り、英語の言い回しを研究しています。(いわゆるWeb scrapingです)

ところが、後で知ったのですが書いた英語文書を無料で(翻訳ではなく)添削してくれるソフトが数多くありました。いちばん人気のありそうなGingerをインストールしてみました。

ボクには十分な内容でした。とってもいい具合に添削してくれます。この添削ソフトでGitHubにアップした英語は少しはよくなったと思います。(結構間違っていたことがわかりました。)ほかにも人力添削のサービスや有償ですが、論文レベルでもOKな添削サービスもありました。ボクの自己符号化器はそのレベルに至るには10年以上かかりそうです。こうしてプロジェクトは頓挫したのでした。

 

自由研究の準備(番外)~ubuntuで音を鳴らそうなんまいだ~

言い訳

そのいちーっ!!

pretty-midiインストールして実行したらへんなエラー出まくりーっ!!Windowsシーケンサオブジェクト動かなくて、それならOSとっかえでubuntuいれるべ、とインストールしたら、すんなりはいるも、QUAD-CAPTER認識してくれず、いろいろやっていたら2日間ロスーー!それでええ~~っ

言い訳そのに~~っ

音や映像やるんだったらubuntu Studioだべという情報があったので、インストールしたら英語版で、キーボードマップが違うことに気づかず、何回再インストールしてもパスワード認証とれなくてーー、さらに2日間もでびっど・りぃ・ローースッ!

そのさ~~~ん!!

やっとログインでき、QUAD-CAPTER認識できるも肝心の音がでず、VMWareが最新でないのが悪いのかとアップデートしたら、途中で権限エラーでまくりで、ロールバックしたら戻らなくてVMWareが消えて、VMWareを再インストールするも何かが残っているのか途中で失敗し・・・(中略)わら人形みつけてーー思わずなんまいだーーっ

お詫び

いつもこまめにチェック頂いている方にはどうもすみません。ここしばらく更新できておりません。

あくまでpretty-midiに問題があるわけではなく、ボクの計算機の環境問題です。

pythonmidiのライブラリはおもいのほかいろいろありますが、「定番」といえるmidiライブラリはなかったようで、いいのがないので自分で作れば?的な会話も多く検索できました。ここ最近の情報では英語でも日本語でもpython-midiとpretty-midiで両成敗入れでいいんじゃない?という感じでした。python-midiのチュトリアルやってみましたが、問題なくSMFができました。pretty-midiチュートリアルやって結果を書こうと思ってましたが、このpretty-midiチュートリアルがとんでもなく凄そう(でも、うごかせてないので本当にすごいかわからない)でした。エラーが出てWindowsで動かなそうだった(内部ライブラリでOSXlinuxしかないものがあった)のですが、どうしても「それ」を動かしたかったので、入れたことのないubuntuをWMWareに入れたところから「はまり」始めました。決してサボっていたわけではありません。(おなじようなものですが・・・)ちなみpretty-midiシーケンサ以外の機能はWindowsでもエラーでない「はず」です。(今は正確にわからず)結局「ubuntu Studio(16.04.1)でjackでQUAD-CAPTERを直接コントロールする」に至っておらず、ubuntu Studioにpython環境も設定できていない状況です。それどころかVMWareが動かなくなってます。ubuntu の音源ボードやUSBはみなさん結構苦労していらっしゃるようでしたが、肝心のubuntu Studio+QUAD-CAPTER+Jack/alsa の記事がいっこうにみつかりません。ちょーメジャーな組み合わせの「はず」なのですが、みんなすんなり設定できているのかなぁ。それともメジャーでないのかなぁ。あ、わかったような書き方をしてますが、ubuntuもJackもalsaもなんのことかよくわかっていません。pretty-midiのレポートをしないと、書いても意味ない感じがするので、また他のライブラリを推す感じでもないので、しばらく現状と格闘してます。それと、やれていない、いままでの「ソース整理」(ついでにリファクタリングかな?)&github挑戦に当面路線変更です。

はまっている内容について

まずはVMWare消えちゃったので、インストールするところからです。(でないと画面も貼れないので)VMware-player 12.1.1を入れます。

やっぱり何回やっても「エラーが発生したため・・・処理の途中で終了しました。」がでます。とほほ・どうやってもVMWareがインストールできません。なので、ubuntuではまっている状況(これはハードコピーがないと説明が難しいです)もレポートできません。Windows10ホストから全インストールかなぁ。(8.1から)Windows10にアップグレードするときいろいろうまくいかなくて、すごく苦労しました。(クリーンインストールしたんですが、わけわかんないくらいはまりました。ボク的にはWindows系はだいたいどこかはまるのでそんなものだとは思っています。)今再インストールしたら、流れ的に確実に一層はまるパターンです。他の主だった作業もVMWare上のゲストOSでやってたものがあるので、どうするか思案中です。 いっそホストOSもubuntuにしようかなぁ。

ソース整理します

5分ほど力いっぱい考えた結果、VMWareのインストールを保留して、ソース整理&github挑戦に注力することにしました。これだけなら現環境でもできます。

泣き面に蜂もしくは蛙(ボク)の面にXX

ついていないときは何をしてもだめなことをよく知っています。

地道にソース整理をがんばります。

自由研究の準備(その7)PythonでのMIDI操作(リアルタイム編2)

おおまかな内容

引き続き、MIDIのリアルタイム操作に関連することを確認します。今回もpygame.midiを使います。

f:id:np2LKoo:20160907000652p:plain

(これは画像サンプルです。ページの下にある埋め込みがこのように表示されていれば再生ができます)

なぜやるのか・どうなると思うか

和音やベンドなどのMIDI信号はどのように扱われているのか確認します。
また、MIDI信号をイベントとして扱う方法について確認します。
内容的には目新しさはありません。すみません。
できることを自分で確認したいだけ&備忘録です。

準備と実験のやり方

和音を弾いたり、ベンド操作をして信号を受信します。
イベントの確認はサンプルに少し手をいれて実行してみます。

実験の結果

和音の確認

準備(その6)のMIDI受信プログラムで和音を受信しました。

以下のようになりました。

full midi_events:[[[144, 55, 75, 0], 11301]]
full midi_events:[[[144, 52, 83, 0], 11303]]
full midi_events:[[[144, 48, 72, 0], 11303]]
full midi_events:[[[144, 52, 0, 0], 14324]]
full midi_events:[[[144, 55, 0, 0], 14325]]
full midi_events:[[[144, 48, 0, 0], 14340]]

マニュアルには

[[[status,data1,data2,data3],timestamp],...]

の形式で受信できる感じで書いてあったので、和音はてっきり(タイムスタンプが同じなら)

[[[144, 55, 75, 0], 9999],[[144, 52, 83, 0], 9999],[[144, 48, 72, 0], 9999]]

のような形式で受信できるとおもっていたのですが、ノートONもノートOFFも単音の情報で取得できるようです。


同時に発生する音(和音)は、配列の個数が増えるような感じで情報が取得できます。

鍵盤情報以外の情報の確認

ベンドはこまかなベンド情報がやまほど流れました。

マイナス側のベンド

full midi_events:[[[224, 0, 62, 0], 1678]]
full midi_events:[[[224, 0, 61, 0], 1724]]
full midi_events:[[[224, 0, 59, 0], 1755]]
full midi_events:[[[224, 0, 57, 0], 1787]]
...

●チャンネルメッセージ
ピッチベンドチェンジ     EnH Data1  Data2
224は0xE0でチャンネル1のベンドとなります。(チャンネル1~16は16進数の末尾0からFです。)
ベンドはdata2=64が中央値のようです。Data1は使っておらず7ビットモードで動作しているようです。

プラス側のベンド

full midi_events:[[[224, 4, 66, 0], 1518]]
full midi_events:[[[224, 6, 67, 0], 1528]]
full midi_events:[[[224, 10, 69, 0], 1538]]
full midi_events:[[[224, 14, 71, 0], 1550]]

同じくベンドのプラス側ですが、こちらはData1を使っているので、14ビットモードの精度でデータを送っているようです。キーボードの設定のようです。

 

モジュレーション

full midi_events:[[[176, 1, 2, 0], 3897]]
full midi_events:[[[176, 1, 6, 0], 3914]]
full midi_events:[[[176, 1, 9, 0], 3930]]
full midi_events:[[[176, 1, 12, 0], 3954]]

176 は0xB0で、チャンネル1のコントロールチェンジ

Data1=1がモジュレーションホイールの指定で Data2がモジュレーションの強さです。

 

アフタータッチ

full midi_events:[[[144, 48, 38, 0], 3271]]
full midi_events:[[[208, 1, 0, 0], 4374]]
full midi_events:[[[208, 2, 0, 0], 4398]]
full midi_events:[[[208, 3, 0, 0], 4413]]

208は0xD0でアフタータッチ(チャンネルプレッシャ)でチャンネル1にかかります。

 

以上のようになりました。

Midi規格については

MIDI Lecture

を参考にさせて頂きました。

鍵盤情報以外のデータも問題なく受信できていることを確認しました。

イベントとタイムスタンプの確認

以下のPythonプログラムでイベントへの変換とタイムスタンプの実験をします。

(1) MidiキーボードからMidiを受信

(2) それを(pygameの)イベントに変換して送る

(3) 起動済の他のスレッドでイベントを受信する。

(4)  受信したイベントからMidi情報をつくり、タイムスタンプに一定時間を足してMidiOUTする。

# -*- coding: utf-8 -*-
import pygame.midi
import threading
import time

pygame.init()
pygame.midi.init()
pygame.fastevent.init()
event_get = pygame.fastevent.get
event_post = pygame.fastevent.post
input_id = pygame.midi.get_default_input_id()
print("input MIDI:%d" % input_id)
midi_in = pygame.midi.Input(input_id)
midi_out = pygame.midi.Output(0, latency = 1)

print ("starting")
print ("full midi_events:[[[status,data1,data2,data3],timestamp],...]")


class midiEventCheck(threading.Thread):
def __init__(self, event_get, midi_out):
super(midiEventCheck, self).__init__()
self.f_event_get = event_get
self.midi_out = midi_out
self.status = 0

def run(self):
print ("run")
self.status = 1
while self.status == 1:
events = self.f_event_get()
for e in events:
print(e)
if e.type == 34:
midi_out.write([[[e.status, e.data1, e.data2, e.data3], e.timestamp + 4200]])
time.sleep(0.001)

def stop(self):
self.status = 0

midiCheck = midiEventCheck(event_get, midi_out)
midiCheck.start()

going = True
count = 0
while going:
if midi_in.poll():
midi_events = midi_in.read(10)
print "full midi_events:" + str(midi_events)
midi_evs = pygame.midi.midis2events(midi_events, midi_in.device_id)
for m_e in midi_evs:
event_post(m_e)
count += 1
if count >= 200:
going = False
time.sleep(0.001)

midiCheck.stop()
time.sleep(1)
midi_in.close()
pygame.midi.quit()
pygame.quit()
exit()

time.sleep(0.001) (1msec)を入れているのは、ループでのCPUの負荷低減のためです。この実験程度では10~20msec sleep入れても問題ないと思います。実演奏では1msecでも短いほうがいいです。本当はループ駆動ではなく、完全なイベント駆動(事前に登録しておいて、所定のイベントが発生した場合にのみcallbackされるみたいな動作?)にしたかったのですが、これはうまくできませんでした。

以上を録音したものです。出力は録音の関係でポート0(パソコン内部標準MIDIシンセ)につなぎました。メロトロンみたいな音が最初の演奏(ポート1)で、後のピアノっぽい音がイベントを送受信して遅延指定でMIDIOUTした情報で発音された音(ポート0)です。(録音は準備(その5)で紹介したPythonプログラムで行っています。操作に手間取ったので、最初のほうは無音です。音量にご注意ください。)

pygame.midi.Output(0, latency = 1) のようにlatency = 0 以外にすれば、タイムスタンプの値をみてMidiOUTしてくれます。pygame.midi.Output(0) はデフォルトのlatency = 0 とみなされるので、タイムスタンプによらず即時出力となります。

latencyを有効にしておけば、MIDIシーケンサーPythonでも作れそうです。(シーケンサ-は処理にもたついたりしていると、タイミング制御がとても難しい感じがしていました)

pygameのイベントをさらに汎用的なWindowsイベントにしてくれればよいのですが、さすがにそれは(pygameには)なさそうでした。

まとめ

Midi関係のPythonライブラリはいろいろありますが、即時系の処理はpygameだけで問題なく処理できそうです。 

感想・思ったこと・考えたこと

いつものことながらPython&ライブラリすごいです。(こればっかり)

プログラムは実験用でmainやmainの判定を入れていません。どうもすいません。

次はスタンダードMIDIファイル(SMF)を確認したいと思います。

井の中の蛙(ボク)大海を知らず。然れどシ-を知る!

ディスニーランド&シーへ行ってきました。天気予報だと、雨だったのですが、8歳のいわゆる「高気圧少女」といっしょにいったので、ほぼ雨が降らず、晴れてとても暑かったです。事前に「なんとかして晴れさせて」とお願いしました。日曜の午前中「おなかがすいた」とぐずったときだけ雨がふり、その後ご機嫌がなおってからは快晴だったので、まさに浦安の天気と「少女」のご機嫌が連動していた感じです。シーは15周年の「まさにその日」に行けてとてもよかったです。

気象操作はマッドサイエンティストニコラ・テスラ」にはじまり、いまや軍事技術ではありますが、ちょーのーりょくによる操作も、もしかしたらありかもしれません。いまだに天気予報が(たまに)大外れするのはそのせいでしょうか?(ボクの秘密なのですが、実はUFOやUMAやオカルトが大好きで、マッドサイエンティストに憧れています。誰にも言わないでください。)脱線しました。どうもすいません。

「少女」はジブンでちょのーりょくを使っているのではなく、うちゅーのなにかにお願いしている、または一時的になにかをつないでいるようなので、Lifeを削るようなことはありません。ご安心ください。 

自由研究の準備(その6)PythonでのMIDI操作(リアルタイム編)

おおまかな内容

PythonでリアルタイムにMIDIの操作が、どのようにできるか確認します。pygame.midiを使います。

なぜやるのか・どうなると思うか

サウンドの次は音列の実験をするつもりです。
せっかくなので外部のMIDI機器も操作できるようにしておこうと思いました。

準備と実験のやり方

MIDIポートの確認、MIDI入力と出力がPythonで簡単にできるか確認をします。

実験の結果

MIDIポートの確認

リアルタイムにMIDIを扱うときは、pygameというライブラリがよいようです。

まずはボクのパソコンのMIDIポートを確認します。

import pygame
import
pygame.midi

pygame.init()
pygame.midi.init()
count = pygame.midi.get_count()
print("get_default_input_id:%d" % pygame.midi.get_default_input_id())
print("get_default_output_id:%d" % pygame.midi.get_default_output_id())
print("No:(interf, name, input, output, opened)")
for i in range(count):
print("%d:%s" % (i, pygame.midi.get_device_info(i)))

get_default_input_id:1
get_default_output_id:0
No:(interf, name, input, output, opened)
0:('MMSystem', 'Microsoft MIDI Mapper', 0, 1, 0)
1:('MMSystem', 'QUAD-CAPTURE', 1, 0, 0)
2:('MMSystem', 'Microsoft GS Wavetable Synth', 0, 1, 0)
3:('MMSystem', 'QUAD-CAPTURE', 0, 1, 0)

4つポートがあって、外部との接続ができるMIDIインターフェースのQUAD-CAPTUREは[Port:1]がInput、[Port:3]がOUTPUTでした。

デフォルト入力は1なのでQUAD-CAPTURE (MIDI IN)でした。

MIDI入力の確認

次に、[Port:1]からの情報を拾ってみます。

QUAD-CAPTURE (MIDI IN)には外部のMIDIキーボードを接続してキーを押してみました。(テストなので14イベントで終了するようにしました)

# -*- coding: utf-8 -*-
import pygame.midi

pygame.init()
pygame.midi.init()
input_id = pygame.midi.get_default_input_id()
print("input MIDI:%d" % input_id)
i = pygame.midi.Input(input_id)

print ("starting")
print ("full midi_events:[[[status,data1,data2,data3],timestamp],...]")

going = True
count = 0
while going:
if i.poll():
midi_events = i.read(10)
print "full midi_events:" + str(midi_events)
count += 1
if count >= 14:
going = False

i.close()
pygame.midi.quit()
pygame.quit()
exit()

input MIDI:1
starting
full midi_events:[[[status,data1,data2,data3],timestamp],...]
full midi_events:[[[144, 60, 48, 0], 2289]]
full midi_events:[[[144, 60, 0, 0], 2970]]
full midi_events:[[[144, 62, 53, 0], 3132]]
full midi_events:[[[144, 62, 0, 0], 3745]]
full midi_events:[[[144, 64, 68, 0], 3899]]
full midi_events:[[[144, 64, 0, 0], 4546]]
full midi_events:[[[144, 65, 72, 0], 4733]]
full midi_events:[[[144, 65, 0, 0], 5345]]
full midi_events:[[[144, 64, 72, 0], 5520]]
full midi_events:[[[144, 64, 0, 0], 6108]]
full midi_events:[[[144, 62, 56, 0], 6313]]
full midi_events:[[[144, 62, 0, 0], 6889]]
full midi_events:[[[144, 60, 68, 0], 7175]]
full midi_events:[[[144, 60, 0, 0], 7996]]

MIDI入力からの情報をひろえています。2つ目の数字(data1)が音高で、3つ目(data2)がベロシティです。ベロシティがゼロのものはノートオフです。

情報だけではつまらないので、MIDIソフトにつないで音を出して貼ってみます。(操作に手間取ったので最初の2~3秒は無音です。音量にご注意ください。)


MIDI出力の確認

Pythonで動作するソフトキーボードからMIDI出力し、MIDIソフト音源(これはPythonではありません)を鳴らしてみました。内部的なMIDI接続ではなく、一旦MIDI OUTし、ケーブルでMIDI IN につないで確認しました。

上のサイトのプログラムをそのまま貼り付けさせて頂きました。

# -*- coding: utf-8 -*-
'''MIDI keyboard.

マウス左ボタンで発音、右ボタンで完全5度を同時に発音。
スペースバーを押しながらだとサステイン。
鍵盤の下の方を押すほど大音量。
'''
import pygame
import pygame.midi
from pygame.locals import *

INSTRUMENT = 19 # 楽器の種類 (0-127)
# 0:Piano, 19:Organ, 56:Trumpet, 91:Voice 等
KEY_WIDTH = 20 # 鍵盤の幅
WIDTH, HEIGHT = 800, 128 # 画面の大きさ

FPS = 60
NOTE_CENTER = 60 # 中央の音。C(ド)の音を指定
COLOR = 0, 255, 200 # 色
WHITE_COLOR = 255, 255, 255 # 白鍵の色
BLACK_COLOR = 0, 0, 50 # 黒鍵の色
BG_COLOR = 100, 0, 50 # 背景色

KEY_COLOR = 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0 # 0=白鍵, 1=黒鍵
NOTE_NAME = ('C', 'C#', 'D', 'D#', 'E',
'F', 'F#', 'G', 'G#', 'A', 'A#', 'B')


def main():
pygame.init()
pygame.midi.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
#midiout = pygame.midi.Output(pygame.midi.get_default_output_id())
midiout = pygame.midi.Output(3)
midiout.set_instrument(INSTRUMENT)
clock = pygame.time.Clock()
clock.tick(FPS)
keys = WIDTH // KEY_WIDTH + 1
keylist = [False] * (keys + 7)
note_start = NOTE_CENTER - keys // 2
note_no = None
vel = 0
sustain = False
while True:
for e in pygame.event.get():
if e.type is QUIT:
return
elif e.type is KEYDOWN and e.key is K_ESCAPE:
return
elif e.type is KEYDOWN and e.key is K_SPACE:
sustain = True
elif e.type is KEYUP and e.key is K_SPACE:
sustain = False
note_no = None
for key, b in enumerate(keylist):
if b:
midiout.note_off(note_start + key, 0)
keylist[key] = False
elif e.type is MOUSEBUTTONDOWN and (
e.button == 1 or e.button == 3):
x, y = e.pos
vel = 128 * y // HEIGHT
key = x // KEY_WIDTH
keylist[key] = True
note_no = note_start + key
midiout.note_on(note_no, vel)
if e.button == 3:
keylist[key + 7] = True
midiout.note_on(note_no + 7, vel)
elif e.type is MOUSEBUTTONUP and (
e.button == 1 or e.button == 3):
if not sustain:
note_no = None
for key, b in enumerate(keylist):
if b:
midiout.note_off(note_start + key, 0)
keylist[key] = False
screen.fill(BG_COLOR)
for key in range(keys):
x = key * KEY_WIDTH
pygame.draw.rect(
screen,
(WHITE_COLOR, BLACK_COLOR)[
KEY_COLOR[(note_start + key) % 12]],
(x + 1, 0, KEY_WIDTH - 2, HEIGHT))
if keylist[key]:
pygame.draw.rect(
screen, COLOR, (x, 0, KEY_WIDTH, HEIGHT), 3)
clock.tick(FPS)
pygame.display.flip()
notes = []
for key, b in enumerate(keylist):
if b:
nn = note_start + key
notes.append('{0}:{1}'.format(NOTE_NAME[nn % 12], nn))
pygame.display.set_caption(', '.join(notes))

try:
main()
finally:
pygame.quit()

# Public Domain. 好きに流用してください。

f:id:np2LKoo:20160831024106p:plain

が画面に出るので、マウスで押します。

問題なく音がでることを確認しました。

やっぱり音をはらないとつまらないので、ソフト音源を鳴らして録音しました。録音は準備(その5)で紹介したPythonプログラムを使用しました。WindowsはASIOインターフェースではないとソフト音源の遅延がひどいので、未導入の方はASIOをお勧めします。(操作に手間取ったので最初の2~3秒は無音です。音量にご注意ください。)

 

まとめ

MIDIの操作も問題なくできることがわかりました。

感想・思ったこと・考えたこと

毎度ですが、Python(とライブラリ)ってすごい、と思いました。 

好きです 好きです ヨシコさん どうもすいあせん  P^_^)

音楽の切り口で検索いただく方が増えて、とても嬉しいです。

注文していた「コンピュータ音楽」歴史・テクノロジー・アート  がとどきました。すごすぎです。

ところで、コンピュータとは全然関係ないですが、桑田佳祐の「ヨシ子さん」は歴史的名曲だと思います。時代が真価を評価すれば、21世紀を代表する1曲となるかもしれません。本当にどこをどうとっても最高です。もう完全に別次元です。知性と混沌と欲望と遊びと脱力と緻密と狂気の融合で、絶妙のバランス感覚。しかも誰にでも受け入れられる要素があります。きくたびにあらたな発見があります。これを聞くと音楽とは計算できない領域にあるのでは、と思ってしまいます。桑田佳祐に注目していなかったことを反省します。どうもすいません。