6#include <QtCore/qdebug.h>
7#include <QtCore/qfile.h>
8#include <QtCore/qurl.h>
9#include <QtMultimedia/qaudiodecoder.h>
10#include <QtMultimedia/qaudiosink.h>
11#include <QtMultimedia/private/qaudio_qspan_support_p.h>
12#include <QtMultimedia/private/qmultimedia_ranges_p.h>
13#include <QtSpatialAudio/private/qambientsound_p.h>
14#include <QtSpatialAudio/private/qaudioengine_p.h>
16#include <resonance_audio.h>
21QAmbientSoundPrivate::QAmbientSoundPrivate(QAudioEngine *engine,
int nchannels)
22 : nchannels(nchannels), engine(engine)
26QAmbientSoundPrivate::~QAmbientSoundPrivate() =
default;
28void QAmbientSoundPrivate::setVolume(
float volume)
34void QAmbientSoundPrivate::applyVolume()
36 auto *ep = QAudioEnginePrivate::get(engine);
38 ep->resonanceAudio->api->SetSourceVolume(sourceId, m_volume);
41void QAmbientSoundPrivate::loadUrl(
const QUrl &url)
48 QMutexLocker l(&mutex);
55 m_loadFuture.cancel();
57 auto *ep = QAudioEnginePrivate::get(engine);
59 f.setSampleFormat(QAudioFormat::Float);
60 f.setSampleRate(ep->sampleRate());
61 f.setChannelConfig(nchannels == 2 ? QAudioFormat::ChannelConfigStereo
62 : QAudioFormat::ChannelConfigMono);
64 QUrl resolved = m_sourceResolver->resolve(url);
65 m_loadFuture = load(resolved, f).then(q, [
this](QAmbientSoundPrivate::LoadResult result) {
66 QMutexLocker l(&mutex);
68 buffers = std::move(*result);
72 qWarning() <<
"QAmbientSound: cannot load file";
77auto QAmbientSoundPrivate::load(QUrl resolvedUrl, QAudioFormat format) -> QFuture<LoadResult>
79 m_loadFuture.cancelChain();
81 auto promise = std::make_shared<QPromise<LoadResult>>();
82 auto future = promise->future();
84 m_decoder = std::make_unique<QAudioDecoder>();
85 m_decoder->setAudioFormat(format);
87 std::shared_ptr<QIODevice> file;
88 if (resolvedUrl.scheme().compare(u"qrc", Qt::CaseInsensitive) == 0) {
89 file = std::make_unique<QFile>(u':' + resolvedUrl.path());
90 if (!file->open(QFile::ReadOnly)) {
92 promise->addResult(q23::unexpected{ QAudioDecoder::Error::ResourceError });
99 m_decoder->setSourceDevice(file.get());
101 m_decoder->setSource(resolvedUrl);
104 auto accum = std::make_shared<QList<QAudioBuffer>>();
105 QObject::connect(m_decoder.get(), &QAudioDecoder::bufferReady, m_decoder.get(), [accum,
this] {
106 accum->append(m_decoder->read());
111 static_cast<
void (QAudioDecoder::*)(QAudioDecoder::Error)>(&QAudioDecoder::error),
112 m_decoder.get(), [promise, file](QAudioDecoder::Error error) {
114 promise->addResult(q23::unexpected{ error });
118 QObject::connect(m_decoder.get(), &QAudioDecoder::finished, m_decoder.get(),
119 [promise, file, accum] {
121 promise->addResult(std::move(*accum));
129void QAmbientSoundPrivate::getBuffer(QSpan<
float> output,
int nframes,
int channels)
131 Q_ASSERT(channels == nchannels);
132 Q_ASSERT(output.size() == channels * nframes);
134 QMutexLocker l(&mutex);
135 namespace ranges = QtMultimediaPrivate::ranges;
137 if (!m_playing || currentBuffer >= buffers.size()) {
138 ranges::fill(output, 0.f);
140 using QtMultimediaPrivate::drop;
141 using QtMultimediaPrivate::take;
143 int frames = nframes;
145 if (currentBuffer < buffers.size()) {
146 const QAudioBuffer &b = buffers.at(currentBuffer);
147 const float *sourceData = b.constData<
float>() + bufPos * nchannels;
150 int framesToCopy = qMin(b.frameCount() - bufPos, frames);
151 QSpan<
const float> source(sourceData, framesToCopy * nchannels);
152 QSpan<
float> destination = take(output, framesToCopy * nchannels);
153 std::copy(source.begin(), source.end(), destination.begin());
156 output = drop(output, framesToCopy * nchannels);
158 frames -= framesToCopy;
159 bufPos += framesToCopy;
160 Q_ASSERT(bufPos <= b.frameCount());
162 if (bufPos == b.frameCount()) {
168 ranges::fill(output, 0.f);
172 if (currentBuffer == buffers.size()) {
176 if (m_loops > 0 && m_currentLoop >= m_loops) {
181 Q_ASSERT(output.size() == 0);
186
187
188
189
190
191
192
193
194
195
196
199
200
201QAmbientSound::QAmbientSound(QAudioEngine *engine) : QObject(*
new QAmbientSoundPrivate(engine))
205 auto *ep = QAudioEnginePrivate::get(d->engine);
207 ep->addStereoSound(
this);
212QAmbientSound::~QAmbientSound()
216 auto *ep = QAudioEnginePrivate::get(d->engine);
218 ep->removeStereoSound(
this);
222
223
224
225
226
227
228
229void QAmbientSound::setVolume(
float volume)
232 if (volume != d->volume()) {
233 d->setVolume(volume);
234 emit volumeChanged();
238float QAmbientSound::volume()
const
240 Q_D(
const QAmbientSound);
244void QAmbientSound::setSource(
const QUrl &url)
251 emit sourceChanged();
255
256
257
258
259QUrl QAmbientSound::source()
const
261 Q_D(
const QAmbientSound);
265
266
267
268
269
270
271
273
274
275
276
277
278
279
280
281int QAmbientSound::loops()
const
283 Q_D(
const QAmbientSound);
284 return d->m_loops.load(std::memory_order_relaxed);
287void QAmbientSound::setLoops(
int loops)
290 int oldLoops = d->m_loops.exchange(loops, std::memory_order_relaxed);
291 if (oldLoops != loops)
296
297
298
299
300
301
302
303bool QAmbientSound::autoPlay()
const
305 Q_D(
const QAmbientSound);
306 return d->m_autoPlay.load(std::memory_order_relaxed);
309void QAmbientSound::setAutoPlay(
bool autoPlay)
313 bool old = d->m_autoPlay.exchange(autoPlay, std::memory_order_relaxed);
315 emit autoPlayChanged();
319
320
321void QAmbientSound::play()
328
329
330void QAmbientSound::pause()
337
338
339
340void QAmbientSound::stop()
347
348
349QAudioEngine *QAmbientSound::engine()
const
351 Q_D(
const QAmbientSound);
358#include "moc_qambientsound.cpp"