Toshusai blog

知識の保管庫

Python3でマイクから録音して音声をスペクトル表示する

Python3でマイクから録音して音声をスペクトル表示する

http://toshusai.hatenablog.com/entry/2017/11/10/224330
この記事で波形データを取ったが、今回はフーリエ変換してスペクトラム表示したかった。

マイクから音声を録音する

https://www.ningendesu.net/blog/entry/655 こちらの記事にとても分かりやすく書いてある。(Naruto Ishikawaさんに感謝)

# -*- coding: utf-8 -*-
import pyaudio
import wave

FORMAT = pyaudio.paInt16
CHANNELS = 1        #モノラル
RATE = 44100        #サンプルレート
CHUNK = 2**11       #データ点数
RECORD_SECONDS = 10 #録音する時間の長さ
WAVE_OUTPUT_FILENAME = "file.wav"

audio = pyaudio.PyAudio()

stream = audio.open(format=FORMAT, channels=CHANNELS,
        rate=RATE, input=True,
        input_device_index=4,   #デバイスのインデックス番号
        frames_per_buffer=CHUNK)
print ("recording...")

frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)
print ("finished recording")

stream.stop_stream()
stream.close()
audio.terminate()

waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()

スペクトラム表示する

たたフーリエ変換するだけ。
https://www.ningendesu.net/blog/entry/668
またNaruto Ishikawaさんの記事を参考にするが、こちらではpyqtgraphというライブラリを使ってリアルタイム表示しているが、Tkinterという標準ライブラリを使いたかった。
こちらの記事と違う部分はscipyをnumpyに、pyqtgraphをTkinterに、リアルタイムから非リアルタイムになっただけ。
任意の時間マイク入力を受け付けて、その後Tkinterのキャンバスでスペクトラム表示(厳密にはちょっと違うっぽい)される。

import tkinter
~~~~
root = tkinter.Tk()
canvas = tkinter.Canvas(root, width = 2048, height = 300)
canvas.pack()
~~~~
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    buf = stream.read(CHUNK)
    frames.append(buf)
    data = np.fft.fft(np.frombuffer(buf, dtype="int16"))#追加
    dframes.append(data.real)#追加
~~~
for i in range(len(dframes)):
    canvas.delete("all")
    for j in range(len(dframes[i])):
        canvas.create_line(j, 150, j, 150 + dframes[i][j] / 1000)#そのままだと大きすぎるので1000で割る
    canvas.update()
    if(i == len(dframes) - 1):
        root.destroy()

 root.mainloop()

参考

Python3でwavファイルから24fpsの波形データをとる

Python3でwavファイルから24fpsの波形データをとる

音の波形データを動画(24fps)に合わせて取りたかったのでやってみる。

wavファイルの読み込み

Pythonには標準ライブラリでwavファイルを扱うwavというものが用意されている。

wave.open(file, mode=None) file が文字列ならその名前のファイルを開き、そうでないなら file like オブジェクトとして扱います。 mode は以下のうちのいずれかです:
'rb'
読み出しのみのモード。
'wb'
書き込みのみのモード。
Wave_read.getnframes()
オーディオフレーム数を返します。
Wave_read.readframes(n)
最大 n 個のオーディオフレームを読み込んで、 bytes オブジェクトとして返します。

bytes(バイナリデータ)でしか取れないのでnumpy等で変換する。

import wave
import numpy

wf = wave.open([filename], "r")
buf = wf.readframes(wf.getnframes())
data = np.frombuffer(buf, dtype = "int16")

24fpsでデータを取る

大体の音声は44100Hzで動画に使うには使いづらいので24fpsにしたい。

Wave_read.getframerate()
サンプリングレートを返します。

サンプリングレートを24で割った値ごとにデータを取っていけば24fpsにできる。 しかし、バイナリから変換した配列の長さは総オーディオフレームの2倍ある(右と左の分)ので、その2倍ごとに値を取る必要がある。

import wave
import numpy

wf = wave.open([filename], "r")
buf = wf.readframes(wf.getnframes())
data = np.frombuffer(buf, dtype = "int16")
frame = wf.getnframe()#総フレーム
rate = wf.getframerate()#サンプリングレート
vframe = int(rate / 24)#1837.5
vdata = []
for i in range(int(frame / vframe)):
    vdata.append(data[i * vframe * 2])
print(vdata)
"""
[289, 6628, 3149, -3613, 4529, -932, -2145, ...省略...
"""

参考

Unityでボタンに引数付きのイベントをスクリプトから設定する

Unityでボタンに引数付きのイベントをスクリプトから設定する

prefabとかでスクリプトからボタンを作るときに、ボタンごとに違うOnClick()イベントを渡したい。バージョンはUnity2017.2.0f2を使った。

ボタンにイベントをスクリプトから設定する

Start()下2行目
button.GetComponent<Button>().onClick.AddListener(Hello);
スクリプトから設定されたイベントはエディタ上のButtonコンポーネントに表示されないので注意。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour {

    public GameObject canvas;//エディタから指定
    public GameObject prefab;//エディタから指定

    void Start () {
        GameObject button = Instantiate(prefab, canvas.transform);
        button.GetComponent<Button>().onClick.AddListener(Hello);
    }

    void Hello(){
        Debug.Log ("Hello world!");
    }
}

ボタンに引数付きのイベントをスクリプトから設定する

引数付きのイベントを渡すにはラムダ式を使う必要がある。 しかし、注意すべきはラムダ式で使う変数を変更した場合、全てのラムダ式に反映されてしまう現象。 ラムダ式はその変数へのポインタをメモリに入れているのが原因らしい。 そこで一時変数を宣言してラムダ式に使う事で、ボタン毎に異なるイベントを設定することができる。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour {

    public GameObject canvas;//エディタから指定
    public GameObject prefab;//エディタから指定

    void Start () {
        for(int i = 0; i < 5; i++){
            GameObject button = Instantiate(prefab, canvas.transform);
            string tmp = i.ToString ();//一時変数
            button.GetComponent<Button>().onClick.AddListener(() => {Hello(tmp);});
        }
    }
    
    void Hello(string str){
        Debug.Log ("Hello " + str + "!");
    }
}

もし、次のように書いてしまったら、全てのボタンはHello 5!とコンソールに出力するイベントを持つ事になる。

button.GetComponent<Button>().onClick.AddListener(() => {Hello(i.ToString());});

参考

PHPでSQLite3を使う

PHPでSQLite3を使う

SQLite3とは、簡単に言えばサーバーを必要としないデータベースのこと。
(PHP 5 >= 5.3.0, PHP 7)で使える。

インストール

apt-get install php5-sqlite

データベースにアクセスする

ファイルがなければ作られる。

<?php
$db = new SQLite3(filename);
?>

クエリを実行する

public bool SQLite3::exec ( string $query )でクエリを実行できる。
成功したらtrue、失敗したらfalseが返る。

<?php
$db->exec("CREATE TABLE fruits (id, name)");
$db->exec("INSERT INTO fruits VALUES (1, 'apple')");
?>

SELECT文を実行する

execでSELECT文を実行してもデータは抜き出せないので、SELECT文を使いたい時は、
public SQLite3Result SQLite3::query ( string $query )
SQLIte3Resultから抜き出したカラム(行)を得るには、
public array SQLite3Result::fetchArray ([ int $mode = SQLITE3_BOTH ] )
引数の$modeによって得られる配列の形が変わる。
SQLITE3_ASSOC:文字列のインデックス(カラム名)
SQLITE3_NUM:数字のインデックス(0から始まる)
SQLITE3_BOTH:文字列、数字、両方

<?php
$res = $db->query("SELECT * FROM fruits");
var_dump($res->fetchArray());
/*
array(4) {
  [0]=>
  int(1)
  ["id"]=>
  int(3)
  [1]=>
  string(4) "apple"
  ["name"]=>
  string(4) "apple"
}
*/
?>

SELECTしたデータを扱う

実はfetchArray()で取れるのは1行だけなので、SELECTした行を順に取るには、while文で繰り返す必要がある。

<?php
$db->exec("INSERT INTO fruits VALUES (2, 'banana')");
$db->exec("INSERT INTO fruits VALUES (3, 'orange')");
$res = $db->query("SELECT * FROM fruits");
while($row = $res->fetchArray()){
    print($row["id"] . ", " . $row["name"] . "\n");
}
/*
1, apple
2, banana
3, orange
*/
?>

fetchArray()とwhile文について

fetchArray()は1行だけ取り、呼び出す度に次の行を取ってくる。 全部の行を取り終わったらfalseを返す。
そのためwhileが止まる。
気になったので実際に挙動を確認した。出力は一部省略してある。

<?php
$res = $db->query("SELECT * FROM fruits");
for($i = 0; $i < 5; $i++){
    var_dump($array = $res->fetchArray());
}
/*
array(4) {
  ["name"]=>
  string(4) "apple"
}
array(4) {
  string(3) "banana"
}
array(4) {
  ["name"]=>
  string(7) "orange"
}
bool(false)    <<<<これ
array(4) {
  ["name"]=>
  string(4) "apple"
}
*/
?>

ちょっと見にくいが4回目でfalseが返ってきて、5回目でまた始めに戻った。

C#で日付を扱う

C#で日付を扱う

System名前空間にあるDateTime構造体で日付を扱うことができる。

using System

コンストラク

引数は年月日が最低必要、時間分秒ミリ秒まで初期化できる。デフォルトでは0。

DateTime date = new DateTime(int year, int month, int day);

今の時間を取得する

DateTime date = DateTime.Now;

年、月、日、曜日、時間、分、秒、ミリ秒、日付を取得する。

曜日の型はDayOfWeek、日付はDateTime、それ以外はInt32。.ToString()で変換しなくても標準出力できるっぽい。

DateTime date = DateTime.Now;
Console.WriteLine(date.Year);       //2017
Console.WriteLine(date.Month);      //11
Console.WriteLine(date.Day);        //6
Console.WriteLine(date.DayOfWeek);  //Monday
Console.WriteLine(date.Hour);       //20
Console.WriteLine(date.Minute);     //50
Console.WriteLine(date.Second);     //18
Console.WriteLine(date.Millisecond);//850
Console.WriteLine(date.Date);       //11/6/2017 12:00:00 AM

日付を使った計算をする

AddDays(double value)で日付を足したりできる、引数を負にすれば引くこともできる。doubleで1.5は1日と12時間を意味する。 似たような構造体のTimeSpanを使えば演算子も使って計算できる。

DateTime date = new DateTime(2017, 11, 6);
TimeSpan span = new TimeSpan(10, 5, 20);
Console.WriteLine(date.AddDays(1.123)); //11/7/2017 7:42:14 AM
Console.WriteLine(date.Add(span));      //11/6/2017 10:05:20 AM
Console.WriteLine(date - span);         //11/5/2017 1:54:40 PM

参考