2015/07/28

LADSPA ステレオアンプ

前回エフェクトを作った時、Audacityそのものを再構築することで、ビルトインエフェクトを作った。しかし開発環境の都合上linuxでしか使えなかった。やはりWindowsでも使いたくなり、大げさなビルトインエフェクトも作らなくなってしまった。
そんな状況の中、Audacityが2.1.0になって事態は急変。 エフェクトの一部では、リアルタイムプレビューができるようになり、さらに設定の保存が出来るようになった。下記のようにエフェクトの種類ごとにできるできないがある。

Audacity2.1.0エフェクト種類による違い

ビルトイン: リアルタイムプレビュー不可。設定の保存はできないものが多い。
Nyquist: リアルタイムプレビュー不可。設定の保存はできないものが多い。
VST: リアルタムプレビュー可能 設定の保存可能 ただしlinuxでは利用不可
LADSPA: リアルタムプレビュー可能 設定の保存可能 マルチプラットフォーム

設定保存も初期値も融通が効かず見劣りしてたLADSPAが一気に理想的なエフェクトになってしまった。 LADSPAはC言語で開発でき、開発に必要なSDKもヘッダファイル1個というシンプルな仕様。個人でちょろちょろ開発するにはお手軽なのだ。
またWindowsでもMacでもlinuxでも問題なく使えるマルチぶり。 いろんなOSを並行して使っている場合は、都合が良い。 再びLADSPAで、エフェクトをいろいろ作ってみようかと思い始める。

前回LADSPAでエフェクトを作ったのが2013年なので、すでに2年半経ってしまった。思い出しながら試してないステレオ処理のサンプルを作ってみた。LADSPAの仕様は十分把握していないが、簡単なものであれば作れそうだ。


LADSPA ステレオアンプの概要

試しに作ったものはステレオアンプで、左右独立したボリュームを配置。モノラルトラックに適用する場合は、左チャンネルがモノラル信号を受け持つ。右チャンネルは無効になる。この延長線上で複雑な左右チャンネルの処理もできるだろう。

下スクリーンショットは、linuxのLubuntuで作成したもの。

設定の保存先はlinuxでは以下のところだった。
/home/usr/.audacity-datapluginsettings.cfg


LADSPA ステレオアンプ ソースコード

久しぶりだったので、いろいろ忘れている。今回手こずったのはポート数の一致。ポート数が異なっていてもコンパイルは出来てしまうので、なかなか気付かない部分。ポート数が違うと、Audacityが落ちてしまう。
/* namagi_amp_stereo.c 2015.7.28
compile windows
gcc -shared -o namagi_amp_stereo.dll namagi_amp_stereo.c -ID 

compile Ubuntu
gcc -fPIC -DPIC -shared -nostartfiles
 -o namagi_amp_stereo.so namagi_amp_stereo.c
*/
/**********************************************************/
#include <stdlib.h>
#include <string.h>
/**********************************************************/
#include "ladspa.h"
/**********************************************************/
#define AMP_CONTROL_L 0
#define AMP_CONTROL_R 1
#define AMP_INPUT_L   2
#define AMP_OUTPUT_L  3
#define AMP_INPUT_R   4
#define AMP_OUTPUT_R  5
/**********************************************************/
#ifdef WIN32
   int bIsFirstTime = 1;
   void _init();
   #define _WINDOWS_DLL_EXPORT_ __declspec(dllexport)
#else
   #define _WINDOWS_DLL_EXPORT_ 
#endif
/**********************************************************/
typedef struct {
  LADSPA_Data * m_pfControlValue_L; /* L */
  LADSPA_Data * m_pfControlValue_R; /* R */
  LADSPA_Data * m_pfInputBuffer_L;  /* L */
  LADSPA_Data * m_pfOutputBuffer_L; /* L */
  LADSPA_Data * m_pfInputBuffer_R;  /* R */
  LADSPA_Data * m_pfOutputBuffer_R; /* R */
} Amplifier;
/**********************************************************/
LADSPA_Handle 
instantiateAmplifier(const LADSPA_Descriptor * Descriptor,
               unsigned long SampleRate) {
  return malloc(sizeof(Amplifier));
}
/**********************************************************/
void connectPortToAmplifier(LADSPA_Handle Instance,
         unsigned long Port,
         LADSPA_Data * DataLocation) {
  Amplifier * psAmplifier;
  psAmplifier = (Amplifier *)Instance;

  switch (Port) {
  case AMP_CONTROL_L:
    psAmplifier -> m_pfControlValue_L = DataLocation;
    break;
  case AMP_CONTROL_R:
    psAmplifier -> m_pfControlValue_R = DataLocation;
    break;
  case AMP_INPUT_L:
    psAmplifier -> m_pfInputBuffer_L = DataLocation;
    break;
  case AMP_OUTPUT_L:
    psAmplifier -> m_pfOutputBuffer_L = DataLocation;
    break;
  case AMP_INPUT_R:
    psAmplifier->m_pfInputBuffer_R = DataLocation;
    break;
  case AMP_OUTPUT_R:
    psAmplifier->m_pfOutputBuffer_R = DataLocation;
    break;
  }
}
/**********************************************************/
void runStereoAmplifier(LADSPA_Handle Instance,
     unsigned long SampleCount) {
  LADSPA_Data * pfInput;
  LADSPA_Data * pfOutput;
  LADSPA_Data fGainL;
  LADSPA_Data fGainR;
  Amplifier * psAmplifier;
  unsigned long lSampleIndex;

  psAmplifier = (Amplifier *)Instance;

  fGainL = *(psAmplifier -> m_pfControlValue_L);
  fGainR = *(psAmplifier -> m_pfControlValue_R);

  pfInput = psAmplifier  -> m_pfInputBuffer_L;
  pfOutput = psAmplifier -> m_pfOutputBuffer_L;
  for (lSampleIndex = 0;
    lSampleIndex < SampleCount; lSampleIndex++) 
    *(pfOutput++) = *(pfInput++) * fGainL;

  pfInput = psAmplifier  -> m_pfInputBuffer_R;
  pfOutput = psAmplifier -> m_pfOutputBuffer_R;
  for (lSampleIndex = 0; 
    lSampleIndex < SampleCount; lSampleIndex++) 
    *(pfOutput++) = *(pfInput++) * fGainR;
}
/**********************************************************/
void cleanupAmplifier(LADSPA_Handle Instance) {
  free(Instance);
}
/**********************************************************/
LADSPA_Descriptor * g_psStereoDescriptor = NULL;
/**********************************************************/
void _init() {
  char ** pcPortNames;
  LADSPA_PortDescriptor * piPortDescriptors;
  LADSPA_PortRangeHint  * psPortRangeHints;

    g_psStereoDescriptor 
      = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor));
  
    g_psStereoDescriptor -> UniqueID
      = 1049;
    g_psStereoDescriptor -> Label
      = strdup("amplifier_s");
    g_psStereoDescriptor -> Properties
      = LADSPA_PROPERTY_HARD_RT_CAPABLE;
    g_psStereoDescriptor -> Name 
      = strdup("Namagi: Amplifier Stereo Linear ver.150728");
    g_psStereoDescriptor -> Maker
      = strdup("Namagi Products");
    g_psStereoDescriptor -> Copyright
      = strdup("None");
    g_psStereoDescriptor -> PortCount = 6;
    piPortDescriptors
      = (LADSPA_PortDescriptor *)calloc(6, 
      sizeof(LADSPA_PortDescriptor));
    g_psStereoDescriptor -> PortDescriptors
      = (const LADSPA_PortDescriptor *)piPortDescriptors;
    piPortDescriptors[AMP_CONTROL_L]
      = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    piPortDescriptors[AMP_CONTROL_R]
      = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    piPortDescriptors[AMP_INPUT_L]
      = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
    piPortDescriptors[AMP_OUTPUT_L]
      = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
    piPortDescriptors[AMP_INPUT_R]
      = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
    piPortDescriptors[AMP_OUTPUT_R]
      = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
    pcPortNames
      = (char **)calloc(6, sizeof(char *));
    g_psStereoDescriptor->PortNames 
      = (const char **)pcPortNames;
    pcPortNames[AMP_CONTROL_L]
      = strdup("Gain Left(mono)");
    pcPortNames[AMP_CONTROL_R]
      = strdup("Gain Right");
    pcPortNames[AMP_INPUT_L]
      = strdup("Input (Left)");
    pcPortNames[AMP_OUTPUT_L]
      = strdup("Output (Left)");
    pcPortNames[AMP_INPUT_R]
      = strdup("Input (Right)");
    pcPortNames[AMP_OUTPUT_R]
      = strdup("Output (Right)");
    psPortRangeHints = ((LADSPA_PortRangeHint *)
      calloc(6, sizeof(LADSPA_PortRangeHint)));
    g_psStereoDescriptor -> PortRangeHints
      = (const LADSPA_PortRangeHint *)psPortRangeHints;
    psPortRangeHints[AMP_CONTROL_L].HintDescriptor
      = (LADSPA_HINT_BOUNDED_BELOW 
      | LADSPA_HINT_LOGARITHMIC | LADSPA_HINT_DEFAULT_0);
    psPortRangeHints[AMP_CONTROL_R].HintDescriptor
      = (LADSPA_HINT_BOUNDED_BELOW 
      | LADSPA_HINT_LOGARITHMIC | LADSPA_HINT_DEFAULT_0);
  
    psPortRangeHints[AMP_CONTROL_L].LowerBound  = 0;
    psPortRangeHints[AMP_CONTROL_R].LowerBound  = 0;
    psPortRangeHints[AMP_INPUT_L].HintDescriptor = 0;
    psPortRangeHints[AMP_OUTPUT_L].HintDescriptor = 0;
    psPortRangeHints[AMP_INPUT_R].HintDescriptor = 0;
    psPortRangeHints[AMP_OUTPUT_R].HintDescriptor = 0;
    
    g_psStereoDescriptor -> instantiate 
      = instantiateAmplifier;
    g_psStereoDescriptor -> connect_port
      = connectPortToAmplifier;
    g_psStereoDescriptor -> activate = NULL;
    g_psStereoDescriptor -> run = runStereoAmplifier;
    g_psStereoDescriptor -> run_adding = NULL;
    g_psStereoDescriptor -> set_run_adding_gain = NULL;
    g_psStereoDescriptor -> deactivate = NULL;
    g_psStereoDescriptor -> 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_psStereoDescriptor);
}
/**********************************************************/
_WINDOWS_DLL_EXPORT_ 

const 
LADSPA_Descriptor * ladspa_descriptor(unsigned long Index) {
#ifdef WIN32
  if (bIsFirstTime) {
  _init();
  bIsFirstTime = 0;
  }
#endif
  if (Index == 0) /* Index == 0:mono, 1:stereo */
    return g_psStereoDescriptor;
  else
    return NULL;
}


sound programming 目次はこちら