| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 | #pragma once#include <atomic>#include <cmath>#include <functional>#include <iostream>#include <memory>#include <mutex>#include "BellUtils.h"#include "CircularBuffer.h"#include "StreamInfo.h"#include "WrappedSemaphore.h"#ifdef _WIN32#define __attribute__(X)#endiftypedef std::function<void(std::string)> shutdownEventHandler;namespace bell {class CentralAudioBuffer { private:  std::mutex accessMutex;  std::atomic<bool> isLocked = false;  std::mutex dataAccessMutex; public:  static const size_t PCM_CHUNK_SIZE = 4096;  std::unique_ptr<bell::WrappedSemaphore> chunkReady;  // Audio marker for track change detection, and DSP autoconfig  struct AudioChunk {    // Timeval    int32_t sec;    int32_t usec;    // Unique track hash, used for track change detection    size_t trackHash;    // Audio format    uint32_t sampleRate;    uint8_t channels;    uint8_t bitWidth;    // PCM data size    size_t pcmSize;    // PCM data    uint8_t pcmData[PCM_CHUNK_SIZE];  } __attribute__((packed));  CentralAudioBuffer(size_t chunks) {    audioBuffer = std::make_shared<CircularBuffer>(chunks * sizeof(AudioChunk));    chunkReady = std::make_unique<bell::WrappedSemaphore>(50);  }  std::shared_ptr<bell::CircularBuffer> audioBuffer;  uint32_t currentSampleRate = 44100;  /**	 * Returns current sample rate	 * @return sample rate	 */  uint32_t getSampleRate() { return currentSampleRate; }  /**	 * Clears input buffer, to be called for track change and such	 */  void clearBuffer() {    std::scoped_lock lock(this->dataAccessMutex);    audioBuffer->emptyBuffer();    hasChunk = false;  }  void emptyCompletely() {    std::scoped_lock lock(this->dataAccessMutex);    audioBuffer->emptyBuffer();  }  bool hasAtLeast(size_t chunks) {    return this->audioBuffer->size() >= chunks * sizeof(AudioChunk);  }  /**	 * Locks access to audio buffer. Call after starting playback	 */  void lockAccess() {    if (!isLocked) {      clearBuffer();      this->accessMutex.lock();      isLocked = true;    }  }  /**	 * Frees access to the audio buffer. Call during shutdown	 */  void unlockAccess() {    if (isLocked) {      clearBuffer();      this->accessMutex.unlock();      isLocked = false;    }  }  AudioChunk currentChunk = {};  bool hasChunk = false;  AudioChunk lastReadChunk = {};  AudioChunk* readChunk() {    std::scoped_lock lock(this->dataAccessMutex);    if (audioBuffer->size() < sizeof(AudioChunk)) {      lastReadChunk.pcmSize = 0;      return nullptr;    }    audioBuffer->read((uint8_t*)&lastReadChunk, sizeof(AudioChunk));    currentSampleRate = static_cast<uint32_t>(lastReadChunk.sampleRate);    return &lastReadChunk;  }  size_t writePCM(const uint8_t* data, size_t dataSize, size_t hash,                  uint32_t sampleRate = 44100, uint8_t channels = 2,                  BitWidth bitWidth = BitWidth::BW_16, int32_t sec = 0,                  int32_t usec = 0) {    std::scoped_lock lock(this->dataAccessMutex);    if (hasChunk && (currentChunk.trackHash != hash ||                     currentChunk.pcmSize >= PCM_CHUNK_SIZE)) {      if ((audioBuffer->size() - audioBuffer->capacity()) <          sizeof(AudioChunk)) {        return 0;      }      // Track changed or buf full, return current chunk      hasChunk = false;      this->audioBuffer->write((uint8_t*)¤tChunk, sizeof(AudioChunk));      // this->chunkReady->give();    }    // New chunk requested, initialize    if (!hasChunk) {      currentChunk.trackHash = hash;      currentChunk.sampleRate = sampleRate;      currentChunk.channels = channels;      currentChunk.bitWidth = 16;      currentChunk.sec = sec;      currentChunk.usec = usec;      currentChunk.pcmSize = 0;      hasChunk = true;    }    // Calculate how much data we can write    size_t toWriteSize = dataSize;    if (currentChunk.pcmSize + toWriteSize > PCM_CHUNK_SIZE) {      toWriteSize = PCM_CHUNK_SIZE - currentChunk.pcmSize;    }    // Copy it over :)    memcpy(currentChunk.pcmData + currentChunk.pcmSize, data, toWriteSize);    currentChunk.pcmSize += toWriteSize;    return toWriteSize;  }};}  // namespace bell
 |