2012/12/31

C言語 LADSPA サイン波生成

Windows用Audacityで使えるLADSPAプラグインを実験的に試してみる。サンプルを改造してサイン波を作れるようにしてみた。AudacityにはGenerateにToneというサイン波を生成するものがあるが、既存トラックに直接サイン波を合成できなかったので、自作では出来るようにしてみた。

C言語の基礎は学習していたのだが、サンプルをちゃんと理解するには、もう少しレベルアップしないとダメだわ。おおよそ分かったところで、作ってみたのだが、細かな部分は理解できてないところがある。とりあえず動くレベルは作れるかな。

サイン波生成プラグイン

/* namagi_sine.c
2012.12.31

compile windows
gcc -shared -o namagi_sine.dll namagi_sine.c -ID

compile Ubuntu
gcc -fPIC -DPIC -shared -nostartfiles -o namagi_sine.so namagi_sine.c
 */
/**********************************************************/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
/**********************************************************/
#include "ladspa.h"
#define MAX_FREQ 20000
#define MAX_GAIN 1
/**********************************************************/
#define SINE_CONTROL1 0
#define SINE_CONTROL2 1
#define SINE_INPUT1  2
#define SINE_OUTPUT1 3
/**********************************************************/
#ifdef WIN32
int bIsFirstTime = 1;
void _init();
#endif
/**********************************************************/
#ifdef WIN32
 #define _WINDOWS_DLL_EXPORT_ __declspec(dllexport)
#else
    #define _WINDOWS_DLL_EXPORT_ 
#endif
/**********************************************************/
typedef struct {
  LADSPA_Data  m_fSampleRate;
  LADSPA_Data *m_pfControlValue1;
  LADSPA_Data *m_pfControlValue2;
  LADSPA_Data *m_pfInputBuffer1;
  LADSPA_Data *m_pfOutputBuffer1;
} Sine;
/**********************************************************/
LADSPA_Handle instantiateSine(
                        const LADSPA_Descriptor *Descriptor, 
                        unsigned long  SampleRate){
  Sine * psSine;
  psSine = (Sine *)malloc(sizeof(Sine));
  psSine -> m_fSampleRate = (LADSPA_Data)SampleRate;
  return psSine;
}
/**********************************************************/
void connectPortToSine(
               LADSPA_Handle Instance,
         unsigned long Port,
         LADSPA_Data *DataLocation) {
  Sine *psSine;
  psSine = (Sine *)Instance;

  switch (Port) {
  case SINE_CONTROL1:
    psSine -> m_pfControlValue1 = DataLocation;
    break;  
  case SINE_CONTROL2:
    psSine -> m_pfControlValue2 = DataLocation;
    break;
  case SINE_INPUT1:
    psSine -> m_pfInputBuffer1 = DataLocation;
    break;
  case SINE_OUTPUT1:
    psSine -> m_pfOutputBuffer1 = DataLocation;
    break;
  }
}
/**********************************************************/
void runSine(LADSPA_Handle Instance,
             unsigned long SampleCount){
  LADSPA_Data *pfInput;
  LADSPA_Data *pfOutput;
  LADSPA_Data fSine1;
  LADSPA_Data fGain1;

  Sine *psSine; /* 構造体 */
  psSine = (Sine *)Instance;

  float w = 0; /* 角速度 1Hz */
  float step = 2 * M_PI / psSine -> m_fSampleRate;
  unsigned long lSampleIndex;

  fSine1 = *(psSine -> m_pfControlValue1);/* Hz */
  fGain1 = *(psSine -> m_pfControlValue2);/* Gain */

  pfInput = psSine -> m_pfInputBuffer1;
  pfOutput = psSine -> m_pfOutputBuffer1;
 for (lSampleIndex=0; lSampleIndex < SampleCount; lSampleIndex++){
    *(pfOutput++) = *(pfInput++)+(fGain1 * sin(w * fSine1));
    w += step;
  }
}
/**********************************************************/
void cleanupSine(LADSPA_Handle Instance) {
  free(Instance);
}
/**********************************************************/
LADSPA_Descriptor * g_psDescriptor = NULL;
/**********************************************************/
void _init() {
  char ** pcPortNames;
  LADSPA_PortDescriptor * piPortDescriptors;
  LADSPA_PortRangeHint * psPortRangeHints;
  g_psDescriptor
    = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor));
  if (g_psDescriptor) {
    g_psDescriptor -> UniqueID
      = 1;
    g_psDescriptor -> Label
      = strdup("sine");
    g_psDescriptor -> Properties
      = LADSPA_PROPERTY_HARD_RT_CAPABLE;
    g_psDescriptor -> Name 
      = strdup("Namagi: SineWave ver.121231");
    g_psDescriptor -> Maker
      = strdup("Namagi Products");
    g_psDescriptor -> Copyright
      = strdup("None");
    g_psDescriptor -> PortCount
      = 4;
    piPortDescriptors
      = (LADSPA_PortDescriptor *)calloc(4, 
            sizeof(LADSPA_PortDescriptor));
    g_psDescriptor -> PortDescriptors
      = (const LADSPA_PortDescriptor *)piPortDescriptors;

    piPortDescriptors[SINE_CONTROL1]
      = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    piPortDescriptors[SINE_CONTROL2]
      = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;

    piPortDescriptors[SINE_INPUT1]
      = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
    piPortDescriptors[SINE_OUTPUT1]
      = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;

    pcPortNames
      = (char **)calloc(4, sizeof(char *));
    g_psDescriptor -> PortNames 
      = (const char **)pcPortNames;

    pcPortNames[SINE_CONTROL1]
      = strdup("Sine1");
    pcPortNames[SINE_CONTROL2]
      = strdup("Gain1");
    pcPortNames[SINE_INPUT1]
      = strdup("Input");
    pcPortNames[SINE_OUTPUT1]
      = strdup("Output");

    psPortRangeHints = ((LADSPA_PortRangeHint *)
   calloc(4, sizeof(LADSPA_PortRangeHint)));

    g_psDescriptor -> PortRangeHints
      = (const LADSPA_PortRangeHint *)psPortRangeHints;

/* Sine1 */
    psPortRangeHints[SINE_CONTROL1].HintDescriptor
      = (LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE); 
    psPortRangeHints[SINE_CONTROL1].LowerBound = 0;
    psPortRangeHints[SINE_CONTROL1].UpperBound
                     =(LADSPA_Data)MAX_FREQ;
/* Gain1 */
    psPortRangeHints[SINE_CONTROL2].HintDescriptor
      = (LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE); 
    psPortRangeHints[SINE_CONTROL2].LowerBound = 0;
    psPortRangeHints[SINE_CONTROL2].UpperBound
                         =(LADSPA_Data)MAX_GAIN;
                                  
    psPortRangeHints[SINE_INPUT1].HintDescriptor = 0;
    psPortRangeHints[SINE_OUTPUT1].HintDescriptor = 0;

    g_psDescriptor -> instantiate  = instantiateSine;
    g_psDescriptor -> connect_port  = connectPortToSine;
    g_psDescriptor -> activate  = NULL;
    g_psDescriptor -> run   = runSine;
    g_psDescriptor -> run_adding  = NULL;
    g_psDescriptor -> set_run_adding_gain  = NULL;
    g_psDescriptor -> deactivate  = NULL;
    g_psDescriptor -> cleanup  = cleanupSine;
  }
}
/**********************************************************/
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_psDescriptor);
}
/**********************************************************/
_WINDOWS_DLL_EXPORT_
const LADSPA_Descriptor *ladspa_descriptor(unsigned long Index){
 #ifdef WIN32
    if (bIsFirstTime) {
      _init();
      bIsFirstTime = 0;
    }
 #endif
 if (Index == 0)
     return g_psDescriptor;
 else
     return NULL;
}

Windowsでコンパイルするときは以下のようにする。
gcc -shared -o namagi_sine.dll namagi_sine.c -ID

これでnamagi_sine.dllが同じ階層に作られるので、それをAudacityのPlug-Insフォルダに入れると使えるようになる。

Audacityで実行してみたときの画面。パラメーターはSineの周波数の設定と、Gainのみ。選択トラックに対して波形を重ねることができる。

実際に100Hzを生成して、次に同じトラックに150Hzを重ねてみたところ。サイン波の合成が手軽にできる。


sound programming 目次
C言語 ANSI C89 Meadow & MinGW GCC 目次