7#include <QtMultimedia/qaudiodecoder.h>
8#include <QtMultimedia/qaudiosink.h>
9#include <QtMultimedia/private/qaudio_qspan_support_p.h>
10#include <QtMultimedia/private/qmultimedia_ranges_p.h>
11#include <QtSpatialAudio/private/qaudioengine_p.h>
12#include <QtCore/qdebug.h>
13#include <QtCore/qfile.h>
14#include <QtCore/qurl.h>
16#include <resonance_audio.h>
30 using QtMultimediaPrivate::drop;
31 using QtMultimediaPrivate::take;
32 namespace ranges = QtMultimediaPrivate::ranges;
34 if (!m_playing.load(std::memory_order_relaxed)) {
35 ranges::fill(output, 0.f);
39 QSpan<
const float> wholeSampleBuffer{
40 m_buffer.constData<
float>(),
41 m_buffer.sampleCount(),
44 while (!output.empty()) {
45 QSpan remainingSamples = drop(wholeSampleBuffer, m_currentSample);
46 const QSpan samplesToCopy = take(remainingSamples, output.size());
47 ranges::copy(samplesToCopy, output.begin());
48 output = drop(output, samplesToCopy.size());
49 m_currentSample += samplesToCopy.size();
55 Q_ASSERT(m_currentSample == wholeSampleBuffer.size());
59 case QAmbientSound::Infinite:
64 ranges::fill(output, 0.f);
68 if (m_currentLoop >= m_loops) {
71 ranges::fill(output, 0.f);
91 return m_buffer.format();
103std::optional<QAudioBuffer> joinBuffers(QSpan<
const QAudioBuffer> buffers)
109 for (
const QAudioBuffer &b : buffers) {
113 if (b.format() != buffers.front().format()) {
114 qWarning() <<
"QAmbientSound: all buffers must have the same format";
118 data.append(b.constData<
char>(), b.byteCount());
123 buffers.front().format(),
124 buffers.front().startTime(),
128int addStereoSource(QAudioEngine *engine)
130 auto *ep = QAudioEnginePrivate::get(engine);
133 return ep->resonanceAudio->api->CreateStereoSource(2);
142 addStereoSource(engine)
,
151 auto *ep = QAudioEnginePrivate::get(engine);
159 auto *ep = QAudioEnginePrivate::get(engine);
161 ep->removeSound(
this);
163 withResonanceApi([&](
vraudio::ResonanceAudioApi *api) {
178 m_playbackState->setLoops(loops);
183 m_autoPlay = enabled;
188 withResonanceApi([&](
vraudio::ResonanceAudioApi *api) {
189 api->SetSourceVolume(
sourceId, m_volume);
199 setState(
std::make_shared<QSpatialAudioPlaybackState>(*m_buffer,
true,
208 m_playbackState->resume();
219 setState(
std::make_shared<QSpatialAudioPlaybackState>(*m_buffer,
false,
231 m_playbackState->pause();
256 auto *ep = QAudioEnginePrivate::get(engine);
262 m_loadFuture.cancel();
267 f.setSampleFormat(QAudioFormat::Float);
268 f.setSampleRate(ep->sampleRate());
269 f.setChannelConfig(nchannels == 2 ? QAudioFormat::ChannelConfigStereo
270 : QAudioFormat::ChannelConfigMono);
272 QUrl resolved = m_sourceResolver->resolve(url);
273 m_loadFuture = load(resolved, f).then(q, [
this](QAmbientSoundPrivate::LoadResult result) {
275 m_buffer = joinBuffers(*result);
277 qWarning() <<
"QAmbientSound: failed to join audio buffers";
281 bool startingPlayback = m_autoPlay || m_state != State::Stopped;
282 if (!startingPlayback)
284 bool startPaused = (m_state == State::Paused) && !m_autoPlay;
285 setState(std::make_shared<QSpatialAudioPlaybackState>(
286 *m_buffer, !startPaused, m_loops));
288 m_state = startPaused ? State::Paused : State::Playing;
291 qWarning() <<
"QAmbientSound: cannot load file";
298 m_loadFuture.cancelChain();
300 auto promise = std::make_shared<QPromise<LoadResult>>();
301 auto future = promise->future();
303 m_decoder = std::make_unique<QAudioDecoder>();
304 m_decoder->setAudioFormat(format);
306 std::shared_ptr<QIODevice> file;
307 if (resolvedUrl.scheme().compare(u"qrc", Qt::CaseInsensitive) == 0) {
308 file = std::make_unique<QFile>(u':' + resolvedUrl.path());
309 if (!file->open(QFile::ReadOnly)) {
311 promise->addResult(q23::unexpected{ QAudioDecoder::Error::ResourceError });
318 m_decoder->setSourceDevice(file.get());
320 m_decoder->setSource(resolvedUrl);
323 auto accum = std::make_shared<QList<QAudioBuffer>>();
324 QObject::connect(m_decoder.get(), &QAudioDecoder::bufferReady, m_decoder.get(), [accum,
this] {
325 accum->append(m_decoder->read());
330 static_cast<
void (QAudioDecoder::*)(QAudioDecoder::Error)>(&QAudioDecoder::error),
331 m_decoder.get(), [promise, file](QAudioDecoder::Error error) {
333 promise->addResult(q23::unexpected{ error });
337 QObject::connect(m_decoder.get(), &QAudioDecoder::finished, m_decoder.get(),
338 [promise, file, accum] {
340 promise->addResult(std::move(*accum));
350 auto *ep = QAudioEnginePrivate::get(engine);
353 return ep->resonanceAudio->api.get();
358 auto *ep = QAudioEnginePrivate::get(engine);
360 ep->setSoundPlaybackData(
this, state);
361 m_playbackState = std::move(state);
365
366
367
368
369
370
371
372
373
374
375
376
377
378
381
382
383
384
385QAmbientSound::QAmbientSound(QAudioEngine *engine) : QObject(*
new QAmbientSoundPrivate(engine))
388 qWarning() <<
"Cannot create QAmbientSound without a valid QAudioEngine";
391QAmbientSound::~QAmbientSound()
394 if (d->state() != QAmbientSoundPrivate::State::Stopped)
399
400
401
402
403
404
405
406void QAmbientSound::setVolume(
float volume)
409 if (volume != d->volume()) {
410 d->setVolume(volume);
411 emit volumeChanged();
415float QAmbientSound::volume()
const
417 Q_D(
const QAmbientSound);
421void QAmbientSound::setSource(
const QUrl &url)
428 emit sourceChanged();
432
433
434
435
436QUrl QAmbientSound::source()
const
438 Q_D(
const QAmbientSound);
442
443
444
445
446
447
448
450
451
452
453
454
455
456
457
458int QAmbientSound::loops()
const
460 Q_D(
const QAmbientSound);
464void QAmbientSound::setLoops(
int loops)
467 if (loops != d->loops()) {
474
475
476
477
478
479
480
481bool QAmbientSound::autoPlay()
const
483 Q_D(
const QAmbientSound);
484 return d->autoPlay();
487void QAmbientSound::setAutoPlay(
bool autoPlay)
490 if (autoPlay != d->autoPlay()) {
491 d->setAutoPlay(autoPlay);
492 emit autoPlayChanged();
497
498
499void QAmbientSound::play()
506
507
508void QAmbientSound::pause()
515
516
517
518void QAmbientSound::stop()
525
526
527QAudioEngine *QAmbientSound::engine()
const
529 Q_D(
const QAmbientSound);
536#include "moc_qambientsound.cpp"
void setVolume(float volume)
void loadUrl(const QUrl &url)
virtual void applyVolume()
QAudioEngine *const engine
QAmbientSoundPrivate(QAudioEngine *engine, int nchannels, int sourceId)
QSpatialAudioPlaybackState(QAudioBuffer buffer, bool playing, int loops)
QAudioFormat format() const
void getBuffer(QSpan< float > output)