2017-03-17 31 views
1

我对此感到迷茫—我有几个个人项目,基本上要求我“轻敲”音频流:读取音频数据,做一些处理并在最终将音频数据发送到音频设备之前修改音频数据。ALSA挂钩 - 修改正在播放的音频

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

请注意,我知道如何编写这样做的播放器应用程序—,我想这样做是为了让来自任何来源(音频播放器,视频播放器,YouTube或任何其他音频源网络浏览器等)受到这种处理。

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

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

+1

这听起来像是用于[外部过滤器插件]的作业(http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___ext_plug.html)。 (例如,请参阅'upmix'或'vdownmix'插件。) –

+0

我理解这个权利吗?来自http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_external_plugins.html中的文档:“过滤器类型的插件是一个插件,用于将来自输入和馈送的PCM信号转换为输出”。这不是我所追求的---我想拦截发送到输出的数据(来自图腾或VLC视频播放器;来自YouTube或一般来自网络浏览器;来自游戏等),修改它(包括改变频道数量等),并让它继续到达输出设备。我误解了吗?我可以通过外部过滤器插件来实现吗? –

+1

这正是过滤器插件的功能; “输入”和“输出”不是指单独的设备,而是指数据流。 –

回答

0

如果你想用一些代码弄脏你的手,你可以查看Paul Davis的some of these articles(Paul Davis是一位Linux音频专家)。您必须结合播放和捕捉示例才能获得实时音频。试一试,如果你有问题,你可以在SO上发布代码特定的问题。

一旦你得到现场音频工作,你可以实现LP filter并从那里去。

有很多LADSPA和LV2音频插件可实现LP,HP和BP过滤器,但我不确定是否有任何适用于您特定通道配置的音频插件。这听起来像你想要推出自己的东西。

+0

是的,数字滤波器部分对我来说很简单......曾几何时,当奔腾II漫游地球,C/C++用户日志仍然存在时,我创建了这个(可以说是整洁的)C++ FIR滤波器库和在C/C++ UJ上发表文章---我有我自己的副本,以防万一有人好奇:https://mochima.com/articles/FIR/FIR.html –

+0

@ Cal-linux哦,这就是很酷。你打算为你的过滤器使用一个DSP库吗? – tay10r

+0

当我达到那一点时,我会决定哪些方法可行,而且很容易 - 再次,有了使用DSP的经验,这是不会对我造成任何压力的部分:-)对于今天的PC,性能不应该成为问题(我知道,我知道---着名的遗言?:-)) –

0

您可以实现您的项目作为一个LADSPA插件的,与audacity或任何其他程序支持LADSPA插件测试它,当你喜欢它,把它插入到ALSA /的PulseAudio /插孔播放链。

“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; 
} 

(如果你喜欢的“短” 40线版本见https://pastebin.com/unCnjYfD

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

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

测试它audacity或任何其他程序支持LADSPA插件。为了在终端测试它,你可以从“LADSPA-SDK”包中使用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" 

参见: