1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9
10 #include "ui/effects/animation_value.h"
11 #include "ui/chat/attach/attach_prepare.h"
12 #include "core/file_location.h"
13 #include "data/data_audio_msg_id.h"
14 #include "base/bytes.h"
15 #include "base/timer.h"
16
17 #include <QtCore/QTimer>
18
19 namespace Media {
20 struct ExternalSoundData;
21 struct ExternalSoundPart;
22 } // namespace Media
23
24 namespace Media {
25 namespace Streaming {
26 struct TimePoint;
27 } // namespace Streaming
28 } // namespace Media
29
30 namespace Media {
31 namespace Audio {
32
33 class Instance;
34
35 // Thread: Main.
36 void Start(not_null<Instance*> instance);
37 void Finish(not_null<Instance*> instance);
38
39 // Thread: Main. Locks: AudioMutex.
40 bool IsAttachedToDevice();
41
42 // Thread: Any. Must be locked: AudioMutex.
43 bool AttachToDevice();
44
45 // Thread: Any.
46 void ScheduleDetachFromDeviceSafe();
47 void ScheduleDetachIfNotUsedSafe();
48 void StopDetachIfNotUsedSafe();
49 bool SupportsSpeedControl();
50
51 } // namespace Audio
52
53 namespace Player {
54
55 constexpr auto kDefaultFrequency = 48000; // 48 kHz
56 constexpr auto kTogetherLimit = 4;
57 constexpr auto kWaveformSamplesCount = 100;
58
59 class Fader;
60 class Loaders;
61
62 base::Observable<AudioMsgId> &Updated();
63
64 float64 ComputeVolume(AudioMsgId::Type type);
65
66 enum class State {
67 Stopped = 0x01,
68 StoppedAtEnd = 0x02,
69 StoppedAtError = 0x03,
70 StoppedAtStart = 0x04,
71
72 Starting = 0x08,
73 Playing = 0x10,
74 Stopping = 0x18,
75 Pausing = 0x20,
76 Paused = 0x28,
77 PausedAtEnd = 0x30,
78 Resuming = 0x38,
79 };
80
IsStopped(State state)81 inline bool IsStopped(State state) {
82 return (state == State::Stopped)
83 || (state == State::StoppedAtEnd)
84 || (state == State::StoppedAtError)
85 || (state == State::StoppedAtStart);
86 }
87
IsStoppedOrStopping(State state)88 inline bool IsStoppedOrStopping(State state) {
89 return IsStopped(state) || (state == State::Stopping);
90 }
91
IsStoppedAtEnd(State state)92 inline bool IsStoppedAtEnd(State state) {
93 return (state == State::StoppedAtEnd);
94 }
95
IsPaused(State state)96 inline bool IsPaused(State state) {
97 return (state == State::Paused)
98 || (state == State::PausedAtEnd);
99 }
100
IsPausedOrPausing(State state)101 inline bool IsPausedOrPausing(State state) {
102 return IsPaused(state) || (state == State::Pausing);
103 }
104
IsFading(State state)105 inline bool IsFading(State state) {
106 return (state == State::Starting)
107 || (state == State::Stopping)
108 || (state == State::Pausing)
109 || (state == State::Resuming);
110 }
111
IsActive(State state)112 inline bool IsActive(State state) {
113 return !IsStopped(state) && !IsPaused(state);
114 }
115
ShowPauseIcon(State state)116 inline bool ShowPauseIcon(State state) {
117 return !IsStoppedOrStopping(state)
118 && !IsPausedOrPausing(state);
119 }
120
121 struct TrackState {
122 AudioMsgId id;
123 State state = State::Stopped;
124 int64 position = 0;
125 int64 receivedTill = 0;
126 int64 length = 0;
127 int frequency = kDefaultFrequency;
128 int fileHeaderSize = 0;
129 bool waitingForData = false;
130 };
131
132 class Mixer final : public QObject {
133 Q_OBJECT
134
135 public:
136 explicit Mixer(not_null<Audio::Instance*> instance);
137
138 void play(
139 const AudioMsgId &audio,
140 std::unique_ptr<ExternalSoundData> externalData,
141 crl::time positionMs);
142 void pause(const AudioMsgId &audio, bool fast = false);
143 void resume(const AudioMsgId &audio, bool fast = false);
144 void stop(const AudioMsgId &audio);
145 void stop(const AudioMsgId &audio, State state);
146
147 // External player audio stream interface.
148 void feedFromExternal(ExternalSoundPart &&part);
149 void forceToBufferExternal(const AudioMsgId &audioId);
150
151 // Thread: Main. Locks: AudioMutex.
152 void setSpeedFromExternal(const AudioMsgId &audioId, float64 speed);
153
154 Streaming::TimePoint getExternalSyncTimePoint(
155 const AudioMsgId &audio) const;
156 crl::time getExternalCorrectedTime(
157 const AudioMsgId &id,
158 crl::time frameMs,
159 crl::time systemMs);
160
161 void stopAndClear();
162
163 TrackState currentState(AudioMsgId::Type type);
164
165 // Thread: Main. Must be locked: AudioMutex.
166 void prepareToCloseDevice();
167
168 // Thread: Main. Must be locked: AudioMutex.
169 void reattachIfNeeded();
170
171 // Thread: Any. Must be locked: AudioMutex.
172 void reattachTracks();
173
174 // Thread: Any.
175 void setSongVolume(float64 volume);
176 float64 getSongVolume() const;
177 void setVideoVolume(float64 volume);
178 float64 getVideoVolume() const;
179
180 ~Mixer();
181
182 private Q_SLOTS:
183 void onError(const AudioMsgId &audio);
184 void onStopped(const AudioMsgId &audio);
185
186 void onUpdated(const AudioMsgId &audio);
187
188 Q_SIGNALS:
189 void updated(const AudioMsgId &audio);
190 void stoppedOnError(const AudioMsgId &audio);
191 void loaderOnStart(const AudioMsgId &audio, qint64 positionMs);
192 void loaderOnCancel(const AudioMsgId &audio);
193
194 void faderOnTimer();
195
196 void suppressSong();
197 void unsuppressSong();
198 void suppressAll(qint64 duration);
199
200 private:
201 struct SpeedEffect {
202 uint32 effect = 0;
203 uint32 effectSlot = 0;
204 uint32 filter = 0;
205 int coarseTune = 0;
206 float64 speed = 1.;
207 };
208
209 class Track {
210 public:
211 static constexpr int kBuffersCount = 3;
212
213 // Thread: Any. Must be locked: AudioMutex.
214 void reattach(AudioMsgId::Type type);
215
216 // Thread: Main. Must be locked: AudioMutex.
217 void detach();
218 void clear();
219
220 void started();
221
222 bool isStreamCreated() const;
223 void ensureStreamCreated(AudioMsgId::Type type);
224
225 int getNotQueuedBufferIndex();
226
227 // Thread: Main. Must be locked: AudioMutex.
228 void setExternalData(std::unique_ptr<ExternalSoundData> data);
229 void changeSpeedEffect(float64 speed);
230
231 ~Track();
232
233 TrackState state;
234
235 Core::FileLocation file;
236 QByteArray data;
237 int64 bufferedPosition = 0;
238 int64 bufferedLength = 0;
239 bool loading = false;
240 bool loaded = false;
241 int64 fadeStartPosition = 0;
242
243 int32 format = 0;
244 int32 frequency = kDefaultFrequency;
245 int samplesCount[kBuffersCount] = { 0 };
246 QByteArray bufferSamples[kBuffersCount];
247
248 struct Stream {
249 uint32 source = 0;
250 uint32 buffers[kBuffersCount] = { 0 };
251 };
252 Stream stream;
253 std::unique_ptr<ExternalSoundData> externalData;
254
255 std::unique_ptr<SpeedEffect> speedEffect;
256 crl::time lastUpdateWhen = 0;
257 crl::time lastUpdatePosition = 0;
258
259 private:
260 void createStream(AudioMsgId::Type type);
261 void destroyStream();
262 void resetStream();
263 void resetSpeedEffect();
264 void applySourceSpeedEffect();
265 void removeSourceSpeedEffect();
266
267 };
268
269 bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
270 void resetFadeStartPosition(AudioMsgId::Type type, int positionInBuffered = -1);
271 bool checkCurrentALError(AudioMsgId::Type type);
272
273 void externalSoundProgress(const AudioMsgId &audio);
274
275 // Thread: Any. Must be locked: AudioMutex.
276 void setStoppedState(Track *current, State state = State::Stopped);
277
278 Track *trackForType(AudioMsgId::Type type, int index = -1); // -1 uses currentIndex(type)
279 const Track *trackForType(AudioMsgId::Type type, int index = -1) const;
280 int *currentIndex(AudioMsgId::Type type);
281 const int *currentIndex(AudioMsgId::Type type) const;
282
283 // Thread: Any. Must be locked: AudioMutex.
284 void scheduleEffectDestruction(const SpeedEffect &effect);
285 void scheduleEffectsDestruction();
286
287 // Thread: Main. Must be locked: AudioMutex.
288 void destroyStaleEffects();
289 void destroyEffectsOnClose();
290
291 // Thread: Main. Locks: AudioMutex.
292 void destroyStaleEffectsSafe();
293
294 const not_null<Audio::Instance*> _instance;
295
296 int _audioCurrent = 0;
297 Track _audioTracks[kTogetherLimit];
298
299 int _songCurrent = 0;
300 Track _songTracks[kTogetherLimit];
301
302 Track _videoTrack;
303
304 std::vector<std::pair<crl::time, SpeedEffect>> _effectsForDestruction;
305 base::Timer _effectsDestructionTimer;
306
307 QAtomicInt _volumeVideo;
308 QAtomicInt _volumeSong;
309
310 friend class Fader;
311 friend class Loaders;
312
313 QThread _faderThread, _loaderThread;
314 Fader *_fader;
315 Loaders *_loader;
316
317 rpl::lifetime _lifetime;
318
319 };
320
321 Mixer *mixer();
322
323 class Fader : public QObject {
324 Q_OBJECT
325
326 public:
327 Fader(QThread *thread);
328
329 Q_SIGNALS:
330 void error(const AudioMsgId &audio);
331 void playPositionUpdated(const AudioMsgId &audio);
332 void audioStopped(const AudioMsgId &audio);
333 void needToPreload(const AudioMsgId &audio);
334
335 public Q_SLOTS:
336 void onInit();
337 void onTimer();
338
339 void onSuppressSong();
340 void onUnsuppressSong();
341 void onSuppressAll(qint64 duration);
342 void onSongVolumeChanged();
343 void onVideoVolumeChanged();
344
345 private:
346 enum {
347 EmitError = 0x01,
348 EmitStopped = 0x02,
349 EmitPositionUpdated = 0x04,
350 EmitNeedToPreload = 0x08,
351 };
352 int32 updateOnePlayback(Mixer::Track *track, bool &hasPlaying, bool &hasFading, float64 volumeMultiplier, bool volumeChanged);
353 void setStoppedState(Mixer::Track *track, State state = State::Stopped);
354
355 QTimer _timer;
356
357 bool _volumeChangedSong = false;
358 bool _volumeChangedVideo = false;
359
360 bool _suppressAll = false;
361 bool _suppressAllAnim = false;
362 bool _suppressSong = false;
363 bool _suppressSongAnim = false;
364 anim::value _suppressVolumeAll;
365 anim::value _suppressVolumeSong;
366 crl::time _suppressAllStart = 0;
367 crl::time _suppressAllEnd = 0;
368 crl::time _suppressSongStart = 0;
369
370 };
371
372 [[nodiscard]] Ui::PreparedFileInformation::Song PrepareForSending(
373 const QString &fname,
374 const QByteArray &data);
375
376 namespace internal {
377
378 // Thread: Any. Must be locked: AudioMutex.
379 bool CheckAudioDeviceConnected();
380
381 // Thread: Main. Locks: AudioMutex.
382 void DetachFromDevice(not_null<Audio::Instance*> instance);
383
384 // Thread: Any.
385 QMutex *audioPlayerMutex();
386
387 // Thread: Any.
388 bool audioCheckError();
389
390 } // namespace internal
391
392 } // namespace Player
393 } // namespace Media
394
395 VoiceWaveform audioCountWaveform(const Core::FileLocation &file, const QByteArray &data);
396
397 namespace Media {
398 namespace Audio {
399
ReadOneSample(uchar data)400 TG_FORCE_INLINE uint16 ReadOneSample(uchar data) {
401 return qAbs((static_cast<int16>(data) - 0x80) * 0x100);
402 }
403
ReadOneSample(int16 data)404 TG_FORCE_INLINE uint16 ReadOneSample(int16 data) {
405 return qAbs(data);
406 }
407
408 template <typename SampleType, typename Callback>
IterateSamples(bytes::const_span bytes,Callback && callback)409 void IterateSamples(bytes::const_span bytes, Callback &&callback) {
410 auto samplesPointer = reinterpret_cast<const SampleType*>(bytes.data());
411 auto samplesCount = bytes.size() / sizeof(SampleType);
412 auto samplesData = gsl::make_span(samplesPointer, samplesCount);
413 for (auto sampleData : samplesData) {
414 callback(ReadOneSample(sampleData));
415 }
416 }
417
418 } // namespace Audio
419 } // namespace Media
420