LADSPA(ラドスパ)とは
C言語の学習をさぼって半年経ってしまった。リハビリということでボチボチ再開してみようかと思う。実用的なプログラムを作った方がモチベーションも下がりにくいのでLADSPAにチャレンジしてみる。
LADSPAとはLinux Audio Developers Simple Plugin APIの略で、2000年に誕生したLinux発祥のデジタル音声信号の加工、生成規格。スタインバーグのVSTと似たようなもの。移植性の高さからWinでもMacでも利用できる。個人的によく使っているAudacityにもLADSPAのプラグインがいくつか入っている。
開発する立場から見るとLADSPAは簡素で扱いやすく思えた。以前VSTでも作ってみようかと思って開発環境だけ整えてみたけど、あまりにも巨大環境過ぎて、すぐにやめてしまった。マイクロソフトのVisual C++は、仕事で使う人にとってはよい開発環境なのかもしれないけど、趣味でちょろっと作るには本格的過ぎて・・・ その点、LADSPAは純粋なC言語で手軽に作ることができる。テキストエディタでちょろっと書いてGCCで手軽にコンパイル。開発に必要なのはladspa.hというヘッダファイルだけあればいい。PCのディスクスペースも食わず、なんともお手軽だ。
まずはサンプルを改造
さっそく作ってみようと思って調べてみる。まずはオフィシャルページにアクセスしてSDK(ladspa_sdk_1.13.tgz)を入手。
LADSPAオフィシャル
http://www.ladspa.org/
SDKをダウンロードしてサンプルを眺めるところからスタート。まずはサンプルをコンパイルしてAudacityで実行してみる。LADSPAはLinux用なので、Windowsで実行する場合はdllを作る必要がある。そこでソースファイルにちょっと手を加えてdllを作ってみた。
参考になる解説サイトが皆無に等しく、海外のいくつかのサイトやら、掲示板を見て、その方法を試行錯誤。LADSPAの開発の困難さは情報不足にありそうだな。日本人で開発している人はほとんどいなさそうだわ。日本語情報はあきらめよう。
海外でもそれほど活発に開発されているとは言えない。それでもWindowsのAudacityで使えるようになったので内部構造を理解してしまえば、いろいろ作れそうだ。ただルールをある程度は理解しておかないとダメだと思われる。今まで言語仕様だけでプログラムを作ってきたから、何かのルールに沿って作るのは面倒くさいなぁ。まずはサンプルの改造あたりから始めて、徐々にオリジナルを作って行こうと思う。最終的にはAudacityで使えるエフェクターを作ろうと思っているが、何をつくろうかな? まだ作ったことがないリバーブかな?
下はサンプルのampを改造してWindows版Audacityで実行した状態。ゲインの幅やら、入出力関係をいろいろいじって実験中。音声信号は32bit floatなのね。予想通り、かなりお手軽だわ。
アンプ プラグイン サンプル改造
サンプルのアンプを改造して、よりシンプルなソースコードにするためにモノラル専用アンプにしてみた。ただAudacityで扱う場合は、ステレオトラックに適用しても問題なく機能する。
ソースコードは結構ダラダラと長くなる。ただシンプルなので、慣れればそれほど厄介ではないはず。
/* namagi_amp.c 2012.12.24
compile windows
gcc -shared -o namagi_amp.dll namagi_amp.c -ID
compile Ubuntu
gcc -fPIC -DPIC -shared -nostartfiles -o namagi_amp.so namagi_amp.c
*/
/**********************************************************/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
/**********************************************************/
/*LADSPAのヘッダファイルを読み込む*/
#include "ladspa.h"
/**********************************************************/
/* マクロ定数を定義 コントロールと出力 */
#define AMP_CONTROL 0
#define AMP_INPUT1 1
#define AMP_OUTPUT1 2
/**********************************************************/
/* Windowsでdllを生成するため条件コンパイルを行う。
WIN32というマクロ定義がされていたならば実行しdllを作る。
プラットフォームごとの処理をわけるための#ifdefを使う。
winでコンパイルするとWIN32が定義されるので有効になる。
unixでは無効と判断される。
*/
#ifdef WIN32
int bIsFirstTime = 1; /* 定義して1と設定 */
void _init(); /* ここから読まれる */
/* プリプロセッサ dllを作成するためのマクロ */
#define _WINDOWS_DLL_EXPORT_ __declspec(dllexport)
#else
#define _WINDOWS_DLL_EXPORT_
#endif
/**********************************************************/
/* 構造体で入出力とコントロールを定義 */
typedef struct {
LADSPA_Data * m_pfControlValue; /* コントロール */
LADSPA_Data * m_pfInputBuffer1; /* mono 入力 */
LADSPA_Data * m_pfOutputBuffer1; /* mono 出力 */
} Amplifier;
/**********************************************************/
/* インスタンス化 LADSPA_Handleはポインタ型のtypedef宣言 */
LADSPA_Handle instantiateAmplifier(const LADSPA_Descriptor * Descriptor,
unsigned long SampleRate) {
return malloc(sizeof(Amplifier));
}
/**********************************************************/
/* portに接続 コントロールと入出力 */
void connectPortToAmplifier(LADSPA_Handle Instance,
unsigned long Port,
LADSPA_Data * DataLocation)
{
Amplifier * psAmplifier;
psAmplifier = (Amplifier *)Instance;
switch (Port) {
case AMP_CONTROL:
psAmplifier->m_pfControlValue = DataLocation;
break;
case AMP_INPUT1:
psAmplifier->m_pfInputBuffer1 = DataLocation;
break;
case AMP_OUTPUT1:
psAmplifier->m_pfOutputBuffer1 = DataLocation;
break;
}
}
/**********************************************************/
/* モノラルアンプ処理 */
void runMonoAmplifier(LADSPA_Handle Instance,
unsigned long SampleCount)
{
LADSPA_Data *pfInput;
LADSPA_Data *pfOutput;
LADSPA_Data fGain;
Amplifier *psAmplifier;
unsigned long lSampleIndex;
psAmplifier = (Amplifier *)Instance;
pfInput = psAmplifier->m_pfInputBuffer1;
pfOutput = psAmplifier->m_pfOutputBuffer1;
fGain = *(psAmplifier->m_pfControlValue);
/* 実際の処理はここだけ */
for (lSampleIndex = 0; lSampleIndex < SampleCount; lSampleIndex++)
*(pfOutput++) = *(pfInput++) * fGain;
}
/**********************************************************/
/* 解放 */
void cleanupAmplifier(LADSPA_Handle Instance) {
free(Instance);
}
/**********************************************************/
/* */
LADSPA_Descriptor * g_psMonoDescriptor = NULL;
/**********************************************************/
/* 最初に呼び出される部分 インターフェイスやエフェクト情報の記述 */
void _init()
{
char ** pcPortNames;
LADSPA_PortDescriptor * piPortDescriptors;
LADSPA_PortRangeHint * psPortRangeHints;
g_psMonoDescriptor
= (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor));
g_psMonoDescriptor->UniqueID
= 1048;
g_psMonoDescriptor->Label
= strdup("amplifier");
g_psMonoDescriptor->Properties
= LADSPA_PROPERTY_HARD_RT_CAPABLE;
g_psMonoDescriptor->Name
= strdup("Namagi: Amplifier Linear ver.121224");
g_psMonoDescriptor->Maker
= strdup("Namagi Products");
g_psMonoDescriptor->Copyright
= strdup("None");
g_psMonoDescriptor->PortCount
= 3;
piPortDescriptors
= (LADSPA_PortDescriptor *)calloc(3,
sizeof(LADSPA_PortDescriptor));
g_psMonoDescriptor->PortDescriptors
= (const LADSPA_PortDescriptor *)piPortDescriptors;
piPortDescriptors[AMP_CONTROL]
= LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
piPortDescriptors[AMP_INPUT1]
= LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
piPortDescriptors[AMP_OUTPUT1]
= LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
pcPortNames
= (char **)calloc(3, sizeof(char *));
g_psMonoDescriptor->PortNames
= (const char **)pcPortNames;
pcPortNames[AMP_CONTROL]
= strdup("Gain");
pcPortNames[AMP_INPUT1]
= strdup("Input");
pcPortNames[AMP_OUTPUT1]
= strdup("Output");
psPortRangeHints
= ((LADSPA_PortRangeHint *)
calloc(3, sizeof(LADSPA_PortRangeHint)));
g_psMonoDescriptor->PortRangeHints
= (const LADSPA_PortRangeHint *)psPortRangeHints;
psPortRangeHints[AMP_CONTROL].HintDescriptor
= (LADSPA_HINT_BOUNDED_BELOW
| LADSPA_HINT_LOGARITHMIC
| LADSPA_HINT_DEFAULT_1);
psPortRangeHints[AMP_CONTROL].LowerBound
= 0;
psPortRangeHints[AMP_INPUT1].HintDescriptor
= 0;
psPortRangeHints[AMP_OUTPUT1].HintDescriptor
= 0;
g_psMonoDescriptor->instantiate
= instantiateAmplifier;
g_psMonoDescriptor->connect_port
= connectPortToAmplifier;
g_psMonoDescriptor->activate
= NULL;
g_psMonoDescriptor->run
= runMonoAmplifier;
g_psMonoDescriptor->run_adding
= NULL;
g_psMonoDescriptor->set_run_adding_gain
= NULL;
g_psMonoDescriptor->deactivate
= NULL;
g_psMonoDescriptor->cleanup
= cleanupAmplifier;
}
/**********************************************************/
/* 解放 インターフェイスまわり */
void deleteDescriptor(LADSPA_Descriptor * psDescriptor) {
unsigned long lIndex;
if (psDescriptor) {
free((char *)psDescriptor->Label);
free((char *)psDescriptor->Name);
free((char *)psDescriptor->Maker);
free((char *)psDescriptor->Copyright);
free((LADSPA_PortDescriptor *)psDescriptor->PortDescriptors);
for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++)
free((char *)(psDescriptor->PortNames[lIndex]));
free((char **)psDescriptor->PortNames);
free((LADSPA_PortRangeHint *)psDescriptor->PortRangeHints);
free(psDescriptor);
}
}
/**********************************************************/
/* 終了時メモリ解放 */
void _fini() {
deleteDescriptor(g_psMonoDescriptor);
}
/**********************************************************/
_WINDOWS_DLL_EXPORT_ /* マクロで定義したもの */
const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index) {
#ifdef WIN32
if (bIsFirstTime) { /* 変数は1になっている if文は0の場合実行しない */
_init(); /* 実行する */
bIsFirstTime = 0; /* 0にしてしまう */
}
#endif
if (Index == 0)
return g_psMonoDescriptor;
else
return NULL;
}
|
Windowsでのコンパイルは、以下のコマンドを使った。
gcc -shared -o namagi_amp.dll namagi_amp.c -ID
|
Windowsでは、namagi_amp.dllが同じ階層に作られるので、それをAudacityのPlug-Insフォルダに入れると使えるようになる。
Ubuntu環境では、以下のコマンドを使って.soファイルを作成。
gcc -fPIC -DPIC -shared -nostartfiles -o namagi_amp.so namagi_amp.c
|
できたnamagi_amp.soを隠しフォルダの
/home/usr/.audacity-data/Plug-Ins/
に入れればAudacityで使用できる。
主に使われている関数の中で、記述するのは以下の★部分だけ。
基本的に必要なポートを追加し、処理を記述すれば、それだけで動く。
typedef struct{}StructEffect;★新しいポート追加
LADSPA_Handle instantiateEffect(){}
void connectPortToEffect(){}★新しいポート追加
void runStereoEffect(){}★処理の記述
void cleanupEffect(){}
LADSPA_Descriptor *g_psDescriptor = NULL;
void _init(){}★新しいポート追加 インターフェイス
void deleteDescriptor(){}
void _fini(){}
sound programming 目次
C言語 ANSI C89 Meadow & MinGW GCC 目次