media_audio_track.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "media/audio/media_audio_track.h"
  8. #include "media/audio/media_audio_ffmpeg_loader.h"
  9. #include "media/audio/media_audio.h"
  10. #include "core/application.h"
  11. #include "core/core_settings.h"
  12. #include "core/file_location.h"
  13. #include <al.h>
  14. #include <alc.h>
  15. namespace Media {
  16. namespace Audio {
  17. namespace {
  18. constexpr auto kMaxFileSize = 10 * 1024 * 1024;
  19. constexpr auto kDetachDeviceTimeout = crl::time(500); // destroy the audio device after 500ms of silence
  20. constexpr auto kTrackUpdateTimeout = crl::time(100);
  21. ALuint CreateSource() {
  22. auto source = ALuint(0);
  23. alGenSources(1, &source);
  24. alSourcef(source, AL_PITCH, 1.f);
  25. alSourcef(source, AL_GAIN, 1.f);
  26. alSource3f(source, AL_POSITION, 0, 0, 0);
  27. alSource3f(source, AL_VELOCITY, 0, 0, 0);
  28. return source;
  29. }
  30. ALuint CreateBuffer() {
  31. auto buffer = ALuint(0);
  32. alGenBuffers(1, &buffer);
  33. return buffer;
  34. }
  35. } // namespace
  36. Track::Track(not_null<Instance*> instance) : _instance(instance) {
  37. _instance->registerTrack(this);
  38. }
  39. void Track::samplePeakEach(crl::time peakDuration) {
  40. _peakDurationMs = peakDuration;
  41. }
  42. void Track::fillFromData(bytes::vector &&data) {
  43. FFMpegLoader loader(Core::FileLocation(), QByteArray(), std::move(data));
  44. auto position = qint64(0);
  45. if (!loader.open(position)) {
  46. _failed = true;
  47. return;
  48. }
  49. auto format = loader.format();
  50. _peakEachPosition = _peakDurationMs ? ((loader.samplesFrequency() * _peakDurationMs) / 1000) : 0;
  51. const auto samplesCount = (loader.duration() * loader.samplesFrequency()) / 1000;
  52. const auto peaksCount = _peakEachPosition ? (samplesCount / _peakEachPosition) : 0;
  53. _peaks.reserve(peaksCount);
  54. auto peakValue = uint16(0);
  55. auto peakSamples = 0;
  56. auto peakEachSample = (format == AL_FORMAT_STEREO8 || format == AL_FORMAT_STEREO16) ? (_peakEachPosition * 2) : _peakEachPosition;
  57. _peakValueMin = 0x7FFF;
  58. _peakValueMax = 0;
  59. auto peakCallback = [this, &peakValue, &peakSamples, peakEachSample](uint16 sample) {
  60. accumulate_max(peakValue, sample);
  61. if (++peakSamples >= peakEachSample) {
  62. peakSamples -= peakEachSample;
  63. _peaks.push_back(peakValue);
  64. accumulate_max(_peakValueMax, peakValue);
  65. accumulate_min(_peakValueMin, peakValue);
  66. peakValue = 0;
  67. }
  68. };
  69. do {
  70. using Error = AudioPlayerLoader::ReadError;
  71. const auto result = loader.readMore();
  72. Assert(result != Error::Wait && result != Error::RetryNotQueued);
  73. if (result == Error::Retry) {
  74. continue;
  75. } else if (result == Error::EndOfFile) {
  76. break;
  77. } else if (result == Error::Other || result == Error::Wait) {
  78. _failed = true;
  79. break;
  80. }
  81. Assert(v::is<bytes::const_span>(result));
  82. const auto sampleBytes = v::get<bytes::const_span>(result);
  83. Assert(!sampleBytes.empty());
  84. _samplesCount += sampleBytes.size() / loader.sampleSize();
  85. _samples.insert(_samples.end(), sampleBytes.data(), sampleBytes.data() + sampleBytes.size());
  86. if (peaksCount) {
  87. if (format == AL_FORMAT_MONO8 || format == AL_FORMAT_STEREO8) {
  88. Media::Audio::IterateSamples<uchar>(sampleBytes, peakCallback);
  89. } else if (format == AL_FORMAT_MONO16 || format == AL_FORMAT_STEREO16) {
  90. Media::Audio::IterateSamples<int16>(sampleBytes, peakCallback);
  91. }
  92. }
  93. } while (true);
  94. _alFormat = loader.format();
  95. _sampleRate = loader.samplesFrequency();
  96. _lengthMs = loader.duration();
  97. }
  98. void Track::fillFromFile(const Core::FileLocation &location) {
  99. if (location.accessEnable()) {
  100. fillFromFile(location.name());
  101. location.accessDisable();
  102. } else {
  103. LOG(("Track Error: Could not enable access to file '%1'.").arg(location.name()));
  104. _failed = true;
  105. }
  106. }
  107. void Track::fillFromFile(const QString &filePath) {
  108. QFile f(filePath);
  109. if (f.open(QIODevice::ReadOnly)) {
  110. auto size = f.size();
  111. if (size > 0 && size <= kMaxFileSize) {
  112. auto bytes = bytes::vector(size);
  113. if (f.read(reinterpret_cast<char*>(bytes.data()), bytes.size()) == bytes.size()) {
  114. fillFromData(std::move(bytes));
  115. } else {
  116. LOG(("Track Error: Could not read %1 bytes from file '%2'.").arg(bytes.size()).arg(filePath));
  117. _failed = true;
  118. }
  119. } else {
  120. LOG(("Track Error: Bad file '%1' size: %2.").arg(filePath).arg(size));
  121. _failed = true;
  122. }
  123. } else {
  124. LOG(("Track Error: Could not open file '%1'.").arg(filePath));
  125. _failed = true;
  126. }
  127. }
  128. void Track::playWithLooping(bool looping) {
  129. _active = true;
  130. if (failed() || _samples.empty()) {
  131. finish();
  132. return;
  133. }
  134. ensureSourceCreated();
  135. alSourceStop(_alSource);
  136. _looping = looping;
  137. alSourcei(_alSource, AL_LOOPING, _looping ? 1 : 0);
  138. alSourcef(_alSource, AL_GAIN, _volume);
  139. alSourcePlay(_alSource);
  140. _instance->trackStarted(this);
  141. }
  142. void Track::finish() {
  143. if (_active) {
  144. _active = false;
  145. _instance->trackFinished(this);
  146. }
  147. _alPosition = 0;
  148. }
  149. void Track::ensureSourceCreated() {
  150. if (alIsSource(_alSource)) {
  151. return;
  152. }
  153. {
  154. QMutexLocker lock(Player::internal::audioPlayerMutex());
  155. if (!AttachToDevice()) {
  156. _failed = true;
  157. return;
  158. }
  159. }
  160. _alSource = CreateSource();
  161. _alBuffer = CreateBuffer();
  162. alBufferData(_alBuffer, _alFormat, _samples.data(), _samples.size(), _sampleRate);
  163. alSourcei(_alSource, AL_BUFFER, _alBuffer);
  164. }
  165. void Track::updateState() {
  166. if (!isActive() || !alIsSource(_alSource)) {
  167. return;
  168. }
  169. _stateUpdatedAt = crl::now();
  170. auto state = ALint(0);
  171. alGetSourcei(_alSource, AL_SOURCE_STATE, &state);
  172. if (state != AL_PLAYING) {
  173. finish();
  174. } else {
  175. auto currentPosition = ALint(0);
  176. alGetSourcei(_alSource, AL_SAMPLE_OFFSET, &currentPosition);
  177. _alPosition = currentPosition;
  178. }
  179. }
  180. float64 Track::getPeakValue(crl::time when) const {
  181. if (!isActive() || !_samplesCount || _peaks.empty() || _peakValueMin == _peakValueMax) {
  182. return 0.;
  183. }
  184. auto sampleIndex = (_alPosition + ((when - _stateUpdatedAt) * _sampleRate / 1000));
  185. while (sampleIndex < 0) {
  186. sampleIndex += _samplesCount;
  187. }
  188. sampleIndex = sampleIndex % _samplesCount;
  189. auto peakIndex = (sampleIndex / _peakEachPosition) % _peaks.size();
  190. return (_peaks[peakIndex] - _peakValueMin) / float64(_peakValueMax - _peakValueMin);
  191. }
  192. void Track::detachFromDevice() {
  193. if (alIsSource(_alSource)) {
  194. updateState();
  195. alSourceStop(_alSource);
  196. alSourcei(_alSource, AL_BUFFER, AL_NONE);
  197. alDeleteBuffers(1, &_alBuffer);
  198. alDeleteSources(1, &_alSource);
  199. }
  200. _alBuffer = 0;
  201. _alSource = 0;
  202. }
  203. void Track::reattachToDevice() {
  204. if (!isActive() || alIsSource(_alSource)) {
  205. return;
  206. }
  207. ensureSourceCreated();
  208. alSourcei(_alSource, AL_LOOPING, _looping ? 1 : 0);
  209. alSourcei(_alSource, AL_SAMPLE_OFFSET, static_cast<ALint>(_alPosition));
  210. alSourcePlay(_alSource);
  211. }
  212. Track::~Track() {
  213. detachFromDevice();
  214. _instance->unregisterTrack(this);
  215. }
  216. Instance::Instance()
  217. : _playbackDeviceId(
  218. &Core::App().mediaDevices(),
  219. Webrtc::DeviceType::Playback,
  220. Webrtc::DeviceIdOrDefault(
  221. Core::App().settings().playbackDeviceIdValue()))
  222. , _captureDeviceId(
  223. &Core::App().mediaDevices(),
  224. Webrtc::DeviceType::Capture,
  225. Webrtc::DeviceIdOrDefault(
  226. Core::App().settings().captureDeviceIdValue())) {
  227. _updateTimer.setCallback([this] {
  228. auto hasActive = false;
  229. for (auto track : _tracks) {
  230. track->updateState();
  231. if (track->isActive()) {
  232. hasActive = true;
  233. }
  234. }
  235. if (hasActive) {
  236. Audio::StopDetachIfNotUsedSafe();
  237. }
  238. });
  239. _detachFromDeviceTimer.setCallback([=] {
  240. _detachFromDeviceForce = false;
  241. Player::internal::DetachFromDevice(this);
  242. });
  243. _playbackDeviceId.changes(
  244. ) | rpl::start_with_next([=](Webrtc::DeviceResolvedId id) {
  245. if (Player::internal::DetachIfDeviceChanged(this, id)) {
  246. _detachFromDeviceForce = false;
  247. }
  248. }, _lifetime);
  249. }
  250. Webrtc::DeviceResolvedId Instance::playbackDeviceId() const {
  251. return _playbackDeviceId.threadSafeCurrent();
  252. }
  253. Webrtc::DeviceResolvedId Instance::captureDeviceId() const {
  254. return _captureDeviceId.current();
  255. }
  256. std::unique_ptr<Track> Instance::createTrack() {
  257. return std::make_unique<Track>(this);
  258. }
  259. Instance::~Instance() {
  260. Expects(_tracks.empty());
  261. }
  262. void Instance::registerTrack(Track *track) {
  263. _tracks.insert(track);
  264. }
  265. void Instance::unregisterTrack(Track *track) {
  266. _tracks.erase(track);
  267. }
  268. void Instance::trackStarted(Track *track) {
  269. stopDetachIfNotUsed();
  270. if (!_updateTimer.isActive()) {
  271. _updateTimer.callEach(kTrackUpdateTimeout);
  272. }
  273. }
  274. void Instance::trackFinished(Track *track) {
  275. if (!hasActiveTracks()) {
  276. _updateTimer.cancel();
  277. scheduleDetachIfNotUsed();
  278. }
  279. }
  280. void Instance::detachTracks() {
  281. for (auto track : _tracks) {
  282. track->detachFromDevice();
  283. }
  284. }
  285. void Instance::reattachTracks() {
  286. if (!IsAttachedToDevice()) {
  287. return;
  288. }
  289. for (auto track : _tracks) {
  290. track->reattachToDevice();
  291. }
  292. }
  293. bool Instance::hasActiveTracks() const {
  294. for (auto track : _tracks) {
  295. if (track->isActive()) {
  296. return true;
  297. }
  298. }
  299. return false;
  300. }
  301. void Instance::scheduleDetachFromDevice() {
  302. _detachFromDeviceForce = true;
  303. scheduleDetachIfNotUsed();
  304. }
  305. void Instance::scheduleDetachIfNotUsed() {
  306. if (!_detachFromDeviceTimer.isActive()) {
  307. _detachFromDeviceTimer.callOnce(kDetachDeviceTimeout);
  308. }
  309. }
  310. void Instance::stopDetachIfNotUsed() {
  311. if (!_detachFromDeviceForce) {
  312. _detachFromDeviceTimer.cancel();
  313. }
  314. }
  315. Instance &Current() {
  316. return Core::App().audio();
  317. }
  318. } // namespace Audio
  319. } // namespace Media