VoiceMessages::VoiceMessages() : _current(0), _fader(new VoiceMessagesFader(&_faderThread)), _loader(new VoiceMessagesLoader(&_loaderThread)) { connect(this, SIGNAL(faderOnTimer()), _fader, SLOT(onTimer())); connect(this, SIGNAL(loaderOnStart(AudioData*)), _loader, SLOT(onStart(AudioData*))); connect(this, SIGNAL(loaderOnCancel(AudioData*)), _loader, SLOT(onCancel(AudioData*))); connect(&_faderThread, SIGNAL(started()), _fader, SLOT(onInit())); connect(&_loaderThread, SIGNAL(started()), _loader, SLOT(onInit())); connect(&_faderThread, SIGNAL(finished()), _fader, SLOT(deleteLater())); connect(&_loaderThread, SIGNAL(finished()), _loader, SLOT(deleteLater())); connect(_loader, SIGNAL(needToCheck()), _fader, SLOT(onTimer())); connect(_loader, SIGNAL(error(AudioData*)), this, SLOT(onError(AudioData*))); connect(_fader, SIGNAL(needToPreload(AudioData*)), _loader, SLOT(onLoad(AudioData*))); connect(_fader, SIGNAL(playPositionUpdated(AudioData*)), this, SIGNAL(updated(AudioData*))); connect(_fader, SIGNAL(audioStopped(AudioData*)), this, SIGNAL(stopped(AudioData*))); connect(_fader, SIGNAL(error(AudioData*)), this, SLOT(onError(AudioData*))); _loaderThread.start(); _faderThread.start(); }
void VoiceMessagesLoader::onLoad(AudioData *audio) { bool started = false; int32 audioindex = -1; Loader *l = 0; Loaders::iterator j = _loaders.end(); { QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (!voice) return; for (int32 i = 0; i < AudioVoiceMsgSimultaneously; ++i) { VoiceMessages::Msg &m(voice->_data[i]); if (m.audio != audio || !m.loading) continue; audioindex = i; j = _loaders.find(audio); if (j != _loaders.end() && (j.value()->fname != m.fname || j.value()->data.size() != m.data.size())) { delete j.value(); _loaders.erase(j); j = _loaders.end(); } if (j == _loaders.end()) { l = (j = _loaders.insert(audio, new Loader())).value(); l->fname = m.fname; l->data = m.data; int ret; if (m.data.isEmpty()) { l->file = op_open_file(m.fname.toUtf8().constData(), &ret); } else { l->file = op_open_memory((const unsigned char*)m.data.constData(), m.data.size(), &ret); } if (!l->file) { LOG(("Audio Error: op_open_file failed for '%1', data size '%2', error code %3").arg(m.fname).arg(m.data.size()).arg(ret)); m.state = VoiceMessageStopped; return loadError(j); } ogg_int64_t duration = op_pcm_total(l->file, -1); if (duration < 0) { LOG(("Audio Error: op_pcm_total failed to get full duration for '%1', data size '%2', error code %3").arg(m.fname).arg(m.data.size()).arg(duration)); m.state = VoiceMessageStopped; return loadError(j); } m.duration = duration; m.skipStart = 0; m.skipEnd = duration; m.position = 0; m.started = 0; started = true; } else { if (!m.skipEnd) continue; l = j.value(); } break; } } if (j == _loaders.end()) { LOG(("Audio Error: trying to load part of audio, that is not playing at the moment")); emit error(audio); return; } if (started) { l->pcm_offset = op_pcm_tell(l->file); l->pcm_print_offset = l->pcm_offset - AudioVoiceMsgFrequency; } bool finished = false; DEBUG_LOG(("Audio Info: reading buffer for file '%1', data size '%2', current pcm_offset %3").arg(l->fname).arg(l->data.size()).arg(l->pcm_offset)); QByteArray result; int64 samplesAdded = 0; while (result.size() < AudioVoiceMsgBufferSize) { opus_int16 pcm[AudioVoiceMsgFrequency * AudioVoiceMsgChannels]; int ret = op_read_stereo(l->file, pcm, sizeof(pcm) / sizeof(*pcm)); if (ret < 0) { { QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (voice) { VoiceMessages::Msg &m(voice->_data[audioindex]); if (m.audio == audio) { m.state = VoiceMessageStopped; } } } LOG(("Audio Error: op_read_stereo failed, error code %1").arg(ret)); return loadError(j); } int li = op_current_link(l->file); if (li != l->prev_li) { const OpusHead *head = op_head(l->file, li); const OpusTags *tags = op_tags(l->file, li); for (int32 ci = 0; ci < tags->comments; ++ci) { const char *comment = tags->user_comments[ci]; if (opus_tagncompare("METADATA_BLOCK_PICTURE", 22, comment) == 0) { OpusPictureTag pic; int err = opus_picture_tag_parse(&pic, comment); if (err >= 0) { opus_picture_tag_clear(&pic); } } } if (!op_seekable(l->file)) { l->pcm_offset = op_pcm_tell(l->file) - ret; } } if (li != l->prev_li || l->pcm_offset >= l->pcm_print_offset + AudioVoiceMsgFrequency) { l->pcm_print_offset = l->pcm_offset; } l->pcm_offset = op_pcm_tell(l->file); if (!ret) { DEBUG_LOG(("Audio Info: read completed")); finished = true; break; } result.append((const char*)pcm, sizeof(*pcm) * ret * AudioVoiceMsgChannels); l->prev_li = li; samplesAdded += ret; { QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (!voice) return; VoiceMessages::Msg &m(voice->_data[audioindex]); if (m.audio != audio || !m.loading || m.fname != l->fname || m.data.size() != l->data.size()) { LOG(("Audio Error: playing changed while loading")); m.state = VoiceMessageStopped; return loadError(j); } } } QMutexLocker lock(&voicemsgsMutex); VoiceMessages *voice = audioVoice(); if (!voice) return; VoiceMessages::Msg &m(voice->_data[audioindex]); if (m.audio != audio || !m.loading || m.fname != l->fname || m.data.size() != l->data.size()) { LOG(("Audio Error: playing changed while loading")); m.state = VoiceMessageStopped; return loadError(j); } if (started) { if (m.source) { alSourceStop(m.source); for (int32 i = 0; i < 3; ++i) { if (m.samplesCount[i]) { alSourceUnqueueBuffers(m.source, 1, m.buffers + i); m.samplesCount[i] = 0; } } m.nextBuffer = 0; } } if (samplesAdded) { if (!m.source) { alGenSources(1, &m.source); alSourcef(m.source, AL_PITCH, 1.f); alSourcef(m.source, AL_GAIN, 1.f); alSource3f(m.source, AL_POSITION, 0, 0, 0); alSource3f(m.source, AL_VELOCITY, 0, 0, 0); alSourcei(m.source, AL_LOOPING, 0); } if (!m.buffers[m.nextBuffer]) alGenBuffers(3, m.buffers); if (!_checkALError()) { m.state = VoiceMessageStopped; return loadError(j); } if (m.samplesCount[m.nextBuffer]) { alSourceUnqueueBuffers(m.source, 1, m.buffers + m.nextBuffer); m.skipStart += m.samplesCount[m.nextBuffer]; } m.samplesCount[m.nextBuffer] = samplesAdded; alBufferData(m.buffers[m.nextBuffer], AL_FORMAT_STEREO16, result.constData(), result.size(), AudioVoiceMsgFrequency); alSourceQueueBuffers(m.source, 1, m.buffers + m.nextBuffer); m.skipEnd -= samplesAdded; m.nextBuffer = (m.nextBuffer + 1) % 3; if (!_checkALError()) { m.state = VoiceMessageStopped; return loadError(j); } } else { finished = true; } if (finished) { m.skipEnd = 0; m.duration = m.skipStart + m.samplesCount[0] + m.samplesCount[1] + m.samplesCount[2]; } m.loading = false; if (m.state == VoiceMessageResuming || m.state == VoiceMessagePlaying || m.state == VoiceMessageStarting) { ALint state = AL_INITIAL; alGetSourcei(m.source, AL_SOURCE_STATE, &state); if (_checkALError()) { if (state != AL_PLAYING) { alSourcePlay(m.source); emit needToCheck(); } } } }
void AudioPlayerLoaders::loadData(AudioMsgId audio, qint64 position) { SetupError err = SetupNoErrorStarted; auto type = audio.type(); AudioPlayerLoader *l = setupLoader(audio, err, position); if (!l) { if (err == SetupErrorAtStart) { emitError(type); } return; } bool started = (err == SetupNoErrorStarted); bool finished = false; bool waiting = false; bool errAtStart = started; QByteArray samples; int64 samplesCount = 0; if (l->holdsSavedDecodedSamples()) { l->takeSavedDecodedSamples(&samples, &samplesCount); } while (samples.size() < AudioVoiceMsgBufferSize) { auto res = l->readMore(samples, samplesCount); using Result = AudioPlayerLoader::ReadResult; if (res == Result::Error) { if (errAtStart) { { QMutexLocker lock(internal::audioPlayerMutex()); AudioPlayer::AudioMsg *m = checkLoader(type); if (m) m->playbackState.state = AudioPlayerStoppedAtStart; } emitError(type); return; } finished = true; break; } else if (res == Result::EndOfFile) { finished = true; break; } else if (res == Result::Ok) { errAtStart = false; } else if (res == Result::Wait) { waiting = (samples.size() < AudioVoiceMsgBufferSize); if (waiting) { l->saveDecodedSamples(&samples, &samplesCount); } break; } QMutexLocker lock(internal::audioPlayerMutex()); if (!checkLoader(type)) { clear(type); return; } } QMutexLocker lock(internal::audioPlayerMutex()); AudioPlayer::AudioMsg *m = checkLoader(type); if (!m) { clear(type); return; } if (started) { if (m->source) { alSourceStop(m->source); for (int32 i = 0; i < 3; ++i) { if (m->samplesCount[i]) { ALuint buffer = 0; alSourceUnqueueBuffers(m->source, 1, &buffer); m->samplesCount[i] = 0; } } m->nextBuffer = 0; } m->skipStart = position; m->skipEnd = m->playbackState.duration - position; m->playbackState.position = position; m->started = 0; } if (samplesCount) { if (!m->source) { alGenSources(1, &m->source); alSourcef(m->source, AL_PITCH, 1.f); alSource3f(m->source, AL_POSITION, 0, 0, 0); alSource3f(m->source, AL_VELOCITY, 0, 0, 0); alSourcei(m->source, AL_LOOPING, 0); } if (!m->buffers[m->nextBuffer]) { alGenBuffers(3, m->buffers); } // If this buffer is queued, try to unqueue some buffer. if (m->samplesCount[m->nextBuffer]) { ALint processed = 0; alGetSourcei(m->source, AL_BUFFERS_PROCESSED, &processed); if (processed < 1) { // No processed buffers, wait. l->saveDecodedSamples(&samples, &samplesCount); return; } // Unqueue some processed buffer. ALuint buffer = 0; alSourceUnqueueBuffers(m->source, 1, &buffer); if (!internal::audioCheckError()) { setStoppedState(m, AudioPlayerStoppedAtError); emitError(type); return; } // Find it in the list and make it the nextBuffer. bool found = false; for (int i = 0; i < 3; ++i) { if (m->buffers[i] == buffer) { found = true; m->nextBuffer = i; break; } } if (!found) { LOG(("Audio Error: Could not find the unqueued buffer! Buffer %1 in source %2 with processed count %3").arg(buffer).arg(m->source).arg(processed)); setStoppedState(m, AudioPlayerStoppedAtError); emitError(type); return; } if (m->samplesCount[m->nextBuffer]) { m->skipStart += m->samplesCount[m->nextBuffer]; m->samplesCount[m->nextBuffer] = 0; } } auto frequency = l->frequency(); auto format = l->format(); m->samplesCount[m->nextBuffer] = samplesCount; alBufferData(m->buffers[m->nextBuffer], format, samples.constData(), samples.size(), frequency); alSourceQueueBuffers(m->source, 1, m->buffers + m->nextBuffer); m->skipEnd -= samplesCount; m->nextBuffer = (m->nextBuffer + 1) % 3; if (!internal::audioCheckError()) { setStoppedState(m, AudioPlayerStoppedAtError); emitError(type); return; } } else { if (waiting) { return; } finished = true; } if (finished) { m->skipEnd = 0; m->playbackState.duration = m->skipStart + m->samplesCount[0] + m->samplesCount[1] + m->samplesCount[2]; clear(type); } m->loading = false; if (m->playbackState.state == AudioPlayerResuming || m->playbackState.state == AudioPlayerPlaying || m->playbackState.state == AudioPlayerStarting) { ALint state = AL_INITIAL; alGetSourcei(m->source, AL_SOURCE_STATE, &state); if (internal::audioCheckError()) { if (state != AL_PLAYING) { audioPlayer()->resumeDevice(); switch (type) { case AudioMsgId::Type::Voice: alSourcef(m->source, AL_GAIN, internal::audioSuppressGain()); break; case AudioMsgId::Type::Song: alSourcef(m->source, AL_GAIN, internal::audioSuppressSongGain() * Global::SongVolume()); break; case AudioMsgId::Type::Video: alSourcef(m->source, AL_GAIN, internal::audioSuppressSongGain() * Global::VideoVolume()); break; } if (!internal::audioCheckError()) { setStoppedState(m, AudioPlayerStoppedAtError); emitError(type); return; } alSourcePlay(m->source); if (!internal::audioCheckError()) { setStoppedState(m, AudioPlayerStoppedAtError); emitError(type); return; } emit needToCheck(); } } else { setStoppedState(m, AudioPlayerStoppedAtError); emitError(type); } } }