diff --git a/include/f1x/openauto/autoapp/Projection/RtAudioOutput.hpp b/include/f1x/openauto/autoapp/Projection/RtAudioOutput.hpp new file mode 100644 index 0000000..1e111b8 --- /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(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: + 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_; + bool playbackStarted_; + RtAudio dac_; + std::mutex mutex_; +}; + +} +} +} +} diff --git a/src/autoapp/Projection/AudioService.cpp b/src/autoapp/Projection/AudioService.cpp index 3e68c11..d5d2da5 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()); diff --git a/src/autoapp/Projection/RtAudioOutput.cpp b/src/autoapp/Projection/RtAudioOutput.cpp new file mode 100644 index 0000000..68a1756 --- /dev/null +++ b/src/autoapp/Projection/RtAudioOutput.cpp @@ -0,0 +1,148 @@ +/* +* 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) + , playbackStarted_(false) +{ +} + +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 = 256; + dac_.openStream(¶meters, nullptr, RTAUDIO_SINT16, sampleRate_, &bufferFrames, &RtAudioOutput::audioBufferReadHandler, static_cast(this)); + + 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(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->suspend(); + + if(dac_.isStreamOpen()) + { + dac_.closeStream(); + } +} + +void RtAudioOutput::suspend() +{ + std::lock_guard lock(mutex_); + + if(!dac_.isStreamOpen() && !dac_.isStreamRunning()) + { + try + { + dac_.stopStream(); + } + catch(const RtAudioError& e) + { + OPENAUTO_LOG(error) << "[RtAudioOutput] Failed to suspend audio output, what: " << e.what(); + } + } +} + +uint32_t RtAudioOutput::getSampleSize() const +{ + return sampleSize_; +} + +uint32_t RtAudioOutput::getChannelCount() const +{ + return channelCount_; +} + +uint32_t RtAudioOutput::getSampleRate() const +{ + return sampleRate_; +} + +int RtAudioOutput::audioBufferReadHandler(void* outputBuffer, void* inputBuffer, unsigned int nBufferFrames, + double streamTime, RtAudioStreamStatus status, void* userData) +{ + RtAudioOutput* self = static_cast(userData); + 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 9205b25..2e54710 100644 --- a/src/autoapp/Projection/ServiceFactory.cpp +++ b/src/autoapp/Projection/ServiceFactory.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -64,17 +65,20 @@ ServiceList ServiceFactory::create(aasdk::messenger::IMessenger::Pointer messeng if(configuration_->musicAudioChannelEnabled()) { - IAudioOutput::Pointer mediaAudioOutput(new QtAudioOutput(2, 16, 48000), std::bind(&QObject::deleteLater, std::placeholders::_1)); + //IAudioOutput::Pointer mediaAudioOutput(new QtAudioOutput(2, 16, 48000), std::bind(&QObject::deleteLater, std::placeholders::_1)); + auto mediaAudioOutput(std::make_shared(2, 16, 48000)); serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(mediaAudioOutput))); } if(configuration_->speechAudioChannelEnabled()) { IAudioOutput::Pointer speechAudioOutput(new QtAudioOutput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1)); + //auto speechAudioOutput(std::make_shared(1, 16, 1600)); serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(speechAudioOutput))); } IAudioOutput::Pointer systemAudioOutput(new QtAudioOutput(1, 16, 16000), std::bind(&QObject::deleteLater, std::placeholders::_1)); + //auto systemAudioOutput(std::make_shared(1, 16, 1600)); serviceList.emplace_back(std::make_shared(ioService_, messenger, std::move(systemAudioOutput))); serviceList.emplace_back(std::make_shared(ioService_, messenger));