misc.
音響信号処理の研究を進めるにあたって有益と思われる情報を発信したいと思います。
MATLABからplayrecを使ってマルチチャネルオーディオ信号の同時入出力を行う方法
playrec(http://www.playrec.co.uk/)というソフトウェアを使って,MATLABからASIO対応のオーディオインタフェースを直接コントロールできるようになります。つまり,
- MATLABのM行N列変数x(M,N)がそのままマルチチャネルの音になります。
但し,-1 < x(m,n) < 1
- 同時入出力もできるのでインパルス応答の測定もできます。
- 連続した録音や再生もできるようです。
注意事項
本編に記載の事項は2012年12月末現在の情報であり、今後、利用しているソフトウェア等のサポートがなくなる、また、MATLAB自体が直接マルチチャンネルオーディオを操作できるようになる可能性(※)がありますので、導入は自己責任でお願いいたします。(※)http://www.mathworks.co.jp/matlabexpo/download/pdf/matlabexpo2011_A2.pdf
また,MATLABから多ch信号を直接扱えるということで特に初学者に便利な機能である一方,インストールや利用にあたっては若干慣れが必要という難点もあるような気もします(私だけ?)。
インストール方法
利用するソフトウェア
Playrec:http://www.playrec.co.uk/ただし、上記ソフトウェアを利用するためには下記のソフトウェアが必要となります。
PortAudio, ASIOSDK2,MATLAB, Visual Stdio 2010
利用するハードウェア
Windows PC (Windows 7: 64 bit) + ASIO対応オーディオインタフェース(たとえば、ローランドOCTA-CAPTUER, MOTU 24 I/Oなど)インストール手順
- MATLABはインストールされているものとする。
- コンパイラをインストールする。
コンパイラとしては,Microsoft Visual C++ Express 2008や2010を利用する。これらはMicrosoftのHPから無償でダウンロードできる。
- Playrecをダウンロードする。
http://www.playrec.co.uk/download.phpのDownload Playrec 2.1.0からPlayrec 2.1.0のzipファイルをダウンロードし,適当なフォルダに解凍する。
- PortAudioをPortAudioのサイトからダウンロードする
ダウンロードしたファイルはtar+gzipなので、「7-zip」などを使って2回解凍する必要がある。
Playrecを解凍したフォルダにあるplayrec/libの下にportaudioのフォルダを移動する。
- ASIO SDKをSteinbergのHPからダウンロードする。無料だがレジストレーションが最初に必要となる。
ASIOSDK2というフォルダができるがASIOSDKにrenameしてplayrec/libの下に移動する。
- ASIO以外でも使えるようにするためには、playrecのHPに記載の5のDirectXのインストールも行っておく。Windows SDK 7.1, Microsoft DirectX SDK (June 2010)も必要に応じてインストールしておく。
- playrec/m_filesでcompile_playrec を実行する前に以下の変更をm_fileに加える。
これらの修正の背景はplayrecが64bitマシンに未対応なまま,かつPortAudioのバージョンアップに追従していないことに起因していると思われます。
------------------------------------------------------------ playrec\m_files\compile_playrec_func.m ------------------------------------------------------------ Before (line 122): compiler_flags = [compiler_flags, {'WIN32'}]; After: %compiler_flags = [compiler_flags, {'WIN32'}]; if strcmp(computer, 'PCWIN64') compiler_flags = [compiler_flags, {'WIN64'}]; else %strcmp(computer, 'PCWIN32') compiler_flags = [compiler_flags, {'WIN32'}]; end ------------------------------------------------------------ playrec\m_files\build_mex.m ------------------------------------------------------------ Before (line 80): build_args = {build_args{:}, ['LIB#', cell2mat(lib_dirs(n)), ';$LIB']}; After: %build_args = {build_args{:}, ['LIB#', cell2mat(lib_dirs(n)), ';$LIB']}; if strcmp(computer, 'PCWIN64') build_args = {build_args{:}, ['-L', cell2mat(lib_dirs(n))]}; else %strcmp(computer, 'PCWIN32') build_args = {build_args{:}, ['LIB#', cell2mat(lib_dirs(n)), ';$LIB']}; end ------------------------------------------------------------ Fix for WASAPI: playrec\m_files\compile_playrec_func.m ------------------------------------------------------------ Before (line 185): pa_api_specific_files = [pa_api_specific_files,... {'src/hostapi/wasapi/pa_win_wasapi.cpp'}]; After: %pa_api_specific_files = [pa_api_specific_files,... % {'src/hostapi/wasapi/pa_win_wasapi.cpp'}]; pa_api_specific_files = [pa_api_specific_files,... {'src/hostapi/wasapi/pa_win_wasapi.c'}]; ------------------------------------------------------------ Fix for ASIO: playrec\m_files\compile_playrec_func.m ------------------------------------------------------------ Before (line 57): pa_common_files = {'src/common/pa_allocation.c',... 'src/common/pa_converters.c',... 'src/common/pa_cpuload.c',... 'src/common/pa_debugprint.c',... 'src/common/pa_dither.c',... 'src/common/pa_front.c',... 'src/common/pa_process.c',... 'src/common/pa_skeleton.c',... 'src/common/pa_stream.c',... 'src/common/pa_trace.c'}; After: pa_common_files = {'src/common/pa_allocation.c',... 'src/common/pa_converters.c',... 'src/common/pa_cpuload.c',... 'src/common/pa_debugprint.c',... 'src/common/pa_dither.c',... 'src/common/pa_front.c',... 'src/common/pa_process.c',... 'src/common/pa_skeleton.c',... 'src/common/pa_stream.c',... 'src/common/pa_ringbuffer.c',... 'src/common/pa_trace.c'}; ------------------------------------------------------------ さらに134行目のなかに、 'src/os/win/pa_win_coinitialize.c',... を追加する必要がある。 pa_os_specific_files = [pa_os_specific_files, ... {'src/os/win/pa_win_hostapis.c',... 'src/os/win/pa_win_util.c',... 'src/os/win/pa_win_coinitialize.c',... 'src/os/win/pa_win_waveformat.c',... 'src/os/win/pa_x86_plain_converters.c'}]; ------------------------------------------------------------
- PortAudioの変更
\playrec\lib\portaudio\src\hostapi\skeleton\pa_hostapi_skeleton.c というのがあるが、これをpa_skeleton.cとして\playrec\lib\portaudio\src\commonに移動しておく。 (PortAudioのバージョンアップに伴いフォルダを移動した模様だが、Playrec側で未対応だったため修正)
- MATLABを立ち上げ、mex -setupでコンパイラーを選択する。
Would you like mex to locate installed compilers [y]/n?
と聞かれるのでnoと答えると、利用可能なコンパイラーの一覧が出てくるので、 Microsoft Visual C++ 2010 などを選ぶと、コンパイラーの場所を聞かれるので、適切なフォルダを貼り付ける。その場にコンパイラーがないと「正常に終了できません」と出るので、利用可能なコンパイラーから Microsoft Software Development Kit (SDK) 7.1 を選んで、場所としてはC:\Program Files (x86)\Microsoft Visual Studio 11.0などすべてがインストールされているフォルダを指定するとうまくいく(はず)。
- playrec/m_filesでcompile_playrec を実行する。
GUIが立ち上がるので,ASIOを外さずにcompileをする。成功するとplayrec.mexw64ができあがるので,http://www.playrec.co.uk/download.phpにあるDownload Playrec Example Scriptsをダウンロードし,サンプルプログラムのtest_playなどでテストをする。
使い方のちょっとした解説
サンプルプログラムを参照する
本家のサンプルプログラムはここにあります。本家ではありませんが, ここの Matlab m-files for project 3:にあるm-fileがplayrec関連のm-fileです。
pageという概念を理解する
たぶんですが,ハードウェアが利用するバッファと区別するために,playrecではページ(page)というバッファの概念を取り入れているようです(間違っていたらメールください,訂正します)。基本的な使い方として,たとえば、playrecで出力しながら録音するときに、playBufferにある2ch信号の音を1,2 chのD/Aから出し切って,それと同期させて1,2chのA/Dで音を録音したい場合は,
pageNumber=playrec('playrec',playBuffer,[1 2],-1,[1 2]); playrec('block',pageNumber) [recBuffer,recChanList]=playrec('getRec',pageNumber); playrec('delPage',pageNumber);
となる。1行目はD/Aに送る信号とチャネル,途中にある「-1」はA/DのバッファサイズがplayBufferと同じであることを示し,最後はA/Dのチャネルを指定するベクトルになっている。このコマンドが実行されるとA/D,D/Aが動作し始める。2行目は,1行目で実行されたplayrecが終わるまで待機することを示し,3行目は,pageNumberに保存されたA/DデータをrecBufferにコピーし,最後にはpageNumberのメモリを解放している(と思われる)。連続して行う場合は,
outChannels=[1 2]; inChannels=[1 2]; %まず,音を出し始める pageNumber_old=playrec('playrec',playBuffer,outChannels,-1,inChannels); for k=1:3 %連続して新しいページを設定して、出力バッファに音を加える pageNumber_new=playrec('playrec',playBuffer,outChannels,-1,inChannels); %playBufferはアップデートされた音でも違う音でもよい。 %新たにplayrecで追加されたpageNumber_newは,pageNumber_oldのバッファの後に追加される。 %1個前のページpageNumber_oldの出力が終わるまで待つ playrec('block',pageNumber_old); %1個前のページpageNumber_oldの出力が終わったら、そのページの録音データを変数にコピーする [recBuffer,recChanList]=playrec('getRec',pageNumber_old); %recBufferは上書きされてしまうので,取っておくときはほかの変数にコピーしておく。 % 古いページのデータを(バッファから)削除する playrec('delPage',pageNumber_old); %現時刻のページを古いページと読み替えて、forループの最初に戻る pageNumber_old=pageNumber_new; end
のように書けば動作するようですが,確かめていません。