Робот NAO: перенос ALSoundExtractor на фреймворк qi - PullRequest
2 голосов
/ 03 августа 2020

При портировании с NAOqi на фреймворк qi добился частичного успеха. Однако у меня все еще есть следующая проблема. Я не знаю, как реализовать обработку звука с помощью ALSoundExtractor в qi framework.

В старом Naoqi есть пример:

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

где создается класс:

class ALSoundProcessing: publi c ALSoundExtractor

затем объявляется функция, заменяющая виртуальную функцию, которая используется для обработки звука:

void process (...)

Чего у меня сейчас нет:

  1. Как создать класс в фреймворке qi, который наследуется от старого класса ALSoundExtractor?
  2. Как объявить функцию, которая заменяет виртуальную функцию - технически функция базового класса process () ожидает переменные в старом соглашении AL ::.

В качестве альтернативы, есть ли другой способ читать аудиоканалы?

Ответы [ 4 ]

1 голос
/ 27 августа 2020

Следующее - это то, что в конечном итоге сработало для меня и завершает топи c.

// **************** 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;
  }
1 голос
/ 10 августа 2020

Чтобы использовать этот 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 framework:

  • AL::ALValue заменяется qi::AnyValue. Получение двоичных данных ( aka"raw") немного отличается.
  • 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");

Обратите внимание, что я не тестировал этот код, поэтому дайте мне знать, что может быть не так.

1 голос
/ 04 августа 2020

Я никогда не работал ни с ALExtractor, ни с ALSoundExtractor, но вот то, что я знаю.

в старом Naoqi «ALExtractor»

  • мог запускаться либо из основного процесса (с использованием autoload.ini), либо из другого (известного как удаленный режим ). В фреймворке qi поддерживается только удаленный режим.
  • может наследовать от ALExtractor или ALAudioExtractor, чтобы исключить некоторый код. Эти классы не были перенесены в платформу qi. Так что, если вы не хотите продолжать использовать libnaoqi, вам следует найти способ обойтись без них.

Хорошие новости: наследование от них никогда не было действительно необходимым. Вы окажетесь в том же положении, что и в следующем вопросе, где экстрактор реализован в python (и, следовательно, не может наследовать от класса C ++ и не может быть загружен в основной процесс из autoload.ini). Проблемы со звуком удаленного робота NAO

Как объявить функцию, которая переопределяет виртуальную функцию - технически функция базового класса process () ожидает переменных в старом соглашении AL ::.

Каждый раз, когда вы используете "старый Naoqi "вы фактически используете слой совместимости поверх qi framework. Так что всякий раз, когда вы используете «старый Naoqi», вы уже используете фреймворк qi. libqi qi :: AnyValue расширяется во время выполнения, libnaoqi расширяет его, чтобы дать ему знать, как обрабатывать ALValue: как преобразовать его в примитивные типы (число с плавающей запятой, список целых чисел, строка, буфер и т. д. c.).

Итак, всякий раз, когда старый ALSoundExtractor получает AL :: ALvalue, на самом деле это qi :: AnyValue, которое было преобразовано в ALValue непосредственно перед вызовом метода process (). Если вы не связываетесь с libnaoqi, вы не сможете использовать это значение как ALValue, но вы можете использовать его как qi :: AnyValue или даже как примитивный тип.

исходный прототип (cfr doxygen http://doc.aldebaran.com/2-8/ref/libalaudio/classAL_1_1ALSoundExtractor.html) is

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);

Я не уверен, как обрабатывать буферную переменную, но позвольте сначала поработать остальным.

0 голосов
/ 05 августа 2020

Я сделал следующее. Код компилируется, но у меня не будет возможности протестировать его на живом роботе около недели.

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 (), гарантирует ли подписка моего модуля обратный вызов моему процессу (...)?

...