From 7873cb4373f53fed9424913735d33e693eb0cfe2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 21 May 2017 16:16:39 +0300 Subject: [PATCH] Using media player for voice / video messages. Media::Player::Widget displays the current voice / video message if it is played and the current song otherwise. It is created when a voice / video message starts and is destroyed when all the voice / video messages in the playlist are finished. --- Telegram/Resources/langs/lang.strings | 4 + Telegram/SourceFiles/history/history_item.cpp | 2 +- .../history/history_media_types.cpp | 12 +- .../inline_bot_layout_internal.cpp | 4 +- Telegram/SourceFiles/mainwidget.cpp | 57 ++++--- Telegram/SourceFiles/media/media_audio.cpp | 76 +++++---- Telegram/SourceFiles/media/media_audio.h | 13 +- .../SourceFiles/media/media_clip_ffmpeg.cpp | 18 +- .../SourceFiles/media/media_clip_ffmpeg.h | 2 - .../media/media_clip_implementation.h | 2 - Telegram/SourceFiles/media/media_clip_qtgif.h | 4 - .../SourceFiles/media/media_clip_reader.cpp | 23 ++- .../media/player/media_player_cover.cpp | 32 ++-- .../media/player/media_player_instance.cpp | 28 ++-- .../media/player/media_player_instance.h | 38 +++-- .../media/player/media_player_list.cpp | 4 +- .../media/player/media_player_widget.cpp | 156 ++++++++++++++---- .../media/player/media_player_widget.h | 12 +- .../media/view/media_clip_controller.cpp | 6 +- .../media/view/media_clip_playback.cpp | 6 +- .../SourceFiles/overview/overview_layout.cpp | 4 +- Telegram/SourceFiles/shortcuts.h | 2 +- Telegram/SourceFiles/structs.cpp | 8 +- .../ui/effects/widget_slide_wrap.h | 4 + 24 files changed, 325 insertions(+), 192 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index aca50c4c49..856a43148f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1165,6 +1165,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_call_rate_label" = "Please rate the quality of your call"; "lng_call_rate_comment" = "Comment (optional)"; +"lng_player_message_today" = "Today at {time}"; +"lng_player_message_yesterday" = "Yesterday at {time}"; +"lng_player_message_date" = "{date} at {time}"; + // Not used "lng_topbar_info" = "Info"; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index a0c00fb729..ad178facad 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -997,7 +997,7 @@ void HistoryItem::audioTrackUpdated() { auto audio = reader->audioMsgId(); auto current = Media::Player::mixer()->currentState(audio.type()); - if (current.id != audio || Media::Player::IsStopped(current.state) || current.state == Media::Player::State::Finishing) { + if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) { media->stopInline(); } else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) { if (!reader->videoPaused()) { diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 1fcf6afeb6..5e46415da7 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -1427,7 +1427,7 @@ HistoryTextState HistoryDocument::getState(int x, int y, HistoryStateRequest req auto waveformbottom = st::msgFilePadding.top() - topMinus + st::msgWaveformMax + st::msgWaveformMin; if (x >= nameleft && x < nameleft + namewidth && y >= nametop && y < waveformbottom) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStopped(state.state)) { + if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { if (!voice->seeking()) { voice->setSeekingStart((x - nameleft) / float64(namewidth)); } @@ -1585,7 +1585,7 @@ bool HistoryDocument::updateStatusText() const { statusSize = FileStatusSizeLoaded; if (_data->voice()) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { if (auto voice = Get()) { bool was = (voice->_playback != nullptr); voice->ensurePlayback(this); @@ -1615,7 +1615,7 @@ bool HistoryDocument::updateStatusText() const { } } else if (_data->song()) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { statusSize = -1 - (state.position / state.frequency); realDuration = (state.length / state.frequency); showPause = (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); @@ -2337,10 +2337,10 @@ void HistoryGif::updateStatusText() const { if (state.id == _gif->audioMsgId()) { if (state.length) { auto position = int64(0); - if (!Media::Player::IsStopped(state.state) && state.state != Media::Player::State::Finishing) { - position = state.position; - } else if (state.state == Media::Player::State::StoppedAtEnd) { + if (Media::Player::IsStoppedAtEnd(state.state)) { position = state.length; + } else if (!Media::Player::IsStoppedOrStopping(state.state)) { + position = state.position; } accumulate_max(statusSize, -1 - int((state.length - position) / state.frequency + 1)); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 8e15bdcf68..acd8284803 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -836,7 +836,7 @@ bool File::updateStatusText() const { if (document->voice()) { statusSize = FileStatusSizeLoaded; auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(document, FullMsgId()) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(document, FullMsgId()) && !Media::Player::IsStoppedOrStopping(state.state)) { statusSize = -1 - (state.position / state.frequency); realDuration = (state.length / state.frequency); showPause = (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); @@ -844,7 +844,7 @@ bool File::updateStatusText() const { } else if (document->song()) { statusSize = FileStatusSizeLoaded; auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); - if (state.id == AudioMsgId(document, FullMsgId()) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(document, FullMsgId()) && !Media::Player::IsStoppedOrStopping(state.state)) { statusSize = -1 - (state.position / state.frequency); realDuration = (state.length / state.frequency); showPause = (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 36a00eefae..8f45252c27 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -186,6 +186,14 @@ MainWidget::MainWidget(QWidget *parent, gsl::not_null contr _playerPlaylist->hideFromOther(); } }); + subscribe(Media::Player::instance()->tracksFinishedNotifier(), [this](AudioMsgId::Type type) { + if (type == AudioMsgId::Type::Voice) { + auto songState = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); + if (!songState.id || IsStoppedOrStopping(songState.state)) { + closeBothPlayers(); + } + } + }); subscribe(Adaptive::Changed(), [this]() { handleAdaptiveLayoutUpdate(); }); @@ -1540,14 +1548,9 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) { } } - if (state.id == audioId && audioId.type() == AudioMsgId::Type::Song) { - if (!Media::Player::IsStopped(state.state) && state.state != State::Finishing) { - if (!_playerUsingPanel && !_player) { - createPlayer(); - } - } else if (_player && _player->isHidden() && !_playerUsingPanel) { - _player.destroyDelayed(); - _playerVolume.destroyDelayed(); + if (state.id == audioId && (audioId.type() == AudioMsgId::Type::Song || audioId.type() == AudioMsgId::Type::Voice)) { + if (!Media::Player::IsStoppedOrStopping(state.state)) { + createPlayer(); } } @@ -1604,30 +1607,38 @@ void MainWidget::closeBothPlayers() { Media::Player::instance()->usePanelPlayer().notify(false, true); _playerPanel->hideIgnoringEnterEvents(); _playerPlaylist->hideIgnoringEnterEvents(); + Media::Player::instance()->stop(AudioMsgId::Type::Voice); Media::Player::instance()->stop(AudioMsgId::Type::Song); Shortcuts::disableMediaShortcuts(); } void MainWidget::createPlayer() { - _player.create(this, [this] { playerHeightUpdated(); }); - _player->entity()->setCloseCallback([this] { closeBothPlayers(); }); - _playerVolume.create(this); - _player->entity()->volumeWidgetCreated(_playerVolume); - orderWidgets(); - if (_a_show.animating()) { - _player->showFast(); - _player->hide(); - } else { - _player->hideFast(); - if (_player) { + if (_playerUsingPanel) { + return; + } + if (!_player) { + _player.create(this, [this] { playerHeightUpdated(); }); + _player->entity()->setCloseCallback([this] { closeBothPlayers(); }); + _playerVolume.create(this); + _player->entity()->volumeWidgetCreated(_playerVolume); + orderWidgets(); + if (_a_show.animating()) { + _player->showFast(); + _player->hide(); + Shortcuts::enableMediaShortcuts(); + } else { + _player->hideFast(); + } + } + if (_player && _player->isHiddenOrHiding()) { + if (!_a_show.animating()) { _player->showAnimated(); _playerHeight = _contentScrollAddToY = _player->contentHeight(); updateControlsGeometry(); + Shortcuts::enableMediaShortcuts(); } } - - Shortcuts::enableMediaShortcuts(); } void MainWidget::playerHeightUpdated() { @@ -1638,8 +1649,8 @@ void MainWidget::playerHeightUpdated() { updateControlsGeometry(); } if (!_playerHeight && _player->isHidden()) { - auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); - if (state.id && Media::Player::IsStopped(state.state)) { + auto state = Media::Player::mixer()->currentState(Media::Player::instance()->getActiveType()); + if (!state.id || Media::Player::IsStoppedOrStopping(state.state)) { _playerVolume.destroyDelayed(); _player.destroyDelayed(); } diff --git a/Telegram/SourceFiles/media/media_audio.cpp b/Telegram/SourceFiles/media/media_audio.cpp index 8fcff4c864..485a4bb6eb 100644 --- a/Telegram/SourceFiles/media/media_audio.cpp +++ b/Telegram/SourceFiles/media/media_audio.cpp @@ -596,12 +596,12 @@ bool Mixer::fadedStop(AudioMsgId::Type type, bool *fadedStart) { case State::Starting: case State::Resuming: case State::Playing: { - current->state.state = State::Finishing; + current->state.state = State::Stopping; resetFadeStartPosition(type); if (fadedStart) *fadedStart = true; } break; case State::Pausing: { - current->state.state = State::Finishing; + current->state.state = State::Stopping; if (fadedStart) *fadedStart = true; } break; case State::Paused: @@ -642,7 +642,7 @@ void Mixer::play(const AudioMsgId &audio, std::unique_ptr videoD current->state.state = State::Pausing; resetFadeStartPosition(type); } break; - case State::Finishing: { + case State::Stopping: { current->state.state = State::Pausing; } break; @@ -781,7 +781,7 @@ void Mixer::pause(const AudioMsgId &audio, bool fast) { } } break; - case State::Finishing: { + case State::Stopping: { track->state.state = fast ? State::Paused : State::Pausing; } break; } @@ -874,7 +874,7 @@ void Mixer::seek(AudioMsgId::Type type, int64 position) { auto fastSeek = (position >= current->bufferedPosition && position < current->bufferedPosition + current->bufferedLength - (current->loaded ? 0 : kDefaultFrequency)); if (!streamCreated) { fastSeek = false; - } else if (IsStopped(current->state.state) || (current->state.state == State::Finishing)) { + } else if (IsStoppedOrStopping(current->state.state)) { fastSeek = false; } if (fastSeek) { @@ -908,7 +908,7 @@ void Mixer::seek(AudioMsgId::Type type, int64 position) { emit unsuppressSong(); } } break; - case State::Finishing: + case State::Stopping: case State::Stopped: case State::StoppedAtEnd: case State::StoppedAtError: @@ -940,6 +940,29 @@ void Mixer::stop(const AudioMsgId &audio) { if (current) emit updated(current); } +void Mixer::stop(const AudioMsgId &audio, State state) { + Expects(IsStopped(state)); + + AudioMsgId current; + { + QMutexLocker lock(&AudioMutex); + auto type = audio.type(); + auto track = trackForType(type); + if (!track || track->state.id != audio || IsStopped(track->state.state)) { + return; + } + + current = track->state.id; + setStoppedState(track, state); + if (type == AudioMsgId::Type::Voice) { + emit unsuppressSong(); + } else if (type == AudioMsgId::Type::Video) { + track->clear(); + } + } + if (current) emit updated(current); +} + void Mixer::stopAndClear() { Track *current_audio = nullptr, *current_song = nullptr; { @@ -1012,11 +1035,7 @@ void Mixer::reattachIfNeeded() { auto reattachNeeded = [this] { auto isPlayingState = [](const Track &track) { auto state = track.state.state; - return (state == State::Starting) - || (state == State::Playing) - || (state == State::Finishing) - || (state == State::Pausing) - || (state == State::Resuming); + return (state == State::Playing) || IsFading(state); }; for (auto i = 0; i != kTogetherLimit; ++i) { if (isPlayingState(*trackForType(AudioMsgId::Type::Voice, i)) @@ -1174,7 +1193,7 @@ int32 Fader::updateOnePlayback(Mixer::Track *track, bool &hasPlaying, bool &hasF } switch (track->state.state) { - case State::Finishing: + case State::Stopping: case State::Pausing: case State::Starting: case State::Resuming: { @@ -1186,29 +1205,33 @@ int32 Fader::updateOnePlayback(Mixer::Track *track, bool &hasPlaying, bool &hasF } auto fullPosition = track->bufferedPosition + positionInBuffered; - if (fading && (state == AL_PLAYING || !track->loading)) { - auto fadingForSamplesCount = (fullPosition - track->fadeStartPosition); - - if (state != AL_PLAYING) { + if (state != AL_PLAYING && !track->loading) { + if (fading || playing) { fading = false; - if (track->stream.source) { + playing = false; + if (track->isStreamCreated()) { alSourceStop(track->stream.source); alSourcef(track->stream.source, AL_GAIN, 1); if (errorHappened()) return EmitError; } if (track->state.state == State::Pausing) { track->state.state = State::PausedAtEnd; + } else if (track->state.state == State::Stopping) { + setStoppedState(track, State::Stopped); } else { setStoppedState(track, State::StoppedAtEnd); } emitSignals |= EmitStopped; - } else if (TimeMs(1000) * fadingForSamplesCount >= kFadeDuration * track->state.frequency) { + } + } else if (fading && state == AL_PLAYING) { + auto fadingForSamplesCount = (fullPosition - track->fadeStartPosition); + if (TimeMs(1000) * fadingForSamplesCount >= kFadeDuration * track->state.frequency) { fading = false; alSourcef(track->stream.source, AL_GAIN, 1. * volumeMultiplier); if (errorHappened()) return EmitError; switch (track->state.state) { - case State::Finishing: { + case State::Stopping: { alSourceStop(track->stream.source); if (errorHappened()) return EmitError; @@ -1229,23 +1252,14 @@ int32 Fader::updateOnePlayback(Mixer::Track *track, bool &hasPlaying, bool &hasF } } else { auto newGain = TimeMs(1000) * fadingForSamplesCount / float64(kFadeDuration * track->state.frequency); - if (track->state.state == State::Pausing || track->state.state == State::Finishing) { + if (track->state.state == State::Pausing || track->state.state == State::Stopping) { newGain = 1. - newGain; } alSourcef(track->stream.source, AL_GAIN, newGain * volumeMultiplier); if (errorHappened()) return EmitError; } - } else if (playing && (state == AL_PLAYING || !track->loading)) { - if (state != AL_PLAYING) { - playing = false; - if (track->isStreamCreated()) { - alSourceStop(track->stream.source); - alSourcef(track->stream.source, AL_GAIN, 1); - if (errorHappened()) return EmitError; - } - setStoppedState(track, State::StoppedAtEnd); - emitSignals |= EmitStopped; - } else if (volumeChanged) { + } else if (playing && state == AL_PLAYING) { + if (volumeChanged) { alSourcef(track->stream.source, AL_GAIN, 1. * volumeMultiplier); if (errorHappened()) return EmitError; } diff --git a/Telegram/SourceFiles/media/media_audio.h b/Telegram/SourceFiles/media/media_audio.h index 42b58ed0b4..05c1906364 100644 --- a/Telegram/SourceFiles/media/media_audio.h +++ b/Telegram/SourceFiles/media/media_audio.h @@ -69,7 +69,7 @@ enum class State { Starting = 0x08, Playing = 0x10, - Finishing = 0x18, + Stopping = 0x18, Pausing = 0x20, Paused = 0x28, PausedAtEnd = 0x30, @@ -83,6 +83,14 @@ inline bool IsStopped(State state) { || (state == State::StoppedAtStart); } +inline bool IsStoppedOrStopping(State state) { + return IsStopped(state) || (state == State::Stopping); +} + +inline bool IsStoppedAtEnd(State state) { + return (state == State::StoppedAtEnd); +} + inline bool IsPaused(State state) { return (state == State::Paused) || (state == State::PausedAtEnd); @@ -90,7 +98,7 @@ inline bool IsPaused(State state) { inline bool IsFading(State state) { return (state == State::Starting) - || (state == State::Finishing) + || (state == State::Stopping) || (state == State::Pausing) || (state == State::Resuming); } @@ -119,6 +127,7 @@ public: void resume(const AudioMsgId &audio, bool fast = false); void seek(AudioMsgId::Type type, int64 position); // type == AudioMsgId::Type::Song void stop(const AudioMsgId &audio); + void stop(const AudioMsgId &audio, State state); // Video player audio stream interface. void feedFromVideo(VideoSoundPart &&part); diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp index 3ab0e88082..26002c3db9 100644 --- a/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp +++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.cpp @@ -218,18 +218,6 @@ TimeMs FFMpegReaderImplementation::durationMs() const { return (_fmtContext->streams[_streamId]->duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den; } -void FFMpegReaderImplementation::pauseAudio() { - if (_audioStreamId >= 0) { - Player::mixer()->pause(_audioMsgId, true); - } -} - -void FFMpegReaderImplementation::resumeAudio() { - if (_audioStreamId >= 0) { - Player::mixer()->resume(_audioMsgId, true); - } -} - bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const QSize &size) { Expects(_frameRead); _frameRead = false; @@ -425,7 +413,7 @@ bool FFMpegReaderImplementation::start(Mode mode, TimeMs &positionMs) { positionMs = countPacketMs(&packet); } - if (_audioStreamId >= 0) { + if (hasAudio()) { auto position = (positionMs * soundData->frequency) / 1000LL; Player::mixer()->play(_audioMsgId, std::move(soundData), position); } @@ -480,10 +468,6 @@ QString FFMpegReaderImplementation::logData() const { } FFMpegReaderImplementation::~FFMpegReaderImplementation() { - if (_audioStreamId >= 0) { - Player::mixer()->stop(_audioMsgId); - } - clearPacketQueue(); if (_frameRead) { diff --git a/Telegram/SourceFiles/media/media_clip_ffmpeg.h b/Telegram/SourceFiles/media/media_clip_ffmpeg.h index 1a2e5d8658..459b3dfdd6 100644 --- a/Telegram/SourceFiles/media/media_clip_ffmpeg.h +++ b/Telegram/SourceFiles/media/media_clip_ffmpeg.h @@ -48,8 +48,6 @@ public: bool hasAudio() const override { return (_audioStreamId >= 0); } - void pauseAudio() override; - void resumeAudio() override; bool start(Mode mode, TimeMs &positionMs) override; bool inspectAt(TimeMs &positionMs); diff --git a/Telegram/SourceFiles/media/media_clip_implementation.h b/Telegram/SourceFiles/media/media_clip_implementation.h index 8ae3436b12..43e6d5fc9f 100644 --- a/Telegram/SourceFiles/media/media_clip_implementation.h +++ b/Telegram/SourceFiles/media/media_clip_implementation.h @@ -55,8 +55,6 @@ public: virtual TimeMs durationMs() const = 0; virtual bool hasAudio() const = 0; - virtual void pauseAudio() = 0; - virtual void resumeAudio() = 0; virtual bool start(Mode mode, TimeMs &positionMs) = 0; diff --git a/Telegram/SourceFiles/media/media_clip_qtgif.h b/Telegram/SourceFiles/media/media_clip_qtgif.h index 79243f1fe6..35a8ef6da1 100644 --- a/Telegram/SourceFiles/media/media_clip_qtgif.h +++ b/Telegram/SourceFiles/media/media_clip_qtgif.h @@ -42,10 +42,6 @@ public: bool hasAudio() const override { return false; } - void pauseAudio() override { - } - void resumeAudio() override { - } bool start(Mode mode, TimeMs &positionMs) override; diff --git a/Telegram/SourceFiles/media/media_clip_reader.cpp b/Telegram/SourceFiles/media/media_clip_reader.cpp index 16bd15da6c..0d1beb75be 100644 --- a/Telegram/SourceFiles/media/media_clip_reader.cpp +++ b/Telegram/SourceFiles/media/media_clip_reader.cpp @@ -421,8 +421,8 @@ public: } if (!_started) { _started = true; - if (!_videoPausedAtMs) { - _implementation->resumeAudio(); + if (!_videoPausedAtMs && _hasAudio) { + Player::mixer()->resume(_audioMsgId, true); } } @@ -436,7 +436,7 @@ public: auto frameMs = _seekPositionMs + ms - _animationStarted; auto readResult = _implementation->readFramesTill(frameMs, ms); if (readResult == internal::ReaderImplementation::ReadResult::EndOfFile) { - stop(); + stop(Player::State::StoppedAtEnd); _state = State::Finished; return ProcessResult::Finished; } else if (readResult == internal::ReaderImplementation::ReadResult::Error) { @@ -501,7 +501,9 @@ public: if (_videoPausedAtMs) return; // Paused already. _videoPausedAtMs = ms; - _implementation->pauseAudio(); + if (_hasAudio) { + Player::mixer()->pause(_audioMsgId, true); + } } void resumeVideo(TimeMs ms) { @@ -512,17 +514,22 @@ public: _nextFrameWhen += delta; _videoPausedAtMs = 0; - _implementation->resumeAudio(); + if (_hasAudio) { + Player::mixer()->resume(_audioMsgId, true); + } } ProcessResult error() { - stop(); + stop(Player::State::StoppedAtError); _state = State::Error; return ProcessResult::Error; } - void stop() { + void stop(Player::State audioState) { _implementation = nullptr; + if (_hasAudio) { + Player::mixer()->stop(_audioMsgId, audioState); + } if (_location) { if (_accessed) { @@ -534,7 +541,7 @@ public: } ~ReaderPrivate() { - stop(); + stop(Player::State::Stopped); _data.clear(); } diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index f184426886..7a1487697a 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -117,17 +117,25 @@ CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent) Global::RefSongVolumeChanged().notify(); }); subscribe(Global::RefSongVolumeChanged(), [this] { updateVolumeToggleIcon(); }); - subscribe(instance()->repeatChangedNotifier(), [this] { - updateRepeatTrackIcon(); + subscribe(instance()->repeatChangedNotifier(), [this](AudioMsgId::Type type) { + if (type == AudioMsgId::Type::Song) { + updateRepeatTrackIcon(); + } }); - subscribe(instance()->playlistChangedNotifier(), [this] { - handlePlaylistUpdate(); + subscribe(instance()->playlistChangedNotifier(), [this](AudioMsgId::Type type) { + if (type == AudioMsgId::Type::Song) { + handlePlaylistUpdate(); + } }); subscribe(instance()->updatedNotifier(), [this](const TrackState &state) { - handleSongUpdate(state); + if (state.id.type() == AudioMsgId::Type::Song) { + handleSongUpdate(state); + } }); - subscribe(instance()->songChangedNotifier(), [this] { - handleSongChange(); + subscribe(instance()->trackChangedNotifier(), [this](AudioMsgId::Type type) { + if (type == AudioMsgId::Type::Song) { + handleSongChange(); + } }); handleSongChange(); @@ -246,7 +254,7 @@ void CoverWidget::handleSongUpdate(const TrackState &state) { _playback->updateState(state); } - auto stopped = (IsStopped(state.state) || state.state == State::Finishing); + auto stopped = IsStoppedOrStopping(state.state); auto showPause = !stopped && (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); if (instance()->isSeeking(AudioMsgId::Type::Song)) { showPause = true; @@ -268,7 +276,7 @@ void CoverWidget::updateTimeText(const TrackState &state) { QString time; qint64 position = 0, length = 0, display = 0; auto frequency = state.frequency; - if (!IsStopped(state.state) && state.state != State::Finishing) { + if (!IsStoppedOrStopping(state.state)) { display = position = state.position; length = state.length; } else { @@ -305,7 +313,7 @@ void CoverWidget::updateTimeLabel() { } void CoverWidget::handleSongChange() { - auto ¤t = instance()->current(AudioMsgId::Type::Song); + auto current = instance()->current(AudioMsgId::Type::Song); auto song = current.audio()->song(); if (!song) { return; @@ -325,8 +333,8 @@ void CoverWidget::handleSongChange() { } void CoverWidget::handlePlaylistUpdate() { - auto ¤t = instance()->current(AudioMsgId::Type::Song); - auto &playlist = instance()->playlist(AudioMsgId::Type::Song); + auto current = instance()->current(AudioMsgId::Type::Song); + auto playlist = instance()->playlist(AudioMsgId::Type::Song); auto index = playlist.indexOf(current.contextId()); if (!current || index < 0) { destroyPrevNextButtons(); diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 85a4304f0e..a00f499371 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -86,7 +86,7 @@ AudioMsgId::Type Instance::getActiveType() const { auto voiceData = getData(AudioMsgId::Type::Voice); if (voiceData->current) { auto state = mixer()->currentState(voiceData->type); - if (IsActive(state.state)) { + if (voiceData->current == state.id && !IsStoppedOrStopping(state.state)) { return voiceData->type; } } @@ -136,7 +136,7 @@ void Instance::setCurrent(const AudioMsgId &audioId) { data->history = nullptr; data->migrated = nullptr; } - _songChangedNotifier.notify(true); + _trackChangedNotifier.notify(data->type, true); if (data->history != history || data->migrated != migrated) { rebuildPlaylist(data); } @@ -163,25 +163,27 @@ void Instance::rebuildPlaylist(Data *data) { data->playlist.push_back(FullMsgId(data->history->channelId(), msgId)); } } - _playlistChangedNotifier.notify(true); + _playlistChangedNotifier.notify(data->type, true); } -void Instance::moveInPlaylist(Data *data, int delta) { +bool Instance::moveInPlaylist(Data *data, int delta) { Expects(data != nullptr); auto index = data->playlist.indexOf(data->current.contextId()); auto newIndex = index + delta; if (!data->current || index < 0 || newIndex < 0 || newIndex >= data->playlist.size()) { rebuildPlaylist(data); - return; + return false; } auto msgId = data->playlist[newIndex]; if (auto item = App::histItemById(msgId)) { if (auto media = item->getMedia()) { media->playInline(); + return true; } } + return false; } Instance *instance() { @@ -254,16 +256,18 @@ void Instance::playPause(AudioMsgId::Type type) { } } -void Instance::next(AudioMsgId::Type type) { +bool Instance::next(AudioMsgId::Type type) { if (auto data = getData(type)) { - moveInPlaylist(data, 1); + return moveInPlaylist(data, 1); } + return false; } -void Instance::previous(AudioMsgId::Type type) { +bool Instance::previous(AudioMsgId::Type type) { if (auto data = getData(type)) { - moveInPlaylist(data, -1); + return moveInPlaylist(data, -1); } + return false; } void Instance::playPauseCancelClicked(AudioMsgId::Type type) { @@ -272,7 +276,7 @@ void Instance::playPauseCancelClicked(AudioMsgId::Type type) { } auto state = mixer()->currentState(type); - auto stopped = (IsStopped(state.state) || state.state == State::Finishing); + auto stopped = IsStoppedOrStopping(state.state); auto showPause = !stopped && (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); auto audio = state.id.audio(); if (audio && audio->loading()) { @@ -319,8 +323,8 @@ void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) { if (data->isPlaying && state.state == State::StoppedAtEnd) { if (data->repeatEnabled) { play(data->current); - } else { - next(type); + } else if (!next(type)) { + _tracksFinishedNotifier.notify(type); } } auto isPlaying = !IsStopped(state.state); diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 650da26b8f..8771f5dc79 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -42,8 +42,10 @@ public: void pause(AudioMsgId::Type type); void stop(AudioMsgId::Type type); void playPause(AudioMsgId::Type type); - void next(AudioMsgId::Type type); - void previous(AudioMsgId::Type type); + bool next(AudioMsgId::Type type); + bool previous(AudioMsgId::Type type); + + AudioMsgId::Type getActiveType() const; void play() { play(getActiveType()); @@ -57,11 +59,11 @@ public: void playPause() { playPause(getActiveType()); } - void next() { - next(getActiveType()); + bool next() { + return next(getActiveType()); } - void previous() { - previous(getActiveType()); + bool previous() { + return previous(getActiveType()); } void playPauseCancelClicked(AudioMsgId::Type type); @@ -83,7 +85,7 @@ public: void toggleRepeat(AudioMsgId::Type type) { if (auto data = getData(type)) { data->repeatEnabled = !data->repeatEnabled; - _repeatChangedNotifier.notify(); + _repeatChangedNotifier.notify(type); } } @@ -115,13 +117,16 @@ public: base::Observable &updatedNotifier() { return _updatedNotifier; } - base::Observable &playlistChangedNotifier() { + base::Observable &tracksFinishedNotifier() { + return _tracksFinishedNotifier; + } + base::Observable &playlistChangedNotifier() { return _playlistChangedNotifier; } - base::Observable &songChangedNotifier() { - return _songChangedNotifier; + base::Observable &trackChangedNotifier() { + return _trackChangedNotifier; } - base::Observable &repeatChangedNotifier() { + base::Observable &repeatChangedNotifier() { return _repeatChangedNotifier; } @@ -148,8 +153,6 @@ private: bool isPlaying = false; }; - AudioMsgId::Type getActiveType() const; - // Observed notifications. void notifyPeerUpdated(const Notify::PeerUpdate &update); void handleSongUpdate(const AudioMsgId &audioId); @@ -157,7 +160,7 @@ private: void checkPeerUpdate(AudioMsgId::Type type, const Notify::PeerUpdate &update); void setCurrent(const AudioMsgId &audioId); void rebuildPlaylist(Data *data); - void moveInPlaylist(Data *data, int delta); + bool moveInPlaylist(Data *data, int delta); void preloadNext(Data *data); void handleLogout(); @@ -189,9 +192,10 @@ private: base::Observable _titleButtonOver; base::Observable _playerWidgetOver; base::Observable _updatedNotifier; - base::Observable _playlistChangedNotifier; - base::Observable _songChangedNotifier; - base::Observable _repeatChangedNotifier; + base::Observable _tracksFinishedNotifier; + base::Observable _playlistChangedNotifier; + base::Observable _trackChangedNotifier; + base::Observable _repeatChangedNotifier; }; diff --git a/Telegram/SourceFiles/media/player/media_player_list.cpp b/Telegram/SourceFiles/media/player/media_player_list.cpp index 3bfcc5add9..106a1525ab 100644 --- a/Telegram/SourceFiles/media/player/media_player_list.cpp +++ b/Telegram/SourceFiles/media/player/media_player_list.cpp @@ -30,7 +30,7 @@ namespace Player { ListWidget::ListWidget(QWidget *parent) : TWidget(parent) { setMouseTracking(true); playlistUpdated(); - subscribe(instance()->playlistChangedNotifier(), [this] { playlistUpdated(); }); + subscribe(instance()->playlistChangedNotifier(), [this](AudioMsgId::Type type) { playlistUpdated(); }); subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { itemRemoved(item); }); @@ -183,7 +183,7 @@ int ListWidget::marginTop() const { void ListWidget::playlistUpdated() { auto newHeight = 0; - auto &playlist = instance()->playlist(AudioMsgId::Type::Song); + auto playlist = instance()->playlist(AudioMsgId::Type::Song); auto playlistSize = playlist.size(); auto existingSize = _list.size(); if (playlistSize > existingSize) { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index b63655d509..73f279ccfa 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" #include "ui/effects/ripple_animation.h" +#include "lang.h" #include "media/media_audio.h" #include "media/view/media_clip_playback.h" #include "media/player/media_player_button.h" @@ -107,15 +108,21 @@ Widget::Widget(QWidget *parent) : TWidget(parent) _playbackSlider->setValue(value); }); _playbackSlider->setChangeProgressCallback([this](float64 value) { + if (_type != AudioMsgId::Type::Song) { + return; // Round video seek is not supported for now :( + } handleSeekProgress(value); _playback->setValue(value, false); }); _playbackSlider->setChangeFinishedCallback([this](float64 value) { + if (_type != AudioMsgId::Type::Song) { + return; // Round video seek is not supported for now :( + } handleSeekFinished(value); _playback->setValue(value, false); }); _playPause->setClickedCallback([this] { - instance()->playPauseCancelClicked(AudioMsgId::Type::Song); + instance()->playPauseCancelClicked(_type); }); updateVolumeToggleIcon(); @@ -131,23 +138,35 @@ Widget::Widget(QWidget *parent) : TWidget(parent) instance()->toggleRepeat(AudioMsgId::Type::Song); }); - subscribe(instance()->repeatChangedNotifier(), [this] { - updateRepeatTrackIcon(); - }); - subscribe(instance()->playlistChangedNotifier(), [this] { - handlePlaylistUpdate(); - }); - subscribe(instance()->updatedNotifier(), [this](const TrackState &state) { - if (state.id.type() == AudioMsgId::Type::Song) { - handleSongUpdate(state); + subscribe(instance()->repeatChangedNotifier(), [this](AudioMsgId::Type type) { + if (type == _type) { + updateRepeatTrackIcon(); } }); - subscribe(instance()->songChangedNotifier(), [this] { - handleSongChange(); + subscribe(instance()->playlistChangedNotifier(), [this](AudioMsgId::Type type) { + if (type == _type) { + handlePlaylistUpdate(); + } }); - handleSongChange(); - - handleSongUpdate(mixer()->currentState(AudioMsgId::Type::Song)); + subscribe(instance()->updatedNotifier(), [this](const TrackState &state) { + handleSongUpdate(state); + }); + subscribe(instance()->trackChangedNotifier(), [this](AudioMsgId::Type type) { + if (type == _type) { + handleSongChange(); + } + }); + subscribe(instance()->tracksFinishedNotifier(), [this](AudioMsgId::Type type) { + if (type == AudioMsgId::Type::Voice) { + _voiceIsActive = false; + auto currentSong = instance()->current(AudioMsgId::Type::Song); + auto songState = mixer()->currentState(AudioMsgId::Type::Song); + if (currentSong == songState.id && !IsStoppedOrStopping(songState.state)) { + setType(AudioMsgId::Type::Song); + } + } + }); + setType(AudioMsgId::Type::Song); _playPause->finishTransform(); } @@ -167,8 +186,19 @@ void Widget::updateVolumeToggleIcon() { _volumeToggle->setIconOverride(icon()); } -void Widget::setCloseCallback(CloseCallback &&callback) { - _close->setClickedCallback(std::move(callback)); +void Widget::setCloseCallback(base::lambda callback) { + _close->setClickedCallback([this, callback = std::move(callback)] { + _voiceIsActive = false; + if (_type == AudioMsgId::Type::Voice) { + auto songData = instance()->current(AudioMsgId::Type::Song); + auto songState = mixer()->currentState(AudioMsgId::Type::Song); + if (songData == songState.id && !IsStoppedOrStopping(songState.state)) { + instance()->stop(AudioMsgId::Type::Voice); + return; + } + } + callback(); + }); } void Widget::setShadowGeometryToLeft(int x, int y, int w, int h) { @@ -206,7 +236,7 @@ void Widget::handleSeekProgress(float64 progress) { _seekPositionMs = positionMs; updateTimeLabel(); - instance()->startSeeking(AudioMsgId::Type::Song); + instance()->startSeeking(_type); } } @@ -216,13 +246,12 @@ void Widget::handleSeekFinished(float64 progress) { auto positionMs = snap(static_cast(progress * _lastDurationMs), 0LL, _lastDurationMs); _seekPositionMs = -1; - auto type = AudioMsgId::Type::Song; - auto state = mixer()->currentState(type); + auto state = mixer()->currentState(_type); if (state.id && state.length) { - mixer()->seek(type, qRound(progress * state.length)); + mixer()->seek(_type, qRound(progress * state.length)); } - instance()->stopSeeking(AudioMsgId::Type::Song); + instance()->stopSeeking(_type); } void Widget::resizeEvent(QResizeEvent *e) { @@ -287,7 +316,10 @@ int Widget::getLabelsLeft() const { } int Widget::getLabelsRight() const { - auto result = st::mediaPlayerCloseRight + _close->width() + _repeatTrack->width() + _volumeToggle->width(); + auto result = st::mediaPlayerCloseRight + _close->width(); + if (_type == AudioMsgId::Type::Song) { + result += _repeatTrack->width() + _volumeToggle->width(); + } result += st::mediaPlayerPadding; return result; } @@ -310,8 +342,35 @@ void Widget::updateRepeatTrackIcon() { _repeatTrack->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); } +void Widget::checkForTypeChange() { + auto hasActiveType = [](AudioMsgId::Type type) { + auto current = instance()->current(type); + auto state = mixer()->currentState(type); + return (current == state.id && !IsStoppedOrStopping(state.state)); + }; + if (hasActiveType(AudioMsgId::Type::Voice)) { + _voiceIsActive = true; + setType(AudioMsgId::Type::Voice); + } else if (!_voiceIsActive && hasActiveType(AudioMsgId::Type::Song)) { + setType(AudioMsgId::Type::Song); + } +} + +void Widget::setType(AudioMsgId::Type type) { + if (_type != type) { + _type = type; + _repeatTrack->setVisible(_type == AudioMsgId::Type::Song); + _volumeToggle->setVisible(_type == AudioMsgId::Type::Song); + _playbackSlider->setVisible(_type == AudioMsgId::Type::Song); + updateLabelsGeometry(); + handleSongChange(); + handleSongUpdate(mixer()->currentState(_type)); + } +} + void Widget::handleSongUpdate(const TrackState &state) { - if (!state.id.audio()) { + checkForTypeChange(); + if (state.id.type() != _type || !state.id.audio()) { return; } @@ -321,9 +380,9 @@ void Widget::handleSongUpdate(const TrackState &state) { _playback->updateState(state); } - auto stopped = (IsStopped(state.state) || state.state == State::Finishing); + auto stopped = IsStoppedOrStopping(state.state); auto showPause = !stopped && (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); - if (instance()->isSeeking(AudioMsgId::Type::Song)) { + if (instance()->isSeeking(_type)) { showPause = true; } auto buttonState = [audio = state.id.audio(), showPause] { @@ -343,7 +402,7 @@ void Widget::updateTimeText(const TrackState &state) { QString time; qint64 position = 0, length = 0, display = 0; auto frequency = state.frequency; - if (!IsStopped(state.state) && state.state != State::Finishing) { + if (!IsStoppedOrStopping(state.state)) { display = position = state.position; length = state.length; } else if (state.length) { @@ -381,16 +440,41 @@ void Widget::updateTimeLabel() { } void Widget::handleSongChange() { - auto ¤t = instance()->current(AudioMsgId::Type::Song); - auto song = current.audio()->song(); + auto current = instance()->current(_type); + if (!current || !current.audio()) { + return; + } TextWithEntities textWithEntities; - if (!song || song->performer.isEmpty()) { - textWithEntities.text = (!song || song->title.isEmpty()) ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title; + if (current.audio()->voice() || current.audio()->isRoundVideo()) { + if (auto item = App::histItemById(current.contextId())) { + auto name = App::peerName(item->fromOriginal()); + auto date = [item] { + auto date = item->date.date(); + auto time = item->date.time().toString(cTimeFormat()); + auto today = QDateTime::currentDateTime().date(); + if (date == today) { + return lng_player_message_today(lt_time, time); + } else if (date.addDays(1) == today) { + return lng_player_message_yesterday(lt_time, time); + } + return lng_player_message_date(lt_date, langDayOfMonthFull(date), lt_time, time); + }; + + textWithEntities.text = name + ' ' + date(); + textWithEntities.entities.append({ EntityInTextBold, 0, name.size(), QString() }); + } else { + textWithEntities.text = lang(lng_media_audio); + } } else { - auto title = song->title.isEmpty() ? qsl("Unknown Track") : textClean(song->title); - textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title; - textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() }); + auto song = current.audio()->song(); + if (!song || song->performer.isEmpty()) { + textWithEntities.text = (!song || song->title.isEmpty()) ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title; + } else { + auto title = song->title.isEmpty() ? qsl("Unknown Track") : textClean(song->title); + textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title; + textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() }); + } } _nameLabel->setMarkedText(textWithEntities); @@ -398,8 +482,8 @@ void Widget::handleSongChange() { } void Widget::handlePlaylistUpdate() { - auto ¤t = instance()->current(AudioMsgId::Type::Song); - auto &playlist = instance()->playlist(AudioMsgId::Type::Song); + auto current = instance()->current(_type); + auto playlist = instance()->playlist(_type); auto index = playlist.indexOf(current.contextId()); if (!current || index < 0) { destroyPrevNextButtons(); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 0bc8ae4fc3..94df727510 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -45,8 +45,7 @@ class Widget : public TWidget, private base::Subscriber { public: Widget(QWidget *parent); - using CloseCallback = base::lambda; - void setCloseCallback(CloseCallback &&callback); + void setCloseCallback(base::lambda callback); void setShadowGeometryToLeft(int x, int y, int w, int h); void showShadow(); @@ -81,6 +80,8 @@ private: void updateVolumeToggleIcon(); + void checkForTypeChange(); + void setType(AudioMsgId::Type type); void handleSongUpdate(const TrackState &state); void handleSongChange(); void handlePlaylistUpdate(); @@ -92,6 +93,13 @@ private: TimeMs _lastDurationMs = 0; QString _time; + // We display all the controls according to _type. + // We switch to Type::Voice if a voice/video message is played. + // We switch to Type::Song only if _voiceIsActive == false. + // We change _voiceIsActive to false only manually or from tracksFinished(). + AudioMsgId::Type _type = AudioMsgId::Type::Unknown; + bool _voiceIsActive = false; + class PlayButton; object_ptr _nameLabel; object_ptr _timeLabel; diff --git a/Telegram/SourceFiles/media/view/media_clip_controller.cpp b/Telegram/SourceFiles/media/view/media_clip_controller.cpp index d33742894c..edf7b15a0d 100644 --- a/Telegram/SourceFiles/media/view/media_clip_controller.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_controller.cpp @@ -133,10 +133,10 @@ void Controller::updatePlayPauseResumeState(const Player::TrackState &state) { void Controller::updateTimeTexts(const Player::TrackState &state) { qint64 position = 0, length = state.length; - if (!Player::IsStopped(state.state) && state.state != Player::State::Finishing) { - position = state.position; - } else if (state.state == Player::State::StoppedAtEnd) { + if (Player::IsStoppedAtEnd(state.state)) { position = state.length; + } else if (!Player::IsStoppedOrStopping(state.state)) { + position = state.position; } else { position = 0; } diff --git a/Telegram/SourceFiles/media/view/media_clip_playback.cpp b/Telegram/SourceFiles/media/view/media_clip_playback.cpp index f6927a6ff8..173ad568c7 100644 --- a/Telegram/SourceFiles/media/view/media_clip_playback.cpp +++ b/Telegram/SourceFiles/media/view/media_clip_playback.cpp @@ -46,10 +46,10 @@ void Playback::updateState(const Player::TrackState &state) { } _playing = !Player::IsStopped(state.state); - if (_playing || state.state == Player::State::Stopped) { - position = state.position; - } else if (state.state == Player::State::StoppedAtEnd) { + if (Player::IsStoppedAtEnd(state.state)) { position = state.length; + } else if (!Player::IsStoppedOrStopping(state.state)) { + position = state.position; } else { position = 0; } diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index a0d2fef190..e9e1837694 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -666,7 +666,7 @@ bool Voice::updateStatusText() { statusSize = FileStatusSizeLoaded; using State = Media::Player::State; auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { statusSize = -1 - (state.position / state.frequency); realDuration = (state.length / state.frequency); showPause = (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); @@ -950,7 +950,7 @@ bool Document::updateStatusText() { statusSize = FileStatusSizeLoaded; using State = Media::Player::State; auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); - if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(_data, _parent->fullId()) && !Media::Player::IsStoppedOrStopping(state.state)) { statusSize = -1 - (state.position / state.frequency); realDuration = (state.length / state.frequency); showPause = (state.state == State::Playing || state.state == State::Resuming || state.state == State::Starting); diff --git a/Telegram/SourceFiles/shortcuts.h b/Telegram/SourceFiles/shortcuts.h index ae1b3234aa..3cd371e14d 100644 --- a/Telegram/SourceFiles/shortcuts.h +++ b/Telegram/SourceFiles/shortcuts.h @@ -36,4 +36,4 @@ void disableMediaShortcuts(); void finish(); -} +} // namespace Shortcuts diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index a6cba1b749..ab002789e6 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -1202,7 +1202,7 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, using State = Media::Player::State; if (playVoice) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(data, msgId) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(data, msgId) && !Media::Player::IsStoppedOrStopping(state.state)) { if (Media::Player::IsPaused(state.state) || state.state == State::Pausing) { Media::Player::mixer()->resume(state.id); } else { @@ -1218,7 +1218,7 @@ void DocumentOpenClickHandler::doOpen(DocumentData *data, HistoryItem *context, } } else if (playMusic) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); - if (state.id == AudioMsgId(data, msgId) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(data, msgId) && !Media::Player::IsStoppedOrStopping(state.state)) { if (Media::Player::IsPaused(state.state) || state.state == State::Pausing) { Media::Player::mixer()->resume(state.id); } else { @@ -1510,7 +1510,7 @@ void DocumentData::performActionOnLoad() { if (playVoice) { if (loaded()) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice); - if (state.id == AudioMsgId(this, _actionOnLoadMsgId) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(this, _actionOnLoadMsgId) && !Media::Player::IsStoppedOrStopping(state.state)) { if (Media::Player::IsPaused(state.state) || state.state == State::Pausing) { Media::Player::mixer()->resume(state.id); } else { @@ -1524,7 +1524,7 @@ void DocumentData::performActionOnLoad() { } else if (playMusic) { if (loaded()) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); - if (state.id == AudioMsgId(this, _actionOnLoadMsgId) && !Media::Player::IsStopped(state.state) && state.state != State::Finishing) { + if (state.id == AudioMsgId(this, _actionOnLoadMsgId) && !Media::Player::IsStoppedOrStopping(state.state)) { if (Media::Player::IsPaused(state.state) || state.state == State::Pausing) { Media::Player::mixer()->resume(state.id); } else { diff --git a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h index 8eed9bf3e5..68ed3267cf 100644 --- a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h +++ b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h @@ -53,6 +53,10 @@ public: } void toggleFast(bool visible); + bool isHiddenOrHiding() const { + return isHidden() || (_a_height.animating() && _hiding); + } + void finishAnimation() { _a_height.finish(); myEnsureResized(_entity);