Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5a9c811
Ring buffer for recording first implementation
steven-jaro May 23, 2025
c6f8047
Fixed ring buffer implementation approach to make it more stable and …
steven-jaro May 25, 2025
2fc50ec
Better approach for no audio saturation, code optimization, no segfau…
steven-jaro May 25, 2025
2c43aa8
Removed resize and reserve from any real time context
steven-jaro May 29, 2025
aa33e45
Fixed spaces instead of tabs size 4 and Johannes requests
steven-jaro May 30, 2025
8541272
Changed DEFAULT_BUFFER_SIZE to 100
steven-jaro May 30, 2025
dad148e
Update src/core/AudioEngine.cpp
steven-jaro May 31, 2025
e6c1097
Update include/AudioEngine.h
steven-jaro May 31, 2025
e25a58e
Update src/core/AudioEngine.cpp
steven-jaro May 31, 2025
bb00189
Update src/core/AudioEngine.cpp
steven-jaro May 31, 2025
264d15b
Update src/core/AudioEngine.cpp
steven-jaro May 31, 2025
3d02d41
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
6b4d07d
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
69734f9
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
1dae270
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
d9a22b2
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
cae9d61
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
00ab49d
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
d6cbe34
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
ed4ecb8
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
2047da5
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
11432b1
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
5820a43
Update src/core/AudioEngine.cpp
steven-jaro Jun 1, 2025
52868fd
Fixed unsafe substraction in processBufferedInputFrames
steven-jaro Jun 2, 2025
89f6521
Fixed szeli1 Requested changes includir refator
steven-jaro Dec 4, 2025
45cdb83
Update src/core/AudioEngine.cpp
steven-jaro Dec 9, 2025
95bbfd6
Update src/core/AudioEngine.cpp
steven-jaro Dec 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixed szeli1 Requested changes includir refator
  • Loading branch information
steven-jaro committed Dec 4, 2025
commit 89f6521ceb77749889ce4c3bcf0324f4702e6274
9 changes: 3 additions & 6 deletions include/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,12 @@ class LMMS_EXPORT AudioEngine : public QObject

inline const SampleFrame* inputBuffer()
{
return m_inputBuffer[m_inputBufferRead];
return m_inputBuffer[m_inputBufferRead].data();
}

inline f_cnt_t inputBufferFrames() const
{
return m_inputBufferFrames[ m_inputBufferRead ];
return m_inputBuffer[m_inputBufferRead].size();
}

inline const SampleFrame* nextBuffer()
Expand Down Expand Up @@ -375,9 +375,7 @@ class LMMS_EXPORT AudioEngine : public QObject

fpp_t m_framesPerPeriod;

SampleFrame* m_inputBuffer[2];
f_cnt_t m_inputBufferFrames[2];
f_cnt_t m_inputBufferSize[2];
std::array<std::vector<SampleFrame>, 2> m_inputBuffer;
sample_rate_t m_baseSampleRate;
int m_inputBufferRead;
int m_inputBufferWrite;
Expand Down Expand Up @@ -426,7 +424,6 @@ class LMMS_EXPORT AudioEngine : public QObject

std::unique_ptr<LocklessRingBuffer<SampleFrame>> m_inputAudioRingBuffer;
std::unique_ptr<LocklessRingBufferReader<SampleFrame>> m_inputAudioRingBufferReader;
std::vector<SampleFrame> m_tempInputProcessingBuffer;
} ;

} // namespace lmms
Expand Down
33 changes: 14 additions & 19 deletions src/core/AudioEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,7 @@ AudioEngine::AudioEngine( bool renderOnly ) :
{
for( int i = 0; i < 2; ++i )
{
m_inputBufferFrames[i] = 0;
m_inputBufferSize[i] = FIXED_INPUT_BUFFER_CAPACITY;
m_inputBuffer[i] = new SampleFrame[FIXED_INPUT_BUFFER_CAPACITY];
zeroSampleFrames(m_inputBuffer[i], FIXED_INPUT_BUFFER_CAPACITY);
m_inputBuffer[i].reserve(FIXED_INPUT_BUFFER_CAPACITY);
}

// determine FIFO size and number of frames per period
Expand Down Expand Up @@ -157,7 +154,6 @@ AudioEngine::AudioEngine( bool renderOnly ) :

m_inputAudioRingBuffer = std::make_unique<LocklessRingBuffer<SampleFrame>>(FIXED_INPUT_BUFFER_CAPACITY);
m_inputAudioRingBufferReader = std::make_unique<LocklessRingBufferReader<SampleFrame>>(*m_inputAudioRingBuffer);
m_tempInputProcessingBuffer.reserve(m_inputAudioRingBuffer->capacity());
}


Expand Down Expand Up @@ -215,20 +211,18 @@ AudioEngine::~AudioEngine()
m_fifo = nullptr;
}

delete m_midiClient; m_midiClient = nullptr;
delete m_audioDev; m_audioDev = nullptr;
delete m_midiClient;
m_midiClient = nullptr;
delete m_audioDev;
m_audioDev = nullptr;

if (m_oldAudioDev)
{
delete m_oldAudioDev;
}
m_oldAudioDev = nullptr;

for (int i = 0; i < 2; ++i)
{
delete[] m_inputBuffer[i];
m_inputBuffer[i] = nullptr;
}
// Input buffers are std::vectors and clean up automatically
}


Expand Down Expand Up @@ -347,9 +341,9 @@ void AudioEngine::processBufferedInputFrames()

requestChangeInModel();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really real time safe when it uses mutexes?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is real-time safe. The mutex in requestChangeInModel() is NOT in the real-time
audio callback path. Here's why:

Normal playback mode:

  • Uses fifoWriter (needsFifo = true by default in startProcessing())
  • Audio callback only calls nextBuffer()m_fifo->read() (lockless, no mutex)
  • The fifoWriter thread (separate, non-RT thread) calls renderNextBuffer() which includes
    the mutex, but this is outside the RT path

Export/rendering mode:

  • Doesn't use fifoWriter (needsFifo = false in ProjectRenderer.cpp:171)
  • But there's no real-time audio callback in this mode, just writing to a file
  • No RT constraints, so mutex is perfectly fine

The architecture ensures that pushInputFrames() (called from audio device callback)
only writes to the lockless ring buffer, while processBufferedInputFrames() (which has
the mutex) runs either in the fifoWriter thread or in rendering mode where RT-safety
isn't required.


SampleFrame* currentWriteBufPtr = m_inputBuffer[m_inputBufferWrite];
f_cnt_t currentWriteBufCapacity = m_inputBufferSize[m_inputBufferWrite];
f_cnt_t currentFramesInWriteBuf = m_inputBufferFrames[m_inputBufferWrite];
auto& currentWriteBuf = m_inputBuffer[m_inputBufferWrite];
f_cnt_t currentWriteBufCapacity = currentWriteBuf.capacity();
f_cnt_t currentFramesInWriteBuf = currentWriteBuf.size();

f_cnt_t totalFramesSuccessfullyCopiedToMain = 0;

Expand All @@ -370,7 +364,8 @@ void AudioEngine::processBufferedInputFrames()
framesWeCanActuallyCopy - totalFramesSuccessfullyCopiedToMain);
Comment thread
steven-jaro marked this conversation as resolved.
if (framesToCopyFromPart1 > 0)
{
memcpy(&currentWriteBufPtr[currentFramesInWriteBuf + totalFramesSuccessfullyCopiedToMain],
currentWriteBuf.resize(currentFramesInWriteBuf + totalFramesSuccessfullyCopiedToMain + framesToCopyFromPart1);
memcpy(&currentWriteBuf[currentFramesInWriteBuf + totalFramesSuccessfullyCopiedToMain],
sequence.first_half_ptr(), framesToCopyFromPart1 * sizeof(SampleFrame));
totalFramesSuccessfullyCopiedToMain += framesToCopyFromPart1;
}
Expand All @@ -382,13 +377,13 @@ void AudioEngine::processBufferedInputFrames()
framesWeCanActuallyCopy - totalFramesSuccessfullyCopiedToMain);
Comment thread
steven-jaro marked this conversation as resolved.
if (framesToCopyFromPart2 > 0)
{
memcpy(&currentWriteBufPtr[currentFramesInWriteBuf + totalFramesSuccessfullyCopiedToMain],
currentWriteBuf.resize(currentFramesInWriteBuf + totalFramesSuccessfullyCopiedToMain + framesToCopyFromPart2);
memcpy(&currentWriteBuf[currentFramesInWriteBuf + totalFramesSuccessfullyCopiedToMain],
sequence.second_half_ptr(), framesToCopyFromPart2 * sizeof(SampleFrame));
Comment thread
steven-jaro marked this conversation as resolved.
totalFramesSuccessfullyCopiedToMain += framesToCopyFromPart2;
}
}

m_inputBufferFrames[m_inputBufferWrite] += totalFramesSuccessfullyCopiedToMain;

doneChangeInModel();
}
Expand Down Expand Up @@ -551,7 +546,7 @@ void AudioEngine::swapBuffers()
{
m_inputBufferWrite = (m_inputBufferWrite + 1) % 2;
m_inputBufferRead = (m_inputBufferRead + 1) % 2;
m_inputBufferFrames[m_inputBufferWrite] = 0;
m_inputBuffer[m_inputBufferWrite].clear();

std::swap(m_outputBufferRead, m_outputBufferWrite);
zeroSampleFrames(m_outputBufferWrite.get(), m_framesPerPeriod);
Expand Down
Loading