YoutubeやSpotifyをそのままスクラッチ・ループ・エフェクト再生できるアプリを作りました。
ちょっと前にも似たようなの作ったけど、進化版。
YoutubeやSpotifyで流している音をそのままスクラッチしたりループさせたりできるアプリを作りました。
YoutubeやSpotify,Macのミュージックアプリ、その他なんでも再生している曲(音ならなんでも)に対して、リアルタイムに以下のことができます。
Releases · kyab/Going-Zero · GitHub
Going-Zero-Installer.pkgをダウンロード・実行してください。ごめんなさいMac専用です。
機能説明動画。練習不足で曲が台無しになってる気もするけど、気にしない。
動かないとかあればTwitterまで!
Macで流れている音楽をなんでもスクラッチするアプリを作りました。
Macで流れている音楽をなんでもスクラッチするアプリ "Scratch Now" を作りました。
Spotify,Youtube, ミュージックアプリやDAWからの出音など、Macでなっている音ならほぼ全てスクラッチできます。
仕組みとしては、仮想ループバックデバイスがイントールされ、デフォルトの音声出力をアプリ起動時に切り替えることで、アプリからの音がスクラッチできます。
仮想デバイス(音量メニューバーにScratch Nowと表示される)は独自なので、SoundFlowerなどの他の仮想デバイスのインストールは不要です。
なお、インストール後、再起動が必要です。そのうちインストーラの改善で不要になるかも。
ダウンロードはこちらから。ソースもあります。
アンインストールする場合は、"Scratch Now"アプリをゴミ箱に捨てて、その後 /Library/Audio/Plug-Ins/HAL フォルダからScratch Now Driver.driverフォルダもゴミ箱に捨ててください。まぁ残してても無害です。
動作しないなどあればTwitterまで。
Spleeterを使ってMacで流れる音楽を(5秒遅れで)楽器毎に音量調整できるアプリを作った(v0.2)
Spleeterを使ってMacで流れる音楽を(5秒遅れで)楽器毎に音量調整できるアプリを作った
Late5
Spleeterを使って、Macで流れる音楽のボーカル・ドラム・ベース・ピアノ・その他のパートを個別にリアルタイムで音量調整できるアプリを作りました。
下記からダウンロードできます。
デモ。Spotifyで流している曲の楽器を個別に音量調整しています。
リアルタイムと言いつつ、実は音声は約5秒遅延して流れます。
この遅延は入力された音声を数秒毎に区切ってからSpleeterで分離して出力しているためで、SpotifyやYoutubeで次の曲へのスキップやシークをしても、反応は5秒遅れます・・。
Spleeter
Spleeterは機械学習を使って、入力された音楽をstemと呼ばれる楽器別の音声に分けることができる音源素材分離ライブラリ(エンジン?)です。Deezerがオープンソースとしてしています。事前に用意されたトレーニング済みの分離モデルを使うこともできるし、その気になれば自分で用意したデータをもとにトレーニングさせることもできるようです。かなり良く分離してくれます。AI恐るべし。
事前準備
今はストリーミングサービス全盛なので、曲を手元にファイルとして持っていないケースも多いと思います。
そこで、Late5では音声ループバックデバイスの利用を前提としています。これにより、iTunesだろうがSpotifyだろうがYoutubeだろうが、流れている音声は全て分離できます。
そのループバックデバイスを備えたアプリとして"Background Music"の事前インストールが必要です。"Background Music"はアプリ毎の音量調整ができるアプリなのですが、ついでにループバックデバイスをインストールしてくれます(SoundFlowerをご存知の方は似たようなものと思ってください)。
Background Music
https://github.com/kyleneideck/BackgroundMusic/releases/tag/0.4.0-DEBUG-f20dd57
注意が必要なのは、Background Musicのインストールは必要なものの、欲しいのはループバックデバイスだけなので、"Background Music"アプリをメニューバーから終了しておく必要があることです。そうしないと音声が2重で聞こえてしまいます。
その後、ここからLate5_v0.1.dmgをダウンロードして、SpotifyやYoutube, iTunesなどで音楽を流しながら起動してください。しばらく待つと、5つの音量調整スライダーがついたウィンドウが出ます。あとは各スライダーで音量調整。
うまく動かなかったり、エラーが出る場合はTwitterやGithubで報告いただけると幸いです。
今後はPANを調整したり、楽器毎にフィルターをかけれたらいいなぁと考えています。
mrubyでのありがちなミス4つ
よくやってしまう+これからもやってしまうこと。
(2014/4/28追記:1つめにmruby-clang-plugin紹介)
(2014/4/6追記:4つめ)
mrb_funcall()の可変引数部分は全部mrb_valueじゃないといけない。
ついついintなどをそのまま突っ込んじゃう。可変引数なので何を入れてもコンパイルできちゃう。けど動かない。以下使ってmrb_valueにしてから使う。
([twitter:@take_cheeze]さんのmruby-clang-pluginを使えばclang使ってコンパイル時にチェックできるぽいです。自分はまだ試せてない。。)
RSTRING_PTR()はNULL終端とは限らない。
mrb_valueの中身が文字列の場合にRSTRING_PTR()使ってchar *取り出せるけどNULL終端とは限らない。printf()に渡して死ぬ。NULL終端が欲しい場合は
- RSTRING_LEN()で長さを把握して使う
- mrb_str_to_cstr()でNULL終端文字列を新たに作る。内部で新たにオブジェクトが作られるのでメモリ気にするときは注意。
マクロ定義(MRB_XXX)のミスマッチ
build_config.rbでconf.cc.definesにMRB_WORD_BOXINGとか追加したのに、mrubyを使うプログラム側をコンパイルするときに-DMRB_WORD_BOXING追加するのを忘れてmrb_open()が落ちる/止まる。
使えるMRB_XXXXはinclude/mrbconf.hにあって、全部が全部使う側のプログラムでも指定しないと問題になるわけではないけど、MRB_WORD_BOXING, MRB_USE_FLOAT,MRB_NAN_BOXING,MRB_ENDIAN_BIGあたりは根幹となるmrb_value構造体のレイアウトが変わったりするので特に注意。それ以外も今後変わる可能性があるので基本全部合わせるべき。
自分の場合はメモリ節約しようとして試行錯誤しているうちにbuild_config.rbと使う側のMakefileを合わせるのを忘れて「mrb_open()が返ってこない。メモリ足らない?いやまだ足りてるはず」とかでハマる。
この辺は色々議論もされてるみたいなので改善されていきそう。でもbuild_config.rbはRubyで書けるし、自動生成してもいいし、自分でも工夫できる。
CRubyのGVLとビジーループ
mrubyのVMのマルチスレッド対応がgithubにて議論されています。
multi-thread support on the RiteVM #1657
自分もthread-safeなVMが欲しいなぁと思っています。STM32F4DiscoveryにFreeRTOS載せて使ってみたい。
並行・並列処理の戦国時代?
さて、マルチコアが普通になったし、WebアプリのC10K問題があるので並行・並列処理は昨今のトピックです(多分)。
ただマルチスレッドプログラミングは難しすぎ!というは昔から言われていたことです。
で、もうちょい並行・並列処理を書きやすく出来ないのかよ?ということでErlang,go,Scala(Actor),EventMachine,Thread pool ,node.js,deferred,future,java.util.concurrent色々出てきました (言語とライブラリ、書き方のスタイルごちゃまぜですが)。全部見てたら頭おかしくなりそう・・・。
勝手にまとめると
まず、
A「どうせボトルネックはファイル読み書きとかネットワーク、つまりI/Oじゃん。その辺は全部ノンブロッキング/非同期API使おうぜ!イベントドリブン最強!」
っていうのがありますね。node.jsなんかはシングルスレッドですが、パフォーマンスも良いみたいだし一定の支持を得てる。ただコールバック地獄になったりするんで、いい感じに書けるようにDeferredとかasyncみたいなスタイルも併用。EventMachineもチラッとしか見てないけど似たような感じ? 排他を気にしなくて良いのは嬉しい。
B「俺のマシンはマルチコアなのになんで一つだけ100%で他のやつは暇してるんだ。納得出来ない! 派」
無意味にスレッド増やしてもコア数はそんな多くないので重い計算はいい感じに書くと勝手に並列化してくれると嬉しい。C#のParallelとかJavaのjava.util.Concurrentとか?並列コンテナってやつ。スレッドプールで使いまわしてコストも低め。
C「forkすればいいじゃん。派」
一番簡単にマルチコア使えますね。parallel gemなんか使うとforkした子プロセスから計算結果返すのも余裕。cygwinみたいに死ぬ気でやればWindowsもOK?
結局どうなのよ?
実際にはAとB(とC)は常に完全に別れた要求でもなくて、要するに
「わけわかんねぇけど俺はいい感じに書きたいだけ!I/Oは使えるならノンブロッキング使うとか、重い計算はマルチコア使いまくりでよきにはからってよ!あ、コンテキストスイッチに時間使うとか勘弁ね。あと俺は普通のマルチスレッドとMutexで排他するスタイルではもう書かないって決めてるから。後もう一回言うけどいい感じに書きたいから」
ってことですかね。
アクターモデルってのがあります。Erlangで有名になって、Scalaも売り(の一つ)にしてるし、goではgoroutine、あとRubiniusもActor持ってます。
「Don't communicate by sharing memory; share memory by communicating. 」とか優等生っぽい。ただ馴染めるのかまだよくわかりません。ある程度使ってみないとなぁ。goの本積みっぱなし。
そうそう、並行と並列って言葉ですが、こんな感じ?
並行(concurrent):処理を別々に分けて書くこと(同時に実行されるとは限らない)。ふつうのマルチスレッドで書く場合
並列(parallel) : 複数の計算が同時に実行されること。マルチコアが同時に動いてるイメージ
忘れたら"並行 並列 違い"でググればOK.
CRubyのGVL(GIL)
CRubyのThreadクラスは1.8.xまではグリーンスレッドで実装されていました。で、1.9.0ではインタプリタからVMになって、ThreadクラスもNative Thread(OSが提供してるスレッド)で実装されるようになったというのは有名な話。
ただNative Threadになったけど、マルチコアを(ほとんど)活かせない。なぜならVMが動く際、基本的に一つの巨大な排他をしてるから。この排他というかロックをGVL(Giant VM Lock)またはGIL(Giant Interpreter Lock)と呼ぶ。Native ThreadだからVMが検知できないタイミングでスレッドは切り替わろうとするけど、ロックされてるからまた元のスレッドに戻ってくる(正確にはロックを待っている方のスレッドはOSのスケジューラのキューに入らない)。
GVLはブロッキングするようなC関数(典型的には各OSのSleep()。write()とかも?)呼び出し中には解放される。というかCで書かれたライブラリ中でそういう風に作ってる。なのでその際他のThreadクラスのインスタンスも動作できる。この時だけはマルチコアが同時に動く(可能性がある)。
C拡張は基本的にGVLがかかった状態で呼び出される。これをしないと、C拡張を常に注意深くスレッドセーフにする必要があるし、そういうことをすべてのC拡張開発者に求めるのはキツかろう、という判断みたい。
ただ、C拡張の中でブロッキング関数を呼び出したり、純粋な重い処理をやるときにGVLを解放してやることはできる。その為にrb_thread_blocking_region()というのが用意されている。よってC拡張の中でrb_thread_blocking_region()が使われている場合はマルチコアが同時に動く(可能性がある)。
GVLはCRubyの実装を単純に保ち、かつシングルスレッド性能を落とさないためには今の所まぁしょうがないよね〜。forkでも使えば〜。っていうところらしい。あとは複数のVMを使う(MVM:multi VM)というのも今後CRubyではあり得る感じ。今はCRubyでサクッとMVMするのは難しいけど、mrubyは簡単にできる。mruby-threadというmrbgemはスレッドごとにVMを分けてる。
で、それで満足できない場合は、JRuby、Rubinius、RubyMotionあたりに逃げちゃう手がある。これらにはGVLがない。内部的には所々排他処理してるけど、GVLよりもっと細かく、複数のMutexをつかってやるらしい。これをfine grained lockなどと呼んでGVLと区別してる。
JRubyはJavaだから置いておくとして、RubiniusはC拡張の安全性はどうすんの?って思ったら、前までC拡張にはGVLでロックをかけていたけど、最近はデフォルトでそれもdisableにしたらしい。それでも「みんなフツーに使えてるよ」とのことらしいけど、まぁ楽観的な考えなのかな。CRubyとはユーザベースも違うとは思うけど。
Thread内でのビジーループ
で、そこまで調べて気になったのが、じゃぁ例えば2つのスレッドが(内部でGVLを開放するようなメソッドを一切呼ばないで)ひたすらループしてたらどうなんの?ってこと。片方がGVLをロックしっぱなしだと、もう一方のスレッドは動作するチャンスがなくなっちゃうのでは?
結論からいうと、そんなケースでも時々スレッドは切り替わる。以下の凄まじく長いGVL(=GIL)に関する記事によると、タイマースレッドというのが裏で動いていて、時々フラグを立てるらしい。タイマースレッドはCレベルで書かれていて、もちろんGVLと関係なく動く。そんなこともしてたのか・・。
Nobody understands the GIL - Part 2: Implementation
本当なのか以下のコードで実験してみた。2つのスレッドを作って、aという変数を書き換えるだけ。各スレッドはaの値が書き換わっていたら、その回数を記録する。
a = 1 thread_a_preempted_count = 0 thread_b_preempted_count = 0 t1 = Thread.new do 10000000.times do|i| if (a != 1) thread_a_preempted_count += 1 end a = 1 end end t2 = Thread.new do 10000000.times do |i| if (a != 2) thread_b_preempted_count +=1 end a = 2 end end t1.join t2.join p thread_a_preempted_count p thread_b_preempted_count
Windowsでの実行結果:
C:\home\work\ruby_concurrent>ruby --version ruby 2.0.0p353 (2013-11-22) [i386-mingw32] C:\home\work\ruby_concurrent>ruby threadatomic.rb 30 34
Mac OSXでの実行結果
[koji@macbookpro:~/work/ruby_concurrent]$ ruby --version ruby 2.0.0p247 (2013-06-27 revision 41674) [universal.x86_64-darwin13] [koji@macbookpro:~/work/ruby_concurrent]$ ruby thread_preempt_busy_loop.rb 1 6
ということで確かにビジーループでも時々スレッドは切り替わる。Native Threadがプリエンプションでちゃんと切り替わるのと同じ感覚で使える。
あと、確かにタイマースレッドが存在することも確認できた。Rubyコード上ではスレッドは3つだけど、プロセスレベルでは4つスレッドがある。
秋葉原のmrubyイベントでデモさせてもらった。
秋葉原で開催された東京Rubyプレゼンテーション2014にパネラーとして参加させていただきました。[twitter:@monamour555]さんの紹介に多謝。
自己紹介でホストベースのmirb について簡単にデモしましたのでスライドおいて置きます。
http://www.slideshare.net/kojiyoshioka7/ruby2014-mruby-30670841
はてなに貼る方法がよくわからん。
mruby安定版が発表されたり、あと特にmruby関係者の面々と直接話せてとても有意義でした。
matzに気になっていたマルチスレッド/マルチタスク対応について聞いたところ、POSIX/Windows系以外の組込系のタスクが色々(あんま知らない)なのもあってもう少し検討される感じ。