0

wavファイルをRubyで編集する

Rubyでwavファイルのフォーマットを読んだり、データチャンクを編集するためのgemを作った。

ベースは以前作ったやつで、ついでにwavファイル操作のサンプルをたくさん追加してrubygems.orgに登録しておいた。
あくまでサーバーで音を合成して返すようなwebサービスで使う事を想定している物で、マイクからの入力をリアルタイムに音声処理するための物ではない。

■インストール

gem install wav-file


■使う

フォーマットとデータチャンクを読む
require 'rubygems'
require 'wav-file'

f = open("input.wav")
format = WavFile::readFormat(f)
dataChunk = WavFile::readDataChunk(f)
f.close

puts format

するとこんな感じにフォーマットが取れる。
Format ID:      1
Channels: 2
Sampling Ratio: 48000 (Hz)
Byte per Sec: 192000
Bit per Sample: 16
Block Size: 4


バイナリからwavの波形を配列として取り出す
bit = 's*' if format.bitPerSample == 16 # int16_t
bit = 'c*' if format.bitPerSample == 8 # signed char
wavs = dataChunk.data.unpack(bit) # read binary


音量を半分にしてみる
wavs = wavs.map{|w| w/2}


逆再生にして、バイナリに戻す
dataChunk.data = wavs.reverse.pack(bit) # reverse


wavファイルに保存する
open("output.wav", "w"){|out|
WavFile::write(out, format, [dataChunk])
}


samplesディレクトリの中に色々と例を入れておいた。githubからも見れる。
  • 音量が小さいのを大きくする
  • 左右チャンネルを分けて保存する
  • wavファイル同士を連結する
  • wavファイル同士を重ねて同時に鳴らす
  • 再生速度を上げる
  • 波形をグラフにする
とかまあ色々入れておいた。


■tips
使う前にwavファイルのフォーマットについて理解しておいた方が良いかもしれない。
wav ファイルフォーマットが参考になる。


あと、複数のwavファイルを合成して新しいファイルを作る場合、操作する前にffmpegでformatを揃えた方が良い。その方が楽だし高速。
adjust_wav_format.rbにやり方を書いておいた。

サンプルを実行するのに必要なwavも、ffmpegでmp3とかから変換して作れる。
ffmpeg -i input.mp3 -ac 2 -ar 44100 output.wav

0

serial-socket-gateway

よくある シリアル通信 <=> TCP Socket する物を作った。
しばらく使ってて特に問題なかったので、使い方とか書いておく。
Mac OSX LeopardとUbuntu 9.04,と10.04とWindows XPで動作した。

普通のsocketなので大抵のプログラム言語から使えるし、少なくとも10個ぐらいのプログラムを下にぶらさげてマイコンと通信させられる。
最近はコレでsocket化したシリアルデバイスにJRubyで作ったGUIをつないで色々やってる。


■ソースコード
githubにある
http://github.com/shokai/serial-socket-gateway


■インストール

git clone git://github.com/shokai/serial-socket-gateway.git

中のserial-socket-gatewayがそれ。Rubyで書かれている。
単体のファイルで動くので、そのまま /usr/local/bin/ とか適当なパスの通っているディレクトリに cp すればいいと思います


■必要なgemのインストール
ruby-serialportとeventmachineを使っているのでそれぞれインストールする。

gem install eventmachine ArgsParser


ruby-serialportは0.7が動かなかったので、俺は0.6を使っている。MacやUbuntuはソースからビルドしてインストーrうする
wget http://rubyforge.org/frs/download.php/72/ruby-serialport-0.6.tar.gz
tar -zxvf ruby-serialport-0.6.tar.gz
cd ruby-serialport-0.6
ruby extconf.rb
make
sudo make install


Windowsは、http://rubyforge.org/tracker/download.php/61/321/9924/1800/ruby-serialport-0.6.0-mswin32-gem.zipからwindows用のバイナリ(gem)を持ってきて、解凍してインストール。
gem install serialport-0.6.0-mswin32.gem


■起動
Arduino等をMacに接続すると /dev/ の下に、tty.usbなんとかというデバイスができているはず。
serial-socket-gateway /dev/tty.usbserial-A7006Rqn
デバイス名を引数に渡して起動。


接続してみる。telnetでok
telnet localhost 8782
デフォルトで8782番portで起動している。port番号はソースの上の方をいじれば変えられる。


■通信の仕様
改行(¥n)区切りで、1行毎に通信している。改行が来るまで送受信しない。


■サンプルプログラム Arduinoから送信
こんなかんじでADコンバータで電圧測って、Serial.println()を使って末尾に改行を付けて9600bpsパリティなしストップビット1で送信。
int ad_pin = 0;

void setup(){
Serial.begin(9600);
}

void loop(){
int ad = analogRead(ad_pin);
Serial.println(ad);
}


■サンプルプログラム Rubyで受信
間にserial-socket-gatewayを挟んでシリアルデバイスと通信する。

require 'socket'
s = TCPSocket.open("192.168.1.100", 8782)

loop do
res = s.gets
if res.to_s.size > 0
puts res
end
end
serial-socket-gatewayはclientとの接続が切れていないか確認する為に、たまに空文字列を送ってくる。
受信した文字列の長さを見て無視してください

送信は
s.puts "abc abc"
rubyのputsは末尾に改行(¥n)が付く。



■サンプルプログラム Rubyで送受信を同時に
標準入力をserial-socket-gatewayにそのまま送る。こういうのeventmachineでやると楽だね
#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
require 'eventmachine'

HOST = 'localhost'
PORT = 8782

begin
s = TCPSocket.open(HOST, PORT)
rescue => e
STDERR.puts e
exit 1
end

EventMachine::run do
EventMachine::defer do
loop do
res = s.gets
exit unless res
if res.to_s.size > 0
puts res
end
end
end

EventMachine::defer do
loop do
s.puts gets
end
end
end


eventmachineが便利!!

0

skype-socket-gatewayを作った

Skypeのグループチャットにbotを放ったり色々したかったので作った。

Skype APIはSkypeが起動しているマシンの中でSkypeのプログラムと自分のアプリを通信させて使う物で、developer.skype.comにAPIのドキュメントがある。電話をかけたりチャットの中身を取得したり発言したりできる。

Skypeが起動しているマシンでskype socket gatewayを起動しておくと、他のマシンからもtelnetでSkype APIが使える。また、全チャット(含むグループチャット)のデータがJSONでどんどん流れてくるようになっている。

うちではWindows XPのネットブックでbotアカウントでSkypeを起動してあった。Skype chatはP2Pなのでログをそれぞれが持っていて、後で接続してきた人は先に居た人から取得する様になっている。だから、ただ何もしなくてもずっとオンラインの人がいるだけでログの流れが良くなる。
で、そのbotアカウントを真なるbotにするために、Windows用のActiveScriptRuby 1.8.7でskype socket gatewayを動かしてそこに別のLinuxのサーバーから複数のbotが接続して色々処理をしている。

Linux上で動くSkype用のbotを作る方法 – muddy brown thangにXvfbを使ってlinuxで仮想フレームバッファを作ってSkype APIを使う方法が解説されてるけど、なんかLinuxが難しくて挫折したのでwindowsでも動くsocket gatewayを作った。


■ソースコード
githubに全部置いておいた
shokai's skype-socket-gateway at master – GitHub


■インストール

git clone git://github.com/shokai/skype-socket-gateway.git

必要なgemもインストールする
gem install Ruby4Skype json eventmachine

Ruby4Skypeというのを使っているので、Windows、Mac、Linuxで動く。ただしRuby4Skypeが64bit MacOSXでは動かない。Linuxも、Ruby4Skypeのlib/skype/os/linux.rbを修正してinitialize methodのsuperにapp_nameを渡すように修正しないと動かない。

それとWindowsでRuby4Skypeを動かすには色々DLLを入れないとダメ → WinXPでgem install Ruby4Skypeするメモ – yuisekiのいまさらruby厨日記 – Rubyist


■起動する
先にSkypeを起動しておく。

ruby skype-gateway.rb
でport 20000で起動する。


■使う
telnetで接続する。
telnet 192.168.1.37 20000

そしてそのまま
CALL shokaishokai
MESSAGE shokaishokai hello work!!
とか書く。Skype APIがそのまま実行できる。
レスポンスはJSONで来る。

あと、接続しているだけで全てのchatの発言が流れてくる。


■Windowsのスタートアップに登録する
run_skype_gateway.bat というバッチファイルを作る
"C:\Program Files\ruby-1.8\bin\ruby.exe" "C:\Document and Settings/sho/src/ruby/skype-socket-gateway/skype-gateway.rb"
中身はこの1行でいい。これをスタートメニューのスタートアップに入れておけば自動起動できる。


■Skype botを作る
RubyだとTCPSocketクラスを使うと簡単に作れる。

キーワードに反応する単純なbotの例。誰かが「ざんまい」と書くと、即座に「ざんまい行きたい!」と返す。
samples/zanmai_bot.rb at master from shokai's skype-socket-gateway – GitHub
require 'rubygems'
require 'socket'
require 'json'
require 'eventmachine'
$KCODE = 'u'

HOST = "192.168.1.37"
PORT = 20000

begin
s = TCPSocket.open(HOST, PORT)
s.puts "MESSAGE shokaishokai ざんまいbot start"
rescue => e
STDERR.puts e
exit 1
end

EventMachine::run do

EventMachine::defer do
loop do
res = s.gets
exit unless res
res = JSON.parse(res) rescue next
p res
if res['type'] == 'chat_message'
if res['body'] =~ /ざんまい/ # キーワードに反応
s.puts "CHATMESSAGE #{res['chat']} ざんまい行きたい!"
elsif res['body'] =~ /かず(すけ|助)/
s.puts "CHATMESSAGE #{res['chat']} かずにゃんぺろぺろ"
end
end
end
end

EventMachine::defer do
loop do
mes = gets
s.puts mes if mes
end
end

end


これにもう少し語彙を追加すると、だいたい俺の人格と機能を8割方再現したbotが作れる。
shokai_bot.rb at master from shokai's skype-bots – GitHub
6a8dae3e0028f2f870f194cdbec2af75.png


あと、全chatをmongo dbに入れて検索したりできるbotなんかもいる。超便利。
tools/skype_chat_store_mongod.rb at master from shokai's skype-socket-gateway – GitHub
fecba229c251c20a4543e21bd1395929.png


はてなランドに自分のskypeの発言を送るなんて事もできる
hatenaland_proxy.rb at master from shokai's skype-bots – GitHub


■EventMachine
EventMachineというサイバーパンクな名前のライブラリがRubyにあって、これを使うと並行処理が簡単に書けた。
skype socket gatewayでは
  • 標準入力をSkype APIとして実行する
  • 接続してきたTCP Socketクライアントそれぞれからの入力をSkype APIとして実行し、結果をそれぞれに返す
  • 全ソケットクライアントに全chat logを送る
  • クライアントが接続されているかたまに確認する
という処理をやっていて、これらを適当にEventMachine::defer doの中でloopで書いてやればうまく動いた。

不思議だったのは、新しくacceptしたsocket clientオブジェクトそれぞれを個別に処理するためにEventMachine::deferの中でclient毎にそれぞれEventMachine::deferを作ってみたら、ちゃんとスコープが別になってくれた。
  EventMachine::defer do
loop do
s = sock.accept
clients << s
puts "--- new client : #{clients.size}"

# socket -> invoke Skype API
EventMachine::defer do
loop do
cmd = s.gets
next if cmd == nil or cmd.to_s.size < 1
puts "recv => #{cmd}"
begin
p res = {
:type => 'api_response',
:body => skype.invoke(cmd).to_s
}
rescue => e
res = {
:type => 'error',
:body => 'skype api invoke error'
}
STDERR.puts e
end
begin
s.puts "\n"+res.to_json
rescue => e
STDERR.puts e
c.close
break
end
end
end
end
end


それぞれのdeferの中でAPI呼び出しと結果を返す処理が動いている。これをthreadでやる時
Thread::start(s) do |s|
loop do
res = s.gets
# 何か処理
s.puts response.to_json
sleep 0.01
end
end
みたいに引数に渡さなくても勝手にスコープ分けてくれてた。


でも、clientの配列とかchatメッセージのqueueを作って複数のEventMachine::deferからdeleteしたりpushしたりしていじっている部分では、defer間で共有されていた。不思議だ。
EventMachine::run do
# forward all skype chats -> socket
EventMachine::defer do
loop do
if chat_msgs.size > 0
msg = "\n"+chat_msgs.shift.to_json
clients.each{|c|
begin
c.puts msg
rescue => e
STDERR.puts e
end
}
end
end
end

# check clients connection
EventMachine::defer do
loop do
msg = ''
errors = Array.new
clients.each{|c|
begin
c.puts msg
rescue => e
STDERR.puts e
errors << c
end
}
errors.each{|c|
clients.delete(c)
c.close
}
sleep 15
end
end

よくわからないけどEventMachineが凄い。

1

im.kayac.comのgem作った

im.kayac.comのAPIを使うためのgemを作った。
im.kayac.comは自分のアプリからiPhoneにpush通知したり、自分のgoogle talkにメッセージを送ったりするのに便利なwebサービスで、俺はよく使ってる。

研究室のwikiの更新情報を自分のケータイに通知したりとか、あとtwitter検索の結果を通知したりとかするアプリの中で使う為にこのgemを作った。Androidケータイがgoogle talkをすごいレスポンスで受信できるので、我ながらいいアプリ作ったと思っている。


■インストール

gem install im-kayac


■使う
require 'rubygems'
require 'im-kayac'

begin
p ImKayac.post("username", "hello world")
rescue => e
STDERR.puts e
end


im.kayac.comの設定でパスワード認証をしているなら、引数で渡す。
ImKayac.post("username", "hello world", {:password => 'your-password'})


秘密鍵認証の場合はこうする。
require 'digest/sha1'

message = 'hello world'
sig = Digest::SHA1.hexdigest(message + 'your-sig')
p ImKayac.post("username", message, {:sig => sig})


■ソースコードとか

1

hugeurlというgemを作った

ふげではなくヒュージ。
tinyurlやbit.lyなどの短縮URLを展開できる。


■インストール

gem install hugeurl


■使う
require 'rubygems'
require 'hugeurl'
puts URI.parse("http://bit.ly/d4VYD2").to_huge
するとhttp://shokai.orgのURI::HTTPインスタンスが返ってくる

もしくは
Hugeurl.get("http://bit.ly/d4VYD2")
でもいい。

展開には http://search.twitter.com/hugeurl?url= を使っている。