2019/11/10

sox equalizer

目的


soxのequalizerのヘルプに書かれている [q|o|h|k] の意味がわからないので、調べてみます。
$ sox --help-effect equalizer
sox:      SoX v14.4.2

Effect usage:

equalizer frequency width[q|o|h|k] gain
幅の単位であることは読み取れます。

調査に使ったコード


調査には https://git.code.sf.net/p/sox/code を利用しました。 コミットのハッシュ値は 2f6b3fec2dddfbb869a9f7de3110c9aaa31517c9 (Date: Wed Aug 1 23:45:59 2018 -0400) でした。

結果


hはHz、qはHzの逆数に操作する周波数を掛けた値であることがわかりました。また、oはオクターブっぽく、kはよくわからないこともわかりました。

記載箇所


equalizerは src/biquads.c に記述されています。

オプションの解析部分を追っていくと、

q = width_Q
o = width_bw_oct
h = width_bw_Hz
k = width_bw_kHz
であることがわかります。

このファイル内に、これらを使っている箇所があります。

static int start(sox_effect_t * effp)
{
  ...
  if (p->width) switch (p->width_type) {
    case width_slope:
      alpha = sin(w0)/2 * sqrt((A + 1/A)*(1/p->width - 1) + 2);
      break;

    case width_Q:
      alpha = sin(w0)/(2*p->width);
      break;

    case width_bw_oct:
      alpha = sin(w0)*sinh(log(2.)/2 * p->width * w0/sin(w0));
      break;

    case width_bw_Hz:
      alpha = sin(w0)/(2*p->fc/p->width);
      break;

    case width_bw_kHz: assert(0); /* Shouldn't get here */

    case width_bw_old:
      alpha = tan(M_PI * p->width / effp->in_signal.rate);
      break;
  }
  ...
width_Qはwidth_bw_Hzとは逆に動いているので、qを大きくすると幅が狭くなるようです。

width_bw_kHzについては、assert(0)では止まらないので、はwidth_bw_old扱いになるようです。 全然kHzではありませんね。

参考までに、pの型はbiquad_tで、定義は、src/biquad.h より、

typedef struct {
  double gain;             /* For EQ filters */
  double fc;               /* Centre/corner/cutoff frequency */
  double width;            /* Filter width; interpreted as per width_type */
  width_t width_type;

  filter_t filter_type;

  double b0, b1, b2;       /* Filter coefficients */
  double a0, a1, a2;       /* Filter coefficients */

  sox_sample_t i1, i2;     /* Filter memory */
  double      o1, o2;      /* Filter memory */
} biquad_t;
です。

2019/08/31

Debian10のPCのルータ化

目的


Debian10 (Buster) をインストールしたPCをルータにします。

インターネット側のネットワークインターフェイスenxと、内部ネットワーク側のネットワークインターフェイスeniがあるとして説明します。

[内部PC1]┬(eni)[ルータ化するPC](enx)─[インターネット]
[内部PC2]┘
なお、試行錯誤したあとにまとめているため、記載通りでは動かない可能性があります。

おおまかな手順


おおまかな作業手順は次のとおりです。
  • IPアドレスの固定
  • isc-dhcp-server (DHCPサーバ)のインストールと設定
  • nftables (NAT用)のインストールと設定
  • bind9 (DNSサーバ)のインストールと設定
以下の作業ではすべてルートで行うか、sudoで実行してください。

IPアドレスの固定


Debian10ではNetworkManagerがデフォルトで動作しています。そのため、nmcliを使って、eniのIPアドレスの固定します。 残念なことにGUIを使った設定では、いずれか1つのネットワークインターフェイスしか有効にできないようです。

nmcliの解説はhttps://go-journey.club/archives/4124が参考になります。

有線LANの場合、作業の開始前にLANケーブルを接続しておくことをお薦めします。 接続がないとSTATEに利用不可と表示され、設定した結果を確認できなくなります。

最初に、ネットワークインターフェイスをNetworkManagerから見えるようにします。

$ nmcli connection add type ethernet ifname eni con-name eni
次に、eniのIPアドレスを指定します。
$ nmcli connection modify eni ipv4.method manual ipv4.addresses 10.0.0.1/27
しばらくすると、IPアドレスが固定され、ネットワーク接続が完了します。

ネットワークの状態は

$ nmcli device
で確認できます。

DHCPサーバの設定


isc-dhcp-serverをインストールします。
$ apt install isc-dhcp-server
isc-dhcl-serverの設定ファイルは /etc/dhcp/dhcpd.conf にあります。

そこに次の内容を追記します。

subnet 10.0.0.0 netmask 255.255.255.224 {
  range 10.0.0.10 10.0.0.20;
  option routers 10.0.0.1;
  option domain-name-servers 10.0.0.1;
}
IPアドレスは適宜変更してください。この設定で配布されるIPアドレスは、10.0.0.10〜10.0.0.20の範囲です。

最後に、DHCPサービスを再起動します。

$ systemctl restart isc-dhcp-server
これで内部PCにIPアドレスが配布されます。

NATの設定


ルータとして動作するようにするために、まず、 /etc/sysctl.conf に
  net.ipv4.ip_forward=1
を追記します。コメントアウトされた状態で記載されているので、コメントを外すだけです。

この設定を次のコマンドを実行することで有効にします。

$ sysctl --system
有効になっているかは
$ cat /proc/sys/net/ipv4/ip_forward
で確認できます。出力が1になっていれば、OKです。

次に、NATを有効にします。NATはファイアウォールの機能の一部ですので、Debian10から標準となったnftablesを用います。

$ apt install nftables
でインストールし、/etc/nftables.conf に次の内容を追加します。
table ip nat {
  chain prerouting {
    type nat hook prerouting priority 0;
  }
  chain postrouting {
    type nat hook postrouting priority 100;
    oifname "enx" masquerade
  }
}
追記できればサービスを有効化し、実行します。
$ systemctl enable nftables.service
$ systemctl restart nftables
クライアントから8.8.8.8にpingが通るようになっていれば成功です。

DNSサーバの設定


BIND9をインストールするだけです。
$ apt install bind9
https://www.clear-code.com/blog/2010/11/19.htmlが参考になります。

もし、クライアントのブラウザからドメイン名を使ったアクセスができない場合は、

$ systemctl restart isc-dhcp-server
で、DHCPサービスを再起動してみてください。

以上で、ルータが完成です。セキュリティー関連の設定は別途実施してください。

2019/08/25

Debian10でのデスクトップ環境の変更

Debian10は、インストール時にデスクトップ環境を複数インストールすることができます。

例えば、GNOMEとCinnamonの2つをインストールすると、ログイン時にどちらの環境でログインするかを選択することができます。 この2つをインストールしておくと、デフォルトではGNOMEが選択されるようになっています。

デスクトップ環境をGNOMEからCinnamonに変更するには、ログイン画面のパスワード欄の下にある、小さいギアのアイコンのボタンを押します。 すると、選択できるデスクトップ環境の一覧が表示されるので、使いたい環境を選択します。

その後、ログインすると、選択した環境で作業できるようになります。

ボタンの場所は記事の図を参考にしてください。
https://computingforgeeks.com/how-to-install-cinnamon-desktop-en\ vironment-on-debian/
具体的には、 install-cinnamon-deb10-03.pngです。

Debian10でWacomタブレットを使う

現象


Debian10を標準でインストールし、Wacomのタブレット(CTH-480)をつなげ、ペンで操作していると、マウスカーソルが頻繁に消失します。 また、タブレット操作とペン操作のそれぞれに対してマウスカーソルが表示されるという症状も出ていました。

原因


Waylandがタブレットを正しく処理できていないことが原因のようです。

解決策


/etc/gdm3/daemon.conf でコメントアウトされている次の行のコメントを外して有効にします。
WaylandEnable=false
その後、再起動すると、Waylandの代わりに従来のXが使われるようになります。

Xは正しくペンタブレットを処理できるようで、マウスカーソルが消えたり2つになったりする現象はなくなりました。

WaylandをOFFにする方法は、
https://linuxfan.info/ubuntu-gdm-wayland-false
を参考にしました。

2019/07/27

電気いらずの脱塩装置

太陽光だけで海水を真水にできる装置が発表されました。

https://www.monash.edu/news/articles/water-solutions-without-a-grain-of-salt
https://www.newsweekjapan.jp/stories/world/2019/07/post-12625.php

円盤状の濾紙の中央に水を吸い上げて、太陽光で蒸発させます。
これを集めれば真水になりますね。

水に溶けていた塩は円盤の外側へと排出されます。

もこもこ塩が湧き出てくる動画が面白い。
https://youtu.be/OGZVhcQ33MQ

2019/05/27

J-PlatPat が更新

J-PlatPatが更新されて、いい感じに使いにくくなったので、とりあえずの検索をするときの使い方をメモしておきます。

投稿日時点の使い方なので、今後使い方が変わっていく可能性はあります。
(そもそもずいぶん前に更新されていたのかもしれません)

ざっくり調べたいときは、全文検索かつキーワードをANDでつなげたいことが多いです。
Googleの検索と同じですね。

選択入力


「選択入力」を使うときは、「検索項目」を「全文」に設定して、キーワードに1単語ずつ入力していきます。1単語ずつ!、です。複数単語を入れるとORになります。
残念なことに指定したい単語の数が多いと、この作業はとても手間がかかるので、「論理式入力」を使ったほうが簡単です。

論理式入力


論理式入力で全文検索かつANDで単語をつなげるには、例えば
機械学習/TX*分散処理/TX*プロトコル/TX
または、
(機械学習*分散処理*プロトコル)/TX
のように指定します。*がAND式で、/TXが全文を対象にする式(構造タグ)です。

使える演算子について


ヘルプには記載されているのですが、見たら分かるであろう画面の使い方が記載されている部分が多く、見つけるのに時間がかかったので、ここにURLを貼っておきます。

[条件式一覧]
https://www.j-platpat.inpit.go.jp/help/ja/p01/arithmetic.html
の「使用できる演算子について」を開くと読めます。簡単に書いておくと、

* AND
+ OR
- NOT
[ ] 演算順位の変更(+と-を*より強くするときなどに使う)
( ) /以降をまとめる

例:

('Liquor'*['Wine'-'Japanese beer'])/TX

[構造タグ一覧]
https://www.j-platpat.inpit.go.jp/help/ja/p01/p0101.html#9994

使用頻度が高そうなものをピックアップしておきます。
TX 全文
AB 要約/抄録
CL 請求の範囲
FI FI
FT Fターム
IP IPC

でしょうか。

文献内検索


さて、そうして見つけた文献を表示して、ブラウザの検索機能で当該箇所を探そうとすると、全文が表示されていないため、全文検索ができません。そこで、「要約」「請求の範囲」「詳細な説明」を1つずつ開いておきます。幸いにも、次の文献を表示したときにまた当該項目が閉じられてしまうことはないので、多少はましでしょうか。サーバーの負荷を下げたい一心で設計したんでしょう。手間がかかってしかたがないですね。


また、文献サイズが大きいと、一度に表示できずに分割されてしまいます。
そのため、ブラウザの検索機能を使って全文を検索できません。
そういった場合は、仕方がないので、
- pdf出力をする
- Google patentで検索する
にしましょう。

そもそも最初からGoogle patentでいいのかもしれませんね。

2019/02/03

wavの簡易表示

端末でwavファイルの中身を簡易表示するスクリプトを作ってみました。
https://bitbucket.org/bluewidz/asciiwave/src/default/aw
soxが必要です。

実行例です。

$ ./aw a.wav | head -n5
0.100s  18893   12375   18893   12375   18893   12375
0.200s  10841   6235    10841   6235    10841   6235
0.300s  8328    4031    8328    4031    8328    4031
0.400s  7479    3369    7479    3369    7479    3369
0.500s  6779    3056    6779    3056    6779    3056
0.100sの列は、0s-0.1sの間のサンプル値の絶対値の最大値を表示しています。
列はチャンネルを表しています。一番左が1チャネル目です。6列あるので、このファイルは6チャネル分のデータが含まれていることになります。

スケールを変更する場合は、1行にまとめるサンプル数を直接指定します。
$ ./aw -s 480 a.wav | head -n5
0.010s  0       0       0       0       0       0
0.020s  0       0       0       0       0       0
0.030s  0       0       0       0       0       0
0.040s  0       0       0       0       0       0
0.050s  39      25      39      25      39      25

2019/02/02

ZeroMQでソケット通信

はじめに


Python3 + ZeroMQでソケット通信を試してみます。

PythonでZeroMQを使うためのpyzmqのドキュメントは、
https://pyzmq.readthedocs.io/en/latest/
https://learning-0mq-with-pyzmq.readthedocs.io/en/latest/index.html
が、ZeroMQそのものの説明は
http://zguide.zeromq.org/page:all
が参考になります。

次の4パターン

  • Pair (Exclusive pair)
  • Client/Server (Request-reply)
  • Publish/Subscribe (Pub-sub)
  • Push/Pull (Pipeline)
が使えるようです。今回は、Publish/Subscribeで試します。せっかくなので、送受信にかかる時間も測定してみます。

コード


Publisher側のコードです。socket.send_pyobj でpythonのオブジェクトをそのまま渡すだけです。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import zmq
import time
import numpy as np

context = zmq.Context.instance()
socket = context.socket(zmq.PUB)
socket.bind("ipc://aaa")

w = np.ones(160) # numpy array to be sent
while True:
    t = time.perf_counter()
    socket.send_pyobj([t, w])
    print(t)
    time.sleep(1)

次に、Subscriber側のコードです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import zmq
import time

context = zmq.Context.instance()
socket = context.socket(zmq.SUB)
socket.connect("ipc://aaa")
socket.subscribe('') # Recieve all topics

while True:
    start, w = socket.recv_pyobj()
    end = time.perf_counter()
    print("{0} ms".format((end-start)*1000.0))
pythonオブジェクトの送信から受信までの時間をms単位で出力するようにしています。

実行結果


実行は、端末を2つ立ち上げて、上記のコードをpythonで実行するだけです。

実行してみると、Subscriber側では、
115.46662700129673 ms
0.5929949984420091 ms
0.9222489970852621 ms
0.7364909979514778 ms
0.5779260027338751 ms
0.5179920044611208 ms
0.5140290013514459 ms
0.5147350020706654 ms
0.5380119982874021 ms
0.5411919992184266 ms
0.5420840025180951 ms
0.5289019973133691 ms
0.8248919984907843 ms
0.9655120011302643 ms
0.7765349946566857 ms
0.6397230026777834 ms
0.7840489997761324 ms
1.0322410016669892 ms
0.7769410003675148 ms
0.774603002355434 ms
0.7632400011061691 ms
0.7989099976839498 ms
0.8185699989553541 ms
0.8329580014105886 ms
と出力されました。おおむね1ms以下で送信できているようです。ただ通信したいだけの用途なら十分な速さですね。

送信データ量を増やす


numpy arrayを作るところを1000倍してみたところ、
w = np.ones(160000)
さすがに3ms前後かかるようになりました。
118.689604998508 ms
6.79611799569102 ms
3.675059000670444 ms
3.75746200006688 ms
3.62611300079152 ms
3.6894719960400835 ms
2.672021997568663 ms
3.772675001528114 ms
3.641210001660511 ms
3.5925789998145774 ms
3.0622690028394572 ms

通信をtcpに変更


さらに、通信を次のようにtcpに変更してみました。
[Publisher]
socket.bind("tcp://*:5556")

[Subscriber]
socket.connect("tcp://localhost:5556")
すると、
117.72498200298287 ms
6.720707002386916 ms
4.039794999698643 ms
3.9917790054460056 ms
4.0376230026595294 ms
3.933735999453347 ms
3.9876959999674 ms
4.071578005095944 ms
4.004092996183317 ms
となりました。ipcよりtcpのほうが若干遅いようです。

2018/11/11

場所によって異なる興奮性ニューロン

ニューロンは色々種類があるけれど、満遍なく存在するわけではないらしい。 というわけで、以下ざっくりとメモ。

ざっくり


Tasicらによると、マウスの脳の異なる領域にある興奮性ニューロンはタイプが異なるらしい[1, 2]。

Tasicらは、マウスの脳のAnterior lateral motor cortex (ALM, 運動野のどこか)とPrimary visual (VISp, 視覚野)を対象として、遺伝子発現プロファイルに基づいて細胞タイプを調べた。 そうしたところ、133個の細胞タイプが存在し、101個のタイプがALMに、111個のタイプがVISpにあることが分かった。そのうち、79個は両方の領域で存在することもわかった。

また、おおきく3つに分類すると、抑制(GABAergic)タイプが61個、興奮(glutamatergic)タイプが56個、ニューロンでないタイプが16個、となった。

これらのうち、興奮タイプのニューロンの大部分が上記2つの領域間で共有されていなかった。 抑制タイプの中には領域間で遺伝子発現が異なるものがあったが、統計的に異なるというには不十分であった([2]のFig.2 cを参照)。

ちょこっと計算


計算してみると、領域間で共有されていないタイプはALMでは22個、VISpでは32個あり、合計で54個となります。 共有の有無の基準は良く読まないとわかりませんが、この54個がすべて興奮タイプのニューロンだということのようです。[2]のFig.2 cを見ると、確かに興奮性のものばかり遺伝子発現に差異があるようです(図のDE genesはdifferentially expresed genesの略かな?)。

参考


[1] https://www.nature.com/articles/d41586-018-07027-3
[2] https://doi.org/10.1038/s41586-018-0654-5

Firefoxのタブの設定

Firefoxで新しいタブを開く条件を設定する方法です。
動作確認は63.0.1で実施しています。

まず、about:configを開きます。

ポップアップウィンドウをタブで開くには、

browser.link.open_newwindow.restriction 0
とします。

ロケーションバーから開くときに新しいタブで開くには、

browser.urlbar.openintab true
とします。

ブックマークから開くときに新しいタブで開くには、

browser.tabs.loadBookmarksInTabs true
とします。

検索ボックスから開くときに新しいタブで開くには、

browser.search.openintab true
とします。

これで、何か新しいURLを開こうとしたときに、常に新しいタブで開くようになります。

2018/10/06

LVMでパーティション作成

はじめに


LVMでディスクを設定します。細かい説明は適当にぐぐってください。

環境


VirtualBox環境で、2GBのHDDを2個目のHDDとして追加しています(/dev/sdb)。 この追加したHDDをLVMにします。OSはUbuntu 16.04.4 LTSです。

まとめ


$ sudo parted /dev/sdb
$ (parted) mklabel gpt
$ (parted) mkpart base-partition 0% 100%
$ (parted) set 1 lvm on
$ (parted) quit
$ sudo pvcreate /dev/sdb1
$ sudo vgcreate vg0 /dev/sdb1
$ sudo lvcreate -l 100%FREE -n lv0 vg0
$ sudo mkfs.ext4 /dev/vg0/lv0
$ sudo mkdir /disk1
$ sudo mount /dev/vg0/lv0 /disk1

詳細


  1. まずは元にするパーティションが必要なので、partedで作成します。
    $ sudo parted --list 
    
    で一覧を取得して、対象となるデバイスを選びます。 不明な点があればman partedを読みましょう。 parted --helpは内容を知っている人向けです。
  2. 選択したデバイスを指定してpartedを起動します。
    $ sudo parted /dev/sdb
    
  3. パーティションの情報を表示します。
    (parted) print
    ...
    パーティションテーブル: unknown
    ...
  4. パーティションの管理方式をGPTに変更します。
    (parted) mklabel gpt
    (parted) print
    ...
    パーティションテーブル: gpt
    ...
  5. パーティションを作成します。
    (parted) mkpart base-partition 0% 100%
    (parted) print
    モデル: ATA VBOX HARDDISK (scsi)
    ディスク /dev/sdb: 2147MB
    セクタサイズ (論理/物理): 512B/512B
    パーティションテーブル: gpt
    ディスクフラグ: 
    
    番号  開始    終了    サイズ  ファイルシステム  名前            フラグ
     1    1049kB  2146MB  2145MB                    base-partition
    
    man partedで表示されるマニュアルでは、
    mkpart part-type [fs-type] start end
                         Make  a  part-type  partition  for filesystem fs-type (if
                         specified), beginning at start  and  ending  at  end  (by
                         default  in megabytes).  part-type should be one of "pri‐
                         mary", "logical", or "extended".
    
    と書かれていますが、第1引数に指定した文字列は、パーティションの名前になります。
  6. LVM用のフラグを立てます。
    (parted) set 1 lvm on
    (parted) p                                                                
    モデル: ATA VBOX HARDDISK (scsi)
    ディスク /dev/sdb: 2147MB
    セクタサイズ (論理/物理): 512B/512B
    パーティションテーブル: gpt
    ディスクフラグ: 
    
    番号  開始    終了    サイズ  ファイルシステム  名前            フラグ
     1    1049kB  2146MB  2145MB                    base-partition  lvm
    

    parted上での作業は完了ですので、quitで抜けます。

  7. Physical Volumeを作ります。
    $ sudo pvcreate /dev/sdb1
    sdbではなく、sdb1を指定します。 結果は
    $ sudo pvdisplay
    で確認できます。
  8. Volume Groupを作ります。
    $ sudo vgcreate vg0 /dev/sdb1
    結果は
    $ sudo vgdisplay
    で確認できます。
  9. Logical Volumeを作ります。サイズ指定なら
    sudo lvcreate -L 1.996GiB -n lv0 vg0
    全て使うなら
    sudo lvcreate -l 100%FREE -n lv0 vg0
    とします。 出来上がったサイズは
    $ sudo lvdisplay
      --- Logical volume ---
      LV Path                /dev/vg0/lv0
      LV Name                lv0
      VG Name                vg0
      ...
      LV Size                2.00 GiB
      ...
    で確認できます。
  10. ext4でフォーマットします。
    $ sudo mkfs.ext4 /dev/vg0/lv0
  11. 出来上がったディスクをマウントします。
    $ sudo mkdir /disk1
    $ sudo mount /dev/vg0/lv0 /disk1
    $ df
    Filesystem                  1K-blocks     Used Available Use% Mounted on
    /dev/mapper/vg0-lv0           2027408     3072   1903300   1% /disk1
    
    マウントするときは、UUIDでも可能です。
    $ blkid
    /dev/mapper/vg0-lv0: UUID="0805787f-b7a0-488a-9321-ed7ed77322ab" TYPE="ext4"
    
    $ sudo mount UUID=3be08a7c-449a-4d74-b576-1064ef72a7ef /disk1
    

    fstabに追加したいときは、

    $ man fstab
    を参照するかぐぐりましょう。

2018/09/15

tmuxの画面が固まったら

Windows Subsystem for Linux (WSL)のターミナルでtmuxを使っているときに、
ないしは、byobuのバックエンドでtmuxを使っているときに、
うっかり、SJISの文字コードを表示すると画面が固まります。

復旧するすべは分からないのですが、固まったウィンドウのみを削除することはできます。

表示が乱れるかもしれませんが、固まっているウィンドウを表示し、

 [prefix-key]→ & → y → [prefix-key] → d
を押します。ウィンドウをkillして、tmuxから抜けています。そして、再度、tmux (またはbyobu) に接続すると、固まっていたウィンドウがなくなっています。

または、固まっていないウィンドウに移動し、画面を正常にするために一旦 tmux から抜けて、再度接続し、そこで
$ tmux kill-window -t :[ウィンドウの番号]
を実行すると、固まったウィンドウを削除できます。tmuxバックエンドのbyobuでも同じです。

2018/09/04

非可逆圧縮音声ファイルと非圧縮音声ファイルの自動判定

はじめに


人間の耳では感知しにくい、mp3による圧縮音声と非圧縮音声の違いをニューラルネットワークを使って判定できるか試してみます。

mp3とwavのスペクトログラムを下図のように比較すると、明らかに周波数の高い部分がmp3では消えているので、それを特徴として捉えられれば判定できそうです。

方法


Hennequinらの方法(Codec Independent Lossy Audio Compression Detection, 2017)を参考にしました。

今回の実験に使用したニューラルネットワークを以下に示します。 入力は、フレーム長(窓幅)を512サンプルとした対数パワースペクトルです。Nフレームつなげたものを画像とみなしてCNNで処理します。フレームシフトは256サンプルです。活性化関数は全てLeakyReLUです。

Conv2D     Nx256 32-channel
MaxPooling (N/2)x128 32-channel
Conv2D     (N/2)x128 16-channel
MaxPooling (N/4)x64 16-channel
Conv2D     (N/4)x64 16-channel
MaxPooling (N/8)x32 16-channel
Conv2D     (N/8)x32 16-channel
MaxPooling (N/16)x16 16-channel
Conv2D     (N/16)x16 16-channel
Flatten    (N/16)x16x16
Dense      64
Dropout
Dense      64
Dropout
Dense      2
Softmax(2-classes)

データ


訓練データは12曲で、先頭から曲の終端まで1000フレーム(5.8秒)ごとに1回、特徴ベクトルを抽出しました。 合計506個です。検証用データは15曲で特徴ベクトルは792個です。

結果


1エポック当り100ステップ、バッチサイズ4で50エポック訓練した結果、以下のようになりました。 N=1のみ3回実験しました。
NTrain-accValidation-acc
102410.968250
5120.9992500.955000
2560.9982500.902750
1280.9952500.933000
640.9935000.864750
320.9787500.925500
160.9815000.976750
80.9855000.972250
40.9800000.899500
20.9815000.889000
10.9750000.931500
10.9635000.956000
10.9547500.936250
Nが小さくても判定できています。僅か1フレームでも9割前後の精度がでています。 なお、MaxPooling層がない場合は訓練が進まず、精度がまったくでませんでした(0.5前後で運任せ)。

コード


https://bitbucket.org/bluewidz/mp3detect/src/default/ を参照ください。

2018/08/18

Linuxbrew installer

gccくらいはインストールされている環境にLinuxbrewをインストールするスクリプトを作りました。
https://bitbucket.org/bluewidz/linuxbrew-bootstrap/
動作確認は、LXDのDebian 9のコンテナ内で実施しています。

Linuxbrewって何?という方は、
https://qiita.com/thermes/items/926b478ff6e3758ecfea
をどうぞ。

簡単にいうと、Linuxbrew
http://linuxbrew.sh/
を使うと、ルート権限のないLinuxマシンで色々なソフトウェアを簡単にインストールできます。

pythonなら、例えば、

$ brew install python
のようにします。

2018/08/05

Raspberry Pi で録音と再生

Raspberry Pi で録音と再生をしてみます。

準備


Linuxでオーディオデバイスを扱う場合、ALSAとPulseAudioが使えます。
[Audio device]-[ALSA]-[PulseAudio]-[Application]
の関係があるので、色々と例外はあるようですが、ここではPulseAudioを使うことにします。そのあたりの詳細は
http://mickey-happygolucky.hatenablog.com/entry/2015/04/04/105512
https://cpplover.blogspot.com/2012/03/pulseaudio.html
を参照ください。

Raspberry Pi 3にはマイク端子がないので、USBマイクか、USBオーディオインターフェイスを使ってマイクを接続します。

録音と再生


マイクを接続した後、デバイスの番号を探します。
$ pactl list sources
今回試した環境では、
Source #0
        State: SUSPENDED
        Name: alsa_output.usb-0d8c_C-Media_USB_Headphone_Set-00-Set.analog-stereo.monitor
        Description: Monitor of Audio Adapter アナログステレオ
        <以下略>

Source #1
        State: SUSPENDED
        Name: alsa_input.usb-0d8c_C-Media_USB_Headphone_Set-00-Set.analog-mono
        Description: Audio Adapter アナログモノ
        <以下略>

Source #2
        State: SUSPENDED
        Name: alsa_output.1.analog-stereo.monitor
        Description: Monitor of bcm2835 ALSA アナログステレオ
        <以下略>
と出力されました。USB接続のマイクは、Source #1が該当するので、マイクから録音するには
$ parecord -d 1 test.wav
とします。

録音できていることを確認するには、
$ paplay -d 0 test.wav
とします。paplayで指定するデバイス番号は、
$ pactl list sinks
で調べます。読み方はsourcesの場合と同様です。

デバイス番号を毎回指定したくないときは、スピーカの場合は、
$ pactl set-default-sink [デバイス番号]
で、マイクの場合は、
$ pactl set-default-source [デバイス番号]
でデフォルトで使うデバイスを指定できます。

別のPCのオーディオデバイスの利用


PulseAudioはネットワーク越しのオーディオデバイスも扱えます。
そこで今回は、Windows PCにつながったスピーカやマイクを使う方法を試してみます。

まず、pulseaudio-1.1のWindows用バイナリをどこかから入手して、各種設定をします。バイナリの入手と設定は
https://vogel.at.webry.info/201711/article_9.html
が参考になります。

今回試すWindows側の default.pa は
load-module module-waveout
load-module module-native-protocol-tcp auth-ip-acl=192.168.0.0/16
としました。

Windowsのコマンドプロンプトから
pulseaudio -F ..\default.pa --exit-idle-time=60000
で起動しておいて、Raspberry Piから
$ paplay -s 192.168.0.1 test.wav
とすると、Windows側から再生されます。WindowsのIPアドレスが192.168.0.1だとしています。

同様に録音も出来ます。
$ parecord -s 192.168.0.1 test.wav

GStreamer


PulseAudioだけだと録音できるだけですが、GStreamer (https://gstreamer.freedesktop.org/) を経由すると色々遊べます。

まずは、マイク入力を直接スピーカに流してみます。
$ gst-launch-1.0 pulsesrc device=1 ! pulsesink device=0
デバイス番号は、さきほどpactlで調べた番号と同じです。Ctrl+Cで停止できます。 ちょっと遅延しますが、マイクで録音した音がスピーカから流れます。

deviceの番号については、デバイスが存在していれば間違えてもエラーは起きません。代わりに、全くの無音(振幅0)が録音されます。何も音が出ない場合はデバイス番号を間違っていないか、まずはチェックしましょう。

さて、ただ流すだけでは面白くないので、エコーをかけてみます。

$ gst-launch-1.0 pulsesrc device=1 \
    ! audioecho delay=300000000 intensity=0.5 feedback=0.5 \
    ! pulsesink device=0
delayの単位はナノ秒なので、この設定では300ミリ秒遅延のエコーになります。詳細は、
$ gst-inspect-1.0 audioecho
を実行して、Element Properties 部分の説明を読んでみてください。

今度は、ローパスフィルタを試してみます。

gst-launch-1.0 pulsesrc device=1 ! audiowsinclimit mode=low-pass cutoff=100 ! pulsesink device=0
cutoffの単位はHzなので、100Hz以下を通過させるフィルタになっています。 声がくぐもった感じになります。

せっかくなので、ハイパスフィルタも試してみます。

$ gst-launch-1.0 pulsesrc device=1 ! audiowsinclimit mode=high-pass cutoff=2000 ! pulsesink device=0
低い音が弱くなります。

wav形式で録音するには、

$ gst-launch-1.0 pulsesrc device=1 ! wavenc ! filesink location="test.wav"
とします。これを使うと、加工した音声も簡単に記録できます。

同様に、録音したwavを再生するには、

$ gst-launch-1.0 filesrc location=test.wav ! decodebin ! pulsesink device=0
とします。

もちろん、GStreamerを使うときもPulseAudioのサーバーに接続できます。

$ gst-launch-1.0 pulsesrc server=192.168.0.1 ! pulsesink server=192.168.0.1

GStreamerの不具合


GStreamerのpulsesrc Ver.1.8.3には不具合があり、どう設定しても無音しか記録できません。 古いバージョン(例えば、1.4.4)または新しいバージョン(1.14.2)では動作します。

不具合修正の場所とコミットメッセージは
https://cgit.freedesktop.org/gstreamer/gst-plugins-good/commit/ext/pulse/pulsesrc.c?id=4833f02e47d5b7fc4284517e92d5976b20188a4a
にあります。2016年9月25日にコミットされています。

ブランチ1.8のログ
https://cgit.freedesktop.org/gstreamer/gst-plugins-good/log/?h=1.8
を見ると、1.8.3のリリースの直後に修正されていることがわかります。 tagが付いている範囲だと、Release 1.9.2とRelease 1.9.90の間で修正されています。

Ubuntu 16.04 LTSを使っている人は気をつけましょう。 デフォルトでインストールされるGStreamerがこの不具合を踏んでいます。

2018/07/21

OneNote 2016

OneNote 2016がなぜかCPUを1コア消費し続けるようになった。
再起動しても、OneNoteのキャッシュを削除しても、起動してしばらくすると再発する。

何かのアップデートでバグが入ったようだ。

SysinternalsのProcess Explorerを使ってどのスレッドが消費しているのかを調べたところ、

ntdll.dll!RtlReleaseSRWLockExclusive

がCPUを消費し続けていることが分かった。名前とCPUを消費し続けていることから考えると、排他制御をスピンロックを使って実装したものの、ロックを抜ける条件を書くところで何かをミスったのだろう。

何を処理しているスレッドなのかは分からないが、Process Explorerで当該スレッドをSuspendすることで、とりあえずCPUを消費し続ける状況は回避できた。今のところ、OneNoteの操作には影響はないようにみえる。この方法で不具合修正が入るまで回避かな。

2018/05/27

Linuxターミナルの色設定

はじめに


Linuxのターミナルの色設定が変わってしまった場合の対処方法についてメモしておきます。 Debian 8.10環境を前提にしています。

環境変数TERM


環境変数TERMに設定することで色設定を変更できます。例えば、
$ TERM=xterm-256color
とします。

なお、xterm-256colorの代わりにscreen-256colorを使うと、lessの検索時にヒットした文字列が斜体(イタリック)になり、ひじょーに見にくくなります。

TERMに設定できる値一覧


TERMに設定できる値は、
$ infocmp -D
で表示されるディレクトリ以下にあるファイル名です。

$ infocmp xterm-256color
のように、TERMに指定できる値を引数に指定することで、内部の設定を表示できます。読み方がわかりませんが…。2個指定すると2つの設定の差分が表示されます。

byobu経由


byobu経由でTERMがおかしくなるときは、手動でTERMを設定するか、byobuの設定を変更します。 tmuxバックエンドの場合は、~/.config/byobu/.tmux.conf に
set-option -g default-terminal xterm-256color
set -g terminal-overrides 'xterm:colors=256'
を追記します。もしかすると2行目は不要かもしれません。 ~/.byobu 以下にも設定ファイルがあるかもしれませんが、有効なほうを変更します。

参考


https://unix.stackexchange.com/questions/179173/make-less-highlight-search-patterns-instead-of-italicizing-them
https://www.tldp.org/HOWTO/Text-Terminal-HOWTO-16.html

追記(2018/9/8)


byobuの起動前後でターミナルの色が異なる場合は、
byobu-disable-prompt
を実行しましょう。byobuの色設定が使われなくなります。逆に、byobuの設定を使いたい場合は、
byobu-enable-prompt
を実行します。

2018/05/04

PythonとC++間でのnumpy配列の受け渡し

はじめに


Pythonで作ったnumpy.ndarrayをC++側で加工して、Pythonに返す方法を試します。

PythonからCの関数を呼び出す方法は色々あるようですが、 今回は、numpyを操作したいので、Boost C++ Librariesを使ってみます。

準備


Boost C++ Librariesをインストールします。
https://www.boost.org/doc/libs/1_67_0/more/getting_started/unix-variants.html
の第1節に従ってダウンロードし、第5節に従ってビルドします。

方法(動的リンク版)


まず、C++のコードです。ファイル名は test1.cpp とします。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <boost/python/numpy.hpp>
namespace p = boost::python;
namespace np = boost::python::numpy;

void plus_scalar(np::ndarray &a, float b)
{
  auto N = a.shape(0);
  auto *p = reinterpret_cast<float*>(a.get_data());
  for(auto v=p; v!=p+N; ++v){*v=*v+b;}
}

BOOST_PYTHON_MODULE(test1)
{
  Py_Initialize();
  np::initialize();
  p::def("plus_scalar", plus_scalar);
}
Python側から第1引数で渡される1次元NumPy配列に第2引数で渡される値を足すだけの関数plus_scalarを定義しています。

このtest1.cppをコンパイルします($はシェルのプロンプトです)。

$ g++ --std=c++14 --shared -fPIC -I$BOOST_PATH/include/ -I/usr/include/python2.7/ test1.cpp -L$BOOST_PATH/lib -lboost_numpy27 -lboost_python27 -o test1.so
$BOOST_PATHはBoost C++ Librariesをインストールしたパスです。

ライブラリのパスが通っていないので、

$ export LD_LIBRARY_PATH=$BOOST_PATH/lib
を実行し、.soをロードできるようにしておきます。

これをPythonから呼び出します。次のようにPythonスクリプト t1.py を作成します。

1
2
3
4
5
6
7
8
9
import sys
sys.path.append('.')
import test1
import numpy as np

a = np.array([1,2,3], dtype=np.float32)
print(a)
test1.plus_scalar(a, 3.0)
print(a)

そして、実行します。

python t1.py
すると、
[ 1.  2.  3.]
[ 4.  5.  6.]
が出力されます。確かに、3.0が足されています。

方法(静的リンク版)


動的リンク版では LD_LIBRARY_PATH を設定しましたが、使いにくいのでBoost関連のライブラリだけでも静的リンクしておきましょう。次のようにすることで.soを作ることができます。
g++ --std=c++14 --shared -fPIC -I$BOOST_PATH/include/ -I/usr/include/python2.7/ test2.cpp $BOOST_PATH/lib/libboost_numpy27.a $BOOST_PATH/lib/libboost_python27.a -o test2.so

もし、次のエラー

/usr/bin/ld: $BOOST_PATH/lib/libboost_numpy27.a(ndarray.o): 再配置 R_X86_64_32 (`.rodata.str1.8' に対する) は共有オブジェクト作成時には使用できません。-fPIC を付けて再コンパイルしてください。
$BOOST_PATH/lib/libboost_numpy27.a: error adding symbols: 不正な値です
collect2: error: ld returned 1 exit status
が出て、リンクできないときは、エラーメッセージの通りにBoostをビルドし直します。 具体的には、[4]を参考に、
$ ./b2 --clear
$ ./b2 cxxflags=-fPIC cflags=-fPIC install
のように、cxxflagsを追加してビルドし直します。cflagsは不要かもしれません。なお、ここで追加したオプション名は、--cxxflagsでも、-cxxflagsでもなく、ハイフン無しの単なるcxxflagsです。

test2.cppは

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <boost/python/numpy.hpp>

namespace p = boost::python;
namespace np = boost::python::numpy;

void plus_scalar(np::ndarray &a, float b)
{
  auto N = a.shape(0);
  auto *p = reinterpret_cast<float*>(a.get_data());
  for(auto v=p; v!=p+N; ++v){*v=*v+b;}
}

BOOST_PYTHON_MODULE(test2)
{
  Py_Initialize();
  np::initialize();
  p::def("plus_scalar", plus_scalar);
}
t2.pyは
1
2
3
4
5
6
7
8
9
import sys
sys.path.append('.')
import test2
import numpy as np

a = np.array([1,2,3], dtype=np.float32)
print(a)
test2.plus_scalar(a, 3.0)
print(a)
です。test1がtest2に変わっただけです。
$ python t2.py
を実行すると、動的リンク版のときと同様に
[ 1.  2.  3.]
[ 4.  5.  6.]
と出力されます。

参考


[1] http://d.hatena.ne.jp/nihohi/20120306/1331002942
[2] http://tadaoyamaoka.hatenablog.com/entry/2017/05/25/234934
[3] https://stackoverflow.com/questions/10968309/how-to-import-python-module-from-so-file
[4] https://stackoverflow.com/questions/27848105/how-to-force-compilation-of-boost-to-use-fpic

2018/04/30

CppUTest を試してみる

CppUTestを試してみます。CppUTestの本家はここ。
http://cpputest.github.io/

現時点の最新をダウンロードします。

$ wget https://github.com/cpputest/cpputest/releases/download/v3.8/cpputest-3.8.zip
展開して、
$ unzip cpputest-3.8.zip
$ cd cpputest-3.8/cpputest_build/
ビルドするだけです。なお、cmake版もあります。
$ ../configure
$ make
一応、CppUTest自身のテストもしておきます。
$ make tdd
以下のようなコードを書いて、
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTest/TestHarness.h"

TEST_GROUP(TestSuite){};
TEST(TestSuite, Case1)
{
  LONGS_EQUAL(3, 4);
}

int main(int argc, char *argv[])
{
  return RUN_ALL_TESTS(argc, argv);
}
ビルドします。
$ g++ -Icpputest-3.8/include test.cpp cpputest-3.8/cpputest_build/lib/libCppUTest.a
実行すると、
$ ./a.out
test.cpp:7: error: Failure in TEST(TestSuite, Case1)
        expected <3 0x3>
        but was  <4 0x4>

.
Errors (1 failures, 1 tests, 1 ran, 1 checks, 0 ignored, 0 filtered out, 3 ms)
となり、無事、エラーが検出できました。


なお、次のようにファイルの指定順序をうっかり間違うと、リンカエラーがでます。
$ g++ -Icpputest-3.8/include cpputest-3.8/cpputest_build/lib/libCppUTest.a test.cpp
理由を知りたい方は、
http://www.yunabe.jp/docs/static_library.html
をどうぞ。

2018/04/16

映像を利用した音声分離

複数の音声が混じった音から個別の音声を抜き出す音声分離の性能を映像を使って向上させる方法をgoogleが発表。

評価には、signal-to-distortion ratio (SDR)を利用。
arXivに投稿されている論文のTable 3によると、音声のみを用いた最新手法より分離性能が高い。


arXiv
https://arxiv.org/abs/1804.03619

GIGAZINE
https://gigazine.net/news/20180412-looking-to-listen-google/

Google Research Blog
https://research.googleblog.com/2018/04/looking-to-listen-audio-visual-speech.html


SDRの解説は
https://hal.inria.fr/inria-00564760/document
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.412.3918&rep=rep1&type=pdf
に記載があるが、きちんとは読んでいない。