使用C ++帮助创建ALSA插件

时间:2017-08-05 18:07:36

标签: c++ plugins alsa

我开始调查ALSA插件创建。

我用帮助C创建了简单的插件。 目前我正在尝试用C ++帮助重写它。

我编译了它并且在尝试使用帮助我的插件播放wav文件后我发现了错误:

symbol _snd_pcm_test_open is not defined inside /usr/lib/x86_64-linux-gnu/alsa-lib/libasound_module_pcm_test.so

头文件CAlsaTestPlugin.hpp:

#include <stdint.h>
#include <string>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>
#include <alsa/hwdep.h>


#define DBG(fmt, arg...)  printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
#define ERR(fmt, arg...)  printf("ERROR: %s: " fmt "\n" , __FUNCTION__ , ## arg)
#define WRN(fmt, arg...)  printf("WARNING: %s: " fmt "\n" , __FUNCTION__ , ## arg)

typedef struct test_plug_data
{
    snd_pcm_t *pcm_handle;
    snd_pcm_hw_params_t *params;
    int16_t* buffer;
    u_int16_t buffer_size;
    u_int32_t pos;
}test_plug_data_t;

typedef struct snd_pcm_test
{
    snd_pcm_ioplug_t io_plug;
    struct test_plug_data* data;
}snd_pcm_test_t;


static snd_pcm_ioplug_callback_t createPlaybackCallbaks();
static void test_add_data_to_buffer(test_plug_data_t* data, int16_t value);
static int test_hw_constraint(snd_pcm_ioplug_t *io);
//plugin callbacks
static int test_playback_start(snd_pcm_ioplug_t *io);
static int test_playback_stop(snd_pcm_ioplug_t *io);
static snd_pcm_sframes_t test_pointer(snd_pcm_ioplug_t *io);
static int test_close(snd_pcm_ioplug_t *io);
static int test_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params);
static int test_prepare(snd_pcm_ioplug_t *io);
static snd_pcm_sframes_t test_write(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas,
                                    snd_pcm_uframes_t offset, snd_pcm_uframes_t size);


int _snd_pcm_test_open(snd_pcm_t **pcmp, const char *name,
                  snd_config_t *root, snd_config_t *conf,
                  snd_pcm_stream_t stream, int mode);

源文件CAlsaTestPlugin.cpp

#include "CAlsaTestPlugin.hpp"


#define PCM_DEVICE "default"

static snd_pcm_ioplug_callback_t test_playback_callback = createPlaybackCallbaks();

snd_pcm_ioplug_callback_t createPlaybackCallbaks()
{
    snd_pcm_ioplug_callback_t result;
    result.start        = test_playback_start;
    result.stop            = test_playback_stop;
    result.pointer        = test_pointer;
    result.close        = test_close;
    result.hw_params    = test_hw_params;
    result.prepare        = test_prepare;
    result.transfer        = test_write;

    return result;
}

static int test_playback_start(snd_pcm_ioplug_t *io)
{
    DBG("%s started: c:%d, f:%d, r:%d", (io->stream == SND_PCM_STREAM_CAPTURE) ? "Capture" : "Playback", io->channels, io->format, io->rate);
    return 0;
}

int test_playback_stop(snd_pcm_ioplug_t *io)
{
    DBG("%s stopped\n", (io->stream == SND_PCM_STREAM_CAPTURE) ? "Capture" : "Playback");
    return 0;
}

snd_pcm_sframes_t test_pointer(snd_pcm_ioplug_t *io)
{
    static snd_pcm_sframes_t dummy = 0;
    //DBG("%ld", dummy);
    return 2048 * (dummy = ((dummy + 1) % 4));
}

int test_close(snd_pcm_ioplug_t *io)
{
    DBG("");
    return 0;
}

int test_prepare(snd_pcm_ioplug_t *io)
{
    DBG("stream = %s, access = %s, format = %s, rate = %u, channels = %u, period_size = %lu, buffer_size = %lu",
        snd_pcm_stream_name(io->stream), snd_pcm_access_name(io->access), snd_pcm_format_name(io->format),
        io->rate, io->channels, io->period_size, io->buffer_size);

    test_plug_data_t* data = static_cast<test_plug_data_t*>(io->private_data);
    unsigned int pcm, tmp;
    u_int32_t rate, channels;
    snd_pcm_uframes_t frames;
    snd_pcm_uframes_t period_size = 48;
    char *buff;
    int buff_size;
    rate      = io->rate;
    channels = io->channels;


    if (pcm = snd_pcm_open(&(data->pcm_handle), PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0)
    {
          printf("ERROR: Can't open \"%s\" PCM device. %s", PCM_DEVICE, snd_strerror(pcm));
    }

    /* Allocate parameters object and fill it with default values*/
    snd_pcm_hw_params_alloca(&(data->params));

    snd_pcm_hw_params_any(data->pcm_handle, data->params);

    /* Set parameters */
    if (pcm = snd_pcm_hw_params_set_access(data->pcm_handle, data->params,
                SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
        DBG("ERROR: Can't set interleaved mode. %s", snd_strerror(pcm));

    if (pcm = snd_pcm_hw_params_set_format(data->pcm_handle, data->params,
                    SND_PCM_FORMAT_S16_LE) < 0)
        DBG("ERROR: Can't set format. %s", snd_strerror(pcm));

    if (pcm = snd_pcm_hw_params_set_channels(data->pcm_handle, data->params, channels) < 0)
        DBG("ERROR: Can't set channels number. %s", snd_strerror(pcm));

    if (pcm = snd_pcm_hw_params_set_rate_near(data->pcm_handle, data->params, &rate, 0) < 0)
        DBG("ERROR: Can't set rate. %s", snd_strerror(pcm));

    /* Write parameters */
    if (pcm = snd_pcm_hw_params(data->pcm_handle, data->params) < 0)
        DBG("ERROR: Can't set harware parameters. %s", snd_strerror(pcm));

    /* Resume information */
    DBG("PCM name: '%s'", snd_pcm_name(data->pcm_handle));

    DBG("PCM state: %s", snd_pcm_state_name(snd_pcm_state(data->pcm_handle)));

    snd_pcm_hw_params_get_channels(data->params, &tmp);
    DBG("channels: %i (%s)", tmp, tmp == 1 ? "mono" : "stereo");

    snd_pcm_hw_params_get_rate(data->params, &tmp, 0);
    DBG("rate: %d kHz", tmp);

    /* Allocate buffer to hold single period */
    snd_pcm_hw_params_get_period_size(data->params, &frames, 0);
    DBG("Period size: %lu", frames);

    buff_size = frames * channels /* 2 -> sample size */;
    data->buffer_size = buff_size;
    data->buffer = (int16_t *) malloc(data->buffer_size * 2);
    data->pos = 0;

    snd_pcm_hw_params_get_period_time(data->params, &tmp, NULL);
    DBG("Period time: %lu", tmp);

    return 0;
}



int test_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params)
{
    DBG("");
    return 0;
}

static void test_add_data_to_buffer(test_plug_data_t* data, int16_t value)
{
    data->buffer[data->pos] = value;
    //DBG("pos = %u, value = %i, data[%u] = %i", data->pos, value, data->pos, data->buffer[data->pos]);
    data->pos++;
    if(data->pos == data->buffer_size)
    {
      //DBG("Play!!!!!");

      unsigned int pcm;
      if (pcm = snd_pcm_writei(data->pcm_handle, data->buffer, data->buffer_size / 2) == -EPIPE)
      {
         DBG("XRUN");
         //snd_pcm_prepare(data->pcm_handle);
      }
      else if (pcm < 0) {
         DBG("ERROR. Can't write to PCM device. %s", snd_strerror(pcm));
      }
      else
      {
         DBG("GOOD. pcm = %u", pcm);
      }

      data->pos = 0;
    }
}

static snd_pcm_sframes_t test_write(snd_pcm_ioplug_t *io,
                const snd_pcm_channel_area_t *areas,
                snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
{
    static u_int32_t area_number = 0;
    test_plug_data_t* data = static_cast<test_plug_data_t*>(io->private_data);
    //DBG("area_number = %u, offset = %lu, size = %lu", area_number, offset, size);

    int16_t* value_ptr = (int16_t*)(areas[0].addr + areas[0].first / 8);
    int16_t* value_ptr2 = (int16_t*)(areas[1].addr + areas[1].first / 8);
    for(u_int32_t i = 0; i < size; ++i)
    {
      test_add_data_to_buffer(data, *value_ptr );
      test_add_data_to_buffer(data, *value_ptr2 );
      value_ptr+=2;
      value_ptr2+=2;
    }

    ++area_number;
    return size;
}

static int test_hw_constraint(snd_pcm_ioplug_t *io)
{
    unsigned int access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED };
    unsigned int rate_list[] = { 44100, 48000 };
    unsigned int format_list[] = { SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S32_LE };
    unsigned int period_list[] = { 6 * 8 };
    int err;

    /* access type */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, sizeof(access_list) / sizeof(access_list[0]), access_list);
    if (err < 0)
        return err;

    /* supported formats */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, sizeof(format_list) / sizeof(format_list[0]), format_list);
    if (err < 0)
        return err;

    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, 1, 2);
    if (err < 0)
        return err;

    /* supported buffer sizes*/
    err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, period_list[0] * 2, period_list[0] * 8);
    if (err < 0)
        return err;

    /* supported block sizes: */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, sizeof(period_list) / sizeof(period_list[0]), period_list);
    if (err < 0)
        return err;

    /* supported rates */
    err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, sizeof(rate_list) / sizeof(rate_list[0]), rate_list);
    if (err < 0)
        return err;

    return 0;
}

SND_PCM_PLUGIN_DEFINE_FUNC(test)
{
    snd_config_iterator_t i, next;
    int err;
    snd_pcm_test_t* test_data;
    //TODO use new
    test_data = static_cast<snd_pcm_test_t*>(calloc(1, sizeof(snd_pcm_test_t)));
    if (!test_data)
      return -ENOMEM;

    test_data->data = static_cast<test_plug_data_t*>(calloc(1, sizeof(test_plug_data_t)));
    if (! test_data->data) {
      return -ENOMEM;
    }

    snd_config_for_each(i, next, conf)
    {
        snd_config_t *n = snd_config_iterator_entry(i);
        const char *id;
        if (snd_config_get_id(n, &id) < 0)
            continue;

        if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
            continue;
    }

    test_data->io_plug.version = SND_PCM_IOPLUG_VERSION;
    test_data->io_plug.name = "TEST PLUGIN";
    test_data->io_plug.callback = &test_playback_callback;
    test_data->data->pcm_handle = 0;
    test_data->io_plug.private_data = &test_data->data;
    err = snd_pcm_ioplug_create(&test_data->io_plug, name, stream, mode);


    err = test_hw_constraint(&test_data->io_plug);

    if (err < 0) {
      snd_pcm_ioplug_delete(&test_data->io_plug);
        goto error;
    }

    *pcmp = test_data->io_plug.pcm;
error:
    return err;
}

SND_PCM_PLUGIN_SYMBOL(test)

如果有人有使用C ++创建ALSA插件的经验,请帮助我。

我使用hel cmake编译了lib。 的CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

PROJECT(AlsaPlugins)
MESSAGE("Building ${PROJECT_NAME}")

SET(CMAKE_VERBOSE_MAKEFILE on)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -lasound -g -O2 -MD -MP -fPIC -DPIC")
MESSAGE(STATUS "Compiler options: ${CMAKE_CXX_FLAGS}")

ADD_LIBRARY(asound_module_pcm_test SHARED CAlsaTestPlugin.cpp)

将插件配置添加到.asoundrc:

pcm.test {
    type test
    playback_ports {
        0 alsa_pcm:playback_1
    }
    capture_ports {
        0 alsa_pcm:capture_1
    }
}

已安装的插件库到/ usr / lib / x86_64-linux-gnu / alsa-lib 然后调用控制台命令来测试插件: aplay -Drui~ / Alesis-Fusion-Nylon-String-Guitar-C4.wav&gt; 〜/ out.txt

如果我使用帮助C编译相同的代码,它可以工作。 我使用来自alsa_plugins项目的Makefile的hepl Makefile编译C源代码的主要区别。 添加了Makefile的链接,因为达到了mesage size imit https://drive.google.com/open?id=0B5n7ZdHrpqwJc0Q4dTB1VEFibDQ

2 个答案:

答案 0 :(得分:0)

有必要使用extern“C”覆盖ALSA插件函数定义和类型定义。

#ifdef __cplusplus
extern "C" {
#endif
... ALSA plugin function defininitions
#ifdef __cplusplus
}
#endif

答案 1 :(得分:0)

gtkiostream提供了一个用于实现ALSA插件的C ++类。您可以将该代码库用于C ++抽象的ALSA子系统,也可以找出该代码中缺少的代码。

您可以像这样为外部ALSA插件继承gtkiostream:

#include <ALSA/ALSAExternalPlugin.H>
class ALSAExternalPluginTest : public ALSAExternalPlugin {

    virtual int specifyHWParams(){
       // you have to implement this method, similar to here : https://github.com/flatmax/gtkiostream/blob/master/test/ALSAExternalPluginTest.C#L61
       // you have now specified your data format and channels
       return 0;
    };

    virtual snd_pcm_sframes_t transfer(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset, snd_pcm_uframes_t size){
        // use Eigen to wrap the ALSA audio buffers in Matrices for use in computation. 
        int ch=2;
        Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> stride(1,ch);
        float *srcAddr=(float*)getAddress(src_areas, src_offset);            
        float *dstAddr=(float*)getAddress(dst_areas, dst_offset);            
        Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>, 
              Eigen::Unaligned, Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> >                                                                          
                                                 in(srcAddr, size, ch, stride);
        Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>, 
              Eigen::Unaligned, Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> >
                  out(dstAddr, size, ch, stride);

        // copy the input audio to the output audio - you should implement your algorithm here
        out=in;
        return size;
    }
};

您可以这样定义插件:

ALSAExternalPluginTest aEPlugin;
extern "C" SND_PCM_PLUGIN_DEFINE_FUNC(ALSAExternalPluginTest){
    std::cout<<__func__<<std::endl;
    aEPlugin.parseConfig(name, conf, stream, mode);

    int ret=aEPlugin.create(name, root, stream, mode);
    if (ret<0)
        return ret;

    aEPlugin.specifyHWParams();

    *pcmp=aEPlugin.getPCM();
    std::cout<<__func__<<" returning "<<std::endl;
    return 0;
}

SND_PCM_PLUGIN_SYMBOL(ALSAExternalPluginTest);

以下是使用g ++进行编译的示例标志(也包括libasound2标志):

CPPFLAGS = $(EIGEN_CFLAGS)
LDFLAGS = -module -avoid-version -export-dynamic -no-undefined