CentralAudioBuffer.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #pragma once
  2. #include <atomic>
  3. #include <cmath>
  4. #include <iostream>
  5. #include <memory>
  6. #include <mutex>
  7. #include <functional>
  8. #include "BellUtils.h"
  9. #include "CircularBuffer.h"
  10. #include "StreamInfo.h"
  11. #include "WrappedSemaphore.h"
  12. #ifdef _WIN32
  13. #define __attribute__(X)
  14. #endif
  15. typedef std::function<void(std::string)> shutdownEventHandler;
  16. namespace bell {
  17. class CentralAudioBuffer {
  18. private:
  19. std::mutex accessMutex;
  20. std::atomic<bool> isLocked = false;
  21. std::mutex dataAccessMutex;
  22. public:
  23. static const size_t PCM_CHUNK_SIZE = 4096;
  24. std::unique_ptr<bell::WrappedSemaphore> chunkReady;
  25. // Audio marker for track change detection, and DSP autoconfig
  26. struct AudioChunk {
  27. // Timeval
  28. int32_t sec;
  29. int32_t usec;
  30. // Unique track hash, used for track change detection
  31. size_t trackHash;
  32. // Audio format
  33. uint32_t sampleRate;
  34. uint8_t channels;
  35. uint8_t bitWidth;
  36. // PCM data size
  37. size_t pcmSize;
  38. // PCM data
  39. uint8_t pcmData[PCM_CHUNK_SIZE];
  40. } __attribute__((packed));
  41. CentralAudioBuffer(size_t chunks) {
  42. audioBuffer = std::make_shared<CircularBuffer>(chunks * sizeof(AudioChunk));
  43. chunkReady = std::make_unique<bell::WrappedSemaphore>(50);
  44. }
  45. std::shared_ptr<bell::CircularBuffer> audioBuffer;
  46. uint32_t currentSampleRate = 44100;
  47. /**
  48. * Returns current sample rate
  49. * @return sample rate
  50. */
  51. uint32_t getSampleRate() { return currentSampleRate; }
  52. /**
  53. * Clears input buffer, to be called for track change and such
  54. */
  55. void clearBuffer() {
  56. std::scoped_lock lock(this->dataAccessMutex);
  57. //size_t exceptSize = currentSampleRate + (sizeof(AudioChunk) - (currentSampleRate % sizeof(AudioChunk)));
  58. hasChunk = false;
  59. audioBuffer->emptyBuffer();
  60. }
  61. void emptyCompletely() {
  62. std::scoped_lock lock(this->dataAccessMutex);
  63. audioBuffer->emptyBuffer();
  64. }
  65. bool hasAtLeast(size_t chunks) {
  66. return this->audioBuffer->size() >= chunks * sizeof(AudioChunk);
  67. }
  68. /**
  69. * Locks access to audio buffer. Call after starting playback
  70. */
  71. void lockAccess() {
  72. if (!isLocked) {
  73. clearBuffer();
  74. this->accessMutex.lock();
  75. isLocked = true;
  76. }
  77. }
  78. /**
  79. * Frees access to the audio buffer. Call during shutdown
  80. */
  81. void unlockAccess() {
  82. if (isLocked) {
  83. clearBuffer();
  84. this->accessMutex.unlock();
  85. isLocked = false;
  86. }
  87. }
  88. AudioChunk currentChunk = { };
  89. bool hasChunk = false;
  90. AudioChunk lastReadChunk = { };
  91. AudioChunk* readChunk() {
  92. std::scoped_lock lock(this->dataAccessMutex);
  93. if (audioBuffer->size() < sizeof(AudioChunk)) {
  94. lastReadChunk.pcmSize = 0;
  95. return nullptr;
  96. }
  97. auto readBytes =
  98. audioBuffer->read((uint8_t*)&lastReadChunk, sizeof(AudioChunk));
  99. currentSampleRate = static_cast<uint32_t>(lastReadChunk.sampleRate);
  100. return &lastReadChunk;
  101. }
  102. size_t writePCM(const uint8_t* data, size_t dataSize, size_t hash,
  103. uint32_t sampleRate = 44100, uint8_t channels = 2,
  104. BitWidth bitWidth = BitWidth::BW_16, int32_t sec = 0,
  105. int32_t usec = 0) {
  106. std::scoped_lock lock(this->dataAccessMutex);
  107. if (hasChunk && (currentChunk.trackHash != hash ||
  108. currentChunk.pcmSize >= PCM_CHUNK_SIZE)) {
  109. if ((audioBuffer->size() - audioBuffer->capacity()) <
  110. sizeof(AudioChunk)) {
  111. return 0;
  112. }
  113. // Track changed or buf full, return current chunk
  114. hasChunk = false;
  115. this->audioBuffer->write((uint8_t*)&currentChunk, sizeof(AudioChunk));
  116. // this->chunkReady->give();
  117. }
  118. // New chunk requested, initialize
  119. if (!hasChunk) {
  120. currentChunk.trackHash = hash;
  121. currentChunk.sampleRate = sampleRate;
  122. currentChunk.channels = channels;
  123. currentChunk.bitWidth = 16;
  124. currentChunk.sec = sec;
  125. currentChunk.usec = usec;
  126. currentChunk.pcmSize = 0;
  127. hasChunk = true;
  128. }
  129. // Calculate how much data we can write
  130. size_t toWriteSize = dataSize;
  131. if (currentChunk.pcmSize + toWriteSize > PCM_CHUNK_SIZE) {
  132. toWriteSize = PCM_CHUNK_SIZE - currentChunk.pcmSize;
  133. }
  134. // Copy it over :)
  135. memcpy(currentChunk.pcmData + currentChunk.pcmSize, data, toWriteSize);
  136. currentChunk.pcmSize += toWriteSize;
  137. return toWriteSize;
  138. }
  139. };
  140. } // namespace bell