niszetの日記

細かい情報を載せていくブログ

Rでbit演算について

書籍が見つからない

Rの書籍は色々持っているものの、bit演算や複素数型について詳しく説明されているものってないような気がします。

なので、ちょっと調べました。

先人のお知恵を拝借。 takuyaokada.hatenablog.com

R.utilsというパッケージもあるのですね。これも後ほど調べてみる。

RStudioの入力補完でそれらしいものを見つける

RStudioでbitまで入力して補完を使うと、bitopsというパッケージがある。
baseと別にあるということは、より良いのでは?ということで違いを見てみる。

いくつか入力してみる。

> bitwAnd(15,0)
[1] 0
> bitwAnd(15,7)
[1] 7
> bitops::bitAnd(15,7)
[1] 7
> 15 & 7
[1] TRUE

最後はおまけ。&はlogicalを返しますので、bit演算には使えませぬ。
差異はないように見えますね。

ベクトルでも入力できる。

> bitwAnd(c(15,0),7)
[1] 7 0
> bitops::bitAnd(c(15,0),7)
[1] 7 0

> bitops::bitAnd(c(15,0,1),c(7,1,3))
[1] 7 0 1
> bitwAnd(c(15,0,1),c(7,1,3))
[1] 7 0 1

ここでも差異がみえない。ベクトルを与えられるのは便利ですね。

ヘルプを見るとNAについて記述がある。入力してみる。

> bitops::bitAnd(c(15,0),NA)
[1] NA NA
> bitwAnd(c(15,0),NA)
Error in bitwAnd(c(15, 0), NA) : 'a' and 'b' must have the same type

おっと、差異が出ました。baseの方はNAを入れるとエラー…

( ^ω^)・・・

type…?どうやら、NAがintegerだと思われていないようです。

今度は、NA_integer_で入れてみる。

> bitwAnd(c(15,0),NA_integer_)
[1] NA NA

お、動きますね。
念のため、もう一方も。

> bitops::bitAnd(c(15,0),NA_integer_)
[1] NA NA

こちらも。
bitopsパッケージの方を使っておいた方が、NAの型を合わせてくれるので便利そうです。

ヘルプより、

bitwise {base}   
bitwAnd(a, b)
a, b    integer vectors; numeric vectors are coerced to integer vectors

もう一方

bitAnd {bitops}  
bitAnd(a, b)
a,b numeric vectors of compatible length.

integer… 試しに入力すると…

> bitops::bitAnd(c(15.12501,0,1),c(7,1,3))
[1] 7 0 1
> bitwAnd(c(15.01,0,1),c(0.999,1,3))
[1] 0 0 1
> bitwAnd(c(15.01,0,1),c(1.999,1,3))
[1] 1 0 1

なるほど。使い方を気を付けないといけなさそうです。

Rでバイナリファイルを読む、readBinについて

Rでバイナリファイルを読む

さて、read_wavはwaveファイルを読むのですが、当然?wavファイルはバイナリデータです。
つまり、Rでバイナリファイルを読むための知見が必要なのですが、あまりそういう需要がないためか、情報もあまりないのでした。

readBin

こちらを大変参考にさせていただきました。 blog.recyclebin.jp

最終的にはかなり違うものになりましたが。

Rプログラミングマニュアル[第2版]ならreadBinについて記載があるかなと思ったのですが、残念ながら「ヘルプを読みましょう」と書かれていましたので、ヘルプとにらめっこです。 株式会社サイエンス社 株式会社新世社 株式会社数理工学社

readBinのヘルプ

readBinのヘルプを読みます。 引数で何が与えられるのか、デフォルト値が何で決まっているのか(環境依存とか)、サンプルプログラムとか、色々情報が詰まってますね。
本文は最初は読んでもよくわからないことが多いですが…末尾のプログラムが動かなかったことはなかったはず。

R: Transfer Binary Data To and From Connections

さて、readBinはこんな感じの引数(とデフォルト値)を持っていますね。

readBin(con, what, n = 1L, size = NA_integer_, signed = TRUE,
        endian = .Platform$endian)

conにはコネクションを与えます

  connection <- file(file, "rb")

こんな感じでファイル名fileをバイナリモードのreadモードで開く形です。

whatには型を与えます。wavの場合は"integer"で良いです。"WAVE"などが入っている箇所はreadCharで読めばよいので。

signedは16bitPCMの場合は符号ありなのでTRUE。8bitの場合はFALSEにしないといけませんね。A.I.に追加。

endianはデフォルト値が入っているのですね。"little"を明示的に与えた方が良いのかもしれませんね。A.I.に追加。

sizeはそれぞれの読み込みのサイズをバイト数で与える形です。16bitなら2を与えればよいわけですね。
nは連続して読み込む場合に指定。wavファイルは固定長のデータのため、データ部分は一括で読み込むことが出来ます。

最初はfor文で読んでいましたが、Rのforは遅いので。ファイルサイズが小さい場合は問題にならないと思います。
可変長の場合は考えなくてはならないですが、それは次のread_smfにて考え中。

コネクションは最後に必ず閉じておく必要があります

close(connection)

が、エラーがあるとcloseが実行されなくなってしまうので、on.exit()に入れておく方が良いですかねぇ。これもA.I.…。

on.exit()

実際使ってみるのが、理解するには一番の近道な気がします。バイナリエディタとにらめっこして、思った値が取れているか?を地道にチェックすると良いのです。

read_wavの「完成」はまだまだ先になりそうですね…

前回プロットしたwavファイルの説明

read_wav()の関連知識

さて、条件付きとはいえwavファイルをrで読み込み、data.tabelの形式にしてggplot2で表示させることが出来ました。
前回はCsoundによって生成したwavファイルが、1Hzで非常に見やすいという理由で使っていました。
今回は、Csoundで生成したwavファイルをバイナリエディタで見つつ、read_wav関数内でどのように処理をしているかについて、簡単に説明をしようと思います。

Csoundで生成したwavファイルについて。

前回の記事で使用したwavファイルについては下記をご参照ください。ここで生成した、1Hzのサイン波を説明に使います。

niszet.hatenablog.com

wavのフォーマットについて

前回も紹介した、下記のサイトを見るのが良いと思います。見比べてください。

WAV形式音声ファイル

バイナリエディタでデータを見てみる。

さて、

52 49 46 46 08 00 00 00 57 41 56 45 66 6D 74 20
10 00 00 00 01 00 02 00 44 AC 00 00 10 B1 02 00
04 00 10 00 64 61 74 61 00 00 00 00 00 00 00 00
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
05 00 00 00 07 00 00 00 08 00 00 00 09 00 00 00

こんな感じです。バイナリなので、52は0x52、10進数で82(=16*5+2)を表しています。これで1byteの単位です。
さて、順に見ていきます。
適宜、先のサイトから説明を引用させていただきます。

52 49 46 46

wavファイルの先頭4byteはRIFFヘッダとなっていて、それぞれR,I,F,Fの文字をasciiコードで表しています。

asciiの文字コードはググれば一発ですがこちらが見やすいかな。
検索するときは52なら0x52、49は0x49で探してみてください。それぞれRとIに対応しています。

参考: ASCIIコード表

08 00 00 00

来ました。Csound第一の謎。 wavのフォーマット的に、次の4byteはファイルサイズ-8のはずです。しかしここでの値は8。

あ、ちなみにここ、リトルエンディアンです。良く調べてないけど。 エンディアン - Wikipedia

別のwavファイルはちゃんとファイルサイズ-8で入っていたので正しかろう…。
ともかく、Csoundで生成したwavには不備があります。そのため、通常のプレーヤで再生できない(WMPfoobar2000で確認)のだと思います。read_wavはこの値を無視し、ファイルサイズ-8を使用するようにしています。今後、csoundオプションで指定するかもしれませんが。

57 41 56 45

それぞれ、'W' ‘A’ ‘V’ ‘E'です。

66 6D 74 20

それぞれ、'f' ’m' ’t' ‘ 'です。

10 00 00 00

引用。

4 byte   バイト数    fmt チャンクのバイト数。リニアPCM ならば 16(10 00 00 00)

ということで、このファイルのfmtチャンクのバイト数は16ということを表しています。

余談ですが、PrintMusic2011Jで生成したwavファイルはこの値が18となっていました。リニアPCMフォーマットなのに…なぜ…

01 00

再び引用。

2 byte   フォーマットID    リニアPCM ならば 1(01 00)

ということで、リニアPCMのようです。

02 00

どんどん行きましょう。

2 byte   チャンネル数  モノラル ならば 1(01 00)、 ステレオ ならば 2(02 00)

ステレオのようですね。これ、Csoundの謎その2です。のちに出てきますが、実際は片方にしか音が入っていません。何のためのステレオ…

44 AC 00 00

44.1KHzでした。

4 byte   サンプリングレート 44.1kHz ならば 44100(44 AC 00 00)

10 B1 02 00

こちらもそのままですね。

4 byte   データ速度 (Byte/sec)  44.1kHz 16bitステレオならば 44100×2×2=176400(10 B1 02 00)

04 00

こちらも。

2 byte   ブロックサイズ (Byte/sample×チャンネル数) 16bit ステレオ ならば 2×2 = 4(04 00)

10 00

こちらも想定通り。

2 byte   サンプルあたりのビット数 (bit/sample)   WAV フォーマットでは 8bit か 16bit。16bit ならば 16(10 00)

64 61 74 61

それぞれ、’d' ‘a’ ’t' ‘a'ですね。

00 00 00 00

来ました、第三の謎。
ここで、残りのデータのサイズを示しているはずなのですが、サイズ0となっています。
read_wavではこの値を無視し、ファイルサイズからヘッダのサイズを引いた値でデータサイズを計算しました。値が0以外の場合は使った方が良いかもしれませんね。

データ部分

さて、データ部分についてはこちらを読むと良いです。
wav ファイルフォーマット

16bitステレオなので、L,R,L,R…の順に16bit= 2byteで並んでいるはずです。つまり、1byte毎に見ればL L R Rのまとまりが1秒間に44.1kだけあるはずです。

実際のデータは…

00 00 00 00
01 00 00 00
02 00 00 00
03 00 00 00
04 00 00 00
05 00 00 00
07 00 00 00
08 00 00 00
09 00 00 00

のように、L側だけデータが入っています。これは設定で変えられるのかもしれませんので謎カウントしません。
1サンプルごとに少しずつ増えていることが見れますね。

16bit ならば符号付き signed (-32768 ~ +32767, 無音は 0)

ということで、負値は0xFFみたいな値になります。実際、末尾は

F3 FF 00 00 F4 FF 00 00 F5 FF 00 00 F6 FF 00 00 
F7 FF 00 00 F8 FF 00 00 FA FF 00 00

のようになっています。
そしてまた謎の、ファイル末尾で-1あたりになっていないという。-6て…。
そして、read_wavで読めばわかりますが、44128行あるのでした。44.1kHzじゃなかったっけ( ^ω^)・・・?

というわけで、細かいことを気にするときりがないのですが、Csoundの生成するwavファイルはなんか奇妙だなというお話でした。
値自体はCsound+バイナリエディタ、あとは気合で見れますので、見てみると良いのではと思いまする。

read_wav 試作版できた。

課題が沢山あります。

  1. とりあえず、読みたいデータを読むだけならば、手元では動くことを確認した、wavを読むRのコードが出来ました。
    read_wav(ファイル名) で対象のwavファイルを読むのですが、
    1. Csoundはヘッダにサイズ情報がない。
    2. PrintMusic 2011で作成したwavはリニアPCMなのにヘッダが18バイトある
      …などなど、もうちょっと考えておく必要があるものがありそうです。
  2. もうひとつ、この関数はwavファイルを一気に読んでしまいます。byte単位をRのintegerに変換するのでディスク上のファイルサイズよりもメモリを食います。
  3. 44.1kでサンプリングしている場合、1秒ごとに44.1k行だけデータフレームの行が増えます。試しに4分超のファイルを読ませてplotさせようとしてみましたがRが固まりました…。よって、どこからどこまで読む、というオプションが必要そうです。
  4. 性善説で、wavじゃないものもwavとして読んでしまうので危ない。ヘッダを見て止める仕組みが必要(これは簡単)
  5. コードが長い…ここに貼れるレベルじゃない…。githubに置くためにgitの勉強しなきゃ…

という状態です。

とはいえ、少しは何か書く。

まぁ、色々課題はあるけどちょっと動くし、いちおう進んでるんですよアピールしておこうかな、と。

先日の記事、

niszet.hatenablog.com

で生成したwavファイルをC直下にsample1.wavという名前でおいてあるとします。
これを、こんな感じで

tmp <- read_wav("C:/sample1.wav")

tmp$data %>%  ggplot(aes(x=time, y=Lval))+geom_line()

こうなります。

f:id:niszet:20170607225526p:plain

とりあえず、動くところまではできたのです…。

そういえば、生成されたwavファイルの説明してなかった…。次回に。

rsoundが動いたときの記録。

R力が足らない…

さて、例のrsoundですが私の環境ではしばらくの間、うまく動いていませんでした。
今は動いているものの、うまく更新が出来ず…。とりあえず動いているので触らないことにしています。

さて、この記事自体はずいぶん前に下書きを書いていたのですが、公開にしていないことに気づいて加筆訂正して公開しようと思った次第。

rsoundについて

rsoundはCsoundをRから呼ぶためのライブラリです。

notchained.hatenablog.com

github.com

Csoundはcppで書かれているので、Rcppを使ってCsoundのライブラリを呼ぶ形で作られているのですが、このrsound.dllを作るところで詰んでました。

これは主に僕のRcpp/cppまわりの理解が足りていなかったのですが、反省も兼ねて記録を残しておきます。

Rcppについて

最初は何が問題だったのかわからなかったので、Rcpp自体を勉強することにしました。
rsoundは公開時期がちょっと昔なので、Rtoolsのバージョンupに起因する可能性があったためです。Yutaniさんにご指摘いただきました。 調べた結果、Rtoolsの環境周りはシロっぽいのでした。このとき参考にした本やサイトなどを列挙しておきます。

全部を理解できたわけではないのですが、津駄さんのこの資料がまとまっていて読みやすいと思います。

www.slideshare.net

RcppはAoRPにも載っていますが、分量的には少ないですね。 www.oreilly.co.jp

R言語徹底解説もチラ見してみました。まだ全然読んでない…。 www.kyoritsu-pub.co.jp

あとはパーフェクトRでしょうか。この本、情報量がかなりあるんですが僕はまだあまり読めていません… gihyo.jp

日本語で読めるというのは、最初のとっかかりの時点では重要なので、こういった情報が沢山手に入るのはRの強みですね。

Rcppまわりのトラブルをネットで調べた

一時期、rstanを導入するブーム?がありましたが、そのときに結構な方が色々ハマっていたようです。その時の記録も参考にしました。
結果、これもシロだったのですが。

こちらのリンク集がかなり広範囲に渡っているので、今後お世話になりそうです。 須通り_統計_C++は未経験だけどRcppで開発したい!(1) 環境構築編

中でもこちらは後に書くとおり、Sys.setenvで解決したので参考になりました。

Rcpp 俺様備忘録

こちらも。Macに限らず、です。 suryu.me

ビルドの方法について

最初、Rcpp::sourceCppでビルドしようとしていましたが、うまくいかない。 パッケージ開発入門を読むと、Build&Reloadを使うと良いと書いてあるのでそれに従う。これはRstudioのショートカットキーとしてCtrl+Shift+Bでもできますね。ショートカットキー便利です。覚えましょう。

www.oreilly.co.jp

RStudio (非公式日本語版)

g++のパスとバージョンについて(無罪だった)

私はmingwが動いている環境なので、whichでg++を探してみましたが、問題がなかった(ほかに入っているものもバージョンは同じ) 色々入れるとPATHが頻繁に書き換わるので、今何を参照しているのかはちゃんと見た方が良いですね…。

tjo.hatenablog.com

ファイルパスに空白が入るのはダメ?

これは結局わからず。C直下にCsoundを入れることにしましたが、少なくともこれだけではダメでしたので。たぶん関係ない。

コンパイル時のLとIのオプションについて

私の場合はこれが原因でした。
Rcppはsrc/Makevars にファイルがあればコレに書かれた設定を読んで実行されますが、このオプション周りが良くなかったようです。

最終的にはSys.setenvを使用して以下のように設定をしました。

> Sys.setenv("PKG_LIBS"="-Lc:/Csound6_x64/bin/ -lcsound64 -Lc:/Csound6_x64/bin/ -lcsnd6")
> Sys.setenv("PKG_CXXFLAGS"='-Ic:/Csound6_x64/include')

これでようやく動いたのでした。
Rcppの文法とかではなくて、環境変数?とかオプションまわりだったのでなかなか初学者には厳しい戦いでした…

なんだかリンク集になってしまいましたが、いつか誰かの何かに役に立つと良いなぁ…(当時の自分に見せたい。笑)

Csoundでwavを生成するところまで

実行環境について

そういえば、書いていませんでした。
* OS: Windows10 Pro
* Csound : Version 0.9.3-beta

で行っています。

Rに食わせるwavを作っておく

初手からあまり込み入ったことをすると詰むので、まずは簡単にサイン波を作るところから行きましょう。

こちらの例

write.flossmanuals.net

から転載します。
Csoundは素人なので、素人説明になりますが、マニュアルのリンクも併せて貼っておきますので、本当の意味はそちらでご確認いただきたく。

こちらで、気になったワードを検索するのが手っ取り早いかなと思います。
The Canonical Csound Reference Manual

<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
instr 1
aSin      poscil    0dbfs/4, 440
          out       aSin
endin
</CsInstruments>
<CsScore>
i 1 0 1
</CsScore>
</CsoundSynthesizer>

何も考えず、上記のソースをコピペして、Runボタンを押してみてください。ラの音が聞こえるはずです。
440の部分が音の周波数を表しています。単位はHz。Hzは1秒間に振動する回数ですね。
440Hzはラの音の高さになります。 instr ~ endinは音を鳴らす音源の定義だと思ってもらえればよくって、今回は440Hzの音を生成するブロックなのです。
これをaSinという変数に代入して、out aSinでaSinに定義した音を出力するところまでを含んだ音源だと思えば今はたぶん良いのだと思います。

CsScoreのブロックはそのままスコアを定義していますが、i 1 0 1 は instr 1 を 0 秒から 1 秒間 実行せよということで、結果としてラのRunボタンを押すとラの音が1秒間聞こえるのです。

さて、このままでもいいのですが、Rでwavを見るときに440個の波を見るのは面倒くさいですね。 ということで1Hzの音にしてみましょう。下記のとおりです。

<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
instr 1
aSin      poscil    0dbfs/4, 1
          out       aSin
endin
</CsInstruments>
<CsScore>
i 1 0 1
</CsScore>
</CsoundSynthesizer>

単純に440を1に変えただけですね。
さて、これを同じようにRunすると実行しているようだけど何も聞こえない。1Hzは人間の聞こえる周波数の外なので実行してもわからないのでした。
まぁ、ちゃんとできてるに違いない!ということで、気にせずにwav形式で出力します。Recordというボタンを押すと、現在エディットしているファイル名に.wavの拡張子がついて、マイドキュメントあたりにあると思います。linuxだとhomeディレクトリかな?

これをバイナリエディタで開いてみると…?

52 49 46 46 08 00 00 00 57 41 56 45 66 6D 74 20
10 00 00 00 01 00 02 00 44 AC 00 00 10 B1 02 00
04 00 10 00 64 61 74 61 00 00 00 00 00 00 00 00
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
05 00 00 00 07 00 00 00 08 00 00 00 09 00 00 00

で始まる、数字の羅列と、Stirlingなどであれば右側に変な文字列が並んでいると思います。

これでとりあえず準備は完了です。
ひょっとしたら、環境によっては出方が違うかもしれませんが…

さて、次回はバイナリエディタとにらめっこしつつ、Rでこれを読んでいきます。

しかし、本業多忙のためちょっと更新が開いてしまいます。
上のバイナリを、wavのフォーマットとにらめっこして、仕様通りなのかを見ると良いかもしれませんです。

Csoundでwavファイルを生成したが

Csoundwindowsで使うべきではないのでは…

Csoundが生成する波形、本当に自分が書いたとおりなのだろうか?
…ということを確かめるために、wavファイルをRに取り込んで、値を見てみようとしてみました。
こんなことしなくても、Csoundの機能としてlinuxでは波形を見れるような記述があるが、そのopcode書いても見れませんでした。
ほかの機能でwindows上でも見れるのかしら…。

Csoundのdisplayというopcodeですが、X11とか書いてあるので、たぶんwindowsでは使えないです(サンプルコードを実行しても見れませんでした)

display

wavのお勉強

wav、あるいはwaveファイルってずっと無圧縮のデータしか存在しないのだと思っていました。が、調べてみると別にそういうわけではないということがわかりました。
しかし、実際はそんなwavファイルは見たことはないなぁ…。

wavのファイル構造については色々なサイトを参考にさせていただきました。ありがとうございました。
wav ファイルフォーマット

WAVEファイルの構造

WAV形式音声ファイル

Csoundでwavファイルを作成して再生してみようとする->再生できない

CsoundでRecordあたりを押すとwavファイルが生成されます。これをMedia Playerで再生しようとしてみたのですが、エラーが出て再生ができませんでした。
ちなみに、foobar2000でもダメだったのでMSが悪いわけではないのだと思います。

この後Rでwavを取り込むのですが、バイナリエディタでまずは上記のサイトにあるような値があるのかを見てみたのです。
このとき、バイナリエディタはStirlingを使いましたが別に何でも良いと思います。お好きなバイナリエディタで見ると良いのです。

で、wavにはファイルサイズなどの情報がヘッダに書き込まれていますが、これの値が0になっているのです。
おそらくこれが原因ではないかなぁと疑っています。実際、Rで取り込むときもこれが厄介だったのですが、ファイルサイズからヘッダサイズを引くなどで対応できるので、まぁ何とかなるかな、という感じです。拡張のあるヘッダだとダメですが、Csoundで生成したwavに限ることにすればおそらく問題はないでしょう。

ひとまず今日はここまで。次回に続く…。

もうちょっと図と表、コードなどを載せるべきですね。