diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7a530db..fc87b2e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ find_package(libusb-1.0 REQUIRED)
find_package(Qt5 COMPONENTS Multimedia MultimediaWidgets Bluetooth)
find_package(Protobuf REQUIRED)
find_package(OpenSSL REQUIRED)
+find_package(rtaudio REQUIRED)
if(WIN32)
set(WINSOCK2_LIBRARIES "ws2_32")
@@ -54,6 +55,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}
${LIBUSB_1_INCLUDE_DIRS}
${PROTOBUF_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR}
+ ${RTAUDIO_INCLUDE_DIRS}
${AASDK_PROTO_INCLUDE_DIRS}
${AASDK_INCLUDE_DIRS}
${BCM_HOST_INCLUDE_DIRS}
@@ -80,6 +82,7 @@ target_link_libraries(autoapp
${BCM_HOST_LIBRARIES}
${ILCLIENT_LIBRARIES}
${WINSOCK2_LIBRARIES}
+ ${RTAUDIO_LIBRARIES}
${AASDK_PROTO_LIBRARIES}
${AASDK_LIBRARIES})
diff --git a/Readme.md b/Readme.md
index aaf9217..fa43395 100644
--- a/Readme.md
+++ b/Readme.md
@@ -42,6 +42,7 @@ Copyrights (c) 2018 f1x.studio (Michal Szwaj)
- [Boost libraries](http://www.boost.org/)
- [Qt libraries](https://www.qt.io/)
- [CMake](https://cmake.org/)
+ - [RtAudio](https://www.music.mcgill.ca/~gary/rtaudio/playback.html)
- Broadcom ilclient from RaspberryPI 3 firmware
- OpenMAX IL API
diff --git a/cmake_modules/Findrtaudio.cmake b/cmake_modules/Findrtaudio.cmake
new file mode 100644
index 0000000..60b9384
--- /dev/null
+++ b/cmake_modules/Findrtaudio.cmake
@@ -0,0 +1,70 @@
+#
+# This file is part of openauto project.
+# Copyright (C) 2018 f1x.studio (Michal Szwaj)
+#
+# openauto is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# openauto is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with openauto. If not, see .
+#
+
+if (RTAUDIO_LIBRARIES AND RTAUDIO_INCLUDE_DIRS)
+ # in cache already
+ set(RTAUDIO_FOUND TRUE)
+else (RTAUDIO_LIBRARIES AND RTAUDIO_INCLUDE_DIRS)
+ find_path(RTAUDIO_INCLUDE_DIR
+ NAMES
+ RtAudio.h
+ PATHS
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ /sw/include
+ PATH_SUFFIXES
+ rtaudio
+ )
+
+ find_library(RTAUDIO_LIBRARY
+ NAMES
+ rtaudio
+ PATHS
+ /usr/lib
+ /usr/local/lib
+ /opt/local/lib
+ /sw/lib
+ )
+
+ set(RTAUDIO_INCLUDE_DIRS
+ ${RTAUDIO_INCLUDE_DIR}
+ )
+ set(RTAUDIO_LIBRARIES
+ ${RTAUDIO_LIBRARY}
+)
+
+ if (RTAUDIO_INCLUDE_DIRS AND RTAUDIO_LIBRARIES)
+ set(RTAUDIO_FOUND TRUE)
+ endif (RTAUDIO_INCLUDE_DIRS AND RTAUDIO_LIBRARIES)
+
+ if (RTAUDIO_FOUND)
+ if (NOT rtaudio_FIND_QUIETLY)
+ message(STATUS "Found rtaudio:")
+ message(STATUS " - Includes: ${RTAUDIO_INCLUDE_DIRS}")
+ message(STATUS " - Libraries: ${RTAUDIO_LIBRARIES}")
+ endif (NOT rtaudio_FIND_QUIETLY)
+ else (RTAUDIO_FOUND)
+ if (rtaudio_FIND_REQUIRED)
+ message(FATAL_ERROR "Could not find rtaudio")
+ endif (rtaudio_FIND_REQUIRED)
+ endif (RTAUDIO_FOUND)
+
+ mark_as_advanced(RTAUDIO_INCLUDE_DIRS RTAUDIO_LIBRARIES)
+
+endif (RTAUDIO_LIBRARIES AND RTAUDIO_INCLUDE_DIRS)
diff --git a/include/f1x/openauto/autoapp/Configuration/AudioOutputBackendType.hpp b/include/f1x/openauto/autoapp/Configuration/AudioOutputBackendType.hpp
new file mode 100644
index 0000000..e954874
--- /dev/null
+++ b/include/f1x/openauto/autoapp/Configuration/AudioOutputBackendType.hpp
@@ -0,0 +1,39 @@
+/*
+* This file is part of openauto project.
+* Copyright (C) 2018 f1x.studio (Michal Szwaj)
+*
+* openauto is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or
+* (at your option) any later version.
+
+* openauto is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with openauto. If not, see .
+*/
+
+#pragma once
+
+namespace f1x
+{
+namespace openauto
+{
+namespace autoapp
+{
+namespace configuration
+{
+
+enum class AudioOutputBackendType
+{
+ RTAUDIO,
+ QT
+};
+
+}
+}
+}
+}
diff --git a/include/f1x/openauto/autoapp/Configuration/Configuration.hpp b/include/f1x/openauto/autoapp/Configuration/Configuration.hpp
index 93d44b3..f4a0649 100644
--- a/include/f1x/openauto/autoapp/Configuration/Configuration.hpp
+++ b/include/f1x/openauto/autoapp/Configuration/Configuration.hpp
@@ -69,6 +69,8 @@ public:
void setMusicAudioChannelEnabled(bool value) override;
bool speechAudioChannelEnabled() const override;
void setSpeechAudioChannelEnabled(bool value) override;
+ AudioOutputBackendType getAudioOutputBackendType() const override;
+ void setAudioOutputBackendType(AudioOutputBackendType value) override;
private:
void readButtonCodes(boost::property_tree::ptree& iniConfig);
@@ -88,6 +90,7 @@ private:
std::string bluetoothRemoteAdapterAddress_;
bool musicAudioChannelEnabled_;
bool speechAudiochannelEnabled_;
+ AudioOutputBackendType audioOutputBackendType_;
static const std::string cConfigFileName;
@@ -103,6 +106,7 @@ private:
static const std::string cAudioMusicAudioChannelEnabled;
static const std::string cAudioSpeechAudioChannelEnabled;
+ static const std::string cAudioOutputBackendType;
static const std::string cBluetoothAdapterTypeKey;
static const std::string cBluetoothRemoteAdapterAddressKey;
diff --git a/include/f1x/openauto/autoapp/Configuration/IConfiguration.hpp b/include/f1x/openauto/autoapp/Configuration/IConfiguration.hpp
index 2f086cd..2165a68 100644
--- a/include/f1x/openauto/autoapp/Configuration/IConfiguration.hpp
+++ b/include/f1x/openauto/autoapp/Configuration/IConfiguration.hpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
namespace f1x
{
@@ -77,6 +78,8 @@ public:
virtual void setMusicAudioChannelEnabled(bool value) = 0;
virtual bool speechAudioChannelEnabled() const = 0;
virtual void setSpeechAudioChannelEnabled(bool value) = 0;
+ virtual AudioOutputBackendType getAudioOutputBackendType() const = 0;
+ virtual void setAudioOutputBackendType(AudioOutputBackendType value) = 0;
};
}
diff --git a/include/f1x/openauto/autoapp/Projection/IAudioOutput.hpp b/include/f1x/openauto/autoapp/Projection/IAudioOutput.hpp
index 383ad28..c41de31 100644
--- a/include/f1x/openauto/autoapp/Projection/IAudioOutput.hpp
+++ b/include/f1x/openauto/autoapp/Projection/IAudioOutput.hpp
@@ -19,6 +19,7 @@
#pragma once
#include
+#include
#include
namespace f1x
@@ -39,7 +40,7 @@ public:
virtual ~IAudioOutput() = default;
virtual bool open() = 0;
- virtual void write(const aasdk::common::DataConstBuffer& buffer) = 0;
+ virtual void write(aasdk::messenger::Timestamp::ValueType timestamp, const aasdk::common::DataConstBuffer& buffer) = 0;
virtual void start() = 0;
virtual void stop() = 0;
virtual void suspend() = 0;
diff --git a/include/f1x/openauto/autoapp/Projection/AudioInput.hpp b/include/f1x/openauto/autoapp/Projection/QtAudioInput.hpp
similarity index 92%
rename from include/f1x/openauto/autoapp/Projection/AudioInput.hpp
rename to include/f1x/openauto/autoapp/Projection/QtAudioInput.hpp
index 1dd7db0..97816ea 100644
--- a/include/f1x/openauto/autoapp/Projection/AudioInput.hpp
+++ b/include/f1x/openauto/autoapp/Projection/QtAudioInput.hpp
@@ -32,11 +32,11 @@ namespace autoapp
namespace projection
{
-class AudioInput: public QObject, public IAudioInput
+class QtAudioInput: public QObject, public IAudioInput
{
Q_OBJECT
public:
- AudioInput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate);
+ QtAudioInput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate);
bool open() override;
bool isActive() const override;
diff --git a/include/f1x/openauto/autoapp/Projection/AudioOutput.hpp b/include/f1x/openauto/autoapp/Projection/QtAudioOutput.hpp
similarity index 86%
rename from include/f1x/openauto/autoapp/Projection/AudioOutput.hpp
rename to include/f1x/openauto/autoapp/Projection/QtAudioOutput.hpp
index 9aa4d41..742dbf8 100644
--- a/include/f1x/openauto/autoapp/Projection/AudioOutput.hpp
+++ b/include/f1x/openauto/autoapp/Projection/QtAudioOutput.hpp
@@ -32,14 +32,14 @@ namespace autoapp
namespace projection
{
-class AudioOutput: public QObject, public IAudioOutput
+class QtAudioOutput: public QObject, public IAudioOutput
{
Q_OBJECT
public:
- AudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate);
+ QtAudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate);
bool open() override;
- void write(const aasdk::common::DataConstBuffer& buffer) override;
+ void write(aasdk::messenger::Timestamp::ValueType, const aasdk::common::DataConstBuffer& buffer) override;
void start() override;
void stop() override;
void suspend() override;
diff --git a/include/f1x/openauto/autoapp/Projection/RtAudioOutput.hpp b/include/f1x/openauto/autoapp/Projection/RtAudioOutput.hpp
new file mode 100644
index 0000000..063d5b3
--- /dev/null
+++ b/include/f1x/openauto/autoapp/Projection/RtAudioOutput.hpp
@@ -0,0 +1,63 @@
+/*
+* This file is part of openauto project.
+* Copyright (C) 2018 f1x.studio (Michal Szwaj)
+*
+* openauto is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or
+* (at your option) any later version.
+
+* openauto is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with openauto. If not, see .
+*/
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace f1x
+{
+namespace openauto
+{
+namespace autoapp
+{
+namespace projection
+{
+
+class RtAudioOutput: public IAudioOutput
+{
+public:
+ RtAudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate);
+ bool open() override;
+ void write(aasdk::messenger::Timestamp::ValueType timestamp, const aasdk::common::DataConstBuffer& buffer) override;
+ void start() override;
+ void stop() override;
+ void suspend() override;
+ uint32_t getSampleSize() const override;
+ uint32_t getChannelCount() const override;
+ uint32_t getSampleRate() const override;
+
+private:
+ void doSuspend();
+ static int audioBufferReadHandler(void* outputBuffer, void* inputBuffer, unsigned int nBufferFrames,
+ double streamTime, RtAudioStreamStatus status, void* userData);
+
+ uint32_t channelCount_;
+ uint32_t sampleSize_;
+ uint32_t sampleRate_;
+ SequentialBuffer audioBuffer_;
+ std::unique_ptr dac_;
+ std::mutex mutex_;
+};
+
+}
+}
+}
+}
diff --git a/include/f1x/openauto/autoapp/Projection/ServiceFactory.hpp b/include/f1x/openauto/autoapp/Projection/ServiceFactory.hpp
index 0d929b6..f7fdbd2 100644
--- a/include/f1x/openauto/autoapp/Projection/ServiceFactory.hpp
+++ b/include/f1x/openauto/autoapp/Projection/ServiceFactory.hpp
@@ -40,6 +40,7 @@ private:
IService::Pointer createVideoService(aasdk::messenger::IMessenger::Pointer messenger);
IService::Pointer createBluetoothService(aasdk::messenger::IMessenger::Pointer messenger);
IService::Pointer createInputService(aasdk::messenger::IMessenger::Pointer messenger);
+ void createAudioServices(ServiceList& serviceList, aasdk::messenger::IMessenger::Pointer messenger);
boost::asio::io_service& ioService_;
configuration::IConfiguration::Pointer configuration_;
diff --git a/src/autoapp/Configuration/Configuration.cpp b/src/autoapp/Configuration/Configuration.cpp
index 72e51e1..0e79129 100644
--- a/src/autoapp/Configuration/Configuration.cpp
+++ b/src/autoapp/Configuration/Configuration.cpp
@@ -42,6 +42,7 @@ const std::string Configuration::cVideoMarginHeight = "Video.MarginHeight";
const std::string Configuration::cAudioMusicAudioChannelEnabled = "Audio.MusicAudioChannelEnabled";
const std::string Configuration::cAudioSpeechAudioChannelEnabled = "Audio.SpeechAudioChannelEnabled";
+const std::string Configuration::cAudioOutputBackendType = "Audio.OutputBackendType";
const std::string Configuration::cBluetoothAdapterTypeKey = "Bluetooth.AdapterType";
const std::string Configuration::cBluetoothRemoteAdapterAddressKey = "Bluetooth.RemoteAdapterAddress";
@@ -101,6 +102,7 @@ void Configuration::load()
musicAudioChannelEnabled_ = iniConfig.get(cAudioMusicAudioChannelEnabled, true);
speechAudiochannelEnabled_ = iniConfig.get(cAudioSpeechAudioChannelEnabled, true);
+ audioOutputBackendType_ = static_cast(iniConfig.get(cAudioOutputBackendType, static_cast(AudioOutputBackendType::RTAUDIO)));
}
catch(const boost::property_tree::ini_parser_error& e)
{
@@ -126,6 +128,7 @@ void Configuration::reset()
bluetoothRemoteAdapterAddress_ = "";
musicAudioChannelEnabled_ = true;
speechAudiochannelEnabled_ = true;
+ audioOutputBackendType_ = AudioOutputBackendType::RTAUDIO;
}
void Configuration::save()
@@ -149,6 +152,7 @@ void Configuration::save()
iniConfig.put(cAudioMusicAudioChannelEnabled, musicAudioChannelEnabled_);
iniConfig.put(cAudioSpeechAudioChannelEnabled, speechAudiochannelEnabled_);
+ iniConfig.put(cAudioOutputBackendType, static_cast(audioOutputBackendType_));
boost::property_tree::ini_parser::write_ini(cConfigFileName, iniConfig);
}
@@ -282,6 +286,16 @@ void Configuration::setSpeechAudioChannelEnabled(bool value)
speechAudiochannelEnabled_ = value;
}
+AudioOutputBackendType Configuration::getAudioOutputBackendType() const
+{
+ return audioOutputBackendType_;
+}
+
+void Configuration::setAudioOutputBackendType(AudioOutputBackendType value)
+{
+ audioOutputBackendType_ = value;
+}
+
void Configuration::readButtonCodes(boost::property_tree::ptree& iniConfig)
{
this->insertButtonCode(iniConfig, cInputPlayButtonKey, aasdk::proto::enums::ButtonCode::PLAY);
diff --git a/src/autoapp/Projection/AudioService.cpp b/src/autoapp/Projection/AudioService.cpp
index 3e68c11..d09001f 100644
--- a/src/autoapp/Projection/AudioService.cpp
+++ b/src/autoapp/Projection/AudioService.cpp
@@ -93,6 +93,12 @@ void AudioService::onChannelOpenRequest(const aasdk::proto::messages::ChannelOpe
OPENAUTO_LOG(info) << "[AudioService] open request"
<< ", channel: " << aasdk::messenger::channelIdToString(channel_->getId())
<< ", priority: " << request.priority();
+
+ OPENAUTO_LOG(debug) << "[AudioService] channel: " << aasdk::messenger::channelIdToString(channel_->getId())
+ << " audio output sample rate: " << audioOutput_->getSampleRate()
+ << ", sample size: " << audioOutput_->getSampleSize()
+ << ", channel count: " << audioOutput_->getChannelCount();
+
const aasdk::proto::enums::Status::Enum status = audioOutput_->open() ? aasdk::proto::enums::Status::OK : aasdk::proto::enums::Status::FAIL;
OPENAUTO_LOG(info) << "[AudioService] open status: " << status
<< ", channel: " << aasdk::messenger::channelIdToString(channel_->getId());
@@ -146,14 +152,9 @@ void AudioService::onAVChannelStopIndication(const aasdk::proto::messages::AVCha
channel_->receive(this->shared_from_this());
}
-void AudioService::onAVMediaWithTimestampIndication(aasdk::messenger::Timestamp::ValueType, const aasdk::common::DataConstBuffer& buffer)
+void AudioService::onAVMediaWithTimestampIndication(aasdk::messenger::Timestamp::ValueType timestamp, const aasdk::common::DataConstBuffer& buffer)
{
- this->onAVMediaIndication(buffer);
-}
-
-void AudioService::onAVMediaIndication(const aasdk::common::DataConstBuffer& buffer)
-{
- audioOutput_->write(buffer);
+ audioOutput_->write(timestamp, buffer);
aasdk::proto::messages::AVMediaAckIndication indication;
indication.set_session(session_);
indication.set_value(1);
@@ -164,6 +165,11 @@ void AudioService::onAVMediaIndication(const aasdk::common::DataConstBuffer& buf
channel_->receive(this->shared_from_this());
}
+void AudioService::onAVMediaIndication(const aasdk::common::DataConstBuffer& buffer)
+{
+ this->onAVMediaWithTimestampIndication(0, buffer);
+}
+
void AudioService::onChannelError(const aasdk::error::Error& e)
{
OPENAUTO_LOG(error) << "[AudioService] channel error: " << e.what()
diff --git a/src/autoapp/Projection/AudioInput.cpp b/src/autoapp/Projection/QtAudioInput.cpp
similarity index 77%
rename from src/autoapp/Projection/AudioInput.cpp
rename to src/autoapp/Projection/QtAudioInput.cpp
index 2fe3fbf..1558430 100644
--- a/src/autoapp/Projection/AudioInput.cpp
+++ b/src/autoapp/Projection/QtAudioInput.cpp
@@ -17,7 +17,7 @@
*/
#include
-#include
+#include
#include
namespace f1x
@@ -29,7 +29,7 @@ namespace autoapp
namespace projection
{
-AudioInput::AudioInput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate)
+QtAudioInput::QtAudioInput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate)
: ioDevice_(nullptr)
{
qRegisterMetaType("StartPromise::Pointer");
@@ -42,32 +42,32 @@ AudioInput::AudioInput(uint32_t channelCount, uint32_t sampleSize, uint32_t samp
audioFormat_.setSampleType(QAudioFormat::SignedInt);
this->moveToThread(QApplication::instance()->thread());
- connect(this, &AudioInput::startRecording, this, &AudioInput::onStartRecording, Qt::QueuedConnection);
- connect(this, &AudioInput::stopRecording, this, &AudioInput::onStopRecording, Qt::QueuedConnection);
+ connect(this, &QtAudioInput::startRecording, this, &QtAudioInput::onStartRecording, Qt::QueuedConnection);
+ connect(this, &QtAudioInput::stopRecording, this, &QtAudioInput::onStopRecording, Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "createAudioInput", Qt::BlockingQueuedConnection);
}
-void AudioInput::createAudioInput()
+void QtAudioInput::createAudioInput()
{
OPENAUTO_LOG(debug) << "[AudioInput] create.";
audioInput_ = (std::make_unique(QAudioDeviceInfo::defaultInputDevice(), audioFormat_));
}
-bool AudioInput::open()
+bool QtAudioInput::open()
{
std::lock_guard lock(mutex_);
return ioDevice_ == nullptr;
}
-bool AudioInput::isActive() const
+bool QtAudioInput::isActive() const
{
std::lock_guard lock(mutex_);
return ioDevice_ != nullptr;
}
-void AudioInput::read(ReadPromise::Pointer promise)
+void QtAudioInput::read(ReadPromise::Pointer promise)
{
std::lock_guard lock(mutex_);
@@ -85,32 +85,32 @@ void AudioInput::read(ReadPromise::Pointer promise)
}
}
-void AudioInput::start(StartPromise::Pointer promise)
+void QtAudioInput::start(StartPromise::Pointer promise)
{
emit startRecording(std::move(promise));
}
-void AudioInput::stop()
+void QtAudioInput::stop()
{
emit stopRecording();
}
-uint32_t AudioInput::getSampleSize() const
+uint32_t QtAudioInput::getSampleSize() const
{
return audioFormat_.sampleSize();
}
-uint32_t AudioInput::getChannelCount() const
+uint32_t QtAudioInput::getChannelCount() const
{
return audioFormat_.channelCount();
}
-uint32_t AudioInput::getSampleRate() const
+uint32_t QtAudioInput::getSampleRate() const
{
return audioFormat_.sampleRate();
}
-void AudioInput::onStartRecording(StartPromise::Pointer promise)
+void QtAudioInput::onStartRecording(StartPromise::Pointer promise)
{
std::lock_guard lock(mutex_);
@@ -118,7 +118,7 @@ void AudioInput::onStartRecording(StartPromise::Pointer promise)
if(ioDevice_ != nullptr)
{
- connect(ioDevice_, &QIODevice::readyRead, this, &AudioInput::onReadyRead, Qt::QueuedConnection);
+ connect(ioDevice_, &QIODevice::readyRead, this, &QtAudioInput::onReadyRead, Qt::QueuedConnection);
promise->resolve();
}
else
@@ -127,7 +127,7 @@ void AudioInput::onStartRecording(StartPromise::Pointer promise)
}
}
-void AudioInput::onStopRecording()
+void QtAudioInput::onStopRecording()
{
std::lock_guard lock(mutex_);
@@ -147,7 +147,7 @@ void AudioInput::onStopRecording()
audioInput_->stop();
}
-void AudioInput::onReadyRead()
+void QtAudioInput::onReadyRead()
{
std::lock_guard lock(mutex_);
diff --git a/src/autoapp/Projection/AudioOutput.cpp b/src/autoapp/Projection/QtAudioOutput.cpp
similarity index 67%
rename from src/autoapp/Projection/AudioOutput.cpp
rename to src/autoapp/Projection/QtAudioOutput.cpp
index 7b309d5..dff8049 100644
--- a/src/autoapp/Projection/AudioOutput.cpp
+++ b/src/autoapp/Projection/QtAudioOutput.cpp
@@ -17,7 +17,7 @@
*/
#include
-#include
+#include
#include
namespace f1x
@@ -29,7 +29,7 @@ namespace autoapp
namespace projection
{
-AudioOutput::AudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate)
+QtAudioOutput::QtAudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate)
: playbackStarted_(false)
{
audioFormat_.setChannelCount(channelCount);
@@ -40,63 +40,60 @@ AudioOutput::AudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sa
audioFormat_.setSampleType(QAudioFormat::SignedInt);
this->moveToThread(QApplication::instance()->thread());
- connect(this, &AudioOutput::startPlayback, this, &AudioOutput::onStartPlayback);
- connect(this, &AudioOutput::suspendPlayback, this, &AudioOutput::onSuspendPlayback);
- connect(this, &AudioOutput::stopPlayback, this, &AudioOutput::onStopPlayback);
+ connect(this, &QtAudioOutput::startPlayback, this, &QtAudioOutput::onStartPlayback);
+ connect(this, &QtAudioOutput::suspendPlayback, this, &QtAudioOutput::onSuspendPlayback);
+ connect(this, &QtAudioOutput::stopPlayback, this, &QtAudioOutput::onStopPlayback);
QMetaObject::invokeMethod(this, "createAudioOutput", Qt::BlockingQueuedConnection);
}
-void AudioOutput::createAudioOutput()
+void QtAudioOutput::createAudioOutput()
{
- OPENAUTO_LOG(debug) << "[AudioOutput] create.";
+ OPENAUTO_LOG(debug) << "[QtAudioOutput] create.";
audioOutput_ = std::make_unique(QAudioDeviceInfo::defaultOutputDevice(), audioFormat_);
-
- // Default volume level (max) produces crackles
- audioOutput_->setVolume(static_cast(0.90));
}
-bool AudioOutput::open()
+bool QtAudioOutput::open()
{
return audioBuffer_.open(QIODevice::ReadWrite);
}
-void AudioOutput::write(const aasdk::common::DataConstBuffer& buffer)
+void QtAudioOutput::write(aasdk::messenger::Timestamp::ValueType, const aasdk::common::DataConstBuffer& buffer)
{
audioBuffer_.write(reinterpret_cast(buffer.cdata), buffer.size);
}
-void AudioOutput::start()
+void QtAudioOutput::start()
{
emit startPlayback();
}
-void AudioOutput::stop()
+void QtAudioOutput::stop()
{
emit stopPlayback();
}
-void AudioOutput::suspend()
+void QtAudioOutput::suspend()
{
emit suspendPlayback();
}
-uint32_t AudioOutput::getSampleSize() const
+uint32_t QtAudioOutput::getSampleSize() const
{
return audioFormat_.sampleSize();
}
-uint32_t AudioOutput::getChannelCount() const
+uint32_t QtAudioOutput::getChannelCount() const
{
return audioFormat_.channelCount();
}
-uint32_t AudioOutput::getSampleRate() const
+uint32_t QtAudioOutput::getSampleRate() const
{
return audioFormat_.sampleRate();
}
-void AudioOutput::onStartPlayback()
+void QtAudioOutput::onStartPlayback()
{
if(!playbackStarted_)
{
@@ -109,12 +106,12 @@ void AudioOutput::onStartPlayback()
}
}
-void AudioOutput::onSuspendPlayback()
+void QtAudioOutput::onSuspendPlayback()
{
audioOutput_->suspend();
}
-void AudioOutput::onStopPlayback()
+void QtAudioOutput::onStopPlayback()
{
if(playbackStarted_)
{
diff --git a/src/autoapp/Projection/RtAudioOutput.cpp b/src/autoapp/Projection/RtAudioOutput.cpp
new file mode 100644
index 0000000..eb05420
--- /dev/null
+++ b/src/autoapp/Projection/RtAudioOutput.cpp
@@ -0,0 +1,155 @@
+/*
+* This file is part of openauto project.
+* Copyright (C) 2018 f1x.studio (Michal Szwaj)
+*
+* openauto is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 3 of the License, or
+* (at your option) any later version.
+
+* openauto is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with openauto. If not, see .
+*/
+
+#include
+#include
+
+namespace f1x
+{
+namespace openauto
+{
+namespace autoapp
+{
+namespace projection
+{
+
+RtAudioOutput::RtAudioOutput(uint32_t channelCount, uint32_t sampleSize, uint32_t sampleRate)
+ : channelCount_(channelCount)
+ , sampleSize_(sampleSize)
+ , sampleRate_(sampleRate)
+{
+ std::vector apis;
+ RtAudio::getCompiledApi(apis);
+ dac_ = std::find(apis.begin(), apis.end(), RtAudio::LINUX_PULSE) == apis.end() ? std::make_unique() : std::make_unique(RtAudio::LINUX_PULSE);
+}
+
+bool RtAudioOutput::open()
+{
+ std::lock_guard lock(mutex_);
+
+ if(dac_->getDeviceCount() > 0)
+ {
+ RtAudio::StreamParameters parameters;
+ parameters.deviceId = dac_->getDefaultOutputDevice();
+ parameters.nChannels = channelCount_;
+ parameters.firstChannel = 0;
+
+ try
+ {
+ uint32_t bufferFrames = 128;
+ dac_->openStream(¶meters, nullptr, RTAUDIO_SINT16, sampleRate_, &bufferFrames, &RtAudioOutput::audioBufferReadHandler, static_cast(this), nullptr);
+ return audioBuffer_.open(QIODevice::ReadWrite);
+ }
+ catch(const RtAudioError& e)
+ {
+ OPENAUTO_LOG(error) << "[RtAudioOutput] Failed to open audio output, what: " << e.what();
+ }
+ }
+ else
+ {
+ OPENAUTO_LOG(error) << "[RtAudioOutput] No output devices found.";
+ }
+
+ return false;
+}
+
+void RtAudioOutput::write(aasdk::messenger::Timestamp::ValueType timestamp, const aasdk::common::DataConstBuffer& buffer)
+{
+ audioBuffer_.write(reinterpret_cast(buffer.cdata), buffer.size);
+}
+
+void RtAudioOutput::start()
+{
+ std::lock_guard lock(mutex_);
+
+ if(dac_->isStreamOpen() && !dac_->isStreamRunning())
+ {
+ try
+ {
+ dac_->startStream();
+ }
+ catch(const RtAudioError& e)
+ {
+ OPENAUTO_LOG(error) << "[RtAudioOutput] Failed to start audio output, what: " << e.what();
+ }
+ }
+}
+
+void RtAudioOutput::stop()
+{
+ std::lock_guard lock(mutex_);
+
+ this->doSuspend();
+
+ if(dac_->isStreamOpen())
+ {
+ dac_->closeStream();
+ }
+}
+
+void RtAudioOutput::suspend()
+{
+ std::lock_guard lock(mutex_);
+ this->doSuspend();
+}
+
+uint32_t RtAudioOutput::getSampleSize() const
+{
+ return sampleSize_;
+}
+
+uint32_t RtAudioOutput::getChannelCount() const
+{
+ return channelCount_;
+}
+
+uint32_t RtAudioOutput::getSampleRate() const
+{
+ return sampleRate_;
+}
+
+void RtAudioOutput::doSuspend()
+{
+ if(!dac_->isStreamOpen() && !dac_->isStreamRunning())
+ {
+ try
+ {
+ dac_->stopStream();
+ }
+ catch(const RtAudioError& e)
+ {
+ OPENAUTO_LOG(error) << "[RtAudioOutput] Failed to suspend audio output, what: " << e.what();
+ }
+ }
+}
+
+int RtAudioOutput::audioBufferReadHandler(void* outputBuffer, void* inputBuffer, unsigned int nBufferFrames,
+ double streamTime, RtAudioStreamStatus status, void* userData)
+{
+ RtAudioOutput* self = static_cast(userData);
+ std::lock_guardmutex_)> lock(self->mutex_);
+
+ const auto bufferSize = nBufferFrames * (self->sampleSize_ / 8) * self->channelCount_;
+ self->audioBuffer_.read(reinterpret_cast(outputBuffer), bufferSize);
+ return 0;
+}
+
+}
+}
+}
+}
diff --git a/src/autoapp/Projection/ServiceFactory.cpp b/src/autoapp/Projection/ServiceFactory.cpp
index ade1e38..d10ddff 100644
--- a/src/autoapp/Projection/ServiceFactory.cpp
+++ b/src/autoapp/Projection/ServiceFactory.cpp
@@ -32,8 +32,9 @@
#include
#include
#include
-#include
-#include
+#include
+#include
+#include
#include
#include
#include
@@ -59,24 +60,9 @@ ServiceList ServiceFactory::create(aasdk::messenger::IMessenger::Pointer messeng
{
ServiceList serviceList;
- IAudioInput::Pointer audioInput(new AudioInput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1));
+ IAudioInput::Pointer audioInput(new QtAudioInput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1));
serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(audioInput)));
-
- if(configuration_->musicAudioChannelEnabled())
- {
- IAudioOutput::Pointer mediaAudioOutput(new AudioOutput(2, 16, 48000), std::bind(&QObject::deleteLater, std::placeholders::_1));
- serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(mediaAudioOutput)));
- }
-
- if(configuration_->speechAudioChannelEnabled())
- {
- IAudioOutput::Pointer speechAudioOutput(new AudioOutput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1));
- serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(speechAudioOutput)));
- }
-
- IAudioOutput::Pointer systemAudioOutput(new AudioOutput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1));
- serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(systemAudioOutput)));
-
+ this->createAudioServices(serviceList, messenger);
serviceList.emplace_back(std::make_shared(ioService_, messenger));
serviceList.emplace_back(this->createVideoService(messenger));
serviceList.emplace_back(this->createBluetoothService(messenger));
@@ -141,6 +127,33 @@ IService::Pointer ServiceFactory::createInputService(aasdk::messenger::IMessenge
return std::make_shared(ioService_, messenger, std::move(inputDevice));
}
+void ServiceFactory::createAudioServices(ServiceList& serviceList, aasdk::messenger::IMessenger::Pointer messenger)
+{
+ if(configuration_->musicAudioChannelEnabled())
+ {
+ auto mediaAudioOutput = configuration_->getAudioOutputBackendType() == configuration::AudioOutputBackendType::RTAUDIO ?
+ std::make_shared(2, 16, 48000) :
+ IAudioOutput::Pointer(new QtAudioOutput(2, 16, 48000), std::bind(&QObject::deleteLater, std::placeholders::_1));
+
+ serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(mediaAudioOutput)));
+ }
+
+ if(configuration_->speechAudioChannelEnabled())
+ {
+ auto speechAudioOutput = configuration_->getAudioOutputBackendType() == configuration::AudioOutputBackendType::RTAUDIO ?
+ std::make_shared(1, 16, 16000) :
+ IAudioOutput::Pointer(new QtAudioOutput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1));
+
+ serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(speechAudioOutput)));
+ }
+
+ auto systemAudioOutput = configuration_->getAudioOutputBackendType() == configuration::AudioOutputBackendType::RTAUDIO ?
+ std::make_shared(1, 16, 16000) :
+ IAudioOutput::Pointer(new QtAudioOutput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1));
+
+ serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(systemAudioOutput)));
+}
+
}
}
}
diff --git a/src/autoapp/UI/SettingsWindow.cpp b/src/autoapp/UI/SettingsWindow.cpp
index 3117cf3..5f64b3e 100644
--- a/src/autoapp/UI/SettingsWindow.cpp
+++ b/src/autoapp/UI/SettingsWindow.cpp
@@ -97,6 +97,7 @@ void SettingsWindow::onSave()
configuration_->setMusicAudioChannelEnabled(ui_->checkBoxMusicAudioChannel->isChecked());
configuration_->setSpeechAudioChannelEnabled(ui_->checkBoxSpeechAudioChannel->isChecked());
+ configuration_->setAudioOutputBackendType(ui_->radioButtonRtAudio->isChecked() ? configuration::AudioOutputBackendType::RTAUDIO : configuration::AudioOutputBackendType::QT);
configuration_->save();
this->close();
@@ -149,6 +150,10 @@ void SettingsWindow::load()
ui_->checkBoxMusicAudioChannel->setChecked(configuration_->musicAudioChannelEnabled());
ui_->checkBoxSpeechAudioChannel->setChecked(configuration_->speechAudioChannelEnabled());
+
+ const auto& audioOutputBackendType = configuration_->getAudioOutputBackendType();
+ ui_->radioButtonRtAudio->setChecked(audioOutputBackendType == configuration::AudioOutputBackendType::RTAUDIO);
+ ui_->radioButtonQtAudio->setChecked(audioOutputBackendType == configuration::AudioOutputBackendType::QT);
}
void SettingsWindow::loadButtonCheckBoxes()
diff --git a/src/autoapp/UI/settingswindow.ui b/src/autoapp/UI/settingswindow.ui
index b066a78..4028487 100644
--- a/src/autoapp/UI/settingswindow.ui
+++ b/src/autoapp/UI/settingswindow.ui
@@ -486,6 +486,45 @@ color: rgb(238, 238, 236);
+
+
+
+ 0
+ 130
+ 621
+ 61
+
+
+
+ Output backend
+
+
+
+
+ 10
+ 30
+ 112
+ 23
+
+
+
+ RT audio
+
+
+
+
+
+ 140
+ 30
+ 112
+ 23
+
+
+
+ Qt
+
+
+
@@ -969,6 +1008,8 @@ color: rgb(238, 238, 236);
horizontalSliderScreenDPI
checkBoxMusicAudioChannel
checkBoxSpeechAudioChannel
+ radioButtonRtAudio
+ radioButtonQtAudio
checkBoxEnableTouchscreen
listWidgetButtons
checkBoxPlayButton