Download Source/Shared/SharedAudioBuffer.h from RAM2118/pluginbridge: direct link, hf CLI and curl.
- Browser
- Download file 6.66 kB
-
https://huggingface.co/RAM2118/pluginbridge/resolve/main/Source/Shared/SharedAudioBuffer.h
- Command line
-
hf download hf://RAM2118/pluginbridge/Source/Shared/SharedAudioBuffer.h
-
curl -L -o SharedAudioBuffer.h https://huggingface.co/RAM2118/pluginbridge/resolve/main/Source/Shared/SharedAudioBuffer.h
6.66 kB
| // Shared memory layout for audio transport between Plugin and Helper | |
| // Zero-copy: both processes mmap the same region | |
| 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}; // 0=idle, 1=input_ready, 2=output_ready | |
| std::atomic<uint32_t> helperAlive{0}; // heartbeat from helper | |
| uint32_t padding[3]; // align to 32 bytes | |
| }; | |
| // State machine: | |
| // Parent: writes input → sets state=1 → signals helper | |
| // Helper: reads input → processBlock → writes output → sets state=2 → signals parent | |
| // Parent: reads output → sets state=0 | |
| static constexpr uint32_t kStateIdle = 0; | |
| static constexpr uint32_t kStateInputReady = 1; | |
| static constexpr uint32_t kStateOutputReady = 2; | |
| // Max supported: stereo, 4096 samples per block | |
| 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); // input + output | |
| class SharedAudioBuffer { | |
| public: | |
| SharedAudioBuffer() = default; | |
| ~SharedAudioBuffer() { destroy(); } | |
| // Create (parent) or open (helper) shared memory | |
| bool create(const std::string& name) | |
| { | |
| shmName = "/" + name; | |
| // Remove if exists from previous crash | |
| 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; | |
| } | |
| // Zero the whole region | |
| 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()); | |
| } | |
| } | |
| // --- Audio data access --- | |
| 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; | |
| }; | |
| // --- Semaphore wrapper (macOS uses dispatch semaphores since POSIX named sems are deprecated) --- | |
| class SharedSemaphore { | |
| public: | |
| SharedSemaphore() = default; | |
| ~SharedSemaphore() { destroy(); } | |
| bool create(const std::string& name) | |
| { | |
| semName = name; | |
| // macOS: use named POSIX semaphore (still works despite deprecation warning) | |
| 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; | |
| // macOS doesn't support sem_timedwait, use polling | |
| for (int i = 0; i < timeoutMs; ++i) { | |
| if (sem_trywait(sem) == 0) | |
| return true; | |
| usleep(1000); // 1ms | |
| } | |
| 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; | |
| }; | |