2014-09-04 07:33:44 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2014-09-04 07:33:44 +00:00
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
|
2019-02-21 11:15:44 +00:00
|
|
|
#include "media/streaming/media_streaming_common.h"
|
2017-03-10 14:14:10 +00:00
|
|
|
#include "storage/localimageloader.h"
|
2018-03-27 12:16:00 +00:00
|
|
|
#include "base/bytes.h"
|
2017-03-10 14:14:10 +00:00
|
|
|
|
2016-07-05 17:44:02 +00:00
|
|
|
struct VideoSoundData;
|
|
|
|
struct VideoSoundPart;
|
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
namespace Media {
|
2017-05-03 11:36:39 +00:00
|
|
|
namespace Audio {
|
|
|
|
|
2019-02-01 07:09:55 +00:00
|
|
|
class Instance;
|
|
|
|
|
2017-05-03 11:36:39 +00:00
|
|
|
// Thread: Main.
|
2019-02-01 07:09:55 +00:00
|
|
|
void Start(not_null<Instance*> instance);
|
|
|
|
void Finish(not_null<Instance*> instance);
|
2017-05-03 11:36:39 +00:00
|
|
|
|
|
|
|
// Thread: Main. Locks: AudioMutex.
|
|
|
|
bool IsAttachedToDevice();
|
|
|
|
|
|
|
|
// Thread: Any. Must be locked: AudioMutex.
|
|
|
|
bool AttachToDevice();
|
|
|
|
|
|
|
|
// Thread: Any.
|
|
|
|
void ScheduleDetachFromDeviceSafe();
|
|
|
|
void ScheduleDetachIfNotUsedSafe();
|
|
|
|
void StopDetachIfNotUsedSafe();
|
|
|
|
|
|
|
|
} // namespace Audio
|
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
namespace Player {
|
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
constexpr auto kDefaultFrequency = 48000; // 48 kHz
|
|
|
|
constexpr auto kTogetherLimit = 4;
|
2017-02-10 22:37:37 +00:00
|
|
|
constexpr auto kWaveformSamplesCount = 100;
|
2017-01-24 21:24:39 +00:00
|
|
|
|
|
|
|
class Fader;
|
|
|
|
class Loaders;
|
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
base::Observable<AudioMsgId> &Updated();
|
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
float64 ComputeVolume(AudioMsgId::Type type);
|
|
|
|
|
|
|
|
enum class State {
|
|
|
|
Stopped = 0x01,
|
|
|
|
StoppedAtEnd = 0x02,
|
|
|
|
StoppedAtError = 0x03,
|
|
|
|
StoppedAtStart = 0x04,
|
|
|
|
|
|
|
|
Starting = 0x08,
|
|
|
|
Playing = 0x10,
|
2017-05-21 13:16:39 +00:00
|
|
|
Stopping = 0x18,
|
2017-01-24 21:24:39 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-05-21 13:16:39 +00:00
|
|
|
inline bool IsStoppedOrStopping(State state) {
|
|
|
|
return IsStopped(state) || (state == State::Stopping);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool IsStoppedAtEnd(State state) {
|
|
|
|
return (state == State::StoppedAtEnd);
|
|
|
|
}
|
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
inline bool IsPaused(State state) {
|
|
|
|
return (state == State::Paused)
|
|
|
|
|| (state == State::PausedAtEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool IsFading(State state) {
|
|
|
|
return (state == State::Starting)
|
2017-05-21 13:16:39 +00:00
|
|
|
|| (state == State::Stopping)
|
2017-01-24 21:24:39 +00:00
|
|
|
|| (state == State::Pausing)
|
|
|
|
|| (state == State::Resuming);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool IsActive(State state) {
|
|
|
|
return !IsStopped(state) && !IsPaused(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TrackState {
|
|
|
|
AudioMsgId id;
|
|
|
|
State state = State::Stopped;
|
|
|
|
int64 position = 0;
|
2017-05-03 13:01:15 +00:00
|
|
|
int64 length = 0;
|
2017-01-24 21:24:39 +00:00
|
|
|
int frequency = kDefaultFrequency;
|
|
|
|
};
|
2017-01-19 08:24:43 +00:00
|
|
|
|
|
|
|
class Mixer : public QObject, private base::Subscriber {
|
2014-09-04 07:33:44 +00:00
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
2019-02-01 07:09:55 +00:00
|
|
|
explicit Mixer(not_null<Audio::Instance*> instance);
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2019-02-19 06:57:53 +00:00
|
|
|
void play(const AudioMsgId &audio, crl::time positionMs = 0);
|
|
|
|
void play(
|
|
|
|
const AudioMsgId &audio,
|
|
|
|
std::unique_ptr<VideoSoundData> videoData,
|
|
|
|
crl::time positionMs = 0);
|
2017-05-18 20:18:59 +00:00
|
|
|
void pause(const AudioMsgId &audio, bool fast = false);
|
|
|
|
void resume(const AudioMsgId &audio, bool fast = false);
|
2019-02-19 06:57:53 +00:00
|
|
|
void seek(AudioMsgId::Type type, crl::time positionMs); // type == AudioMsgId::Type::Song
|
2017-05-18 20:18:59 +00:00
|
|
|
void stop(const AudioMsgId &audio);
|
2017-05-21 13:16:39 +00:00
|
|
|
void stop(const AudioMsgId &audio, State state);
|
2015-06-30 21:07:05 +00:00
|
|
|
|
2016-07-05 17:44:02 +00:00
|
|
|
// Video player audio stream interface.
|
2019-02-13 18:10:18 +00:00
|
|
|
void feedFromVideo(const VideoSoundPart &part);
|
2019-02-21 11:15:44 +00:00
|
|
|
struct TimeCorrection {
|
|
|
|
crl::time audioPositionValue = kTimeUnknown;
|
|
|
|
crl::time audioPositionTime = kTimeUnknown;
|
|
|
|
|
|
|
|
explicit operator bool() const {
|
|
|
|
return (audioPositionValue != kTimeUnknown);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
TimeCorrection getVideoTimeCorrection(const AudioMsgId &audio) const;
|
2019-03-09 11:27:36 +00:00
|
|
|
crl::time getVideoCorrectedTime(
|
2019-02-19 06:57:53 +00:00
|
|
|
const AudioMsgId &id,
|
|
|
|
crl::time frameMs,
|
|
|
|
crl::time systemMs);
|
2016-07-05 17:44:02 +00:00
|
|
|
|
2015-11-24 16:19:18 +00:00
|
|
|
void stopAndClear();
|
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
TrackState currentState(AudioMsgId::Type type);
|
2015-06-30 21:07:05 +00:00
|
|
|
|
|
|
|
void clearStoppedAtStart(const AudioMsgId &audio);
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2017-05-03 11:36:39 +00:00
|
|
|
// Thread: Main. Must be locked: AudioMutex.
|
2017-01-24 21:24:39 +00:00
|
|
|
void detachTracks();
|
2017-05-03 11:36:39 +00:00
|
|
|
|
|
|
|
// Thread: Main. Must be locked: AudioMutex.
|
2017-01-24 21:24:39 +00:00
|
|
|
void reattachIfNeeded();
|
2017-05-03 11:36:39 +00:00
|
|
|
|
|
|
|
// Thread: Any. Must be locked: AudioMutex.
|
2017-01-24 21:24:39 +00:00
|
|
|
void reattachTracks();
|
|
|
|
|
2017-05-03 11:36:39 +00:00
|
|
|
// Thread: Any.
|
2017-05-18 20:18:59 +00:00
|
|
|
void setSongVolume(float64 volume);
|
|
|
|
float64 getSongVolume() const;
|
2017-04-15 19:51:53 +00:00
|
|
|
void setVideoVolume(float64 volume);
|
|
|
|
float64 getVideoVolume() const;
|
|
|
|
|
2018-11-08 13:06:22 +00:00
|
|
|
// Thread: Any. Locks AudioMutex.
|
2018-11-30 14:33:12 +00:00
|
|
|
void setVoicePlaybackDoubled(bool doubled);
|
2018-11-08 13:06:22 +00:00
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
~Mixer();
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
private slots:
|
2015-06-30 21:07:05 +00:00
|
|
|
void onError(const AudioMsgId &audio);
|
|
|
|
void onStopped(const AudioMsgId &audio);
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2016-09-21 11:44:20 +00:00
|
|
|
void onUpdated(const AudioMsgId &audio);
|
|
|
|
|
2014-09-04 07:33:44 +00:00
|
|
|
signals:
|
2015-06-30 21:07:05 +00:00
|
|
|
void updated(const AudioMsgId &audio);
|
2015-07-03 08:47:16 +00:00
|
|
|
void stoppedOnError(const AudioMsgId &audio);
|
2017-12-10 08:52:38 +00:00
|
|
|
void loaderOnStart(const AudioMsgId &audio, qint64 positionMs);
|
2015-06-30 21:07:05 +00:00
|
|
|
void loaderOnCancel(const AudioMsgId &audio);
|
2014-09-04 07:33:44 +00:00
|
|
|
|
|
|
|
void faderOnTimer();
|
2015-07-03 08:47:16 +00:00
|
|
|
|
2015-06-30 21:07:05 +00:00
|
|
|
void suppressSong();
|
|
|
|
void unsuppressSong();
|
2017-05-03 13:01:15 +00:00
|
|
|
void suppressAll(qint64 duration);
|
2014-09-04 07:33:44 +00:00
|
|
|
|
|
|
|
private:
|
2016-06-30 12:03:32 +00:00
|
|
|
bool fadedStop(AudioMsgId::Type type, bool *fadedStart = 0);
|
2017-01-24 21:24:39 +00:00
|
|
|
void resetFadeStartPosition(AudioMsgId::Type type, int positionInBuffered = -1);
|
2016-06-30 12:03:32 +00:00
|
|
|
bool checkCurrentALError(AudioMsgId::Type type);
|
|
|
|
|
2016-09-21 11:44:20 +00:00
|
|
|
void videoSoundProgress(const AudioMsgId &audio);
|
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
class Track {
|
|
|
|
public:
|
|
|
|
static constexpr int kBuffersCount = 3;
|
|
|
|
|
2017-05-18 20:18:59 +00:00
|
|
|
// Thread: Any. Must be locked: AudioMutex.
|
2017-01-24 21:24:39 +00:00
|
|
|
void reattach(AudioMsgId::Type type);
|
2017-05-18 20:18:59 +00:00
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
void detach();
|
2016-06-30 12:03:32 +00:00
|
|
|
void clear();
|
2017-01-24 21:24:39 +00:00
|
|
|
void started();
|
|
|
|
|
|
|
|
bool isStreamCreated() const;
|
2018-11-08 13:06:22 +00:00
|
|
|
void ensureStreamCreated(AudioMsgId::Type type);
|
2016-06-30 12:03:32 +00:00
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
int getNotQueuedBufferIndex();
|
|
|
|
|
2019-02-21 09:17:25 +00:00
|
|
|
void setVideoData(std::unique_ptr<VideoSoundData> data);
|
|
|
|
|
2017-05-18 20:18:59 +00:00
|
|
|
~Track();
|
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
TrackState state;
|
2015-11-24 16:19:18 +00:00
|
|
|
|
2015-11-26 17:34:52 +00:00
|
|
|
FileLocation file;
|
2014-09-04 07:33:44 +00:00
|
|
|
QByteArray data;
|
2017-01-24 21:24:39 +00:00
|
|
|
int64 bufferedPosition = 0;
|
|
|
|
int64 bufferedLength = 0;
|
2016-06-30 12:03:32 +00:00
|
|
|
bool loading = false;
|
2017-01-24 21:24:39 +00:00
|
|
|
bool loaded = false;
|
|
|
|
int64 fadeStartPosition = 0;
|
|
|
|
|
|
|
|
int32 format = 0;
|
|
|
|
int32 frequency = kDefaultFrequency;
|
|
|
|
int samplesCount[kBuffersCount] = { 0 };
|
|
|
|
QByteArray bufferSamples[kBuffersCount];
|
2016-06-30 12:03:32 +00:00
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
struct Stream {
|
|
|
|
uint32 source = 0;
|
|
|
|
uint32 buffers[kBuffersCount] = { 0 };
|
|
|
|
};
|
|
|
|
Stream stream;
|
2017-02-21 13:45:56 +00:00
|
|
|
std::unique_ptr<VideoSoundData> videoData;
|
2016-07-10 13:02:22 +00:00
|
|
|
|
2019-02-21 09:17:25 +00:00
|
|
|
struct SpeedEffect {
|
|
|
|
uint32 effect = 0;
|
|
|
|
uint32 effectSlot = 0;
|
|
|
|
uint32 filter = 0;
|
|
|
|
int coarseTune = 0;
|
|
|
|
float64 speed = 1.;
|
|
|
|
};
|
|
|
|
std::unique_ptr<SpeedEffect> speedEffect;
|
2019-02-19 06:57:53 +00:00
|
|
|
crl::time lastUpdateWhen = 0;
|
2019-02-21 11:15:44 +00:00
|
|
|
crl::time lastUpdatePosition = 0;
|
2017-05-18 20:18:59 +00:00
|
|
|
|
2016-07-10 13:02:22 +00:00
|
|
|
private:
|
2018-11-08 13:06:22 +00:00
|
|
|
void createStream(AudioMsgId::Type type);
|
2017-01-24 21:24:39 +00:00
|
|
|
void destroyStream();
|
2019-02-21 09:17:25 +00:00
|
|
|
void destroySpeedEffect();
|
2017-01-24 21:24:39 +00:00
|
|
|
void resetStream();
|
2016-07-10 13:02:22 +00:00
|
|
|
|
2015-06-30 21:07:05 +00:00
|
|
|
};
|
|
|
|
|
2017-05-23 14:04:59 +00:00
|
|
|
// Thread: Any. Must be locked: AudioMutex.
|
2017-01-24 21:24:39 +00:00
|
|
|
void setStoppedState(Track *current, State state = State::Stopped);
|
2018-11-08 13:06:22 +00:00
|
|
|
void updatePlaybackSpeed(Track *track);
|
|
|
|
void updatePlaybackSpeed(Track *track, bool doubled);
|
2015-06-30 21:07:05 +00:00
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
Track *trackForType(AudioMsgId::Type type, int index = -1); // -1 uses currentIndex(type)
|
|
|
|
const Track *trackForType(AudioMsgId::Type type, int index = -1) const;
|
2016-06-30 12:03:32 +00:00
|
|
|
int *currentIndex(AudioMsgId::Type type);
|
|
|
|
const int *currentIndex(AudioMsgId::Type type) const;
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2019-02-01 07:09:55 +00:00
|
|
|
not_null<Audio::Instance*> _instance;
|
|
|
|
|
2016-10-12 19:34:25 +00:00
|
|
|
int _audioCurrent = 0;
|
2017-01-24 21:24:39 +00:00
|
|
|
Track _audioTracks[kTogetherLimit];
|
2016-06-30 12:03:32 +00:00
|
|
|
|
2016-10-12 19:34:25 +00:00
|
|
|
int _songCurrent = 0;
|
2017-01-24 21:24:39 +00:00
|
|
|
Track _songTracks[kTogetherLimit];
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
Track _videoTrack;
|
2016-07-05 17:44:02 +00:00
|
|
|
|
2017-05-18 20:18:59 +00:00
|
|
|
QAtomicInt _volumeVideo;
|
|
|
|
QAtomicInt _volumeSong;
|
2018-11-30 14:33:12 +00:00
|
|
|
QAtomicInt _voicePlaybackDoubled = { 0 };
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
friend class Fader;
|
|
|
|
friend class Loaders;
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2015-05-29 18:52:43 +00:00
|
|
|
QThread _faderThread, _loaderThread;
|
2017-01-19 08:24:43 +00:00
|
|
|
Fader *_fader;
|
|
|
|
Loaders *_loader;
|
2015-05-29 18:52:43 +00:00
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
Mixer *mixer();
|
2015-05-29 18:52:43 +00:00
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
class Fader : public QObject {
|
2014-09-04 07:33:44 +00:00
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
2017-01-19 08:24:43 +00:00
|
|
|
Fader(QThread *thread);
|
2014-09-04 07:33:44 +00:00
|
|
|
|
|
|
|
signals:
|
2015-06-30 21:07:05 +00:00
|
|
|
void error(const AudioMsgId &audio);
|
|
|
|
void playPositionUpdated(const AudioMsgId &audio);
|
|
|
|
void audioStopped(const AudioMsgId &audio);
|
|
|
|
void needToPreload(const AudioMsgId &audio);
|
2014-09-04 07:33:44 +00:00
|
|
|
|
2016-07-12 11:38:16 +00:00
|
|
|
public slots:
|
2014-09-04 07:33:44 +00:00
|
|
|
void onInit();
|
|
|
|
void onTimer();
|
|
|
|
|
2015-06-30 21:07:05 +00:00
|
|
|
void onSuppressSong();
|
|
|
|
void onUnsuppressSong();
|
2017-05-03 13:01:15 +00:00
|
|
|
void onSuppressAll(qint64 duration);
|
2015-07-03 08:47:16 +00:00
|
|
|
void onSongVolumeChanged();
|
2016-07-12 14:11:59 +00:00
|
|
|
void onVideoVolumeChanged();
|
2015-06-30 21:07:05 +00:00
|
|
|
|
2014-09-04 07:33:44 +00:00
|
|
|
private:
|
2015-06-30 21:07:05 +00:00
|
|
|
enum {
|
2016-07-05 17:44:02 +00:00
|
|
|
EmitError = 0x01,
|
|
|
|
EmitStopped = 0x02,
|
2015-06-30 21:07:05 +00:00
|
|
|
EmitPositionUpdated = 0x04,
|
2016-07-05 17:44:02 +00:00
|
|
|
EmitNeedToPreload = 0x08,
|
2015-06-30 21:07:05 +00:00
|
|
|
};
|
2017-05-18 20:18:59 +00:00
|
|
|
int32 updateOnePlayback(Mixer::Track *track, bool &hasPlaying, bool &hasFading, float64 volumeMultiplier, bool volumeChanged);
|
2017-01-24 21:24:39 +00:00
|
|
|
void setStoppedState(Mixer::Track *track, State state = State::Stopped);
|
2015-06-30 21:07:05 +00:00
|
|
|
|
2017-01-24 21:24:39 +00:00
|
|
|
QTimer _timer;
|
2016-10-12 19:34:25 +00:00
|
|
|
|
2017-05-18 20:18:59 +00:00
|
|
|
bool _volumeChangedSong = false;
|
|
|
|
bool _volumeChangedVideo = false;
|
|
|
|
|
2016-10-12 19:34:25 +00:00
|
|
|
bool _suppressAll = false;
|
|
|
|
bool _suppressAllAnim = false;
|
|
|
|
bool _suppressSong = false;
|
|
|
|
bool _suppressSongAnim = false;
|
2017-05-18 20:18:59 +00:00
|
|
|
anim::value _suppressVolumeAll;
|
|
|
|
anim::value _suppressVolumeSong;
|
2019-02-19 06:57:53 +00:00
|
|
|
crl::time _suppressAllStart = 0;
|
|
|
|
crl::time _suppressAllEnd = 0;
|
|
|
|
crl::time _suppressSongStart = 0;
|
2015-06-30 21:07:05 +00:00
|
|
|
|
2014-09-04 07:33:44 +00:00
|
|
|
};
|
|
|
|
|
2017-12-19 16:57:42 +00:00
|
|
|
FileMediaInformation::Song PrepareForSending(const QString &fname, const QByteArray &data);
|
2017-03-10 14:14:10 +00:00
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
namespace internal {
|
2015-05-29 18:52:43 +00:00
|
|
|
|
2017-05-03 11:36:39 +00:00
|
|
|
// Thread: Any. Must be locked: AudioMutex.
|
|
|
|
bool CheckAudioDeviceConnected();
|
|
|
|
|
|
|
|
// Thread: Main. Locks: AudioMutex.
|
2019-02-01 07:09:55 +00:00
|
|
|
void DetachFromDevice(not_null<Audio::Instance*> instance);
|
2017-05-03 11:36:39 +00:00
|
|
|
|
|
|
|
// Thread: Any.
|
2017-01-19 08:24:43 +00:00
|
|
|
QMutex *audioPlayerMutex();
|
2015-05-29 18:52:43 +00:00
|
|
|
|
2017-05-03 11:36:39 +00:00
|
|
|
// Thread: Any.
|
|
|
|
bool audioCheckError();
|
2017-01-24 21:24:39 +00:00
|
|
|
|
2017-01-19 08:24:43 +00:00
|
|
|
} // namespace internal
|
2015-06-30 21:07:05 +00:00
|
|
|
|
2017-05-03 11:36:39 +00:00
|
|
|
} // namespace Player
|
|
|
|
} // namespace Media
|
|
|
|
|
2016-02-12 16:35:06 +00:00
|
|
|
VoiceWaveform audioCountWaveform(const FileLocation &file, const QByteArray &data);
|
2017-05-07 19:09:20 +00:00
|
|
|
|
|
|
|
namespace Media {
|
|
|
|
namespace Audio {
|
|
|
|
|
2018-07-28 21:08:29 +00:00
|
|
|
TG_FORCE_INLINE uint16 ReadOneSample(uchar data) {
|
2017-05-07 19:09:20 +00:00
|
|
|
return qAbs((static_cast<int16>(data) - 0x80) * 0x100);
|
|
|
|
}
|
|
|
|
|
2018-07-28 21:08:29 +00:00
|
|
|
TG_FORCE_INLINE uint16 ReadOneSample(int16 data) {
|
2017-05-07 19:09:20 +00:00
|
|
|
return qAbs(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename SampleType, typename Callback>
|
2018-03-27 12:16:00 +00:00
|
|
|
void IterateSamples(bytes::const_span bytes, Callback &&callback) {
|
2017-05-07 19:09:20 +00:00
|
|
|
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
|