25QSoundEffectPrivateSynchronous::QSoundEffectPrivateSynchronous(QSoundEffect *q,
26 const QAudioDevice &audioDevice)
27 : q_ptr(q), m_audioDevice(audioDevice)
29 open(QIODevice::ReadOnly);
38qint64 QSoundEffectPrivateSynchronous::readData(
char *data, qint64 len)
40 qCDebug(qLcSoundEffect) <<
this <<
"readData" << len << m_runningCount;
43 if (m_sample->state() != QSample::Ready)
45 if (m_runningCount == 0 || !m_playing)
48 qint64 bytesWritten = 0;
50 const int sampleSize = m_audioBuffer.byteCount();
51 const char *sampleData = m_audioBuffer.constData<
char>();
53 while (len && m_runningCount) {
54 int toWrite = qMax(0, qMin(sampleSize - m_offset, len));
55 memcpy(data, sampleData + m_offset, toWrite);
56 bytesWritten += toWrite;
60 if (m_offset >= sampleSize) {
61 if (m_runningCount > 0)
62 setLoopsRemaining(m_runningCount - 1);
84qint64 QSoundEffectPrivateSynchronous::bytesAvailable()
const
86 if (m_sample->state() != QSample::Ready)
88 if (m_loopCount == QSoundEffect::Infinite)
89 return std::numeric_limits<qint64>::max();
90 return m_runningCount * m_audioBuffer.byteCount() - m_offset;
103void QSoundEffectPrivateSynchronous::setLoopsRemaining(
int loopsRemaining)
105 if (m_runningCount == loopsRemaining)
107 qCDebug(qLcSoundEffect) <<
this <<
"setLoopsRemaining " << loopsRemaining;
108 m_runningCount = loopsRemaining;
109 emit q_ptr->loopsRemainingChanged();
112void QSoundEffectPrivateSynchronous::setStatus(QSoundEffect::Status status)
114 qCDebug(qLcSoundEffect) <<
this <<
"setStatus" << status;
115 if (m_status == status)
117 bool oldLoaded = q_ptr->isLoaded();
119 emit q_ptr->statusChanged();
120 if (oldLoaded != q_ptr->isLoaded())
121 emit q_ptr->loadedChanged();
124void QSoundEffectPrivateSynchronous::setPlaying(
bool playing)
126 qCDebug(qLcSoundEffect) <<
this <<
"setPlaying(" << playing <<
")" << m_playing;
128 m_audioSink->reset();
129 if (playing && !m_sampleReady)
133 if (m_playing == playing)
137 if (m_audioSink && playing) {
138 m_audioSink->start(
this);
140 QtMultimediaPrivate::refreshWarmupClient();
144 emit q_ptr->playingChanged();
147bool QSoundEffectPrivateSynchronous::updateAudioOutput()
149 const auto audioDevice =
150 m_audioDevice.isNull() ? QMediaDevices::defaultAudioOutput() : m_audioDevice;
152 if (audioDevice.isNull()) {
154 qCCritical(qLcSoundEffect) <<
"Failed to update audio output. No audio devices available.";
155 setStatus(QSoundEffect::Error);
159 if (m_audioDevice.isNull()) {
160 q_ptr->setAudioDevice(audioDevice);
167 const auto &sampleFormat = m_sample->format();
168 const auto sampleChannelConfig =
169 sampleFormat.channelConfig() == QAudioFormat::ChannelConfigUnknown
170 ? QAudioFormat::defaultChannelConfigForChannelCount(sampleFormat.channelCount())
171 : sampleFormat.channelConfig();
173 if (sampleChannelConfig != audioDevice.channelConfiguration()
174 && audioDevice.channelConfiguration() != QAudioFormat::ChannelConfigUnknown) {
175 qCDebug(qLcSoundEffect) <<
"Create resampler for channels mapping: config"
176 << sampleFormat.channelConfig() <<
"=> config"
177 << audioDevice.channelConfiguration();
178 auto outputFormat = sampleFormat;
179 outputFormat.setChannelConfig(audioDevice.channelConfiguration());
181 const auto resampler = QPlatformMediaIntegration::instance()->createAudioResampler(
182 m_sample->format(), outputFormat);
184 m_audioBuffer = resampler.value()->resample(m_sample->data().constData(),
185 m_sample->data().size());
187 qCDebug(qLcSoundEffect) <<
"Cannot create resampler for channels mapping";
190 if (!m_audioBuffer.isValid())
191 m_audioBuffer = QAudioBuffer(m_sample->data(), m_sample->format());
193 m_audioSink.reset(
new QAudioSink(audioDevice, m_audioBuffer.format()));
195 QObject::connect(m_audioSink.get(), &QAudioSink::stateChanged,
this,
196 [
this](QAudio::State state) {
197 this->stateChanged(state);
201 m_audioSink->setVolume(m_volume);
203 m_audioSink->setVolume(0);
205 QPlatformAudioSink *sinkPrivate = QPlatformAudioSink::get(*m_audioSink);
206 sinkPrivate->setRole(QtMultimediaPrivate::AudioEndpointRole::SoundEffect);
219void QSoundEffectPrivateSynchronous::sampleReady(SharedSamplePtr sample)
221 if (m_status == QSoundEffect::Error)
224 m_sample = std::move(sample);
226 qCDebug(qLcSoundEffect) <<
this <<
"sampleReady: sample size:" << m_sample->data().size();
228 if (!updateAudioOutput())
232 m_sampleReady =
true;
233 setStatus(QSoundEffect::Ready);
235 if (m_playing && m_audioSink->state() == QAudio::StoppedState) {
236 qCDebug(qLcSoundEffect) <<
this <<
"starting playback on audiooutput";
237 m_audioSink->start(
this);
273bool QSoundEffectPrivateSynchronous::setVolume(
float volume)
275 volume = qBound(0.0f, volume, 1.0f);
276 if (m_volume == volume)
281 if (m_audioSink && !m_muted)
282 m_audioSink->setVolume(volume);
305void QSoundEffectPrivateSynchronous::play()
308 setLoopsRemaining(m_loopCount);
309 qCDebug(qLcSoundEffect) <<
this <<
"play" << m_loopCount << m_runningCount;
310 if (m_status == QSoundEffect::Null || m_status == QSoundEffect::Error) {
311 setStatus(QSoundEffect::Null);
331bool QSoundEffectPrivateSynchronous::setAudioDevice(QAudioDevice device)
333 if (m_audioDevice == device)
336 m_audioDevice = device;
341 bool playing = m_playing;
342 std::chrono::microseconds current_time{ m_audioBuffer.format().durationForBytes(m_offset) };
345 if (updateAudioOutput() && playing) {
347 m_offset = m_audioBuffer.format().bytesForDuration(current_time.count());
353bool QSoundEffectPrivateSynchronous::setSource(
const QUrl &url, QSampleCache &sampleCache)
355 if (m_sampleLoadFuture.isValid())
356 m_sampleLoadFuture.cancel();
359 m_sampleReady =
false;
362 setStatus(QSoundEffect::Null);
366 if (!url.isValid()) {
367 setStatus(QSoundEffect::Error);
371 setStatus(QSoundEffect::Loading);
375 QObject::disconnect(m_audioSink.get(), &QAudioSink::stateChanged,
this,
376 &QSoundEffectPrivateSynchronous::stateChanged);
381 sampleCache.requestSampleFuture(url).then(
this, [
this](SharedSamplePtr result) {
383 sampleReady(std::move(result));