| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #pragma once
- #include "ui/effects/animation_value.h"
- #include "core/file_location.h"
- #include "data/data_audio_msg_id.h"
- #include "base/bytes.h"
- #include "base/timer.h"
- #include <QtCore/QTimer>
- namespace Ui {
- struct PreparedFileInformation;
- } // namespace Ui
- namespace Media {
- struct ExternalSoundData;
- struct ExternalSoundPart;
- } // namespace Media
- namespace Media {
- namespace Streaming {
- struct TimePoint;
- } // namespace Streaming
- } // namespace Media
- namespace Webrtc {
- struct DeviceResolvedId;
- } // namespace Webrtc
- namespace Media {
- namespace Audio {
- class Instance;
- // Thread: Main.
- void Start(not_null<Instance*> instance);
- void Finish(not_null<Instance*> instance);
- // Thread: Main. Locks: AudioMutex.
- bool IsAttachedToDevice();
- // Thread: Any. Must be locked: AudioMutex.
- bool AttachToDevice();
- // Thread: Any.
- void ScheduleDetachFromDeviceSafe();
- void ScheduleDetachIfNotUsedSafe();
- void StopDetachIfNotUsedSafe();
- bool SupportsSpeedControl();
- } // namespace Audio
- namespace Player {
- constexpr auto kDefaultFrequency = 48000; // 48 kHz
- constexpr auto kTogetherLimit = 4;
- constexpr auto kWaveformSamplesCount = 100;
- class Fader;
- class Loaders;
- [[nodiscard]] rpl::producer<AudioMsgId> Updated();
- float64 ComputeVolume(AudioMsgId::Type type);
- enum class State {
- Stopped = 0x01,
- StoppedAtEnd = 0x02,
- StoppedAtError = 0x03,
- StoppedAtStart = 0x04,
- Starting = 0x08,
- Playing = 0x10,
- Stopping = 0x18,
- Pausing = 0x20,
- Paused = 0x28,
- PausedAtEnd = 0x30,
- Resuming = 0x38,
- };
- inline bool IsStopped(State state) {
- return (state == State::Stopped)
- || (state == State::StoppedAtEnd)
- || (state == State::StoppedAtError)
- || (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);
- }
- inline bool IsPausedOrPausing(State state) {
- return IsPaused(state) || (state == State::Pausing);
- }
- inline bool IsFading(State state) {
- return (state == State::Starting)
- || (state == State::Stopping)
- || (state == State::Pausing)
- || (state == State::Resuming);
- }
- inline bool IsActive(State state) {
- return !IsStopped(state) && !IsPaused(state);
- }
- inline bool ShowPauseIcon(State state) {
- return !IsStoppedOrStopping(state)
- && !IsPausedOrPausing(state);
- }
- struct TrackState {
- AudioMsgId id;
- State state = State::Stopped;
- int64 position = 0;
- int64 receivedTill = 0;
- int64 length = 0;
- int frequency = kDefaultFrequency;
- int fileHeaderSize = 0;
- bool waitingForData = false;
- };
- class Mixer final : public QObject {
- Q_OBJECT
- public:
- explicit Mixer(not_null<Audio::Instance*> instance);
- void play(
- const AudioMsgId &audio,
- std::unique_ptr<ExternalSoundData> externalData,
- crl::time positionMs);
- void pause(const AudioMsgId &audio, bool fast = false);
- void resume(const AudioMsgId &audio, bool fast = false);
- void stop(const AudioMsgId &audio);
- void stop(const AudioMsgId &audio, State state);
- // External player audio stream interface.
- void feedFromExternal(ExternalSoundPart &&part);
- void forceToBufferExternal(const AudioMsgId &audioId);
- // Thread: Main. Locks: AudioMutex.
- void setSpeedFromExternal(const AudioMsgId &audioId, float64 speed);
- Streaming::TimePoint getExternalSyncTimePoint(
- const AudioMsgId &audio) const;
- crl::time getExternalCorrectedTime(
- const AudioMsgId &id,
- crl::time frameMs,
- crl::time systemMs);
- void stopAndClear();
- TrackState currentState(AudioMsgId::Type type);
- // Thread: Main. Must be locked: AudioMutex.
- void prepareToCloseDevice();
- // Thread: Main. Must be locked: AudioMutex.
- void reattachIfNeeded();
- // Thread: Any. Must be locked: AudioMutex.
- void reattachTracks();
- // Thread: Any.
- void setSongVolume(float64 volume);
- float64 getSongVolume() const;
- void setVideoVolume(float64 volume);
- float64 getVideoVolume() const;
- void scheduleFaderCallback();
- ~Mixer();
- private Q_SLOTS:
- void onError(const AudioMsgId &audio);
- void onStopped(const AudioMsgId &audio);
- void onUpdated(const AudioMsgId &audio);
- Q_SIGNALS:
- void updated(const AudioMsgId &audio);
- void stoppedOnError(const AudioMsgId &audio);
- void loaderOnStart(const AudioMsgId &audio, qint64 positionMs);
- void loaderOnCancel(const AudioMsgId &audio);
- void suppressSong();
- void unsuppressSong();
- void suppressAll(qint64 duration);
- private:
- class Track {
- public:
- static constexpr int kBuffersCount = 3;
- // Thread: Any. Must be locked: AudioMutex.
- void reattach(AudioMsgId::Type type);
- // Thread: Main. Must be locked: AudioMutex.
- void detach();
- void clear();
- void started();
- bool isStreamCreated() const;
- void ensureStreamCreated(AudioMsgId::Type type);
- int getNotQueuedBufferIndex();
- // Thread: Main. Must be locked: AudioMutex.
- void setExternalData(std::unique_ptr<ExternalSoundData> data);
- void updateStatePosition();
- void updateWithSpeedPosition();
- [[nodiscard]] static int64 SpeedIndependentPosition(
- int64 position,
- float64 speed);
- [[nodiscard]] static int64 SpeedDependentPosition(
- int64 position,
- float64 speed);
- ~Track();
- TrackState state;
- Core::FileLocation file;
- QByteArray data;
- int format = 0;
- bool loading = false;
- bool loaded = false;
- bool waitingForBuffer = false;
- // Speed dependent values.
- float64 speed = 1.;
- float64 nextSpeed = 1.;
- struct WithSpeed {
- int64 fineTunedPosition = 0;
- int64 position = 0;
- int64 length = 0;
- int64 bufferedPosition = 0;
- int64 bufferedLength = 0;
- int64 fadeStartPosition = 0;
- int samples[kBuffersCount] = { 0 };
- QByteArray buffered[kBuffersCount];
- };
- WithSpeed withSpeed;
- struct Stream {
- uint32 source = 0;
- uint32 buffers[kBuffersCount] = { 0 };
- };
- Stream stream;
- std::unique_ptr<ExternalSoundData> externalData;
- crl::time lastUpdateWhen = 0;
- crl::time lastUpdatePosition = 0;
- private:
- void createStream(AudioMsgId::Type type);
- void destroyStream();
- void resetStream();
- };
- bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
- void resetFadeStartPosition(AudioMsgId::Type type, int positionInBuffered = -1);
- bool checkCurrentALError(AudioMsgId::Type type);
- void externalSoundProgress(const AudioMsgId &audio);
- // Thread: Any. Must be locked: AudioMutex.
- void setStoppedState(Track *current, State state = State::Stopped);
- Track *trackForType(AudioMsgId::Type type, int index = -1); // -1 uses currentIndex(type)
- const Track *trackForType(AudioMsgId::Type type, int index = -1) const;
- int *currentIndex(AudioMsgId::Type type);
- const int *currentIndex(AudioMsgId::Type type) const;
- const not_null<Audio::Instance*> _instance;
- int _audioCurrent = 0;
- Track _audioTracks[kTogetherLimit];
- int _songCurrent = 0;
- Track _songTracks[kTogetherLimit];
- Track _videoTrack;
- QAtomicInt _volumeVideo;
- QAtomicInt _volumeSong;
- friend class Fader;
- friend class Loaders;
- QThread _faderThread, _loaderThread;
- Fader *_fader;
- Loaders *_loader;
- rpl::lifetime _lifetime;
- };
- Mixer *mixer();
- class Fader : public QObject {
- Q_OBJECT
- public:
- Fader(QThread *thread);
- void songVolumeChanged();
- void videoVolumeChanged();
- Q_SIGNALS:
- void error(const AudioMsgId &audio);
- void playPositionUpdated(const AudioMsgId &audio);
- void audioStopped(const AudioMsgId &audio);
- void needToPreload(const AudioMsgId &audio);
- public Q_SLOTS:
- void onInit();
- void onTimer();
- void onSuppressSong();
- void onUnsuppressSong();
- void onSuppressAll(qint64 duration);
- private:
- enum {
- EmitError = 0x01,
- EmitStopped = 0x02,
- EmitPositionUpdated = 0x04,
- EmitNeedToPreload = 0x08,
- };
- int32 updateOnePlayback(Mixer::Track *track, bool &hasPlaying, bool &hasFading, float64 volumeMultiplier, bool volumeChanged);
- void setStoppedState(Mixer::Track *track, State state = State::Stopped);
- QTimer _timer;
- bool _volumeChangedSong = false;
- bool _volumeChangedVideo = false;
- bool _suppressAll = false;
- bool _suppressAllAnim = false;
- bool _suppressSong = false;
- bool _suppressSongAnim = false;
- anim::value _suppressVolumeAll;
- anim::value _suppressVolumeSong;
- crl::time _suppressAllStart = 0;
- crl::time _suppressAllEnd = 0;
- crl::time _suppressSongStart = 0;
- };
- [[nodiscard]] Ui::PreparedFileInformation PrepareForSending(
- const QString &fname,
- const QByteArray &data);
- namespace internal {
- // Thread: Any. Must be locked: AudioMutex.
- bool CheckAudioDeviceConnected();
- // Thread: Main. Locks: AudioMutex.
- void DetachFromDevice(not_null<Audio::Instance*> instance);
- bool DetachIfDeviceChanged(
- not_null<Audio::Instance*> instance,
- const Webrtc::DeviceResolvedId &nowDeviceId);
- // Thread: Any.
- QMutex *audioPlayerMutex();
- // Thread: Any.
- bool audioCheckError();
- } // namespace internal
- } // namespace Player
- } // namespace Media
- VoiceWaveform audioCountWaveform(const Core::FileLocation &file, const QByteArray &data);
- namespace Media {
- namespace Audio {
- TG_FORCE_INLINE uint16 ReadOneSample(uchar data) {
- return qAbs((static_cast<int16>(data) - 0x80) * 0x100);
- }
- TG_FORCE_INLINE uint16 ReadOneSample(int16 data) {
- return qAbs(data);
- }
- template <typename SampleType, typename Callback>
- void IterateSamples(bytes::const_span bytes, Callback &&callback) {
- auto samplesPointer = reinterpret_cast<const SampleType*>(bytes.data());
- auto samplesCount = bytes.size() / sizeof(SampleType);
- auto samplesData = gsl::make_span(samplesPointer, samplesCount);
- for (auto sampleData : samplesData) {
- callback(ReadOneSample(sampleData));
- }
- }
- } // namespace Audio
- } // namespace Media
|