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/qambisonicdecoder_p.h>
12#include <QtSpatialAudio/private/qspatialaudiosound_p.h>
14#include <resonance_audio.h>
18enum class SourceId :
int { };
40 void processSlice(QSpan<
float> hostBuffer);
41 void processSliceFillResonanceBuffers();
42 void processSliceProcessSurroundMode(QSpan<
float>);
43 void processSliceProcessWithoutReverb(QSpan<
float>);
46 QAudioEngine::OutputMode m_outputMode;
47 const std::shared_ptr<
vraudio::ResonanceAudio> m_resonanceAudio;
48 const QAudioFormat m_format;
49 QAmbisonicDecoder m_ambisonicDecoder;
50 bool m_paused =
false;
54 SharedPlaybackState playbackState;
58 static constexpr size_t poolSize =
64 QtMultimediaPrivate::QTlsfMemoryResource m_rtMemoryPool{
poolSize };
66 QtMultimediaPrivate::pmr::map<SourceId, SoundState> m_sounds{ &
m_rtMemoryPool };
67 QtMultimediaPrivate::pmr::vector<
float> m_leftoverBuffer{ &
m_rtMemoryPool };
84QtMultimediaPrivate::QRtAudioEngineVoice::VoicePlayResult
85QResonanceAudioPlayer::play(QSpan<
float> outputBuffer)
noexcept Q_DECL_NONBLOCKING_FUNCTION
87 using namespace QtMultimediaPrivate;
88 namespace ranges = QtMultimediaPrivate::ranges;
91 ranges::fill(outputBuffer, 0);
92 return VoicePlayResult::Playing;
95 const size_t samplesPerSlice = m_format.channelCount() * qToUnderlying(framesPerBuffer);
97 while (!outputBuffer.empty()) {
98 if (!m_leftoverBuffer.empty()) {
100 QSpan leftoverSpanToCopy = take(QSpan(m_leftoverBuffer), outputBuffer.size());
101 ranges::copy(leftoverSpanToCopy, outputBuffer.begin());
102 m_leftoverBuffer.erase(m_leftoverBuffer.begin(),
103 m_leftoverBuffer.begin() + leftoverSpanToCopy.size());
104 outputBuffer = drop(outputBuffer, leftoverSpanToCopy.size());
108 Q_ASSERT(m_leftoverBuffer.empty());
110 auto sliceBuffer = take(outputBuffer, samplesPerSlice);
111 size_t sliceBufferSize = sliceBuffer.size();
113 if (sliceBufferSize == samplesPerSlice) {
115 processSlice(sliceBuffer);
116 outputBuffer = drop(outputBuffer, samplesPerSlice);
120 pmr::vector<
float> sliceBuffer{
123 pmr::vector<
float>::allocator_type{ m_leftoverBuffer.get_allocator().resource() },
125 processSlice(sliceBuffer);
127 QSpan sliceToOutput = take(QSpan(sliceBuffer), sliceBufferSize);
128 QSpan sliceToKeep = drop(QSpan(sliceBuffer), sliceBufferSize);
129 ranges::copy(sliceToOutput, outputBuffer.begin());
130 m_leftoverBuffer.assign(sliceToKeep.begin(), sliceToKeep.end());
132 outputBuffer = drop(outputBuffer, sliceToOutput.size());
136 return VoicePlayResult::Playing;
139void QResonanceAudioPlayer::addSound(SourceId id,
int numberOfChannels, SharedPlaybackState state)
141 auto [_, inserted] = m_sounds.insert_or_assign(id,
149void QResonanceAudioPlayer::setSoundPlaybackData(SourceId id, SharedPlaybackState state)
151 auto it = m_sounds.find(id);
152 Q_ASSERT(it != m_sounds.end());
153 it->second.playbackState = std::move(state);
156void QResonanceAudioPlayer::removeSound(SourceId sound)
158 m_sounds.erase(sound);
161void QResonanceAudioPlayer::setPaused(
bool paused)
166void QResonanceAudioPlayer::setOutputMode(QAudioEngine::OutputMode mode)
171void QResonanceAudioPlayer::processSlice(QSpan<
float> hostBuffer)
173 using QtMultimediaPrivate::drop;
174 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
175 const qsizetype samplesPerSlice = m_format.channelCount() * framesPerBuffer;
177 Q_ASSERT(hostBuffer.size() == samplesPerSlice);
180 processSliceFillResonanceBuffers();
183 switch (m_outputMode) {
184 case QAudioEngine::Surround:
185 processSliceProcessSurroundMode(hostBuffer);
188 processSliceProcessWithoutReverb(hostBuffer);
194void QResonanceAudioPlayer::processSliceFillResonanceBuffers()
196 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
197 static constexpr std::array<
float, 2 * framesPerBuffer> nullBuffer{};
198 using QtMultimediaPrivate::take;
199 vraudio::ResonanceAudioApi *api = m_resonanceAudio->api.get();
201 for (
auto &&[sourceId, sourceState] : m_sounds) {
202 int numberOfChannels = sourceState.numberOfChannels;
203 SharedPlaybackState &playbackState = sourceState.playbackState;
205 Q_ASSERT(playbackState->format().channelCount() <= 2);
206 std::array<
float, 2 * framesPerBuffer> buf;
208 playbackState->getBuffer(take(
209 QSpan<
float>{ buf }, playbackState->format().channelCount() * framesPerBuffer));
210 api->SetInterleavedBuffer(qToUnderlying(sourceId), buf.data(), numberOfChannels,
213 api->SetInterleavedBuffer(qToUnderlying(sourceId), nullBuffer.data(), numberOfChannels,
219void QResonanceAudioPlayer::processSliceProcessSurroundMode(QSpan<
float> outputBuffer)
221 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
222 using QtMultimediaPrivate::take;
223 namespace ranges = QtMultimediaPrivate::ranges;
224 Q_ASSERT(outputBuffer.size() == m_format.channelCount() * framesPerBuffer);
226 std::array<
const float *, QAmbisonicDecoder::maxAmbisonicChannels> channels;
227 std::array<
const float *, 2> reverbBuffers{};
228 int nFrames = m_resonanceAudio->getAmbisonicOutput(channels.data(), reverbBuffers.data(),
229 m_ambisonicDecoder.nInputChannels());
235 ranges::fill(outputBuffer, 0);
238 auto nSamples = m_ambisonicDecoder.outputSamples(nFrames);
240 Q_ASSERT(m_ambisonicDecoder.nOutputChannels() <= 8);
241 QSpan currentOutput = take(outputBuffer, nSamples);
242 m_ambisonicDecoder.processBufferWithReverb(
243 QSpan{ channels.data(), m_ambisonicDecoder.nInputChannels() }, reverbBuffers,
247void QResonanceAudioPlayer::processSliceProcessWithoutReverb(QSpan<
float> outputBuffer)
249 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
251 Q_ASSERT(outputBuffer.size() == m_format.channelCount() * framesPerBuffer);
252 namespace ranges = QtMultimediaPrivate::ranges;
254 bool ok = m_resonanceAudio->api->FillInterleavedOutputBuffer(
255 m_format.channelCount(), framesPerBuffer, outputBuffer.data());
260 if (m_sounds.empty()) {
261 ranges::fill(outputBuffer, 0.f);
264 qWarning() <<
" processSliceProcessWithoutReverb failure!";
272QAudioEngineWithPlayer::QAudioEngineWithPlayer(
int sampleRate) : QAudioEnginePrivate(sampleRate)
276QAudioEngineWithPlayer::~QAudioEngineWithPlayer()
279 m_playbackEngine =
nullptr;
282void QAudioEngineWithPlayer::start()
284 QAudioDevice device = m_device;
286 device = QMediaDevices::defaultAudioOutput();
288 if (device.isNull()) {
289 qWarning() <<
"QAudioEngine::start() failed: No audio output device found.";
294 format.setSampleFormat(QAudioFormat::Float);
295 format.setSampleRate(sampleRate());
296 format.setChannelCount(std::min(2, device.maximumChannelCount()));
298 m_playbackEngine = std::make_shared<QRtAudioEngine>(device, format, std::nullopt,
299 QAudioEnginePrivate::framesPerBuffer);
302 auto voice = std::make_shared<QResonanceAudioPlayer>(
this, QRtAudioEngine::allocateVoiceId());
304 for (
auto &[sound, state] : playbackStates)
305 voice->addSound(SourceId{ sound->sourceId }, sound->nchannels, state);
307 m_engineVoice = voice;
308 m_playbackEngine->play(voice);
311void QAudioEngineWithPlayer::stop()
315 m_playbackEngine->stop(m_engineVoice);
319void QAudioEngineWithPlayer::setPaused(
bool paused)
321 if (paused == m_paused)
329 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(),
330 [paused](QResonanceAudioPlayer &player) {
331 player.setPaused(paused);
335bool QAudioEngineWithPlayer::isPaused()
const
340void QAudioEngineWithPlayer::setOutputDevice(
const QAudioDevice &device)
342 if (m_device == device)
345 qWarning() <<
"Changing device on a running engine not implemented";
350 emit q->outputDeviceChanged();
353QAudioDevice QAudioEngineWithPlayer::outputDevice()
const
358void QAudioEngineWithPlayer::addSound(QSpatialAudioSoundPrivate *sound)
360 playbackStates.emplace(sound,
nullptr);
365 m_playbackEngine->visitVoiceRt(
366 m_engineVoice->voiceId(),
367 [id = SourceId{ sound->sourceId },
368 numberOfChannels = sound->nchannels](QResonanceAudioPlayer &player)
mutable {
369 player.addSound(id, numberOfChannels);
373void QAudioEngineWithPlayer::removeSound(QSpatialAudioSoundPrivate *sound)
375 SharedPlaybackState oldState = std::move(playbackStates[sound]);
376 playbackStates.erase(sound);
381 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(),
382 [id = SourceId{ sound->sourceId },
383 oldState = std::move(oldState)](QResonanceAudioPlayer &player) {
384 player.removeSound(id);
388void QAudioEngineWithPlayer::setSoundPlaybackData(QSpatialAudioSoundPrivate *sound,
389 SharedPlaybackState state)
391 auto it = playbackStates.find(sound);
392 Q_ASSERT(it != playbackStates.end());
394 SharedPlaybackState oldState = std::exchange(it->second, state);
400 m_playbackEngine->visitVoiceRt(
401 m_engineVoice->voiceId(),
402 [id = SourceId{ sound->sourceId }, oldState = std::move(oldState),
403 state = std::move(state)](QResonanceAudioPlayer &player)
mutable {
404 player.setSoundPlaybackData(id, std::move(state));
408void QAudioEngineWithPlayer::setOutputMode(QAudioEngine::OutputMode mode)
410 if (outputMode() == mode)
412 QAudioEnginePrivate::setOutputMode(mode);
416 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(), [mode](QResonanceAudioPlayer &player) {
417 player.setOutputMode(mode);
421void QAudioEngineWithPlayer::updateRoomEffects()
423 for (
auto [sound, key] : playbackStates)
424 sound->updateRoomEffects();
~QAudioEngineWithPlayer() override
QResonanceAudioPlayer(QAudioEngineWithPlayer *player, VoiceId id)
VoicePlayResult play(QSpan< float >) noexcept Q_DECL_NONBLOCKING_FUNCTION override
QRtAudioEngineVoice overrides.
bool isActive() noexcept Q_DECL_NONBLOCKING_FUNCTION override
void removeSound(SourceId)
void setOutputMode(QAudioEngine::OutputMode mode)
void setSoundPlaybackData(SourceId, SharedPlaybackState)