2016/06/23(木)WinampのDSPプラグインをC#から使う

はてブ数 2016/06/23 00:43 プログラミング::C#つーさ

Winamp をしっていますか……。

DSPプラグインの作り方は、WinampのPluginのSDKが公開されてるので、
それとおんなじ様に呼んであげれば、できる? と思ったのと、
Winampのプラグインって関数ポインタを内部の構造体に入れてその構造体へのポインタを返してくるような形してて、
それらのAPIのP/Invoke・マーシャリングの仕方に興味がわいたのでやってみてた。

標準でついてるプラグイン dsp_sps.dll だけは、ロードしたとたんに落ちちゃう。実装を追いかけて調べてみたところ、
Winampには親ウィンドウにWM_USERを投げて関数ポインタの詰まった構造体をもらって、そこからいろんなAPIを呼ぶ機能があることと、
その子には「非対応だぜエラー!」値を返しても、それをそのままポインタとしてメモリアクセスして死んじゃうらしいことがわかった。
このAPI群を実装する気はまったく起こらないので、とりあえずいったんあきらめていつもの SA Stereo Tool を動かしてみた。

インストール済みのプラグインの名前が取得できた。SA Stereo Tool から音が出た。

satoolssharp.png

ソース
https://github.com/ttsuki/ttsuki/blob/e421ad0a3f6e1a51f72aa2d58b231263c7f87d2d/DllPInvoke/WinampDspPlugin.cs
関数ポインタを入れた構造体を返してくるようなAPIはあんまりないけど、マーシャリングしたい場合は、
関数ポインタに戻り値・引数・Calling Conventionもあわせた delegate を宣言して、
それを構造体のメンバとして宣言した状態で、マーシャリングすれば、勝手に関数ポインタからデリゲートを作ってくれる。
が、その方法だと、どうもNULLチェックができないっぽい? ので、今回は構造体のメンバはIntPtrとして受け取った後に、
関数ポインタをラップするデリゲートを手で作っている。

使い道……?
音ゲーとか作ったときに、Winampのプラグインを(VSTがわりに)エフェクターとして使えるかもしれないなとは思う。
SA Toolsなんかは、ヘボいスピーカでもそれなりに映えるような音に変えてくれるし。
レイテンシの問題はあって、それを回避するには、鳴らした場合の音と鳴らしてない場合の音を両方作っとくみたいな手もあるんだけど、
そもそもWinampのDSPは、(DLLファイルを物理的に複製でもしない限り)設計からして複数インスタンス生成できないし、
フィードバックがあるエフェクタは内部状態を再現するのが無理ゲーなので、1インスタンスでちょっと前に戻ることもできなくて、
リアルタイムにミックスする必要のあるゲームアプリケーション*1では、いまいち使えないかも。

他、自作アプリからDSPがドライブできれば、
自分のミュージックライブラリの中の曲を、外出先の自分の携帯電話から好みのDSP通して聴けるのでは、というのは、ちょっと思う。
上で試してるSA Stereo Toolの場合は、パイプで波形処理してくれるコマンドラインツールがあるのでそれを使った方がいいんだけど。
最近コメントがついてた、HSPで作ってたパイプ使ったプロセス間通信のラッパーみたいなのが要る。

余談だけど、コミットと本記事の公開に時差があるのは
ソースコミットの後に記事書こうと思って先のスクリーンショットをブログにあげたらなぜかサムネイルが作れなくて、
調べたらブログを動かしてるサーバのImageMagickのバージョンをあげたときから PNG 読めなくなってたらしく、
ここ2日ばかりあれこれ苦労して、ようやく再インストールできたからである。。
nice make 中に突然再起動しはじめたりとかして、ちょっとこのサーバもなんとかしたい。

あと、最近このブログがページの表示に2秒とかかかる激重状態になってたのは、
サーバが貧弱だからかなーとか思ってたけどいくらなんでもと思って調べたら、
データディレクトリとキャッシュディレクトリのオーナーとパーミッションがおかしいせいだった。
なおしたのでさくさく動くようになった。わーい。

ぐぅ

*1 : いわゆるキー音のある何か

2013/12/01(日)ぼくんちのNASで動いているValue Domain DDNS Updator

「つーさのくーかん」は、ぼくんちのNAS上で動いてるのですが、
プロバイダを変えてからちょこちょこIPアドレスが変わるようになり、
GoogleのWebmasterツールから、サイトにアクセスできない!と怒られては、
手動でDNSレコードを更新していたりということをやっていたのですが、
最近この作業にうんざりしてきていたので、
ようやくValue DomainのDDNSを更新するためのプログラムを作ることにしました。

DNS更新には、自分のグローバルIPが要るのですが、これをどっから取ってくるか。
手っ取り早くどっか外部サイトのサービスを呼ぶ手もあるにはありますが、
やっぱ自宅鯖してるくらいだからその辺は閉じていたいとも思いまして、
UPnPでルータに問い合わせることにしようと思いました。

予てから、UPnPを使って自分のグローバルIPアドレスを得るC#プログラムを公開してましたが*1
こいつは、Windowsなupnp.dllを使ってる関係で、Linuxでは動かなかったので、
NASが定期的に自分のIPを確認してDDNSに登録することはできなかった。

LinuxでUPnPと言われても門外漢で、
ちょっと調べるとgupnp-toolsとかいうのもあるらしいのですが、
Xなしで動くのかどうかよくわからなかったので、
まぁ、自分で書くことにしました。

https://gist.github.com/ttsuki/7723258

書きました。

VSでexeをビルドして、NASへコピーしました。

nohup mono ValueDomainDDNSUpdator.exe > vdddnsu.log 2>&1 &
echo kill $! > kill-vdddnsu.sh

動きました。

これからは安定して、サイトにアクセスできるようになると思います。

UPnPは、最初にUDP multicastでネットワーク上にいるデバイスを探すのですが、
今回はルータのIPが既知、かつ、変化しないので、その辺はすっ飛ばしました。
って、考えたらコントロールURLも変わらないはずだし、もっと楽できたなぁ……。
反省です。

おわりです。

2013/11/07(木)C# on Windows でMP3をデコードする

はてブ数 2013/11/07 01:04 プログラミング::C#つーさ

Windowsには昔からAudioCodecManagerという音声形式を色々扱える系のAPI群があるので、
それをP/InvokeでC#から呼び出してMP3をデコードすることができる。

Windows 95の頃には既に存在した古のAPIであるが、
Windowsの機能を使うので、ライセンス的にも特に怖くないし、
対象プラットフォームがWindowsで、かつ目的がMP3の仕組みを勉強するのでなければ、
これでいいよね。

https://github.com/ttsuki/ttsuki/blob/master/WinMM/AcmMp3Decoder.cs

2013/01/25(金)C#からDirectShowを使って動画のビットマップのポインタを得てDirect3Dでムービーテクスチャにもできる動画再生ライブラリを作ったのでNYSLで公開しました。

はてブ数 2013/01/25 23:36 プログラミング::C#つーさ

本日(じゃないけど)のアウトプット。
動画プレーヤーつくりました。

今更DirectShowのSampleGrabberを使えるようになってみたり。
再生するというよりは、ビットマップのポインタを得てぐりぐり加工する向けですな。

    /// 
    /// DirectShowのSampleGrabberGraph を使ってメディアファイルを再生するサンプル。
    /// SampleGrabberを使うメリットは、ビデオもオーディオもデコード後のデータのポインタが得られることで、
    /// Marshal.Copyなどでbyte[]にコピーするなり、unsafeコンテキストを使うなりで手軽に加工することができる。
    /// ゲームアプリなどではDynamicTextureに転送することで簡単にムービーテクスチャを作ることもできる。
    /// VMR9を使った方がパフォーマンスが出るが、Device LostがOSのバージョンごとに挙動が違ったりで対応しきれない。
    /// カスタムレンダラを作るのが最良の方法なのだろうけれど、C#のみでカスタムレンダラを作るのは至難の業。
    /// Enter:開く, Space:再生/一時停止, Escape:停止 ←/→:5秒シーク
    /// 

例によってソースをNYSLで公開してます。
https://github.com/ttsuki/ttsuki-csmix/tree/master/WindowsUtil/DirectShow/

例によって、このコードをあなたのプログラムにコピペすると、
特にアセンブリを追加することなくEXE単体で動画再生ができます。
DirectShow.net とか使ってない。

C#の動画再生あれこれと言えば……。

たとえば、Managed DirectXには AudioVideoPlayback というクラスがあって、
実はManaged Direct3Dのテクスチャに転送する機能もついていることにはいるんですが、
強制的に、画面のFPSが動画のFPSに合わせられてしまったりするので、
(ただ再生するならそれでいいですが、ゲーム中のテクスチャとして使うには)非常に扱いが難しいです。

VMR9を使うと動画のフレームを描画したテクスチャを直接受け取ることができなくもないんですが、
これまた動画のレンダリング中(テクスチャを投入してから、次のフレーム時刻になって、テクスチャ描画されて戻ってくるまで)は、
描画スレッド側でDirect3DのAPIが呼べないため、60FPSを維持しながら動画再生するのはなかなか至難の業っぽく。
動画に1FPSの区間があったりすると、ゲームアプリ側も巻き込まれて1FPSになるんですよね、はい。

また、VMR9はDevice Lostしたとき、Pinを切ってDirect3DをResetしてPinをつなぐとWinXPでは動いていたんですが、
Win8にしたらそのコードではCOMExceptionが出るようなので、結局Graphをいったん破棄してとか。
当時はDirectShowに対する知識も浅かったので(未だに浅いですが)使い方間違ってたかもなぁ……。

という感じで一周してきて、結局頼れるのはSampleGrabberですね!

Media Foundation API とか使うとmp4とかも開けるようになるみたいで、そっちも使えてみたいなぁ。
APIのネーミングとかをさらっと見た感じでは、
DirectShowで言うグラフのようにトポロジを構成してデータを流すっぽいんですが。

2012/11/03(土)C#から手軽にUPnPでルータに穴開けるクラスライブラリ作ったった

はてブ数 2012/11/03 01:05 プログラミング::C#つーさ

https://github.com/ttsuki/ttsuki/blob/master/Net/UPnPWanService.cs

Windows標準のupnp.dllを使う方法で。

COM使うけど、TypeLib参照せず、自前でCOM定義してるので、InteropなDLL不要。
この UPnPWanService.cs ファイル1つコンパイルすれば、UPnPがしゃべれるんだぜ!

ゲームの通信対戦とか、P2Pアプリケーションとか作れるね!
これ使って、UDPポート穴開けてKademliaかなんかDHTとP2Pルーティング実装して、
でかいblobの転送に関しても、UPnP使ってFTPのパッシブモードみたいに穴開けたら
オレオレBitTorrentのようなものができそう。
そのネットワーク使って色々楽しいことできるんじゃないかとか妄想してる。

ソフト作っても使ってくれる人がいないと意味ないから、
ちゃんと需要あるソフト作って、ちゃんと宣伝しないとねー。

2012/09/22(土)GitHub はじめました。

はてブ数 2012/09/22 23:53 プログラミング::C#つーさ

たこねこかわゆし。登録したのは8ヶ月前ですが。
https://github.com/ttsuki/ttsuki
C#でWindowsのP/Invokeなライブラリ集になりそうです。
今できることはリンク先のREADME.mdをみてください。

これからはブログ記事に貼り付けるソースコードなんかもこっちにアップしてけばいいよね。
ということで、手始めに、数年前に作ったwinmm.dllをラップするクラス群をPushしてみる。今更!