6#include <QtMultimedia/qmediadevices.h>
7#include <QtMultimedia/private/qaudiosystem_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 <QtMultimedia/private/qtmm_pmr_p.h>
12#include <QtSpatialAudio/private/qambisonicdecoder_p.h>
13#include <QtSpatialAudio/private/qspatialaudiosound_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 };
85QtMultimediaPrivate::QRtAudioEngineVoice::VoicePlayResult
86QResonanceAudioPlayer::play(QSpan<
float> outputBuffer)
noexcept Q_DECL_NONBLOCKING_FUNCTION
88 using namespace QtMultimediaPrivate;
91 return VoicePlayResult::Playing;
93 const size_t samplesPerSlice = m_format.channelCount() * qToUnderlying(framesPerBuffer);
98 while (!outputBuffer.empty()) {
99 if (m_leftoverBuffer.empty() && size_t(outputBuffer.size()) >= samplesPerSlice) {
102 withTemporaryBuffer(samplesPerSlice *
sizeof(
float), [&](QSpan<std::byte> raw) {
103 QSpan<
float> scratch{
104 reinterpret_cast<
float *>(raw.data()),
105 qsizetype(samplesPerSlice),
107 std::fill(scratch.begin(), scratch.end(), 0.0f);
108 processSlice(scratch);
109 for (size_t i = 0; i != samplesPerSlice; ++i)
110 outputBuffer[i] += scratch[i];
112 outputBuffer = drop(outputBuffer, samplesPerSlice);
116 if (m_leftoverBuffer.empty()) {
117 m_leftoverBuffer.assign(samplesPerSlice, 0.0f);
118 processSlice(QSpan<
float>(m_leftoverBuffer));
121 const auto samplesToMix = std::min<qsizetype>(m_leftoverBuffer.size(), outputBuffer.size());
122 for (qsizetype i = 0; i != samplesToMix; ++i)
123 outputBuffer[i] += m_leftoverBuffer[i];
125 m_leftoverBuffer.erase(m_leftoverBuffer.begin(), m_leftoverBuffer.begin() + samplesToMix);
126 outputBuffer = drop(outputBuffer, samplesToMix);
129 return VoicePlayResult::Playing;
132void QResonanceAudioPlayer::addSound(SourceId id,
int numberOfChannels, SharedPlaybackState state)
134 auto [_, inserted] = m_sounds.insert_or_assign(id,
142void QResonanceAudioPlayer::setSoundPlaybackData(SourceId id, SharedPlaybackState state)
144 auto it = m_sounds.find(id);
145 Q_ASSERT(it != m_sounds.end());
146 it->second.playbackState = std::move(state);
149void QResonanceAudioPlayer::removeSound(SourceId sound)
151 m_sounds.erase(sound);
154void QResonanceAudioPlayer::setPaused(
bool paused)
159void QResonanceAudioPlayer::setOutputMode(QAudioEngine::OutputMode mode)
164void QResonanceAudioPlayer::processSlice(QSpan<
float> hostBuffer)
166 using QtMultimediaPrivate::drop;
167 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
168 const qsizetype samplesPerSlice = m_format.channelCount() * framesPerBuffer;
170 Q_ASSERT(hostBuffer.size() == samplesPerSlice);
173 processSliceFillResonanceBuffers();
176 switch (m_outputMode) {
177 case QAudioEngine::Surround:
178 processSliceProcessSurroundMode(hostBuffer);
181 processSliceProcessWithoutReverb(hostBuffer);
187void QResonanceAudioPlayer::processSliceFillResonanceBuffers()
189 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
190 static constexpr std::array<
float, 2 * framesPerBuffer> nullBuffer{};
191 using QtMultimediaPrivate::take;
192 vraudio::ResonanceAudioApi *api = m_resonanceAudio->api.get();
194 for (
auto &&[sourceId, sourceState] : m_sounds) {
195 int numberOfChannels = sourceState.numberOfChannels;
196 SharedPlaybackState &playbackState = sourceState.playbackState;
198 Q_ASSERT(playbackState->format().channelCount() <= 2);
199 std::array<
float, 2 * framesPerBuffer> buf;
201 playbackState->getBuffer(take(
202 QSpan<
float>{ buf }, playbackState->format().channelCount() * framesPerBuffer));
203 api->SetInterleavedBuffer(qToUnderlying(sourceId), buf.data(), numberOfChannels,
206 api->SetInterleavedBuffer(qToUnderlying(sourceId), nullBuffer.data(), numberOfChannels,
212void QResonanceAudioPlayer::processSliceProcessSurroundMode(QSpan<
float> outputBuffer)
214 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
215 using QtMultimediaPrivate::take;
216 namespace ranges = QtMultimediaPrivate::ranges;
217 Q_ASSERT(outputBuffer.size() == m_format.channelCount() * framesPerBuffer);
219 std::array<
const float *, QAmbisonicDecoder::maxAmbisonicChannels> channels;
220 std::array<
const float *, 2> reverbBuffers{};
221 int nFrames = m_resonanceAudio->getAmbisonicOutput(channels.data(), reverbBuffers.data(),
222 m_ambisonicDecoder.nInputChannels());
228 ranges::fill(outputBuffer, 0);
231 auto nSamples = m_ambisonicDecoder.outputSamples(nFrames);
233 Q_ASSERT(m_ambisonicDecoder.nOutputChannels() <= 8);
234 QSpan currentOutput = take(outputBuffer, nSamples);
235 m_ambisonicDecoder.processBufferWithReverb(
236 QSpan{ channels.data(), m_ambisonicDecoder.nInputChannels() }, reverbBuffers,
240void QResonanceAudioPlayer::processSliceProcessWithoutReverb(QSpan<
float> outputBuffer)
242 constexpr auto framesPerBuffer = qToUnderlying(QAudioEnginePrivate::framesPerBuffer);
244 Q_ASSERT(outputBuffer.size() == m_format.channelCount() * framesPerBuffer);
245 namespace ranges = QtMultimediaPrivate::ranges;
247 bool ok = m_resonanceAudio->api->FillInterleavedOutputBuffer(
248 m_format.channelCount(), framesPerBuffer, outputBuffer.data());
253 if (m_sounds.empty()) {
254 ranges::fill(outputBuffer, 0.f);
257 qWarning() <<
" processSliceProcessWithoutReverb failure!";
265QAudioEngineWithPlayer::QAudioEngineWithPlayer(
int sampleRate) : QAudioEnginePrivate(sampleRate)
269QAudioEngineWithPlayer::~QAudioEngineWithPlayer()
272 m_playbackEngine =
nullptr;
275void QAudioEngineWithPlayer::start()
277 QAudioDevice device = m_device;
279 device = QMediaDevices::defaultAudioOutput();
281 if (device.isNull()) {
282 qWarning() <<
"QAudioEngine::start() failed: No audio output device found.";
287 format.setSampleFormat(QAudioFormat::Float);
288 format.setSampleRate(sampleRate());
289 format.setChannelCount(std::min(2, device.maximumChannelCount()));
291 m_playbackEngine = std::make_shared<QRtAudioEngine>(device, format, std::nullopt,
292 QAudioEnginePrivate::framesPerBuffer);
295 auto voice = std::make_shared<QResonanceAudioPlayer>(
this, QRtAudioEngine::allocateVoiceId());
297 for (
auto &[sound, state] : playbackStates)
298 voice->addSound(SourceId{ sound->sourceId }, sound->nchannels, state);
300 m_engineVoice = voice;
301 m_playbackEngine->play(voice);
304void QAudioEngineWithPlayer::stop()
308 m_playbackEngine->stop(m_engineVoice);
312void QAudioEngineWithPlayer::setPaused(
bool paused)
314 if (paused == m_paused)
322 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(),
323 [paused](QResonanceAudioPlayer &player) {
324 player.setPaused(paused);
328bool QAudioEngineWithPlayer::isPaused()
const
333void QAudioEngineWithPlayer::setOutputDevice(
const QAudioDevice &device)
335 if (m_device == device)
338 qWarning() <<
"Changing device on a running engine not implemented";
343 emit q->outputDeviceChanged();
346QAudioDevice QAudioEngineWithPlayer::outputDevice()
const
351void QAudioEngineWithPlayer::addSound(QSpatialAudioSoundPrivate *sound)
353 playbackStates.emplace(sound,
nullptr);
358 m_playbackEngine->visitVoiceRt(
359 m_engineVoice->voiceId(),
360 [id = SourceId{ sound->sourceId },
361 numberOfChannels = sound->nchannels](QResonanceAudioPlayer &player)
mutable {
362 player.addSound(id, numberOfChannels);
366void QAudioEngineWithPlayer::removeSound(QSpatialAudioSoundPrivate *sound)
368 SharedPlaybackState oldState = std::move(playbackStates[sound]);
369 playbackStates.erase(sound);
374 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(),
375 [id = SourceId{ sound->sourceId },
376 oldState = std::move(oldState)](QResonanceAudioPlayer &player) {
377 player.removeSound(id);
381void QAudioEngineWithPlayer::setSoundPlaybackData(QSpatialAudioSoundPrivate *sound,
382 SharedPlaybackState state)
384 auto it = playbackStates.find(sound);
385 Q_ASSERT(it != playbackStates.end());
387 SharedPlaybackState oldState = std::exchange(it->second, state);
393 m_playbackEngine->visitVoiceRt(
394 m_engineVoice->voiceId(),
395 [id = SourceId{ sound->sourceId }, oldState = std::move(oldState),
396 state = std::move(state)](QResonanceAudioPlayer &player)
mutable {
397 player.setSoundPlaybackData(id, std::move(state));
401void QAudioEngineWithPlayer::setOutputMode(QAudioEngine::OutputMode mode)
403 if (outputMode() == mode)
405 QAudioEnginePrivate::setOutputMode(mode);
409 m_playbackEngine->visitVoiceRt(m_engineVoice->voiceId(), [mode](QResonanceAudioPlayer &player) {
410 player.setOutputMode(mode);
414void QAudioEngineWithPlayer::updateRoomEffects()
416 for (
auto [sound, key] : playbackStates)
417 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)