2013/01/25

Reverbの自作2 all-pass filter

やや謎のall-pass filterにトライ。以前Phaserを作ったときにall-pass filterは作っているのだが、名前は同じでも構造も効果も違う。あまり名前にこだわらずにARIの解説の通りに作ることにする。前回のcomb filterと同じ遅延によるフィルターなのだが、all-pass filetrは周波数特性がフラットというのが特徴となっている。このフラットがどれぐらいを意味するのか不明だが、原理的には完全フラットということはあり得ないような気がする。まぁ作って判断するのが早い。

all-pass filter ブロック図


まずは、この通りプログラムをLADSPAで組んでみる。前回のcomb filterを修正するだけなので、数分で完成。むしろこのBlogを書いている時間が長い。

デルタ関数に適用すると、以下のようになった。1回目は反転して0.7がかけられている。2回目以降はプラスのままで、x1.0,x0.7,0.7 ・・・という具合になっている。

ホワイトノイズに適用してみた。確かにcombに比べれば平坦でフラットに近いかもしれないが、遅延時間を短くするとやはりくし形に近くなってくる。多少はマシという程度で考えるといいかもしれない。

これで下準備はできたので、次回から、これらを組合わせてリバーブとして機能させていく。

ソースは以下の通り。

LADSPA all-pass filter

/* namagi_allpass.c 2013.01.25
compile
gcc -shared -o namagi_allpass.dll namagi_allpass.c -ID
*/
/***********************************************************/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "ladspa.h"
#define MAX_DELAY        1000
#define SDL_DELAY        0
#define SDL_FEEDBACK     1
#define SDL_INPUT        2
#define SDL_OUTPUT       3
/***********************************************************/
#ifdef WIN32
int bIsFirstTime = 1; 
void _init();
#endif
#ifdef WIN32
 #define _WINDOWS_DLL_EXPORT_ __declspec(dllexport)
#else
    #define _WINDOWS_DLL_EXPORT_ 
#endif
/***********************************************************/
#define LIMIT_BETWEEN_0_AND_MAX_DELAY(x)  \
(((x) < 0) ? 0 : (((x) > MAX_DELAY) ? MAX_DELAY : (x)))
/***********************************************************/
typedef struct {
  LADSPA_Data m_fSampleRate; 
  LADSPA_Data *m_pfBuffer;
  unsigned long m_lBufferSize;
  unsigned long m_lWritePointer;
  LADSPA_Data *m_pfDelay;
  LADSPA_Data *m_pfFeedback;
  LADSPA_Data *m_pfInput;
  LADSPA_Data *m_pfOutput;
} SimpleDelayLine;
/***********************************************************/
LADSPA_Handle 
instantiateSimpleDelayLine(const LADSPA_Descriptor
        * Descriptor, 
        unsigned long SampleRate){
  unsigned long lMinimumBufferSize;
  SimpleDelayLine * psDelayLine;
  psDelayLine = 
     (SimpleDelayLine *)malloc(sizeof(SimpleDelayLine));
  if (psDelayLine == NULL) return NULL;
  psDelayLine -> m_fSampleRate = (LADSPA_Data)SampleRate;
  lMinimumBufferSize = (LADSPA_Data)SampleRate;
  psDelayLine -> m_lBufferSize = 1;
  while (psDelayLine -> m_lBufferSize < lMinimumBufferSize)
    psDelayLine -> m_lBufferSize <<= 1;
  psDelayLine -> m_pfBuffer = 
    (LADSPA_Data *)calloc(psDelayLine -> 
        m_lBufferSize, sizeof(LADSPA_Data));
  if (psDelayLine -> m_pfBuffer == NULL) {
    free(psDelayLine);
    return NULL;
  }
  psDelayLine -> m_lWritePointer = 0;
  return psDelayLine;
}
/***********************************************************/
void activateSimpleDelayLine(LADSPA_Handle Instance) {
  SimpleDelayLine *psSimpleDelayLine;
  psSimpleDelayLine = (SimpleDelayLine *)Instance;
  memset(psSimpleDelayLine -> m_pfBuffer, 
     0, sizeof(LADSPA_Data)
     *psSimpleDelayLine -> m_lBufferSize);
}
/***********************************************************/
void connectPortToSimpleDelayLine(LADSPA_Handle Instance,
        unsigned long Port,LADSPA_Data * DataLocation){
  SimpleDelayLine * psSimpleDelayLine;
  psSimpleDelayLine = (SimpleDelayLine *)Instance;
  
  switch (Port) {
  case SDL_DELAY:
    psSimpleDelayLine -> m_pfDelay = DataLocation;
    break;  
  case SDL_FEEDBACK:
    psSimpleDelayLine -> m_pfFeedback = DataLocation;
    break;
  case SDL_INPUT:
    psSimpleDelayLine -> m_pfInput = DataLocation;
    break;
  case SDL_OUTPUT:
    psSimpleDelayLine -> m_pfOutput = DataLocation;
    break;
  }
}
/***********************************************************/
void runSimpleDelayLine(LADSPA_Handle Instance, 
     unsigned long SampleCount){
  LADSPA_Data *pfBuffer;
  LADSPA_Data *pfInput;
  LADSPA_Data *pfOutput;
  LADSPA_Data fInputSample;
  LADSPA_Data fOutputSample;
  LADSPA_Data fFeedback;
  SimpleDelayLine *psSimpleDelayLine;
  unsigned long lBufferReadOffset;
  unsigned long lBufferSizeMinusOne;
  unsigned long lBufferWriteOffset;
  unsigned long lDelay;
  unsigned long lSampleIndex;
  psSimpleDelayLine =
     (SimpleDelayLine*) Instance;
  lBufferSizeMinusOne =
     psSimpleDelayLine -> m_lBufferSize - 1;
  lDelay = (unsigned long)
  (LIMIT_BETWEEN_0_AND_MAX_DELAY(*(psSimpleDelayLine ->
     m_pfDelay)) 
     *psSimpleDelayLine -> m_fSampleRate);
  pfInput = psSimpleDelayLine -> m_pfInput;
  pfOutput = psSimpleDelayLine -> m_pfOutput;
  pfBuffer = psSimpleDelayLine -> m_pfBuffer;

  lBufferWriteOffset = 
    psSimpleDelayLine -> m_lWritePointer;
  lBufferReadOffset = 
    lBufferWriteOffset + psSimpleDelayLine -> 
    m_lBufferSize - (float)lDelay/1000;
  fFeedback = *(psSimpleDelayLine -> m_pfFeedback);
  for (lSampleIndex = 0; 
      lSampleIndex < SampleCount; 
      lSampleIndex++){
    fInputSample = *(pfInput++);
    
   *(pfOutput++) = (-1 * fFeedback * fInputSample) +
   pfBuffer[((lSampleIndex + 
   lBufferReadOffset) & lBufferSizeMinusOne)];
 /* gain: 0.7  */
    pfBuffer[((lSampleIndex + lBufferWriteOffset) & 
      lBufferSizeMinusOne)] = 
      (fInputSample +
      fFeedback * pfBuffer[((lSampleIndex + 
      lBufferReadOffset) & lBufferSizeMinusOne)]);
  }
  psSimpleDelayLine -> m_lWritePointer
    = ((psSimpleDelayLine -> m_lWritePointer 
     + SampleCount) & lBufferSizeMinusOne);
}
/***********************************************************/
void cleanupSimpleDelayLine(LADSPA_Handle Instance){
  SimpleDelayLine * psSimpleDelayLine;
  psSimpleDelayLine = (SimpleDelayLine *)Instance;
  free(psSimpleDelayLine -> m_pfBuffer);
  free(psSimpleDelayLine);
}
/***********************************************************/
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 = 10;
    g_psDescriptor->Label = strdup("all-pass");
    g_psDescriptor->Properties = 
       LADSPA_PROPERTY_HARD_RT_CAPABLE;
    g_psDescriptor->Name = strdup("Namagi: all-pass ver.130125");
    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[SDL_DELAY] = 
      LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    piPortDescriptors[SDL_FEEDBACK] = 
       LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    piPortDescriptors[SDL_INPUT] = 
       LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
    piPortDescriptors[SDL_OUTPUT] = 
       LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
    pcPortNames = (char **)calloc(4, sizeof(char *));
    g_psDescriptor->PortNames = 
       (const char **)pcPortNames;
    pcPortNames[SDL_DELAY] = strdup("DelayTime(msec)");
    pcPortNames[SDL_FEEDBACK] = strdup("Feedback");
    pcPortNames[SDL_INPUT] = strdup("Input");
    pcPortNames[SDL_OUTPUT] = strdup("Output");
    psPortRangeHints  = 
      ((LADSPA_PortRangeHint *)calloc(4,
       sizeof(LADSPA_PortRangeHint)));
    g_psDescriptor -> PortRangeHints = 
       (const LADSPA_PortRangeHint *)psPortRangeHints;

    psPortRangeHints[SDL_DELAY].HintDescriptor
      = (LADSPA_HINT_BOUNDED_BELOW | 
        LADSPA_HINT_BOUNDED_ABOVE | 
        LADSPA_HINT_DEFAULT_MAXIMUM);
    psPortRangeHints[SDL_DELAY].LowerBound  = 0;
    psPortRangeHints[SDL_DELAY].UpperBound  = 
       (LADSPA_Data)MAX_DELAY; 
    psPortRangeHints[SDL_FEEDBACK].HintDescriptor = 
       (LADSPA_HINT_BOUNDED_BELOW 
    | LADSPA_HINT_BOUNDED_ABOVE 
           | LADSPA_HINT_DEFAULT_HIGH);
    psPortRangeHints[SDL_FEEDBACK].LowerBound = -1;
    psPortRangeHints[SDL_FEEDBACK].UpperBound = 1;
    psPortRangeHints[SDL_INPUT].HintDescriptor = 0;
    psPortRangeHints[SDL_OUTPUT].HintDescriptor = 0;
    g_psDescriptor -> instantiate = 
      instantiateSimpleDelayLine;
    g_psDescriptor -> connect_port = 
      connectPortToSimpleDelayLine;
    g_psDescriptor -> activate = activateSimpleDelayLine;
    g_psDescriptor -> run = runSimpleDelayLine;
    g_psDescriptor -> run_adding = NULL;
    g_psDescriptor -> set_run_adding_gain = NULL;
    g_psDescriptor -> deactivate = NULL;
    g_psDescriptor -> cleanup = cleanupSimpleDelayLine;
  }
}
/***********************************************************/
void _fini() {
  long lIndex;
  if (g_psDescriptor) {
    free((char *)g_psDescriptor -> Label);
    free((char *)g_psDescriptor -> Name);
    free((char *)g_psDescriptor -> Maker);
    free((char *)g_psDescriptor -> Copyright);
    free((LADSPA_PortDescriptor *)g_psDescriptor -> 
      PortDescriptors);
    for (lIndex = 0; lIndex < g_psDescriptor -> 
      PortCount; lIndex++)
      free((char *)(g_psDescriptor -> PortNames[lIndex]));
    free((char **)g_psDescriptor -> PortNames);
    free((LADSPA_PortRangeHint *)g_psDescriptor -> 
      PortRangeHints);
    free(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;
}
/***********************************************************/
/* EOF */


sound programming 目次