【问题标题】:NAO robot: porting ALSoundExtractor to qi framworkNAO 机器人:将 ALSoundExtractor 移植到 qi 框架
【发布时间】:2020-08-03 15:11:46
【问题描述】:

从 NAOqi 移植到 qi 框架时,我取得了部分成功。但是,我仍然有以下问题。 我不知道如何在qi框架中使用ALSoundExtractor实现声音处理。

在老脑奇中,有一个例子:

http://doc.aldebaran.com/2-8/dev/cpp/examples/audio/soundprocessing/soundprocessing.html

创建类的位置:

类 ALSoundProcessing:公共 ALSoundExtractor

然后声明一个覆盖虚函数的函数,用于声音处理:

无效进程(...)

我现在不知道的是:

  1. 如何在qi框架中创建一个继承自旧式类ALSoundExtractor的类?
  2. 如何声明重写虚函数的函数 - 从技术上讲,基类函数 process() 需要旧 AL:: 约定中的变量。

或者,还有其他方法可以读取音频通道吗?

【问题讨论】:

    标签: nao-robot


    【解决方案1】:

    我从未使用过 ALExtractor 或 ALSoundExtractor,但这是我所知道的。

    1. 如何在qi框架中创建一个继承自旧式类ALSoundExtractor的类?

    在老脑奇中,一个“ALExtractor”

    • 可以从主进程(使用 autoload.ini)或另一个进程(称为远程模式)中运行。 qi 框架只支持远程模式。
    • 可以从 ALExtractor 或 ALAudioExtractor 继承以分解出一些代码。这些类尚未移植到 qi 框架。所以如果你不想继续使用 libnaoqi,你应该想办法不用它们。

    好消息:从未真正需要从他们那里继承。您会发现自己处于与以下问题类似的位置,其中提取器是在 python 中实现的(因此不能从 C++ 类继承,也不能从 autoload.ini 加载到主进程中)。 NAO robot remote audio problems

    1. 如何声明重写虚函数的函数 - 从技术上讲,基类函数 process() 需要旧 AL:: 约定中的变量。

    每当您使用“老脑奇”时,您实际上是在使用 qi 框架之上的兼容层。 因此,每当您使用“老脑奇”时,您就已经在使用 qi 框架。 libqi 的 qi::AnyValue 在运行时是可扩展的,libnaoqi 对其进行了扩展以使其知道如何处理 ALValue:如何将其转换为原始类型(浮点数、整数列表、字符串、缓冲区等)。

    所以每当旧的 ALSoundExtractor 接收到一个 AL::ALvalue,它实际上是一个 qi::AnyValue,它在调用 process() 方法之前已被转换为一个 ALValue。 如果不链接 libnaoqi,则无法将值用作 ALValue,但可以将其用作 qi::AnyValue,甚至可以将其用作原始类型。

    原来的原型是(cfr doxygen http://doc.aldebaran.com/2-8/ref/libalaudio/classAL_1_1ALSoundExtractor.html)是

    void ALSoundExtractor::process (const int &nbOfChannels, const int &nbrOfSamplesByChannel, const AL_SOUND_FORMAT *buffer, const ALValue &timestamp);
    

    由于时间戳可能是两个整数的列表,我会尝试这样的事情

    void TmpSoundExtractor::process (const int &nbOfChannels, const int &nbrOfSamplesByChannel, qi::AnyValue buffer, const std::vector<int> &timestamp);
    

    我不确定如何处理缓冲区变量,但首先让其余的工作。

    【讨论】:

      【解决方案2】:

      要使用这个 API,你必须写一个 Qi Service 来宣传这个方法:

      void processRemote(
          int nbOfChannels,
          int nbrOfSamplesByChannel,
          const qi::AnyValue& timestamp,
          const qi::AnyValue& buffer)
      {
        std::pair<char*, size_t> charBuffer = value.unwrap().asRaw();
        const signed short* data = (const signed short*)charBuffer.first;
        // process the data like in the example.
      }
      

      请注意,使用 Qi 框架:

      • AL::ALValue 替换为 qi::AnyValue。 获取二进制数据(aka“原始”)略有不同。
      • AL_SOUND_FORMAT 替换为 signed short*
      • ALSoundExtractor不可用,需要我们自己转换成const AL_SOUND_FORMAT*

      假设您的服务注册为"MySoundExtractor",您必须告诉ALAudioDevice 开始声音提取并将数据发送到您的服务,如下所示:

      auto audio = session->service("ALAudioDevice").value();
      int nNbrChannelFlag = 0; // ALL_Channels: 0,  AL::LEFTCHANNEL: 1, AL::RIGHTCHANNEL: 2; AL::FRONTCHANNEL: 3  or AL::REARCHANNEL: 4.
      int nDeinterleave = 0;
      int nSampleRate = 48000;
      audio->setClientPreferences("MySoundExtractor", nSampleRate, nNbrChannelFlag, nDeinterleave);
      audio->subscribe("MySoundExtractor");
      

      请注意,我没有测试此代码,所以请告诉我可能出了什么问题。

      【讨论】:

      • 我的代码暂时编译好了。但是,我进行了以下更改: qi::AnyObject audio = _session->service("ALAudioDevice"); audio.call<:anyvalue>("setClientPreferences", "MySoundExtractor", ....);通过您的通话 audio->setClentPreference(...) 我收到缺少成员的错误消息。我还没有测试代码。
      【解决方案3】:

      以下是最终对我有用并结束主题的内容。

      // **************** service.h ****************
      
      typedef signed short AL_SOUND_FORMAT;       // copy from alaudio/alsoundextractor.h
      
      class SoundProcessing
        {
      public:
          SoundProcessing(qi::SessionPtr session);
          void init(void);                // a replacement for a function automatically called in NAOqi 2.1.4
          virtual ~SoundProcessing(void); 
          void processRemote(const int& nbOfChannels, const int& nbrOfSamplesByChannel, const qi::AnyValue& timestamp, const qi::AnyValue& buffer);
      
      private:
          qi::SessionPtr _session;
          qi::AnyObject audio;
        };
      
      // **************** service.cpp ****************
      
      SoundProcessing::SoundProcessing(qi::SessionPtr session) : _session(session)
        {
         _session->waitForService("ALAudioDevice");
          audio = _session->service("ALAudioDevice");
        } // constructor
        
      QI_REGISTER_MT_OBJECT(SoundProcessing, init, processRemote);
      
      
      SoundProcessing::~SoundProcessing(void)
        { 
          audio.call<qi::AnyValue>("unsubscribe", "SoundProcessing");
        } // destructor
        
      
      void SoundProcessing::init(void)    
        {
          audio.call<qi::AnyValue>("setClientPreferences",
                                    "SoundProcessing",
                                   _FREQ48K,          // 48000 Hz requested
                                    0,    
                                    1     
                                  );  
                                  
          audio.call<qi::AnyValue>("subscribe", "SoundProcessing");
        } // SoundProcessing::init
      
      
      void SoundProcessing::processRemote(const int& nbOfChannels,const int& nbrOfSamplesByChannel, const qi::AnyValue& timestamp, const qi::AnyValue& qibuffer)
        { 
          std::pair<char*, size_t> charBuffer = qibuffer.unwrap().asRaw();
          AL_SOUND_FORMAT *buffer = (AL_SOUND_FORMAT *)charBuffer.first;
          
          (...)
        } // SoundProcessing::process
      
      
      // **************** main.cpp ****************
      
      int main(int argc, char* argv[])
        {
          qi::ApplicationSession app(argc, argv);
          app.start();
          qi::SessionPtr session = app.session();
          
          session->registerService("SoundProcessing", qi::AnyObject(boost::make_shared<SoundProcessing>(session)));
      
          qi::AnyObject sp = session->service("SoundProcessing");
          sp.call<qi::AnyValue>("init");
          
          app.run();
          return 0;
        }
      

      【讨论】:

      • 感谢和祝贺。请注意,服务被实例化为boost::shared_ptr,复制时共享所有权。它被包裹在一个qi::AnyObject 中,该qi::AnyObject 转发与客户共享的所有权。因此,ALAudioDevice 使您的服务对象保持活动状态并非不可能,即使在取消注册之后也是如此。因此,您不应依赖析构函数来执行来自ALAudioDevice 的取消订阅。相反,请考虑为取消订阅公开不同的功能。
      • 我会考虑的。事实上,模块无限期地运行并且析构函数永远不会被调用。目前,停止服务的唯一方法是杀死主进程。在 v. 2.1.4 中,当编译为库模块时,nao stop 命令正在调用所有已安装用户模块的析构函数。我不知道是否可以使用二进制模块。
      • 我一直在思考这个问题,但我不明白这一点。当析构函数的主体被执行时,所有的私有变量仍然存在,尤其是有问题的服务对象。这意味着调用“取消订阅”是合法的。随后,服务对象和对象本身被删除。
      • 调用unsubscribe 是合法的,但在你调用它之前,ALAudioDevice 将保留对你的服务的引用,这将使你的对象保持活动状态。因此在调用unsubscribe 之前不会调用析构函数。因此,除非您在其他地方调用unsubscribe,否则您的服务将不会被取消订阅,直到会话关闭(或进程终止)。
      【解决方案4】:

      以下是我所做的。代码可以编译,但大约一周左右我没有机会在真人机器人上对其进行测试。

      typedef signed short AL_SOUND_FORMAT; // copy from alaudio/alsoundextractor.h
      
      void process(const int& nbOfChannels, const int& nbrOfSamplesByChannel, const AL_SOUND_FORMAT *buffer, const qi::AnyValue& timeStamp); // I do not use the timeStamp variable in my code, so AnyValue would work?
      
      qi::AnyObject audioDevice = _session->service("ALAudioDevice"); // same variable name as in the original ALSoundExtractor module, just as a convenience
      
      audioDevice.call<qi::AnyValue>("setClientPreferences", audioDevice.call<qi::AnyValue>("getName"), 48000, 0, 1);
      
      audioDevice.call<qi::AnyValue>("subscribe", audioDevice.call<qi::AnyValue>("getName")); // this is the key call
      
      audioDevice.call<qi::AnyValue>("startDetection"); // is it still necessary?
      

      我的问题是 - 我现在就做吗?如果我无法覆盖虚函数 process(),订阅我的模块是否保证回调我的 process(...)?

      【讨论】:

      • 这篇文章不是答案,而是对我的回复的评论。
      • 这行不通,因为这里你没有写 Qi 服务。使用audioDevice.call&lt;qi::AnyValue&gt;("subscribe", audioDevice.call&lt;qi::AnyValue&gt;("getName"));,您是在告诉 ALAudioDevice 使用缓冲区回调 ALAudioDevice(而不是您的服务!)。
      • 我可以在这里明确地传递我的班级名称,但想调用 getName(),就像在旧的 NAOqi 中一样。坦率地说,我不确定如何设置该名称。在旧的 NAOqi 中,它是由 ALModule::createModule 设置的。现在应该由 session->registerService() 设置吗?
      • 没错,这是你给session-&gt;registerService(...)的名字
      猜你喜欢
      • 2021-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-26
      相关资源
      最近更新 更多