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)
- #endif
- typedef 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
|