【问题标题】:Simple example of playing '.wav' audios simultaneously with alsa-lib in c++?在 c++ 中与 alsa-lib 同时播放“.wav”音频的简单示例?
【发布时间】:2020-05-01 20:34:54
【问题描述】:

我正在用电脑键盘制作钢琴,我需要同时播放“.wav”音频。

我设法使用 SFML/Audio.hpp 和 SDL2/SDL_mixer.h 库完美地做到了这一点,但是下面代码的 PlayAudio :: play() 函数需要几毫秒来播放音频,延迟几乎然而,难以察觉,当钢琴高速弹奏时,我注意到存在小的延迟。

例子:

#include <SFML / Audio.hpp>
#include <SDL2 / SDL_mixer.h>
#include "PlayAudio.h"

sf :: SoundBuffer buffer [10];
sf :: Sound pad [10];

void PlayAudio :: loadBank () {
  buffer [0] .loadFromFile ("src / audiosExa / a1.wav");
  pad [0] .setBuffer (buffer [0]);

  buffer [1] .loadFromFile ("src / audiosExa / a2.wav");
  pad [1] .setBuffer (buffer [1]);

  buffer [2] .loadFromFile ("src / audiosExa / a3.wav");
  pad [2] .setBuffer (buffer [2]);

   buffer [3] .loadFromFile ("src / audiosExa / a4.wav");
   pad [3] .setBuffer (buffer [3]);
};

void PlayAudio :: play (int i) {
  pad [i] .play ();
};

所以我考虑做同样的过程,但使用似乎更快的 alsa-lib,但我设法一次发出一个声音,我不能同时发出音频。我尝试使用线程,但只有在另一个声音结束后才会发出声音。

【问题讨论】:

    标签: c++ audio sdl-2 playback libalsa


    【解决方案1】:

    几个月后,有可能以一种非常简单的方式解决这个大问题......在上面提到的代码中,我只使用了 SFML LIB,不幸的是,正如我已经评论过的那样,它的延迟非常高... lib SDL 它根本没有被使用,它只是被导入。我重新编写了代码,这次只使用了 SDL 库,除了获得出色的延迟之外,我还设法让音频同时播放。按照以下教程进行操作:https://soundprogramming.net/programming/tutorial-using-sdl2-and-sdl_mixer-to-play-samples/

    我对代码做了一些小改动,只在 Linux 上使用。

    ALSA lib 的使用相当复杂,因为级别很低。如果您不需要非常快的音频,请将上面的代码与 lib SFML 一起使用,因为它实现起来非常简单快捷,否则使用 SDL 就可以成功。

    【讨论】:

      【解决方案2】:

      您可以采用几种不同的方法在您的应用程序中获得复音。要么你自己混音,要么让 ALSA 帮你混音。

      可能最简单(也是最耗费资源)的方法是让 ALSA 为您进行混音。在这种情况下,您为每个打击垫打开一次 ALSA dmix 插件。您可以为每个焊盘创建一个类并将它们穿线,以便它们独立运行。每个类将音频数据加载到缓冲区,并在触发时将其播放回 ALSA dmix 插件。这是一个示例(使用gtkIOStream,它提供了 C++ libsox 音频文件处理、线程和 ALSA 处理):

      #include <Sox.H>
      #include <ALSA.H>
      #include <Thread.H>
      using namespace ALSA;
      
      class Pad : public PlayBack, WaitingThread {
        Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> audio; // the Eigen array which holds the audio buffer
      
      public:
      
        /// Constructor opens the audio plawyback device and reads in the audio file data
        Pad(char* devName, string audioFile) : PlayBack(devName) {
          Sox<int> sox; // instantiate libsox
          sox.openRead(audioFile); // open the audio file to read
          sox.read(audio); // read in the audio data
        }
      
        /// Configure the audio playback ([see here for example][2])
        configurePlayback(){
          // set channels, format, etc.
        }
      
        /// Playback the entire audio sound
        play(){
          *this<<audio; // stream the audio out through the PlayBack class
        }
      
        // In our threaded method, we wait to be signalled then we do playback, then wait again, [see here for examples on thread signalling][3]
        void *threadMain(void){
          while (continue) {
            cond.lock();
            cond.wait(); // wait till signalled
            play(); // playback the audio
            cond.unLock(); // unlock to be signalled again
          }
          return NULL;
        }
      };
      
      Pad pad1("dmix", "src/audiosExa/a1.wav"); // instantiate a pad
      Pad pad2("dmix", "src/audiosExa/a2.wav"); // instantiate a pad
      // in your UI thread, you need to run each of the Pad thread
      pad1.run();
      pad2.run();
      // in your UI thread, you need to signal the waiting pad threads 
      // Looks something like this for each pad
      pad1.cond.lock(); pad1.cond.signal(); pad1.cond.unlock();
      

      如果您想进行自己的混音,那么您将跟踪正在播放的音频垫以及您在垫的缓冲区中的位置,以便每次需要播放音频缓冲区时,将它们加在一起。在代码中(使用gtkIOStream),它看起来类似于取自ALSAPlaybackTest.C 的这个sn-p:

      const string deviceName="hw:0,0"; // could also be "default"
      Playback playBack(deviceName.c_str()); // open the playback device
      // configure the parameters of the playback device
      Sox<short int> sox1, sox2; // instantiate two audio file readers (libsox)
      int ret=sox1.openRead("src/audiosExa/a1.wav"); // load in their audio to an Eigen Array data buffer
      Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> pad1, pad2;
      sox1.read(pad1);
      ret=sox2.openRead("src/audiosExa/a2.wav");
      sox2.read(pad2);
      // now loop to constantly output, using outBuf to sum all pads which require output
      Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> outBuf;
      while (continue){
        // construct your output buffer by adding portions of your pads here
        // something like : outBuf=pad1.block(...)+pad2.block(...)
        playBack<<outBuf; // play the audio data
      }
      

      【讨论】:

        猜你喜欢
        • 2019-01-20
        • 2017-08-09
        • 2013-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-09
        • 2023-03-12
        相关资源
        最近更新 更多