6#include <QtMultimedia/qmediadevices.h>
7#include <QtMultimedia/private/q_pmr_emulation_p.h>
8#include <QtMultimedia/private/qmemory_resource_tlsf_p.h>
9#include <QtMultimedia/private/qmultimedia_ranges_p.h>
10#include <QtMultimedia/private/qrtaudioengine_p.h>
11#include <QtSpatialAudio/private/qambientsound_p.h>
12#include <QtSpatialAudio/private/qambisonicdecoder_p.h>
13#include <QtSpatialAudio/private/qspatialsound_p.h>
15#include <resonance_audio.h>
19enum class SourceId :
int { };
41 void processSlice(QSpan<
float> hostBuffer);
42 void processSliceFillResonanceBuffers();
43 void processSliceProcessSurroundMode(QSpan<
float>);
44 void processSliceProcessWithoutReverb(QSpan<
float>);
47 QAudioEngine::OutputMode m_outputMode;
48 const std::shared_ptr<
vraudio::ResonanceAudio> m_resonanceAudio;
49 const QAudioFormat m_format;
50 QAmbisonicDecoder m_ambisonicDecoder;
51 bool m_paused =
false;
55 SharedPlaybackState playbackState;
59 static constexpr size_t poolSize =
65 QtMultimediaPrivate::QTlsfMemoryResource m_rtMemoryPool{
poolSize };
67 QtMultimediaPrivate::pmr::map<SourceId, SoundState> m_sounds{ &
m_rtMemoryPool };
68 QtMultimediaPrivate::pmr::vector<
float> m_leftoverBuffer{ &
m_rtMemoryPool };
89 namespace ranges = QtMultimediaPrivate::ranges;
92 ranges::fill(outputBuffer, 0);
93 return VoicePlayResult::Playing;
96 const size_t samplesPerSlice = m_format.channelCount() * qToUnderlying(framesPerBuffer);
98 while (!outputBuffer.empty()) {
99 if (!m_leftoverBuffer.empty()) {
101 QSpan leftoverSpanToCopy = take(QSpan(m_leftoverBuffer), outputBuffer.size());
102 ranges::copy(leftoverSpanToCopy, outputBuffer.begin());
103 m_leftoverBuffer.erase(m_leftoverBuffer.begin(),
104 m_leftoverBuffer.begin() + leftoverSpanToCopy.size());
105 outputBuffer = drop(outputBuffer, leftoverSpanToCopy.size());
109 Q_ASSERT(m_leftoverBuffer.empty());
111 auto sliceBuffer = take(outputBuffer, samplesPerSlice);
112 size_t sliceBufferSize = sliceBuffer.size();
114 if (sliceBufferSize == samplesPerSlice) {
116 processSlice(sliceBuffer);
117 outputBuffer = drop(outputBuffer, samplesPerSlice);
121 pmr::vector<
float> sliceBuffer{
124 pmr::vector<
float>::allocator_type{ m_leftoverBuffer.get_allocator().resource() },
126 processSlice(sliceBuffer);
128 QSpan sliceToOutput = take(QSpan(sliceBuffer), sliceBufferSize);
129 QSpan sliceToKeep = drop(QSpan(sliceBuffer), sliceBufferSize);
130 ranges::copy(sliceToOutput, outputBuffer.begin());
131 m_leftoverBuffer.assign(sliceToKeep.begin(), sliceToKeep.end());
133 outputBuffer = drop(outputBuffer, sliceToOutput.size());
137 return VoicePlayResult::Playing;
142 auto [_, inserted] = m_sounds.insert_or_assign(id,
152 auto it = m_sounds.find(id);
153 Q_ASSERT(it != m_sounds.end());
154 it->second.playbackState =
std::move(state);
159 m_sounds.erase(sound);
175 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
176 const qsizetype samplesPerSlice = m_format.channelCount() * framesPerBuffer;
178 Q_ASSERT(hostBuffer.size() == samplesPerSlice);
181 processSliceFillResonanceBuffers();
184 switch (m_outputMode) {
185 case QAudioEngine::Surround:
186 processSliceProcessSurroundMode(hostBuffer);
189 processSliceProcessWithoutReverb(hostBuffer);
197 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
198 static constexpr std::array<
float, 2 * framesPerBuffer> nullBuffer{};
200 vraudio::ResonanceAudioApi *api = m_resonanceAudio->api.get();
202 for (
auto &&[sourceId, sourceState] : m_sounds) {
203 int numberOfChannels = sourceState.numberOfChannels;
204 SharedPlaybackState &playbackState = sourceState.playbackState;
206 Q_ASSERT(playbackState->format().channelCount() <= 2);
207 std::array<
float, 2 * framesPerBuffer> buf;
209 playbackState->getBuffer(take(
210 QSpan<
float>{ buf }, playbackState->format().channelCount() * framesPerBuffer));
211 api->SetInterleavedBuffer(qToUnderlying(sourceId), buf.data(), numberOfChannels,
214 api->SetInterleavedBuffer(qToUnderlying(sourceId), nullBuffer.data(), numberOfChannels,
222 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
224 namespace ranges = QtMultimediaPrivate::ranges;
225 Q_ASSERT(outputBuffer.size() == m_format.channelCount() * framesPerBuffer);
227 std::array<
const float *, QAmbisonicDecoder::maxAmbisonicChannels> channels;
228 std::array<
const float *, 2> reverbBuffers{};
229 int nFrames = m_resonanceAudio->getAmbisonicOutput(channels.data(), reverbBuffers.data(),
230 m_ambisonicDecoder.nInputChannels());
236 ranges::fill(outputBuffer, 0);
239 auto nSamples = m_ambisonicDecoder.outputSamples(nFrames);
241 Q_ASSERT(m_ambisonicDecoder.nOutputChannels() <= 8);
242 QSpan currentOutput = take(outputBuffer, nSamples);
243 m_ambisonicDecoder.processBufferWithReverb(
244 QSpan{ channels.data(), m_ambisonicDecoder.nInputChannels() }, reverbBuffers,
250 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
252 Q_ASSERT(outputBuffer.size() == m_format.channelCount() * framesPerBuffer);
253 namespace ranges = QtMultimediaPrivate::ranges;
255 bool ok = m_resonanceAudio->api->FillInterleavedOutputBuffer(
256 m_format.channelCount(), framesPerBuffer, outputBuffer.data());
261 if (m_sounds.empty()) {
262 ranges::fill(outputBuffer, 0.f);
265 qWarning() <<
" processSliceProcessWithoutReverb failure!";
280 m_playbackEngine =
nullptr;
285 QAudioDevice device = m_device;
287 device = QMediaDevices::defaultAudioOutput();
289 if (device.isNull()) {
290 qWarning() <<
"QAudioEngine::start() failed: No audio output device found.";
295 format.setSampleFormat(QAudioFormat::Float);
296 format.setSampleRate(sampleRate());
297 format.setChannelCount(
std::min(2, device.maximumChannelCount()));
299 m_playbackEngine = std::make_shared<QRtAudioEngine>(device, format, std::nullopt,
300 QAudioEnginePrivate::framesPerBuffer);
305 for (
auto &[sound, state] : playbackStates)
306 voice->addSound(SourceId{ sound->sourceId }, sound->nchannels, state);
308 m_engineVoice = voice;
309 m_playbackEngine->play(voice);
316 m_playbackEngine->stop(m_engineVoice);
322 if (paused == m_paused)
330 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(),
331 [paused](QResonanceAudioPlayer &player) {
332 player.setPaused(paused);
343 if (m_device == device)
346 qWarning() <<
"Changing device on a running engine not implemented";
351 emit q->outputDeviceChanged();
361 playbackStates.emplace(sound,
nullptr);
366 m_playbackEngine->visitVoiceRt(
367 m_engineVoice->voiceId(),
368 [id = SourceId{ sound->sourceId },
369 numberOfChannels = sound->nchannels](QResonanceAudioPlayer &player)
mutable {
370 player.addSound(id, numberOfChannels);
376 SharedPlaybackState oldState =
std::move(playbackStates[sound]);
377 playbackStates.erase(sound);
382 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(),
383 [id = SourceId{ sound->sourceId },
384 oldState = std::move(oldState)](QResonanceAudioPlayer &player) {
385 player.removeSound(id);
390 SharedPlaybackState state)
392 auto it = playbackStates.find(sound);
393 Q_ASSERT(it != playbackStates.end());
395 SharedPlaybackState oldState = std::exchange(it->second, state);
401 m_playbackEngine->visitVoiceRt(
402 m_engineVoice->voiceId(),
403 [id = SourceId{ sound->sourceId }, oldState = std::move(oldState),
404 state = std::move(state)](QResonanceAudioPlayer &player)
mutable {
405 player.setSoundPlaybackData(id, std::move(state));
411 if (outputMode() == mode)
413 QAudioEnginePrivate::setOutputMode(mode);
417 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(), [mode](QResonanceAudioPlayer &player) {
418 player.setOutputMode(mode);
424 for (
auto [sound, key] : playbackStates)
425 sound->updateRoomEffects();
void addSound(QAmbientSoundPrivate *sound) override
bool isPaused() const override
void setSoundPlaybackData(QAmbientSoundPrivate *, SharedPlaybackState) override
void setPaused(bool paused) override
void setOutputMode(QAudioEngine::OutputMode) override
~QAudioEngineWithPlayer() override
void setOutputDevice(const QAudioDevice &device) override
QAudioDevice outputDevice() const override
QAudioEngineWithPlayer(int sampleRate)
void updateRoomEffects() override
void removeSound(QAmbientSoundPrivate *sound) override
QResonanceAudioPlayer(QAudioEngineWithPlayer *player, VoiceId id)
void removeSound(SourceId)
void setOutputMode(QAudioEngine::OutputMode mode)
bool isActive() noexcept QT_MM_NONBLOCKING override
void setSoundPlaybackData(SourceId, SharedPlaybackState)
VoicePlayResult play(QSpan< float >) noexcept QT_MM_NONBLOCKING override
QRtAudioEngineVoice overrides.