| #pragma once |
|
|
| #include <cstdint> |
| #include <cstring> |
| #include <atomic> |
| #include <string> |
|
|
| #ifdef __APPLE__ |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <semaphore.h> |
| #include <dispatch/dispatch.h> |
| #endif |
|
|
| |
| |
|
|
| struct SharedAudioHeader { |
| std::atomic<uint32_t> sampleRate{44100}; |
| std::atomic<uint32_t> blockSize{512}; |
| std::atomic<uint32_t> numChannels{2}; |
| std::atomic<uint32_t> state{0}; |
| std::atomic<uint32_t> helperAlive{0}; |
| uint32_t padding[3]; |
| }; |
|
|
| |
| |
| |
| |
|
|
| static constexpr uint32_t kStateIdle = 0; |
| static constexpr uint32_t kStateInputReady = 1; |
| static constexpr uint32_t kStateOutputReady = 2; |
|
|
| |
| static constexpr size_t kMaxChannels = 2; |
| static constexpr size_t kMaxBlockSize = 4096; |
| static constexpr size_t kAudioDataSize = kMaxChannels * kMaxBlockSize * sizeof(float); |
| static constexpr size_t kSharedMemSize = sizeof(SharedAudioHeader) + (kAudioDataSize * 2); |
|
|
| class SharedAudioBuffer { |
| public: |
| SharedAudioBuffer() = default; |
| ~SharedAudioBuffer() { destroy(); } |
|
|
| |
| bool create(const std::string& name) |
| { |
| shmName = "/" + name; |
|
|
| |
| shm_unlink(shmName.c_str()); |
|
|
| fd = shm_open(shmName.c_str(), O_CREAT | O_RDWR, 0666); |
| if (fd < 0) return false; |
|
|
| if (ftruncate(fd, kSharedMemSize) != 0) { |
| close(fd); |
| return false; |
| } |
|
|
| ptr = (uint8_t*)mmap(nullptr, kSharedMemSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| if (ptr == MAP_FAILED) { |
| close(fd); |
| return false; |
| } |
|
|
| |
| memset(ptr, 0, kSharedMemSize); |
|
|
| header = reinterpret_cast<SharedAudioHeader*>(ptr); |
| inputBuffer = reinterpret_cast<float*>(ptr + sizeof(SharedAudioHeader)); |
| outputBuffer = reinterpret_cast<float*>(ptr + sizeof(SharedAudioHeader) + kAudioDataSize); |
|
|
| isOwner = true; |
| return true; |
| } |
|
|
| bool open(const std::string& name) |
| { |
| shmName = "/" + name; |
|
|
| fd = shm_open(shmName.c_str(), O_RDWR, 0666); |
| if (fd < 0) return false; |
|
|
| ptr = (uint8_t*)mmap(nullptr, kSharedMemSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| if (ptr == MAP_FAILED) { |
| close(fd); |
| return false; |
| } |
|
|
| header = reinterpret_cast<SharedAudioHeader*>(ptr); |
| inputBuffer = reinterpret_cast<float*>(ptr + sizeof(SharedAudioHeader)); |
| outputBuffer = reinterpret_cast<float*>(ptr + sizeof(SharedAudioHeader) + kAudioDataSize); |
|
|
| isOwner = false; |
| return true; |
| } |
|
|
| void destroy() |
| { |
| if (ptr && ptr != MAP_FAILED) { |
| munmap(ptr, kSharedMemSize); |
| ptr = nullptr; |
| } |
| if (fd >= 0) { |
| close(fd); |
| fd = -1; |
| } |
| if (isOwner && !shmName.empty()) { |
| shm_unlink(shmName.c_str()); |
| } |
| } |
|
|
| |
|
|
| SharedAudioHeader* getHeader() { return header; } |
|
|
| void writeInput(const float* const* channelData, int numChannels, int numSamples) |
| { |
| int ch = std::min(numChannels, (int)kMaxChannels); |
| int ns = std::min(numSamples, (int)kMaxBlockSize); |
| for (int c = 0; c < ch; ++c) |
| memcpy(inputBuffer + c * kMaxBlockSize, channelData[c], ns * sizeof(float)); |
| } |
|
|
| void readOutput(float* const* channelData, int numChannels, int numSamples) |
| { |
| int ch = std::min(numChannels, (int)kMaxChannels); |
| int ns = std::min(numSamples, (int)kMaxBlockSize); |
| for (int c = 0; c < ch; ++c) |
| memcpy(channelData[c], outputBuffer + c * kMaxBlockSize, ns * sizeof(float)); |
| } |
|
|
| void readInput(float* const* channelData, int numChannels, int numSamples) |
| { |
| int ch = std::min(numChannels, (int)kMaxChannels); |
| int ns = std::min(numSamples, (int)kMaxBlockSize); |
| for (int c = 0; c < ch; ++c) |
| memcpy(channelData[c], inputBuffer + c * kMaxBlockSize, ns * sizeof(float)); |
| } |
|
|
| void writeOutput(const float* const* channelData, int numChannels, int numSamples) |
| { |
| int ch = std::min(numChannels, (int)kMaxChannels); |
| int ns = std::min(numSamples, (int)kMaxBlockSize); |
| for (int c = 0; c < ch; ++c) |
| memcpy(outputBuffer + c * kMaxBlockSize, channelData[c], ns * sizeof(float)); |
| } |
|
|
| bool isValid() const { return ptr != nullptr && ptr != MAP_FAILED; } |
|
|
| private: |
| uint8_t* ptr = nullptr; |
| int fd = -1; |
| bool isOwner = false; |
| std::string shmName; |
|
|
| SharedAudioHeader* header = nullptr; |
| float* inputBuffer = nullptr; |
| float* outputBuffer = nullptr; |
| }; |
|
|
| |
|
|
| class SharedSemaphore { |
| public: |
| SharedSemaphore() = default; |
| ~SharedSemaphore() { destroy(); } |
|
|
| bool create(const std::string& name) |
| { |
| semName = name; |
| |
| sem = sem_open(("/" + name).c_str(), O_CREAT, 0666, 0); |
| return sem != SEM_FAILED; |
| } |
|
|
| bool open(const std::string& name) |
| { |
| semName = name; |
| sem = sem_open(("/" + name).c_str(), 0); |
| return sem != SEM_FAILED; |
| } |
|
|
| void signal() |
| { |
| if (sem != SEM_FAILED) |
| sem_post(sem); |
| } |
|
|
| bool wait(int timeoutMs = 100) |
| { |
| if (sem == SEM_FAILED) return false; |
|
|
| |
| for (int i = 0; i < timeoutMs; ++i) { |
| if (sem_trywait(sem) == 0) |
| return true; |
| usleep(1000); |
| } |
| return false; |
| } |
|
|
| bool tryWait() |
| { |
| if (sem == SEM_FAILED) return false; |
| return sem_trywait(sem) == 0; |
| } |
|
|
| void destroy() |
| { |
| if (sem != SEM_FAILED) { |
| sem_close(sem); |
| sem_unlink(("/" + semName).c_str()); |
| sem = SEM_FAILED; |
| } |
| } |
|
|
| private: |
| sem_t* sem = SEM_FAILED; |
| std::string semName; |
| }; |
|
|