diff --git a/src/AudioProcessor.cpp b/src/AudioProcessor.cpp index 0e0234f..f13dcaf 100644 --- a/src/AudioProcessor.cpp +++ b/src/AudioProcessor.cpp @@ -1,6 +1,7 @@ #include "AudioProcessor.h" #include "SpeexDSP.h" +#include "Timer.h" #include "WebRTCDSP.h" #include @@ -9,7 +10,7 @@ namespace SpeexWebRTCTest { namespace { -Q_LOGGING_CATEGORY(AudioProcessor, "processor") +Q_LOGGING_CATEGORY(processor, "processor") } QVector calculateAudioLevels(const QAudioBuffer& buffer); @@ -78,6 +79,8 @@ void AudioProcessor::process() { while (doWork_) { + auto waitUntil = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(5); + const std::size_t bytesToRead = bufferSize_ * format_.sampleSize() / 8 * format_.channelCount(); const std::size_t monitorToRead = @@ -122,12 +125,14 @@ void AudioProcessor::process() emit readyRead(); } - std::this_thread::sleep_for(std::chrono::milliseconds(5)); + std::this_thread::sleep_until(waitUntil); } } void AudioProcessor::processBuffer(QAudioBuffer& inputBuffer, const QAudioBuffer& monitorBuffer) { + TIMER(qDebug(processor)) + QVector inputLevels = calculateAudioLevels(inputBuffer); if (dsp_) @@ -157,6 +162,10 @@ qint64 AudioProcessor::bytesAvailable() const bool AudioProcessor::open(QIODevice::OpenMode mode) { + std::unique_lock lock1(inputMutex_); + std::unique_lock lock2(outputMutex_); + std::unique_lock lock3(monitorMutex_); + inputBuffer_.clear(); outputBuffer_.clear(); monitorBuffer_.clear(); @@ -195,6 +204,14 @@ Backend AudioProcessor::getCurrentBackend() const void AudioProcessor::switchBackend(Backend backend) { + std::unique_lock lock1(inputMutex_); + std::unique_lock lock2(outputMutex_); + std::unique_lock lock3(monitorMutex_); + + inputBuffer_.clear(); + outputBuffer_.clear(); + monitorBuffer_.clear(); + if (backend == Backend::Speex) dsp_.reset(new SpeexDSP(format_, monitorFormat_)); else diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46b7b9a..c4ae605 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,11 +12,11 @@ file(GLOB SOURCES *.cpp) file(GLOB HEADERS *.h) file(GLOB_RECURSE RESOURCES *.qrc) -if (WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Release") - add_executable(${TARGET_NAME} WIN32 ${SOURCES} ${HEADERS} ${RESOURCES}) -else() +#if (WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Release") +# add_executable(${TARGET_NAME} WIN32 ${SOURCES} ${HEADERS} ${RESOURCES}) +#else() add_executable(${TARGET_NAME} ${SOURCES} ${HEADERS} ${RESOURCES}) -endif() +#endif() target_link_libraries(${TARGET_NAME} speexdsp diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index da6d281..1043388 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -85,6 +85,18 @@ MainWindow::~MainWindow() stopRecording(); } +void fixFormatForDevice(QAudioFormat& format, const QAudioDeviceInfo& info) +{ + if (!info.isFormatSupported(format)) + { + QAudioFormat newFormat = info.nearestFormat(format); + qWarning(Gui).nospace() << "Preferred format " << format << " is not supported by device " + << info.deviceName() << "."; + qWarning(Gui) << "Trying to use nearest format" << newFormat; + format = newFormat; + } +} + void MainWindow::initializeAudio(const QAudioDeviceInfo& inputDeviceInfo, const QAudioDeviceInfo& outputDeviceInfo, const QAudioDeviceInfo& monitorDeviceInfo) @@ -94,24 +106,9 @@ void MainWindow::initializeAudio(const QAudioDeviceInfo& inputDeviceInfo, auto outputFormat = getOutputFormat(); auto monitorFormat = getMonitorFormat(); - if (!inputDeviceInfo.isFormatSupported(captureFormat)) - { - qWarning(Gui) - << "Preferred format is not supported by input device - trying to use nearest"; - captureFormat = inputDeviceInfo.nearestFormat(captureFormat); - } - if (!monitorDeviceInfo.isFormatSupported(monitorFormat)) - { - qWarning(Gui) - << "Preferred format is not supported by monitor device - trying to use nearest"; - monitorFormat = inputDeviceInfo.nearestFormat(monitorFormat); - } - if (!outputDeviceInfo.isFormatSupported(outputFormat)) - { - qWarning(Gui) - << "Preferred format is not supported by output device - trying to use nearest"; - outputFormat = inputDeviceInfo.nearestFormat(outputFormat); - } + fixFormatForDevice(captureFormat, inputDeviceInfo); + fixFormatForDevice(outputFormat, outputDeviceInfo); + fixFormatForDevice(monitorFormat, monitorDeviceInfo); audioInput_.reset(new QAudioInput(inputDeviceInfo, captureFormat)); audioOutput_.reset(new QAudioOutput(outputDeviceInfo, outputFormat)); diff --git a/src/SpeexDSP.cpp b/src/SpeexDSP.cpp index fc2a547..9ab969e 100644 --- a/src/SpeexDSP.cpp +++ b/src/SpeexDSP.cpp @@ -1,5 +1,7 @@ #include "SpeexDSP.h" +#include "Timer.h" + #include #include @@ -36,6 +38,8 @@ SpeexDSP::~SpeexDSP() void SpeexDSP::processFrame(QAudioBuffer& mainBuffer, const QAudioBuffer& auxBuffer) { + TIMER(qDebug(Speex)) + qDebug(Speex).noquote() << QString( "got %1 near-end samples (%2ms) and %3 far-end samples (%4ms)") .arg(mainBuffer.frameCount()) diff --git a/src/Timer.h b/src/Timer.h new file mode 100644 index 0000000..03d96e5 --- /dev/null +++ b/src/Timer.h @@ -0,0 +1,144 @@ +#ifndef _SPEEX_WEBRTC_TEST_TIMER_H_ +#define _SPEEX_WEBRTC_TEST_TIMER_H_ + +#include + +#include +#include +#include + +namespace SpeexWebRTCTest { + +namespace _detail { + +template +struct is_one_of; + +template +struct is_one_of +{ + static constexpr bool value = std::is_same::value; +}; + +template +struct is_one_of +{ + static constexpr bool value = std::is_same::value || is_one_of::value; +}; + + +template ::value>::type> +constexpr const char* duration_suffix() +{ + if (std::is_same::value) + { + return "h"; + } + else if (std::is_same::value) + { + return "min"; + } + else if (std::is_same::value) + { + return "s"; + } + else if (std::is_same::value) + { + return "ms"; + } + else if (std::is_same::value) + { + return "us"; + } + else if (std::is_same::value) + { + return "ns"; + } + return ""; +} + +template +std::string format_impl(DurationIn) +{ + return {}; +} + +template +std::string format_impl(DurationIn d) +{ + std::string out; + + auto val = std::chrono::duration_cast(d); + if (val.count() != 0) + { + out = std::to_string(val.count()) + duration_suffix(); + + if (sizeof...(RestDurations) > 0) + { + out += " " + format_impl(d - val); + } + } + else + { + if (sizeof...(RestDurations) > 0) + { + out = format_impl(d); + } + else if (count == 0) + { + out = std::to_string(0) + duration_suffix(); + } + } + + return out; +} + +} // namespace _detail + +template +std::string format(DurationIn d) +{ + return _detail::format_impl<0, DurationIn, RestDurations...>(d); +} + +template +class Timer final +{ +public: + explicit Timer(const QDebug& debug, QString name = "") : debug_(debug), name_(std::move(name)) + { + start_ = std::chrono::high_resolution_clock::now(); + } + + ~Timer() + { + TimePoint stop = std::chrono::high_resolution_clock::now(); + if (name_.isEmpty()) + debug_.nospace().noquote() << "Timer: " << format(stop - start_).c_str(); + else + debug_.nospace().noquote() + << "Timer [" << name_ << "]: " << format(stop - start_).c_str(); + } + +private: + using TimePoint = std::chrono::time_point; + QDebug debug_; + QString name_; + TimePoint start_; +}; + +using MsTimer = Timer; + +} // namespace SpeexWebRTCTest + +#define TIMER(debug) MsTimer timer(debug, __func__); + +#endif //_SPEEX_WEBRTC_TEST_TIMER_H_ diff --git a/src/WebRTCDSP.cpp b/src/WebRTCDSP.cpp index 6c469a5..509ae4e 100644 --- a/src/WebRTCDSP.cpp +++ b/src/WebRTCDSP.cpp @@ -1,5 +1,7 @@ #include "WebRTCDSP.h" +#include "Timer.h" + #include #include @@ -110,6 +112,8 @@ QString errorDescription(int error) void WebRTCDSP::processFrame(QAudioBuffer& mainBuffer, const QAudioBuffer& auxBuffer) { + TIMER(qDebug(WebRTC)) + qDebug(WebRTC).noquote() << QString( "got %1 near-end samples at %2 Hz (%3ms) and %4 far-end " "samples at %5 (%6ms)")