ALSA Hooks - 在播放音频时修改音频

时间:2017-03-17 20:28:44

标签: audio hook alsa

我对此感到茫然 - 我有几个个人项目,基本上要求我“点击”音频流:在最终发送之前读取音频数据,进行一些处理并修改音频数据到音频设备。

这些个人项目的一个例子是基于软件的主动交叉。如果我有一个带6个通道的音频设备(即3个左侧+ 3个右侧),那么我可以读取数据,应用LP滤波器(×2 - 左+右),BP滤波器和HP滤波器并输出流经六个频道中的每一个。

请注意,我知道如何编写一个执行此操作的播放器应用程序 - 相反,我希望这样做,以便来自任何来源的任何音频(音频播放器,视频播放器,YouTube或任何其他音频来源)网页浏览器等)受此处理。

我见过一些例子(例如,来自alsa-project网站的pcm_min.c,2004年9月Jeff Tranter撰写的Linux Journal文章中的播放和记录示例)但我似乎没有足够的信息做我上面描述的事情。

任何帮助或指示将不胜感激。

2 个答案:

答案 0 :(得分:0)

如果你想弄清楚一些代码,你可以查看Paul Davis的some of these articles(Paul Davis是Linux音频大师)。您必须将播放和捕获示例结合起来才能获得实时音频。试一试,如果遇到问题,可以在SO上发布特定于代码的问题。

实时音频正常工作后,您可以实施LP filter并从那里开始。

有很多LADSPA和LV2音频插件可以实现LP,HP和BP滤波器,但我不确定是否有任何可用于您的特定通道配置。听起来你想要自己动手。

答案 1 :(得分:0)

您可以将项目实现为LADSPA插件,使用audacity或任何其他支持LADSPA插件的程序进行测试,并在需要时将其插入alsa / pulseaudio / jack播放链中。

" LADSPA"是single header file定义一个简单的接口来编写音频处理插件。每个插件都有其输入/输出/控制端口和run()功能。对每个样本块执行run()函数以进行实际的音频处理 - 应用"控制"参与"输入"缓冲并将结果写入"输出"缓冲器。

示例LADSPA立体声放大器插件(单控制参数:"放大系数",两个输入端口,两个输出端口):

///gcc -shared -o /full/path/to/plugindir/amp_example.so amp_example.c
#include <stdlib.h>
#include "ladspa.h"

enum PORTS {
  PORT_CAMP,
  PORT_INPUT1,
  PORT_INPUT2,
  PORT_OUTPUT1,
  PORT_OUTPUT2
};

typedef struct {
  LADSPA_Data *c_amp;
  LADSPA_Data *i_audio1;
  LADSPA_Data *i_audio2;
  LADSPA_Data *o_audio1;
  LADSPA_Data *o_audio2;
} MyAmpData;

static LADSPA_Handle myamp_instantiate(const LADSPA_Descriptor *Descriptor, unsigned long SampleRate)
{
  MyAmpData *data = (MyAmpData*)malloc(sizeof(MyAmpData));
  data->c_amp = NULL;
  data->i_audio1 = NULL;
  data->i_audio2 = NULL;
  data->o_audio1 = NULL;
  data->o_audio2 = NULL;
  return data;
}

static void myamp_connect_port(LADSPA_Handle Instance, unsigned long Port, LADSPA_Data *DataLocation)
{
  MyAmpData *data = (MyAmpData*)Instance;
  switch (Port)
  {
    case PORT_CAMP:    data->c_amp    = DataLocation; break;
    case PORT_INPUT1:  data->i_audio1 = DataLocation; break;
    case PORT_INPUT2:  data->i_audio2 = DataLocation; break;
    case PORT_OUTPUT1: data->o_audio1 = DataLocation; break;
    case PORT_OUTPUT2: data->o_audio2 = DataLocation; break;
  }
}

static void myamp_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
  MyAmpData *data = (MyAmpData*)Instance;
  double amp = *data->c_amp;
  size_t i;
  for (i = 0; i < SampleCount; i++)
  {
    data->o_audio1[i] = data->i_audio1[i]*amp;
    data->o_audio2[i] = data->i_audio2[i]*amp;
  }
}

static void myamp_cleanup(LADSPA_Handle Instance)
{
  MyAmpData *data = (MyAmpData*)Instance;
  free(data);
}

static LADSPA_Descriptor myampDescriptor = {
  .UniqueID = 123, // for public release see http://ladspa.org/ladspa_sdk/unique_ids.html
  .Label = "amp_example",
  .Name = "My Amplify Plugin",
  .Maker = "alsauser",
  .Copyright = "WTFPL",
  .PortCount = 5,
  .PortDescriptors = (LADSPA_PortDescriptor[]){
    LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
    LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
    LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
    LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
    LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO
  },
  .PortNames = (const char * const[]){
    "Amplification factor",
    "Input left",
    "Input right",
    "Output left",
    "Output right"
  },
  .PortRangeHints = (LADSPA_PortRangeHint[]){
    { /* PORT_CAMP */
      LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_1,
      0, /* LowerBound*/
      10 /* UpperBound */
    },
    {0, 0, 0}, /* PORT_INPUT1 */
    {0, 0, 0}, /* PORT_INPUT2 */
    {0, 0, 0}, /* PORT_OUTPUT1 */
    {0, 0, 0}  /* PORT_OUTPUT2 */
  },
  .instantiate = myamp_instantiate,
  //.activate = myamp_activate,
  .connect_port = myamp_connect_port,
  .run = myamp_run,
  //.deactivate = myamp_deactivate,
  .cleanup = myamp_cleanup
};

// NULL-terminated list of plugins in this library
const LADSPA_Descriptor *ladspa_descriptor(unsigned long Index)
{
  if (Index == 0)
    return &myampDescriptor;
  else
    return NULL;
}

(如果您愿意&#34;短&#34; 40行版本,请参阅https://pastebin.com/unCnjYfD

根据需要添加任意数量的输入/输出通道,在myamp_run()函数中实现代码。构建插件并将LADSPA_PATH环境变量设置为您构建它的目录,以便其他应用程序可以找到它:

export LADSPA_PATH=/usr/lib/ladspa:/full/path/to/plugindir

audacity或任何其他支持LADSPA插件的程序中进行测试。要在终端中测试它,您可以使用来自&#34; ladspa-sdk&#34;的applyplugin工具。包:

applyplugin input.wav output.wav /full/path/to/plugindir/amp_example.so amp_example 2

如果您喜欢结果,请将其插入默认播放链。对于普通alsa,您可以使用类似的配置(不会为脉冲/插孔工作):

# ~/.asoundrc
pcm.myamp {
  type plug
  slave.pcm {
    type ladspa
    path "/usr/lib/ladspa" # required but ignored as `filename` is set
    slave.pcm "sysdefault"
    playback_plugins [{
      filename "/full/path/to/plugindir/amp_example.so"
      label "amp_example"
      input.controls [ 2.0 ] # Amplification=2
    }]
  }
}
# to test it:   aplay -Dmyamp input.wav

# to point "default" pcm to it uncomment next line:
#pcm.!default "myamp"

另见: