Skip to content

Commit 6d00417

Browse files
strukturedclaude
andcommitted
Fix PCM buffer thread safety with proper mutex
The circular audio buffer was protected only by a std::atomic on the write index, which is insufficient: a multi-sample AddToBuffer() call can be interrupted mid-write by UpdateFrameAudioData() reading the buffer, producing torn waveform data. Replace the atomic index with a std::mutex that protects both the buffer writes in AddToBuffer() and the bulk copy in UpdateFrameAudioData(). The lock is held only for the buffer access, not for the downstream spectrum analysis. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3649044 commit 6d00417

2 files changed

Lines changed: 14 additions & 6 deletions

File tree

src/libprojectM/Audio/PCM.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "Audio/PCM.hpp"
22

3+
#include <mutex>
4+
35
namespace libprojectM {
46
namespace Audio {
57

@@ -17,6 +19,7 @@ void PCM::AddToBuffer(
1719
return;
1820
}
1921

22+
std::lock_guard<std::mutex> lock(m_pcmMutex);
2023
for (size_t i = 0; i < sampleCount; i++)
2124
{
2225
size_t const bufferOffset = (m_start + i) % AudioBufferSamples;
@@ -48,9 +51,12 @@ void PCM::Add(int16_t const* const samples, uint32_t channels, size_t const coun
4851

4952
void PCM::UpdateFrameAudioData(double secondsSinceLastFrame, uint32_t frame)
5053
{
51-
// 1. Copy audio data from input buffer
52-
CopyNewWaveformData(m_inputBufferL, m_waveformL);
53-
CopyNewWaveformData(m_inputBufferR, m_waveformR);
54+
// 1. Copy audio data from input buffer (lock to prevent tearing with audio thread writes)
55+
{
56+
std::lock_guard<std::mutex> lock(m_pcmMutex);
57+
CopyNewWaveformData(m_inputBufferL, m_waveformL);
58+
CopyNewWaveformData(m_inputBufferR, m_waveformR);
59+
}
5460

5561
// 2. Update spectrum analyzer data for both channels
5662
UpdateSpectrum(m_waveformL, m_spectrumL);
@@ -110,7 +116,7 @@ void PCM::UpdateSpectrum(const WaveformBuffer& waveformData, SpectrumBuffer& spe
110116

111117
void PCM::CopyNewWaveformData(const WaveformBuffer& source, WaveformBuffer& destination)
112118
{
113-
auto const bufferStartIndex = m_start.load();
119+
auto const bufferStartIndex = m_start;
114120

115121
for (size_t i = 0; i < AudioBufferSamples; i++)
116122
{

src/libprojectM/Audio/PCM.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
#include <projectM-4/projectM_cxx_export.h>
1717

18-
#include <atomic>
1918
#include <cstdint>
2019
#include <cstdlib>
20+
#include <mutex>
2121

2222

2323
namespace libprojectM {
@@ -89,10 +89,12 @@ class PROJECTM_CXX_EXPORT PCM
8989
*/
9090
void CopyNewWaveformData(const WaveformBuffer& source, WaveformBuffer& destination);
9191

92+
std::mutex m_pcmMutex; //!< Protects the circular input buffer from concurrent access.
93+
9294
// External input buffer
9395
WaveformBuffer m_inputBufferL{0.f}; //!< Circular buffer for left-channel PCM data.
9496
WaveformBuffer m_inputBufferR{0.f}; //!< Circular buffer for right-channel PCM data.
95-
std::atomic<size_t> m_start{0}; //!< Circular buffer start index.
97+
size_t m_start{0}; //!< Circular buffer start index.
9698

9799
// Frame waveform data
98100
WaveformBuffer m_waveformL{0.f}; //!< Left-channel waveform data, aligned. Only the first WaveformSamples number of samples are valid.

0 commit comments

Comments
 (0)