659 lines
27 KiB
C++
659 lines
27 KiB
C++
/*
|
|
* AudioPlayerIntegrationTest.cpp
|
|
*
|
|
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License").
|
|
* You may not use this file except in compliance with the License.
|
|
* A copy of the License is located at
|
|
*
|
|
* http://aws.amazon.com/apache2.0/
|
|
*
|
|
* or in the "license" file accompanying this file. This file is distributed
|
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
|
* express or implied. See the License for the specific language governing
|
|
* permissions and limitations under the License.
|
|
*/
|
|
|
|
/// @file AudioPlayerIntegrationTest.cpp
|
|
#include <gtest/gtest.h>
|
|
#include <string>
|
|
#include <future>
|
|
#include <fstream>
|
|
#include <chrono>
|
|
#include <deque>
|
|
#include <mutex>
|
|
#include <unordered_map>
|
|
#include <iostream>
|
|
|
|
#include "ACL/Transport/HTTP2MessageRouter.h"
|
|
#include "ACL/Transport/HTTPContentFetcherFactory.h"
|
|
#include "ACL/Transport/PostConnectObject.h"
|
|
#include "ADSL/DirectiveSequencer.h"
|
|
#include "ADSL/MessageInterpreter.h"
|
|
#include "AFML/FocusManager.h"
|
|
#include "AIP/AudioInputProcessor.h"
|
|
#include "AIP/AudioProvider.h"
|
|
#include "AIP/Initiator.h"
|
|
#include "AudioPlayer/AudioPlayer.h"
|
|
#include "AuthDelegate/AuthDelegate.h"
|
|
#include "AVSCommon/AVS/Attachment/AttachmentManager.h"
|
|
#include "AVSCommon/AVS/Attachment/InProcessAttachmentReader.h"
|
|
#include "AVSCommon/AVS/Attachment/InProcessAttachmentWriter.h"
|
|
#include "AVSCommon/AVS/BlockingPolicy.h"
|
|
#include "AVSCommon/Utils/JSON/JSONUtils.h"
|
|
#include "AVSCommon/SDKInterfaces/DirectiveHandlerInterface.h"
|
|
#include "AVSCommon/SDKInterfaces/DirectiveHandlerResultInterface.h"
|
|
#include "AVSCommon/AVS/Initialization/AlexaClientSDKInit.h"
|
|
#include "AVSCommon/Utils/Logger/LogEntry.h"
|
|
#include "ContextManager/ContextManager.h"
|
|
#include "Integration/AuthObserver.h"
|
|
#include "Integration/ClientMessageHandler.h"
|
|
#include "Integration/ConnectionStatusObserver.h"
|
|
#include "Integration/ObservableMessageRequest.h"
|
|
#include "Integration/TestMessageSender.h"
|
|
#include "Integration/TestDirectiveHandler.h"
|
|
#include "Integration/TestExceptionEncounteredSender.h"
|
|
#include "Integration/TestSpeechSynthesizerObserver.h"
|
|
#include "SpeechSynthesizer/SpeechSynthesizer.h"
|
|
#include "System/UserInactivityMonitor.h"
|
|
|
|
#ifdef GSTREAMER_MEDIA_PLAYER
|
|
#include "MediaPlayer/MediaPlayer.h"
|
|
#else
|
|
#include "Integration/TestMediaPlayer.h"
|
|
#endif
|
|
|
|
namespace alexaClientSDK {
|
|
namespace integration {
|
|
namespace test {
|
|
|
|
using namespace acl;
|
|
using namespace adsl;
|
|
using namespace authDelegate;
|
|
using namespace avsCommon;
|
|
using namespace avsCommon::avs;
|
|
using namespace avsCommon::avs::attachment;
|
|
using namespace avsCommon::sdkInterfaces;
|
|
using namespace avsCommon::avs::initialization;
|
|
using namespace avsCommon::utils::mediaPlayer;
|
|
using namespace contextManager;
|
|
using namespace sdkInterfaces;
|
|
using namespace avsCommon::utils::sds;
|
|
using namespace avsCommon::utils::json;
|
|
using namespace capabilityAgents::aip;
|
|
using namespace afml;
|
|
using namespace capabilityAgents::speechSynthesizer;
|
|
using namespace capabilityAgents::system;
|
|
#ifdef GSTREAMER_MEDIA_PLAYER
|
|
using namespace mediaPlayer;
|
|
#endif
|
|
|
|
// This is a 16 bit 16 kHz little endian linear PCM audio file of "Sing me a song" to be recognized.
|
|
static const std::string RECOGNIZE_SING_FILE_NAME = "/recognize_sing_song_test.wav";
|
|
// This is a 16 bit 16 kHz little endian linear PCM audio file of "Flashbriefing" to be recognized.
|
|
static const std::string RECOGNIZE_FLASHBRIEFING_FILE_NAME = "/recognize_flashbriefing_test.wav";
|
|
|
|
// This string to be used for PlaybackStarted Directives which use the AudioPlayer namespace.
|
|
static const std::string NAME_PLAYBACK_STARTED = "PlaybackStarted";
|
|
// This string to be used for PlaybackNearlyFinished Directives which use the AudioPlayer namespace.
|
|
static const std::string NAME_PLAYBACK_NEARLY_FINISHED = "PlaybackNearlyFinished";
|
|
// This string to be used for PlaybackFinished Directives which use the AudioPlayer namespace.
|
|
static const std::string NAME_PLAYBACK_FINISHED = "PlaybackFinished";
|
|
// This string to be used for PlaybackStopped Directives which use the AudioPlayer namespace.
|
|
static const std::string NAME_PLAYBACK_STOPPED = "PlaybackStopped";
|
|
// This string to be used for SynchronizeState Directives.
|
|
static const std::string NAME_SYNC_STATE = "SynchronizeState";
|
|
// This string to be used for Speak Directives which use the NAMESPACE_SPEECH_SYNTHESIZER namespace.
|
|
static const std::string NAME_RECOGNIZE = "Recognize";
|
|
// This string to be used for SpeechStarted Directives which use the NAMESPACE_SPEECH_SYNTHESIZER namespace.
|
|
static const std::string NAME_SPEECH_STARTED = "SpeechStarted";
|
|
// This string to be used for SpeechFinished Directives which use the NAMESPACE_SPEECH_SYNTHESIZER namespace.
|
|
static const std::string NAME_SPEECH_FINISHED = "SpeechFinished";
|
|
|
|
/// The dialog Channel name used in intializing the FocusManager.
|
|
static const std::string DIALOG_CHANNEL_NAME = "Dialog";
|
|
|
|
/// The content Channel name used in intializing the FocusManager.
|
|
static const std::string CONTENT_CHANNEL_NAME = "Content";
|
|
|
|
/// An incorrect Channel name that is never initialized as a Channel.
|
|
static const std::string TEST_CHANNEL_NAME = "Test";
|
|
|
|
/// The priority of the dialog Channel used in intializing the FocusManager.
|
|
static const unsigned int DIALOG_CHANNEL_PRIORITY = 100;
|
|
|
|
/// The priority of the content Channel used in intializing the FocusManager.
|
|
static const unsigned int CONTENT_CHANNEL_PRIORITY = 300;
|
|
|
|
/// The priority of the content Channel used in intializing the FocusManager.
|
|
static const unsigned int TEST_CHANNEL_PRIORITY = 400;
|
|
|
|
/// Sample dialog activity id.
|
|
static const std::string DIALOG_ACTIVITY_ID = "dialog";
|
|
|
|
/// Sample content activity id.
|
|
static const std::string CONTENT_ACTIVITY_ID = "content";
|
|
|
|
/// Sample content activity id.
|
|
static const std::string TEST_ACTIVITY_ID = "test";
|
|
|
|
// This Integer to be used to specify a timeout in seconds.
|
|
static const std::chrono::seconds WAIT_FOR_TIMEOUT_DURATION(15);
|
|
static const std::chrono::seconds NO_TIMEOUT_DURATION(0);
|
|
static const std::chrono::seconds SONG_TIMEOUT_DURATION(120);
|
|
/// The compatible encoding for AIP.
|
|
static const avsCommon::utils::AudioFormat::Encoding COMPATIBLE_ENCODING =
|
|
avsCommon::utils::AudioFormat::Encoding::LPCM;
|
|
/// The compatible endianness for AIP.
|
|
static const avsCommon::utils::AudioFormat::Endianness COMPATIBLE_ENDIANNESS =
|
|
avsCommon::utils::AudioFormat::Endianness::LITTLE;
|
|
/// The compatible sample rate for AIP.
|
|
static const unsigned int COMPATIBLE_SAMPLE_RATE = 16000;
|
|
/// The compatible bits per sample for Kitt.ai.
|
|
static const unsigned int COMPATIBLE_SAMPLE_SIZE_IN_BITS = 16;
|
|
/// The compatible number of channels for Kitt.ai
|
|
static const unsigned int COMPATIBLE_NUM_CHANNELS = 1;
|
|
|
|
/// JSON key to get the event object of a message.
|
|
static const std::string JSON_MESSAGE_EVENT_KEY = "event";
|
|
/// JSON key to get the directive object of a message.
|
|
static const std::string JSON_MESSAGE_DIRECTIVE_KEY = "directive";
|
|
/// JSON key to get the header object of a message.
|
|
static const std::string JSON_MESSAGE_HEADER_KEY = "header";
|
|
/// JSON key to get the namespace value of a header.
|
|
static const std::string JSON_MESSAGE_NAMESPACE_KEY = "namespace";
|
|
/// JSON key to get the name value of a header.
|
|
static const std::string JSON_MESSAGE_NAME_KEY = "name";
|
|
/// JSON key to get the messageId value of a header.
|
|
static const std::string JSON_MESSAGE_MESSAGE_ID_KEY = "messageId";
|
|
/// JSON key to get the dialogRequestId value of a header.
|
|
static const std::string JSON_MESSAGE_DIALOG_REQUEST_ID_KEY = "dialogRequestId";
|
|
/// JSON key to get the payload object of a message.
|
|
static const std::string JSON_MESSAGE_PAYLOAD_KEY = "payload";
|
|
|
|
/// String to identify log entries originating from this file.
|
|
static const std::string TAG("AudioPlayerIntegrationTest");
|
|
|
|
/**
|
|
* Create a LogEntry using this file's TAG and the specified event string.
|
|
*
|
|
* @param The event string for this @c LogEntry.
|
|
*/
|
|
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
|
|
|
|
std::string configPath;
|
|
std::string inputPath;
|
|
|
|
/// A test observer that mocks out the ChannelObserverInterface##onFocusChanged() call.
|
|
class TestClient : public ChannelObserverInterface {
|
|
public:
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
TestClient() : m_focusState(FocusState::NONE), m_focusChangeOccurred(false) {
|
|
}
|
|
|
|
/**
|
|
* Implementation of the ChannelObserverInterface##onFocusChanged() callback.
|
|
*
|
|
* @param focusState The new focus state of the Channel observer.
|
|
*/
|
|
void onFocusChanged(FocusState focusState) override {
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
m_focusState = focusState;
|
|
m_focusChangeOccurred = true;
|
|
m_focusChangedCV.notify_one();
|
|
}
|
|
|
|
/**
|
|
* Waits for the ChannelObserverInterface##onFocusChanged() callback.
|
|
*
|
|
* @param timeout The amount of time to wait for the callback.
|
|
* @param focusChanged An output parameter that notifies the caller whether a callback occurred.
|
|
* @return Returns @c true if the callback occured within the timeout period and @c false otherwise.
|
|
*/
|
|
FocusState waitForFocusChange(std::chrono::milliseconds timeout, bool* focusChanged) {
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
bool success = m_focusChangedCV.wait_for(lock, timeout, [this]() { return m_focusChangeOccurred; });
|
|
|
|
if (!success) {
|
|
*focusChanged = false;
|
|
} else {
|
|
m_focusChangeOccurred = false;
|
|
*focusChanged = true;
|
|
}
|
|
return m_focusState;
|
|
}
|
|
|
|
private:
|
|
/// The focus state of the observer.
|
|
FocusState m_focusState;
|
|
|
|
/// A lock to guard against focus state changes.
|
|
std::mutex m_mutex;
|
|
|
|
/// A condition variable to wait for focus changes.
|
|
std::condition_variable m_focusChangedCV;
|
|
|
|
/// A boolean flag so that we can re-use the observer even after a callback has occurred.
|
|
bool m_focusChangeOccurred;
|
|
};
|
|
|
|
class holdToTalkButton {
|
|
public:
|
|
bool startRecognizing(std::shared_ptr<AudioInputProcessor> aip, std::shared_ptr<AudioProvider> audioProvider) {
|
|
return aip->recognize(*audioProvider, Initiator::PRESS_AND_HOLD).get();
|
|
}
|
|
|
|
bool stopRecognizing(std::shared_ptr<AudioInputProcessor> aip) {
|
|
return aip->stopCapture().get();
|
|
}
|
|
};
|
|
|
|
class AudioPlayerTest : public ::testing::Test {
|
|
protected:
|
|
virtual void SetUp() override {
|
|
std::ifstream infile(configPath);
|
|
ASSERT_TRUE(infile.good());
|
|
ASSERT_TRUE(AlexaClientSDKInit::initialize({&infile}));
|
|
m_authObserver = std::make_shared<AuthObserver>();
|
|
m_authDelegate = AuthDelegate::create();
|
|
m_authDelegate->addAuthObserver(m_authObserver);
|
|
m_attachmentManager = std::make_shared<avsCommon::avs::attachment::AttachmentManager>(
|
|
AttachmentManager::AttachmentType::IN_PROCESS);
|
|
m_connectionStatusObserver = std::make_shared<ConnectionStatusObserver>();
|
|
bool isEnabled = false;
|
|
m_messageRouter = std::make_shared<HTTP2MessageRouter>(m_authDelegate, m_attachmentManager);
|
|
m_exceptionEncounteredSender = std::make_shared<TestExceptionEncounteredSender>();
|
|
m_dialogUXStateAggregator = std::make_shared<avsCommon::avs::DialogUXStateAggregator>();
|
|
|
|
m_directiveSequencer = DirectiveSequencer::create(m_exceptionEncounteredSender);
|
|
m_messageInterpreter = std::make_shared<MessageInterpreter>(
|
|
m_exceptionEncounteredSender, m_directiveSequencer, m_attachmentManager);
|
|
|
|
// Set up connection and connect
|
|
m_avsConnectionManager = std::make_shared<TestMessageSender>(
|
|
m_messageRouter, isEnabled, m_connectionStatusObserver, m_messageInterpreter);
|
|
ASSERT_NE(nullptr, m_avsConnectionManager);
|
|
|
|
FocusManager::ChannelConfiguration dialogChannelConfig{DIALOG_CHANNEL_NAME, DIALOG_CHANNEL_PRIORITY};
|
|
FocusManager::ChannelConfiguration contentChannelConfig{CONTENT_CHANNEL_NAME, CONTENT_CHANNEL_PRIORITY};
|
|
FocusManager::ChannelConfiguration testChannelConfig{TEST_CHANNEL_NAME, TEST_CHANNEL_PRIORITY};
|
|
|
|
std::vector<FocusManager::ChannelConfiguration> channelConfigurations{
|
|
dialogChannelConfig, contentChannelConfig, testChannelConfig};
|
|
|
|
m_focusManager = std::make_shared<FocusManager>(channelConfigurations);
|
|
|
|
m_testContentClient = std::make_shared<TestClient>();
|
|
ASSERT_TRUE(m_focusManager->acquireChannel(TEST_CHANNEL_NAME, m_testContentClient, TEST_ACTIVITY_ID));
|
|
bool focusChanged;
|
|
FocusState state;
|
|
state = m_testContentClient->waitForFocusChange(WAIT_FOR_TIMEOUT_DURATION, &focusChanged);
|
|
ASSERT_TRUE(focusChanged);
|
|
ASSERT_EQ(state, FocusState::FOREGROUND);
|
|
|
|
m_contextManager = ContextManager::create();
|
|
ASSERT_NE(nullptr, m_contextManager);
|
|
PostConnectObject::init(m_contextManager);
|
|
|
|
#ifdef GSTREAMER_MEDIA_PLAYER
|
|
m_speakMediaPlayer = MediaPlayer::create(std::make_shared<HTTPContentFetcherFactory>());
|
|
#else
|
|
m_speakMediaPlayer = std::make_shared<TestMediaPlayer>();
|
|
#endif
|
|
|
|
m_compatibleAudioFormat.sampleRateHz = COMPATIBLE_SAMPLE_RATE;
|
|
m_compatibleAudioFormat.sampleSizeInBits = COMPATIBLE_SAMPLE_SIZE_IN_BITS;
|
|
m_compatibleAudioFormat.numChannels = COMPATIBLE_NUM_CHANNELS;
|
|
m_compatibleAudioFormat.endianness = COMPATIBLE_ENDIANNESS;
|
|
m_compatibleAudioFormat.encoding = COMPATIBLE_ENCODING;
|
|
|
|
size_t nWords = 1024 * 1024;
|
|
size_t wordSize = 2;
|
|
size_t maxReaders = 3;
|
|
size_t bufferSize = AudioInputStream::calculateBufferSize(nWords, wordSize, maxReaders);
|
|
|
|
auto m_Buffer = std::make_shared<avsCommon::avs::AudioInputStream::Buffer>(bufferSize);
|
|
auto m_Sds = avsCommon::avs::AudioInputStream::create(m_Buffer, wordSize, maxReaders);
|
|
ASSERT_NE(nullptr, m_Sds);
|
|
m_AudioBuffer = std::move(m_Sds);
|
|
m_AudioBufferWriter =
|
|
m_AudioBuffer->createWriter(avsCommon::avs::AudioInputStream::Writer::Policy::NONBLOCKABLE);
|
|
ASSERT_NE(nullptr, m_AudioBufferWriter);
|
|
|
|
// Set up hold to talk button.
|
|
bool alwaysReadable = true;
|
|
bool canOverride = true;
|
|
bool canBeOverridden = true;
|
|
m_HoldToTalkAudioProvider = std::make_shared<AudioProvider>(
|
|
m_AudioBuffer,
|
|
m_compatibleAudioFormat,
|
|
ASRProfile::CLOSE_TALK,
|
|
!alwaysReadable,
|
|
canOverride,
|
|
!canBeOverridden);
|
|
|
|
m_holdToTalkButton = std::make_shared<holdToTalkButton>();
|
|
|
|
m_userInactivityMonitor = UserInactivityMonitor::create(m_avsConnectionManager, m_exceptionEncounteredSender);
|
|
m_AudioInputProcessor = AudioInputProcessor::create(
|
|
m_directiveSequencer,
|
|
m_avsConnectionManager,
|
|
m_contextManager,
|
|
m_focusManager,
|
|
m_dialogUXStateAggregator,
|
|
m_exceptionEncounteredSender,
|
|
m_userInactivityMonitor);
|
|
ASSERT_NE(nullptr, m_AudioInputProcessor);
|
|
m_AudioInputProcessor->addObserver(m_dialogUXStateAggregator);
|
|
|
|
// Create and register the SpeechSynthesizer.
|
|
m_speechSynthesizer = SpeechSynthesizer::create(
|
|
m_speakMediaPlayer,
|
|
m_avsConnectionManager,
|
|
m_focusManager,
|
|
m_contextManager,
|
|
m_attachmentManager,
|
|
m_exceptionEncounteredSender);
|
|
ASSERT_NE(nullptr, m_speechSynthesizer);
|
|
m_directiveSequencer->addDirectiveHandler(m_speechSynthesizer);
|
|
m_speechSynthesizerObserver = std::make_shared<TestSpeechSynthesizerObserver>();
|
|
m_speechSynthesizer->addObserver(m_speechSynthesizerObserver);
|
|
|
|
#ifdef GSTREAMER_MEDIA_PLAYER
|
|
m_contentMediaPlayer = MediaPlayer::create(std::make_shared<HTTPContentFetcherFactory>());
|
|
#else
|
|
m_contentMediaPlayer = std::make_shared<TestMediaPlayer>();
|
|
#endif
|
|
|
|
// Create and register the AudioPlayer.
|
|
m_audioPlayer = capabilityAgents::audioPlayer::AudioPlayer::create(
|
|
m_contentMediaPlayer,
|
|
m_avsConnectionManager,
|
|
m_focusManager,
|
|
m_contextManager,
|
|
m_attachmentManager,
|
|
m_exceptionEncounteredSender);
|
|
ASSERT_NE(nullptr, m_audioPlayer);
|
|
m_directiveSequencer->addDirectiveHandler(m_audioPlayer);
|
|
|
|
connect();
|
|
}
|
|
|
|
void TearDown() override {
|
|
disconnect();
|
|
m_AudioInputProcessor->shutdown();
|
|
m_directiveSequencer->shutdown();
|
|
m_speechSynthesizer->shutdown();
|
|
if (m_audioPlayer) {
|
|
m_audioPlayer->shutdown();
|
|
}
|
|
m_avsConnectionManager->shutdown();
|
|
AlexaClientSDKInit::uninitialize();
|
|
}
|
|
|
|
/**
|
|
* Connect to AVS.
|
|
*/
|
|
void connect() {
|
|
ASSERT_TRUE(m_authObserver->waitFor(AuthObserver::State::REFRESHED)) << "Retrieving the auth token timed out.";
|
|
m_avsConnectionManager->enable();
|
|
ASSERT_TRUE(m_connectionStatusObserver->waitFor(ConnectionStatusObserverInterface::Status::CONNECTED))
|
|
<< "Connecting timed out.";
|
|
}
|
|
|
|
/**
|
|
* Disconnect from AVS.
|
|
*/
|
|
void disconnect() {
|
|
m_avsConnectionManager->disable();
|
|
ASSERT_TRUE(m_connectionStatusObserver->waitFor(ConnectionStatusObserverInterface::Status::DISCONNECTED))
|
|
<< "Connecting timed out.";
|
|
}
|
|
|
|
std::string getSentEventName(TestMessageSender::SendParams sendParams) {
|
|
std::string eventString;
|
|
std::string eventHeader;
|
|
std::string eventName;
|
|
jsonUtils::lookupStringValue(sendParams.request->getJsonContent(), JSON_MESSAGE_EVENT_KEY, &eventString);
|
|
jsonUtils::lookupStringValue(eventString, JSON_MESSAGE_HEADER_KEY, &eventHeader);
|
|
jsonUtils::lookupStringValue(eventHeader, JSON_MESSAGE_NAME_KEY, &eventName);
|
|
return eventName;
|
|
}
|
|
|
|
bool checkSentEventName(TestMessageSender::SendParams sendParams, std::string expectedName) {
|
|
if (TestMessageSender::SendParams::Type::SEND == sendParams.type) {
|
|
std::string eventName;
|
|
eventName = getSentEventName(sendParams);
|
|
return eventName == expectedName;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::vector<int16_t> readAudioFromFile(const std::string& fileName, bool* errorOccurred) {
|
|
const int RIFF_HEADER_SIZE = 44;
|
|
|
|
std::ifstream inputFile(fileName.c_str(), std::ifstream::binary);
|
|
if (!inputFile.good()) {
|
|
std::cout << "Couldn't open audio file!" << std::endl;
|
|
if (errorOccurred) {
|
|
*errorOccurred = true;
|
|
}
|
|
return {};
|
|
}
|
|
inputFile.seekg(0, std::ios::end);
|
|
int fileLengthInBytes = inputFile.tellg();
|
|
if (fileLengthInBytes <= RIFF_HEADER_SIZE) {
|
|
std::cout << "File should be larger than 44 bytes, which is the size of the RIFF header" << std::endl;
|
|
if (errorOccurred) {
|
|
*errorOccurred = true;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
inputFile.seekg(RIFF_HEADER_SIZE, std::ios::beg);
|
|
|
|
int numSamples = (fileLengthInBytes - RIFF_HEADER_SIZE) / 2;
|
|
|
|
std::vector<int16_t> retVal(numSamples, 0);
|
|
|
|
inputFile.read((char*)&retVal[0], numSamples * 2);
|
|
|
|
if (inputFile.gcount() != numSamples * 2) {
|
|
std::cout << "Error reading audio file" << std::endl;
|
|
if (errorOccurred) {
|
|
*errorOccurred = true;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
inputFile.close();
|
|
if (errorOccurred) {
|
|
*errorOccurred = false;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
void sendAudioFileAsRecognize(std::string audioFile) {
|
|
// Signal to the AIP to start recognizing.
|
|
ASSERT_NE(nullptr, m_HoldToTalkAudioProvider);
|
|
ASSERT_TRUE(m_holdToTalkButton->startRecognizing(m_AudioInputProcessor, m_HoldToTalkAudioProvider));
|
|
|
|
bool error;
|
|
std::string file = inputPath + audioFile;
|
|
std::vector<int16_t> audioData = readAudioFromFile(file, &error);
|
|
ASSERT_FALSE(error);
|
|
ASSERT_FALSE(audioData.empty());
|
|
m_AudioBufferWriter->write(audioData.data(), audioData.size());
|
|
|
|
// Stop holding the button.
|
|
ASSERT_TRUE(m_holdToTalkButton->stopRecognizing(m_AudioInputProcessor));
|
|
}
|
|
|
|
std::shared_ptr<AuthObserver> m_authObserver;
|
|
std::shared_ptr<AuthDelegate> m_authDelegate;
|
|
std::shared_ptr<ConnectionStatusObserver> m_connectionStatusObserver;
|
|
std::shared_ptr<MessageRouter> m_messageRouter;
|
|
std::shared_ptr<TestMessageSender> m_avsConnectionManager;
|
|
std::shared_ptr<TestExceptionEncounteredSender> m_exceptionEncounteredSender;
|
|
std::shared_ptr<TestDirectiveHandler> m_directiveHandler;
|
|
std::shared_ptr<DirectiveSequencerInterface> m_directiveSequencer;
|
|
std::shared_ptr<MessageInterpreter> m_messageInterpreter;
|
|
std::shared_ptr<ContextManager> m_contextManager;
|
|
std::shared_ptr<avsCommon::avs::attachment::AttachmentManager> m_attachmentManager;
|
|
std::shared_ptr<FocusManager> m_focusManager;
|
|
std::shared_ptr<TestClient> m_testContentClient;
|
|
std::shared_ptr<SpeechSynthesizer> m_speechSynthesizer;
|
|
std::shared_ptr<TestSpeechSynthesizerObserver> m_speechSynthesizerObserver;
|
|
std::shared_ptr<holdToTalkButton> m_holdToTalkButton;
|
|
std::shared_ptr<AudioProvider> m_HoldToTalkAudioProvider;
|
|
avsCommon::utils::AudioFormat m_compatibleAudioFormat;
|
|
std::unique_ptr<AudioInputStream::Writer> m_AudioBufferWriter;
|
|
std::shared_ptr<AudioInputStream> m_AudioBuffer;
|
|
std::shared_ptr<AudioInputProcessor> m_AudioInputProcessor;
|
|
std::shared_ptr<UserInactivityMonitor> m_userInactivityMonitor;
|
|
std::shared_ptr<capabilityAgents::audioPlayer::AudioPlayer> m_audioPlayer;
|
|
|
|
FocusState m_focusState;
|
|
std::mutex m_mutex;
|
|
std::condition_variable m_focusChangedCV;
|
|
bool m_focusChangeOccurred;
|
|
std::shared_ptr<avsCommon::avs::DialogUXStateAggregator> m_dialogUXStateAggregator;
|
|
|
|
#ifdef GSTREAMER_MEDIA_PLAYER
|
|
std::shared_ptr<MediaPlayer> m_speakMediaPlayer;
|
|
std::shared_ptr<MediaPlayer> m_contentMediaPlayer;
|
|
#else
|
|
std::shared_ptr<TestMediaPlayer> m_speakMediaPlayer;
|
|
std::shared_ptr<TestMediaPlayer> m_contentMediaPlayer;
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* Test ability for the AudioPlayer to handle one play directive.
|
|
*
|
|
* This test is intended to test the AudioPlayer's ability to handle a short play directive all the way through. To do
|
|
* this, an audio file of "Sing me a song" is sent as a Recognize event. In response, a Play directive is received. The
|
|
* tests then observe that the correct events are sent in order.
|
|
*
|
|
*/
|
|
TEST_F(AudioPlayerTest, SingASong) {
|
|
// Sing me a song.
|
|
sendAudioFileAsRecognize(RECOGNIZE_SING_FILE_NAME);
|
|
bool focusChanged;
|
|
FocusState state;
|
|
state = m_testContentClient->waitForFocusChange(WAIT_FOR_TIMEOUT_DURATION, &focusChanged);
|
|
ASSERT_TRUE(focusChanged);
|
|
ASSERT_EQ(state, FocusState::BACKGROUND);
|
|
|
|
// Recognize.
|
|
TestMessageSender::SendParams sendParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_RECOGNIZE));
|
|
|
|
// PlaybackStarted
|
|
sendParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_PLAYBACK_STARTED));
|
|
|
|
// PlaybackNearlyFinished
|
|
sendParams = m_avsConnectionManager->waitForNext(SONG_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_PLAYBACK_NEARLY_FINISHED));
|
|
|
|
sendParams = m_avsConnectionManager->waitForNext(SONG_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_PLAYBACK_FINISHED));
|
|
|
|
state = m_testContentClient->waitForFocusChange(WAIT_FOR_TIMEOUT_DURATION, &focusChanged);
|
|
ASSERT_TRUE(focusChanged);
|
|
ASSERT_EQ(state, FocusState::FOREGROUND);
|
|
|
|
m_testContentClient->waitForFocusChange(NO_TIMEOUT_DURATION, &focusChanged);
|
|
ASSERT_FALSE(focusChanged);
|
|
}
|
|
|
|
/**
|
|
* Test ability for the AudioPlayer to handle multiple play directives.
|
|
*
|
|
* This test is intended to test the AudioPlayer's ability to handle a group play directive all the way through. To do
|
|
* this, an audio file of "Flashbriefing" is sent as a Recognize event. In response, a Speak, then an undefined number
|
|
* of Play directives, and a final Speak directive is received. The tests then observe that the correct events are sent
|
|
* in order.
|
|
*
|
|
*/
|
|
TEST_F(AudioPlayerTest, FlashBriefing) {
|
|
// Ask for a flashbriefing.
|
|
sendAudioFileAsRecognize(RECOGNIZE_FLASHBRIEFING_FILE_NAME);
|
|
|
|
bool focusChanged;
|
|
FocusState state;
|
|
state = m_testContentClient->waitForFocusChange(WAIT_FOR_TIMEOUT_DURATION, &focusChanged);
|
|
ASSERT_TRUE(focusChanged);
|
|
ASSERT_EQ(state, FocusState::BACKGROUND);
|
|
|
|
// Recognize event is sent.
|
|
TestMessageSender::SendParams sendParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_RECOGNIZE));
|
|
|
|
state = m_testContentClient->waitForFocusChange(WAIT_FOR_TIMEOUT_DURATION, &focusChanged);
|
|
ASSERT_TRUE(focusChanged);
|
|
ASSERT_EQ(state, FocusState::FOREGROUND);
|
|
|
|
state = m_testContentClient->waitForFocusChange(WAIT_FOR_TIMEOUT_DURATION, &focusChanged);
|
|
if (focusChanged) {
|
|
ASSERT_EQ(state, FocusState::BACKGROUND);
|
|
}
|
|
|
|
// Speech is handled.
|
|
TestMessageSender::SendParams sendStartedParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendStartedParams, NAME_SPEECH_STARTED));
|
|
TestMessageSender::SendParams sendFinishedParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendFinishedParams, NAME_SPEECH_FINISHED));
|
|
|
|
// If no items are in flashbriefing, this section will be skipped. Ensure that at least two items are selected in
|
|
// the Alexa app under Settings -> Flashbriefing.
|
|
sendParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
bool hasFlashbriefingItems = false;
|
|
while (TestMessageSender::SendParams::Type::TIMEOUT != sendParams.type &&
|
|
!checkSentEventName(sendParams, NAME_SPEECH_STARTED) &&
|
|
!checkSentEventName(sendParams, NAME_PLAYBACK_STOPPED)) {
|
|
hasFlashbriefingItems = true;
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_PLAYBACK_STARTED));
|
|
sendParams = m_avsConnectionManager->waitForNext(SONG_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_PLAYBACK_NEARLY_FINISHED));
|
|
sendParams = m_avsConnectionManager->waitForNext(SONG_TIMEOUT_DURATION);
|
|
ASSERT_TRUE(checkSentEventName(sendParams, NAME_PLAYBACK_FINISHED));
|
|
sendParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
}
|
|
|
|
if (hasFlashbriefingItems) {
|
|
// The last speak is then allowed.
|
|
EXPECT_TRUE(checkSentEventName(sendStartedParams, NAME_SPEECH_STARTED));
|
|
sendFinishedParams = m_avsConnectionManager->waitForNext(WAIT_FOR_TIMEOUT_DURATION);
|
|
EXPECT_TRUE(checkSentEventName(sendFinishedParams, NAME_SPEECH_FINISHED));
|
|
}
|
|
|
|
state = m_testContentClient->waitForFocusChange(WAIT_FOR_TIMEOUT_DURATION, &focusChanged);
|
|
ASSERT_EQ(state, FocusState::FOREGROUND);
|
|
|
|
m_testContentClient->waitForFocusChange(NO_TIMEOUT_DURATION, &focusChanged);
|
|
EXPECT_FALSE(focusChanged);
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace integration
|
|
} // namespace alexaClientSDK
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
if (argc < 3) {
|
|
std::cerr << "USAGE: AudioPlayerIntegration <path_to_AlexaClientSDKConfig.json> <path_to_inputs_folder>"
|
|
<< std::endl;
|
|
return 1;
|
|
|
|
} else {
|
|
alexaClientSDK::integration::test::configPath = std::string(argv[1]);
|
|
alexaClientSDK::integration::test::inputPath = std::string(argv[2]);
|
|
return RUN_ALL_TESTS();
|
|
}
|
|
}
|