いらないモノ、ひつようなモノ

書籍、音楽、そして若干のテクノロジー

rubyからcsoundを使う

csoundAPIを具備している。APISWIG経由で利用することより多様な言語からcsoundを呼び出すことが可能だ。配布されているソースコードにもpython, java, luaから用いるswigのインタフェース定義と各言語のテストコードが含まれている。

これらのサンプルを参考にしてFedora7(2.6.22.9-91.fc7)上でruby(1.8.6)からcsound(5.06)をswig(1.3.31)/g++(GCC)(4.1.2)経由で呼ぶことができるようになった。ただしまだswigのtypemapをきっちっと書いたり、C++ポインタをrubyにマッピングをするなど全くやっていないのですべてのcsoundAPIは動作しないと考えるので、このあたりは後々詰める予定。

概要

  1. interfaces/ruby_interface.iを作成
  2. swig -ruby -c++ interfaces/ruby_interface.i
  3. gcc -o interfaces/ruby_interface_wrap.os interfaces/ruby_interface_wrap.cc
  4. g++ -o rcsound -L/usr/lib/ruby/i.8 -lcsnd
  5. test_ruby.rbを作成
  6. テストコードから呼び出すrt.csdを作成
  7. test_ruby.rbを実行してリアルタイムで音が鳴る

詳細

以下、すべてcsoundソースコードを展開しているディレクトリ*1で操作することを前提にしている

  1. csoundをコンパイルしてswigのコードを生成する部分を抜き出す*2
scons buildCsoundAC=1 buildCsound5GUI=1 useOSC=1 buildDSSI=1 buildVirtual=1 \
buildStkOpcodes=1 buildRelease=1 gcc4opt=pentium3 buildJavaWrapper=1 buildInterfaces=1 \
buildPDClass=1 bynamicCsoundLibrary=1 buildPythonOpcodes=1 useDouble=1
  1. interfaces/ruby_interface.iを作成する

interfaces/python_interface.iを参考にする*3directors=1にして良いとswigに書いてあった。

SWIG's Ruby module supports cross-language polymorphism (a.k.a. the "directors" feature) 
similar to that for SWIG's Python module.

のでinterface_ruby.iで残しておいた。他は「よさそう」だったのであまり何も考えずにそのまま。

モジュール名は後で利いてくる。これは最初csndだったがこれをrcsoudに変更する必要があるのは最後に生成するライブラリの名称をcsndにするとpythonのライブラリと名前が被ってしまうためrcsound.soという名前のライブラリにすることを意図してモジュール名もrcsoundとする。

/** only for linux **/
%module(directors="1") rcsound  /* swig ruby module supports directors feature */
%feature("director") CsoundCallbackWrapper;
%feature("nodirector") Csound;

%include "typemaps.i"

%include "std_string.i"
%include "std_vector.i"
%include "carrays.i"

%feature("autodoc", "1");
%{
        #include "csound.h"
        #include "cfgvar.h"
        #include "csound.hpp"
        #include "cs_glue.hpp"
        #include "csPerfThread.hpp"
        #include "CsoundFile.hpp"
        #include "CppSound.hpp"
	#include "Soundfile.hpp"
%}
%apply int { size_t };
typedef unsigned int uint32_t;

%include "exclusions.i"

%include "csound.h"
%include "cfgvar.h"
/*
%apply MYFLT &OUTPUT { MYFLT &dflt, MYFLT &min, MYFLT &max };
%apply MYFLT &OUTPUT { MYFLT &value };
*1191854038*/

%include "csound.hpp"
%include "cs_glue.hpp"
%include "csPerfThread.hpp"
%include "CsoundFile.hpp"
%include "CppSound.hpp"
%include "Soundfile.hpp"

swigでコード生成

このコンパイルオプションは私の環境でsconsしたときに出てきたものなので各環境で異なる可能性があります。要は -pythonだったところを -ruby -c++として他のpython部分をrubyに置き換えて実行しました。

 swig -c++ -includeall -verbose -DLINUX -DPIPES -DHAVE_LIBSNDFILE=1016 -DHAVE_FLTK \
-DUSE_DOUBLE -DHAVE_SOCKETS -DHAVE_FCNTL_H -DHAVE_UNISTD_H -DHAVE_STDINT_H \
-DHAVE_SYS_TIME_H -DHAVE_SYS_TYPES_H -DHAVE_TERMIOS_H -DHAVE_SOCKETS -DHAVE_DIRENT_H \
-D__BUILDING_LIBCSOUND -D_CSOUND_RELEASE_  \
-DCS_DEFAULT_PLUGINDIR=\"/usr/local/lib/csound/plugins64\" -D__BUILDING_CSOUND_INTERFACES \
-I. -I./H -I/usr/java/jdk1.6.0_02/include -I/usr/local/include -I/usr/include \
-I/usr/X11R6/include -Iinterfaces -ruby -c++ -outdir . -o \
interfaces/ruby_interface_wrap.cc interfaces/ruby_interface.i

Warningがむちゃくちゃ出ますがエラーは出ませんでした。以下の4種類のwarningで一つ目はclass名なのでswigが先頭を大文字に変えてくれたみたいですこれは沢山出ていました。2つ目のconst char *を使っているCsoundChannelListEntryのtypedefのところ一箇所でした。
3つめはinterfaces/cs_glue.hppの中でCsoundCallBackWrapperクラスのconst char *StringChannelCallbackという仮想関数でした。名前からすると使わなくても過ごせそうな気もしなくもないのですが、まあ記憶にとどめておくとして。
最後はちょっと重いかもしれない。中身を見るとCsoundCppクラスと言うのがCsoundクラスとCsoundFileクラスから多重継承をしている。さらに中身を見るとなんとCsoundCppクラスではpythonのコンソールに文字を表示するためのsetPythonMessageCallbackとか定義している。これはAPIを勉強してみればどうにか後で解決できるのだろうか?pythonでなければ直接CsoundクラスとCsoundFileクラスを別々に扱って生きていけるのだろうか?C++書かなきゃいけないとかいやだなー。いまさら。

  • ./H/csound.h:301: Warning(801): Wrong class name (corrected to `CsRtAudioParams'
  • ./H/csound.h:335: Warning(451): Setting a const char * variable may leak memory.
  • interfaces/cs_glue.hpp:507: Warning(473): Returning a pointer or reference in a director method is not recommended.
  • interfaces/CppSound.hpp:80: Warning(802): Warning for CppSound proxy: Base CsoundFile ignored. Multiple inheritance is not supported in Ruby.

*1:その直下にinterfacesやHというディレクトリがある

*2:必須な部分は buildCsoundAC=1 buildPythonOpcodes=1もしくはbuildCsoundAC=1 buildJavaWrapper=1のどちらかでもOKだろう

*3:このコードではMACの上では動かない。OSXで動作させる場合はpython_interface.iを参考にするといいだろう