void PlayerWidget::mouseReleaseEvent(QMouseEvent *e) { if (_down == OverVolume) { mouseMoveEvent(e); Local::writeUserSettings(); } else if (_down == OverPlayback) { mouseMoveEvent(e); SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; int64 playingPosition = 0, playingDuration = 0; int32 playingFrequency = 0; audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency); if (playing == _song && playingDuration) { _downDuration = playingDuration; audioPlayer()->seek(qRound(_downProgress * _downDuration)); _showPause = true; a_progress = anim::fvalue(_downProgress, _downProgress); _a_progress.stop(); } update(); } else if (_down == OverClose && _over == OverClose) { stopPressed(); } _down = OverNone; }
void PlayerWidget::pausePressed() { if (!_song || isHidden()) return; SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; audioPlayer()->currentState(&playing, &playingState); if (playing == _song && !(playingState & AudioPlayerStoppedMask)) { if (playingState == AudioPlayerStarting || playingState == AudioPlayerResuming || playingState == AudioPlayerPlaying || playingState == AudioPlayerFinishing) { audioPlayer()->pauseresume(OverviewDocuments); } } }
void PlayerWidget::playPausePressed() { if (!_song || isHidden()) return; SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; audioPlayer()->currentState(&playing, &playingState); if (playing == _song && !(playingState & AudioPlayerStoppedMask)) { audioPlayer()->pauseresume(OverviewDocuments); } else { audioPlayer()->play(_song); if (App::main()) App::main()->documentPlayProgress(_song); } }
void PlayerWidget::mousePressEvent(QMouseEvent *e) { QPoint pos(myrtlpoint(e->pos())); if (e->button() == Qt::LeftButton) { _down = OverNone; if (_song && _over == OverPlay) { playPausePressed(); return; } else if (_over == OverPrev) { prevPressed(); } else if (_over == OverNext) { nextPressed(); } else if (_over == OverClose) { _down = OverClose; } else if (_over == OverVolume) { _down = OverVolume; _downCoord = pos.x() - _volumeRect.x(); cSetSongVolume(snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.)); emit audioPlayer()->songVolumeChanged(); rtlupdate(_volumeRect); } else if (_over == OverPlayback) { SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; int64 playingPosition = 0, playingDuration = 0; int32 playingFrequency = 0; audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency); if (playing == _song && playingDuration) { if (playingState == AudioPlayerPlaying || playingState == AudioPlayerStarting || playingState == AudioPlayerResuming) { audioPlayer()->pauseresume(OverviewDocuments); } _down = OverPlayback; _downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.); _downDuration = playingDuration; _downFrequency = (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency); rtlupdate(_playbackRect); updateDownTime(); } } else if (_over == OverFull && _song) { if (HistoryItem *item = App::histItemById(_song.msgId)) { App::main()->showMediaOverview(item->history()->peer, OverviewAudioDocuments); } } else if (_over == OverRepeat) { _repeat = !_repeat; updateOverRect(OverRepeat); } } }
void PlayerWidget::nextPressed() { if (isHidden()) return; const History::MediaOverview *o = _history ? &_history->overview[OverviewAudioDocuments] : 0; if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) { startPlay(FullMsgId(_history->channelId(), o->at(_index + 1))); } }
void PlayerWidget::startPlay(const FullMsgId &msgId) { if (HistoryItem *item = App::histItemById(msgId)) { if (HistoryDocument *doc = static_cast<HistoryDocument*>(item->getMedia())) { audioPlayer()->play(SongMsgId(doc->getDocument(), item->fullId())); updateState(); } } }
void PlayerWidget::nextPressed() { if (isHidden()) return; History *history = _msgmigrated ? _migrated : _history; const History::MediaOverview *o = history ? &history->overview[OverviewAudioDocuments] : 0; if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) { startPlay(FullMsgId(history->channelId(), o->at(_index + 1))); } else if (o && (_index == o->size() - 1) && _msgmigrated && _history->overviewLoaded(OverviewAudioDocuments)) { o = &_history->overview[OverviewAudioDocuments]; if (!o->isEmpty()) { startPlay(FullMsgId(_history->channelId(), o->at(0))); } } }
void PlayerWidget::prevPressed() { if (isHidden()) return; History *history = _msgmigrated ? _migrated : _history; const History::MediaOverview *o = history ? &history->overview[OverviewAudioDocuments] : 0; if (audioPlayer() && o && _index > 0 && _index <= o->size() && !o->isEmpty()) { startPlay(FullMsgId(history->channelId(), o->at(_index - 1))); } else if (!_index && _history && _migrated && !_msgmigrated) { o = &_migrated->overview[OverviewAudioDocuments]; if (!o->isEmpty()) { startPlay(FullMsgId(_migrated->channelId(), o->at(o->size() - 1))); } } }
void AudioPlayerLoaders::onStart(const AudioMsgId &audio, qint64 position) { auto type = audio.type(); clear(type); { QMutexLocker lock(internal::audioPlayerMutex()); AudioPlayer *voice = audioPlayer(); if (!voice) return; auto data = voice->dataForType(type); if (!data) return; data->loading = true; } loadData(audio, position); }
void TitleButton::updatePauseState() { AudioMsgId playing; auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song); auto stopped = ((playbackState.state & AudioPlayerStoppedMask) || playbackState.state == AudioPlayerFinishing); auto showPause = !stopped && (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting); if (exists() && instance()->isSeeking()) { showPause = true; } auto state = [audio = playing.audio(), showPause] { if (audio && audio->loading()) { return State::Cancel; } else if (showPause) { return State::Pause; } return State::Play; }; _layout->setState(state()); }
void AudioPlayerLoaders::onCancel(const AudioMsgId &audio) { switch (audio.type()) { case AudioMsgId::Type::Voice: if (_audio == audio) clear(audio.type()); break; case AudioMsgId::Type::Song: if (_song == audio) clear(audio.type()); break; case AudioMsgId::Type::Video: if (_video == audio) clear(audio.type()); break; } QMutexLocker lock(internal::audioPlayerMutex()); AudioPlayer *voice = audioPlayer(); if (!voice) return; for (int i = 0; i < AudioSimultaneousLimit; ++i) { auto data = voice->dataForType(audio.type(), i); if (data->audio == audio) { data->loading = false; } } }
AudioPlayer::AudioMsg *AudioPlayerLoaders::checkLoader(AudioMsgId::Type type) { AudioPlayer *voice = audioPlayer(); if (!voice) return 0; auto data = voice->dataForType(type); bool isGoodId = false; AudioPlayerLoader *l = nullptr; switch (type) { case AudioMsgId::Type::Voice: l = _audioLoader.get(); isGoodId = (data->audio == _audio); break; case AudioMsgId::Type::Song: l = _songLoader.get(); isGoodId = (data->audio == _song); break; case AudioMsgId::Type::Video: l = _videoLoader.get(); isGoodId = (data->audio == _video); break; } if (!l || !data) return nullptr; if (!isGoodId || !data->loading || !l->check(data->file, data->data)) { LOG(("Audio Error: playing changed while loading")); return nullptr; } return data; }
void PlayerWidget::updateSelected() { QPoint pos(myrtlpoint(mapFromGlobal(_lastMousePos))); if (_down == OverVolume) { int32 delta = (pos.x() - _volumeRect.x()) - _downCoord; float64 startFrom = snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.); float64 add = delta / float64(4 * st::playerVolume.pxWidth()), result = snap(startFrom + add, 0., 1.); if (result != cSongVolume()) { cSetSongVolume(result); emit audioPlayer()->songVolumeChanged(); rtlupdate(_volumeRect); } } else if (_down == OverPlayback) { _downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.); rtlupdate(_playbackRect); updateDownTime(); } else if (_down == OverNone) { bool inInfo = ((pos.x() >= _infoRect.x()) && (pos.x() < _fullRect.x() + _fullRect.width()) && (pos.y() >= _playRect.y()) && (pos.y() <= _playRect.y() + _playRect.height())); if (_prevAvailable && _prevRect.contains(pos)) { updateOverState(OverPrev); } else if (_nextAvailable && _nextRect.contains(pos)) { updateOverState(OverNext); } else if (_playRect.contains(pos)) { updateOverState(OverPlay); } else if (_closeRect.contains(pos)) { updateOverState(OverClose); } else if (_volumeRect.contains(pos)) { updateOverState(OverVolume); } else if (_repeatRect.contains(pos)) { updateOverState(OverRepeat); } else if (_duration && _playbackRect.contains(pos)) { updateOverState(OverPlayback); } else if (_fullAvailable && inInfo) { updateOverState(OverFull); } else if (_over != OverNone) { updateOverState(OverNone); } } }
AudioPlayerLoader *AudioPlayerLoaders::setupLoader(const AudioMsgId &audio, SetupError &err, qint64 &position) { err = SetupErrorAtStart; QMutexLocker lock(internal::audioPlayerMutex()); AudioPlayer *voice = audioPlayer(); if (!voice) return nullptr; auto data = voice->dataForType(audio.type()); if (!data || data->audio != audio || !data->loading) { emit error(audio); LOG(("Audio Error: trying to load part of audio, that is not current at the moment")); err = SetupErrorNotPlaying; return nullptr; } bool isGoodId = false; AudioPlayerLoader *l = nullptr; switch (audio.type()) { case AudioMsgId::Type::Voice: l = _audioLoader.get(); isGoodId = (_audio == audio); break; case AudioMsgId::Type::Song: l = _songLoader.get(); isGoodId = (_song == audio); break; case AudioMsgId::Type::Video: l = _videoLoader.get(); isGoodId = (_video == audio); break; } if (l && (!isGoodId || !l->check(data->file, data->data))) { clear(audio.type()); l = nullptr; } if (!l) { std_::unique_ptr<AudioPlayerLoader> *loader = nullptr; switch (audio.type()) { case AudioMsgId::Type::Voice: _audio = audio; loader = &_audioLoader; break; case AudioMsgId::Type::Song: _song = audio; loader = &_songLoader; break; case AudioMsgId::Type::Video: _video = audio; break; } if (audio.type() == AudioMsgId::Type::Video) { if (!data->videoData) { data->playbackState.state = AudioPlayerStoppedAtError; emit error(audio); LOG(("Audio Error: video sound data not ready")); return nullptr; } _videoLoader = std_::make_unique<ChildFFMpegLoader>(data->videoPlayId, std_::move(data->videoData)); l = _videoLoader.get(); } else { *loader = std_::make_unique<FFMpegLoader>(data->file, data->data); l = loader->get(); } if (!l->open(position)) { data->playbackState.state = AudioPlayerStoppedAtStart; return nullptr; } int64 duration = l->duration(); if (duration <= 0) { data->playbackState.state = AudioPlayerStoppedAtStart; return nullptr; } data->playbackState.duration = duration; data->playbackState.frequency = l->frequency(); if (!data->playbackState.frequency) data->playbackState.frequency = AudioVoiceMsgFrequency; err = SetupNoErrorStarted; } else { if (!data->skipEnd) { err = SetupErrorLoadedFull; LOG(("Audio Error: trying to load part of audio, that is already loaded to the end")); return nullptr; } } return l; }
void PlayerWidget::stopPressed() { if (!_song || isHidden()) return; audioPlayer()->stop(OverviewFiles); }
void PlayerWidget::stopPressed() { if (!_song || isHidden()) return; audioPlayer()->stop(OverviewDocuments); if (App::main()) App::main()->hidePlayer(); }
void PlayerWidget::updateState(SongMsgId playing, AudioPlayerState playingState, int64 playingPosition, int64 playingDuration, int32 playingFrequency) { if (!playing) { audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency); } bool songChanged = false; if (playing && _song != playing) { songChanged = true; _song = playing; if (HistoryItem *item = App::histItemById(_song.msgId)) { _history = item->history(); if (_history->peer->migrateFrom()) { _migrated = App::history(_history->peer->migrateFrom()->id); _msgmigrated = false; } else if (_history->peer->migrateTo()) { _migrated = _history; _history = App::history(_migrated->peer->migrateTo()->id); _msgmigrated = true; } findCurrent(); } else { _history = 0; _msgmigrated = false; _index = -1; } SongData *song = _song.song->song(); if (song->performer.isEmpty()) { _name.setText(st::linkFont, song->title.isEmpty() ? (_song.song->name.isEmpty() ? qsl("Unknown Track") : _song.song->name) : song->title, _textNameOptions); } else { TextCustomTagsMap custom; custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); _name.setRichText(st::linkFont, QString::fromUtf8("[c]%1[/c] \xe2\x80\x93 %2").arg(textRichPrepare(song->performer)).arg(song->title.isEmpty() ? qsl("Unknown Track") : textRichPrepare(song->title)), _textNameOptions, custom); } updateControls(); } qint64 position = 0, duration = 0, display = 0; if (playing == _song) { if (!(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) { display = position = playingPosition; duration = playingDuration; } else { display = playingDuration; } display = display / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency); } else if (_song) { display = _song.song->song()->duration; } bool showPause = false, stopped = ((playingState & AudioPlayerStoppedMask) || playingState == AudioPlayerFinishing); bool wasPlaying = !!_duration; if (!stopped) { showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting); } QString time; float64 progress = 0.; int32 loaded; float64 loadProgress = 1.; if (duration || !_song || !_song.song || !_song.song->loading()) { time = (_down == OverPlayback) ? _time : formatDurationText(display); progress = duration ? snap(float64(position) / duration, 0., 1.) : 0.; loaded = duration ? _song.song->size : 0; } else { loaded = _song.song->loading() ? _song.song->loadOffset() : 0; time = formatDownloadText(loaded, _song.song->size); loadProgress = snap(float64(loaded) / qMax(_song.song->size, 1), 0., 1.); } if (time != _time || showPause != _showPause) { if (_time != time) { _time = time; _timeWidth = st::linkFont->width(_time); } _showPause = showPause; if (duration != _duration || position != _position || loaded != _loaded) { if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) { a_progress.start(progress); a_loadProgress.start(loadProgress); _a_progress.start(); } else { a_progress = anim::fvalue(progress, progress); a_loadProgress = anim::fvalue(loadProgress, loadProgress); _a_progress.stop(); } _position = position; _duration = duration; _loaded = loaded; } update(); } else if (duration != _duration || position != _position || loaded != _loaded) { if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) { a_progress.start(progress); a_loadProgress.start(loadProgress); _a_progress.start(); } else { a_progress = anim::fvalue(progress, progress); a_loadProgress = anim::fvalue(loadProgress, loadProgress); _a_progress.stop(); } _position = position; _duration = duration; _loaded = loaded; } if (wasPlaying && playingState == AudioPlayerStoppedAtEnd) { if (_repeat) { startPlay(_song.msgId); } else { nextPressed(); } } if (songChanged) { emit playerSongChanged(_song.msgId); } }
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); } } }