Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a9b04a4
Remove FIFO thread
sakertooth Mar 18, 2025
378d6b4
Fix selection of frames per period
sakertooth Mar 18, 2025
c02c27f
Add "is running" flag for audio devices
sakertooth Mar 18, 2025
1311ce6
Remove extra start/stop flags in audio devices
sakertooth Mar 18, 2025
9d9ca6f
Revert "Remove extra start/stop flags in audio devices"
sakertooth Mar 18, 2025
27272f7
Store separate frames per period size in audio device
sakertooth Mar 18, 2025
5eaff81
Add chunking
sakertooth Mar 19, 2025
07737ce
Fix dummy sound output
sakertooth Mar 19, 2025
72d2882
Specify nullptr explicitly
sakertooth Mar 19, 2025
bd52612
Attempt to fix MINGW and MSVC builds
sakertooth Mar 19, 2025
0a130b4
Attempt to fix MINGW and MSVC builds
sakertooth Mar 19, 2025
7fb14b7
Pass in size parameter to getNextBuffer, return bool instead
sakertooth Mar 19, 2025
b84bec1
Remove processNextBuffer and move writeBuffer into AudioDevice
sakertooth Mar 23, 2025
aeb5fdf
Create AudioDevice::nextBuffer function to simplify translation of re…
sakertooth Mar 23, 2025
c8ef1a6
Fix nextBuffer function
sakertooth Mar 23, 2025
3dd62ee
Calculate the number of frames based on the length parameter in sdlAu…
sakertooth Mar 23, 2025
7ebb85a
Use SND_PCM_FORMAT_FLOAT in ALSA
sakertooth Mar 23, 2025
6597ce3
Remove assert
sakertooth Mar 23, 2025
cfd51c1
Fix comparison warnings
sakertooth Mar 23, 2025
f414cf5
Pass in correct frame count when fetching buffers in PortAudio callback
sakertooth Mar 23, 2025
8e5cede
Use PA_SAMPLE_FLOAT32 in PulseAudio
sakertooth Mar 23, 2025
a7d5e4b
Use pa_stream_begin_write to avoid extra copies
sakertooth Mar 23, 2025
f5dc315
Fix soundio callback
sakertooth Mar 23, 2025
f5d0e4c
Do not ignore return type on write call in OSS
sakertooth Mar 23, 2025
d562fe9
Ensure to silence buffer if nextBuffer returns false for callback-bas…
sakertooth Mar 23, 2025
10c55da
Use same signedness
sakertooth Mar 23, 2025
b5bfa07
Fix more signedness issues
sakertooth Mar 23, 2025
bc0abb7
Fix a few issues
sakertooth Mar 25, 2025
f23122b
Split nextBuffer declaration into two (one for interleaved, one for p…
sakertooth Jun 1, 2025
4d1e182
Update src/core/audio/AudioAlsa.cpp
sakertooth Jun 18, 2025
821189e
Use static_cast instead of reinterpret_cast
sakertooth Jun 28, 2025
ce20d72
Use new audio buffer view classes
sakertooth Jun 28, 2025
abd51d6
Render next buffer after resetting timer
sakertooth Jun 28, 2025
ee70d3f
Remove endian handling within audio devices
sakertooth Jun 28, 2025
243f299
Make start and stop functions non-virtual, add pure virtual impl func…
sakertooth Jun 28, 2025
8ffa18a
Make startProcessing and stopProcessing non virtual
sakertooth Jun 29, 2025
0dd7795
Remove unused function that was added in here
sakertooth Jun 29, 2025
717e49f
Remove temp
sakertooth Jul 3, 2025
9a7fc69
Add isRunning function
sakertooth Jul 5, 2025
5bdff28
Merge remote-tracking branch 'upstream' into fix-resampling
sakertooth Jul 31, 2025
a7df657
Fix build
sakertooth Jul 31, 2025
1eb4ef4
Merge remote-tracking branch 'upstream' into revamp-buffers
sakertooth Aug 15, 2025
195b823
Simplify nextBuffer function with new AudioBufferView concept
sakertooth Aug 16, 2025
f8995b7
Simplify loop even further
sakertooth Aug 16, 2025
4d0d8fe
Check if device running in while loop
sakertooth Aug 16, 2025
05f41b2
Remove m_quit variable
sakertooth Aug 16, 2025
cd63c6a
Add sample function in buffer view classes
sakertooth Sep 8, 2025
5f71b0b
Remove framesPerPeriod from AudioDevice
sakertooth Sep 11, 2025
af53322
Set the buffer size for the JACK backend
sakertooth Sep 11, 2025
872baef
Use framesPerAudioBuffer where appropriate
sakertooth Sep 11, 2025
26ba7ad
Move template instantiations down the file
sakertooth Sep 13, 2025
c1857a2
Do not call stopProcessing in destructor, assert that device is stopp…
sakertooth Sep 13, 2025
6c97be8
Merge remote-tracking branch 'upstream' into revamp-buffers
sakertooth Nov 16, 2025
e00f57f
Set frames per audio buffer within engine
sakertooth Nov 21, 2025
4a17480
Add new renderNextPeriod function, move use of nextBuffer to renderNe…
sakertooth Nov 22, 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
7 changes: 2 additions & 5 deletions include/AudioAlsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ class AudioAlsa : public QThread, public AudioDevice
static DeviceInfoCollection getAvailableDevices();

private:
void startProcessing() override;
void stopProcessing() override;
void startProcessingImpl() override;
void stopProcessingImpl() override;
void run() override;

int setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access );
Expand All @@ -98,9 +98,6 @@ class AudioAlsa : public QThread, public AudioDevice

snd_pcm_hw_params_t * m_hwParams;
snd_pcm_sw_params_t * m_swParams;

bool m_convertEndian;

} ;

} // namespace lmms
Expand Down
12 changes: 12 additions & 0 deletions include/AudioBufferView.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,12 @@ class InterleavedBufferView : public detail::BufferViewData<T, channelCount>
}
}

//! @return the sample at the given channel and frame position
constexpr auto sample(proc_ch_t channel, f_cnt_t frame) noexcept -> T&
{
return framePtr(frame)[channel];
}

/**
* @return pointer to the frame at the given index.
* The size of the frame is `channels()`.
Expand Down Expand Up @@ -501,6 +507,12 @@ class PlanarBufferView : public detail::BufferViewData<T* const, channelCount>
return !this->m_data || Base::channels() == 0 || this->m_frames == 0;
}

//! @return the sample at the given channel and frame position
constexpr auto sample(proc_ch_t channel, f_cnt_t frame) noexcept -> T&
{
return bufferPtr(channel)[frame];
}

//! @return the buffer of the given channel
constexpr auto buffer(proc_ch_t channel) const noexcept -> std::span<T>
{
Expand Down
54 changes: 16 additions & 38 deletions include/AudioDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <QMutex>
#include <samplerate.h>

#include "AudioBufferView.h"
#include "LmmsTypes.h"

class QThread;
Expand Down Expand Up @@ -75,66 +76,43 @@ class AudioDevice
return m_sampleRate;
}

void processNextBuffer();
void startProcessing();

virtual void startProcessing()
{
m_inProcess = true;
}
void stopProcessing();

virtual void stopProcessing();
bool isRunning() const { return m_running.test(std::memory_order_acquire); }

protected:
// subclasses can re-implement this for being used in conjunction with
// processNextBuffer()
virtual void writeBuffer(const SampleFrame* /* _buf*/, const fpp_t /*_frames*/) {}

// called by according driver for fetching new sound-data
fpp_t getNextBuffer(SampleFrame* _ab);

// convert a given audio-buffer to a buffer in signed 16-bit samples
// returns num of bytes in outbuf
int convertToS16(const SampleFrame* _ab,
const fpp_t _frames,
int_sample_t * _output_buffer,
const bool _convert_endian = false );
int convertToS16(
const SampleFrame* _ab, const fpp_t _frames, int_sample_t* _output_buffer, const bool _convert_endian = false);

// clear given signed-int-16-buffer
void clearS16Buffer( int_sample_t * _outbuf,
const fpp_t _frames );
void clearS16Buffer(int_sample_t* _outbuf, const fpp_t _frames);

ch_cnt_t channels() const
{
return m_channels;
}
ch_cnt_t channels() const { return m_channels; }

inline void setSampleRate( const sample_rate_t _new_sr )
{
m_sampleRate = _new_sr;
}
AudioEngine* audioEngine() { return m_audioEngine; }

AudioEngine* audioEngine()
{
return m_audioEngine;
}
void setSampleRate(const sample_rate_t _new_sr) { m_sampleRate = _new_sr; }

static void stopProcessingThread( QThread * thread );


protected:
bool m_supportsCapture;


private:
virtual void startProcessingImpl() = 0;
virtual void stopProcessingImpl() = 0;

sample_rate_t m_sampleRate;
ch_cnt_t m_channels;
AudioEngine* m_audioEngine;
bool m_inProcess;

QMutex m_devMutex;
AudioEngine* m_audioEngine = nullptr;

SampleFrame* m_buffer;
QMutex m_devMutex;

std::atomic_flag m_running = ATOMIC_FLAG_INIT;
};

} // namespace lmms
Expand Down
16 changes: 4 additions & 12 deletions include/AudioDummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,31 +78,23 @@ class AudioDummy : public QThread, public AudioDevice


private:
void startProcessing() override
void startProcessingImpl() override
{
start();
}

void stopProcessing() override
void stopProcessingImpl() override
{
stopProcessingThread( this );
}

void run() override
{
MicroTimer timer;
while( true )
while (AudioDevice::isRunning())
{
timer.reset();
const SampleFrame* b = audioEngine()->nextBuffer();
if( !b )
{
break;
}
if( audioEngine()->hasFifoWriter() )
{
delete[] b;
}
audioEngine()->renderNextPeriod();

const int microseconds = static_cast<int>( audioEngine()->framesPerPeriod() * 1000000.0f / audioEngine()->outputSampleRate() - timer.elapsed() );
if( microseconds > 0 )
Expand Down
69 changes: 26 additions & 43 deletions include/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include "LmmsTypes.h"
#include "SampleFrame.h"
#include "LocklessList.h"
#include "FifoBuffer.h"
#include "AudioEngineProfiler.h"
#include "PlayHandle.h"

Expand All @@ -59,6 +58,8 @@ constexpr int BYTES_PER_FRAME = sizeof(SampleFrame);

constexpr float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f;

constexpr sample_rate_t DEFAULT_SAMPLE_RATE = 44100;

constexpr auto SUPPORTED_SAMPLERATES = std::array{44100, 48000, 88200, 96000, 192000};

class LMMS_EXPORT AudioEngine : public QObject
Expand Down Expand Up @@ -126,7 +127,7 @@ class LMMS_EXPORT AudioEngine : public QObject

//! Set new audio device. Old device will be deleted,
//! unless it's stored using storeAudioDevice
void setAudioDevice(AudioDevice* _dev, bool _needs_fifo, bool startNow);
void setAudioDevice(AudioDevice* _dev, bool startNow);
void storeAudioDevice();
void restoreAudioDevice();
inline AudioDevice * audioDev()
Expand Down Expand Up @@ -170,13 +171,19 @@ class LMMS_EXPORT AudioEngine : public QObject

void removePlayHandlesOfTypes(Track * track, PlayHandle::Types types);


// methods providing information for other classes
inline fpp_t framesPerPeriod() const
//! @return the number of frames rendered per period
//! @note this represents the size of the buffer for each call to @ref renderNextBuffer
fpp_t framesPerPeriod() const
{
return m_framesPerPeriod;
}

//! @returns the number of audio frames per audio buffer
//! @note this represents the size of the underlying device buffer
fpp_t framesPerAudioBuffer() const
{
return m_framesPerAudioBuffer;
}

AudioEngineProfiler& profiler()
{
Expand Down Expand Up @@ -235,11 +242,6 @@ class LMMS_EXPORT AudioEngine : public QObject

bool criticalXRuns() const;

inline bool hasFifoWriter() const
{
return m_fifoWriter != nullptr;
}

void pushInputFrames( SampleFrame* _ab, const f_cnt_t _frames );

inline const SampleFrame* inputBuffer()
Expand All @@ -252,10 +254,16 @@ class LMMS_EXPORT AudioEngine : public QObject
return m_inputBufferFrames[ m_inputBufferRead ];
}

inline const SampleFrame* nextBuffer()
{
return hasFifoWriter() ? m_fifo->read() : renderNextBuffer();
}
//! Renders the next period of the audio buffer
//! The audio buffer can contain many periods, depending on the chosen buffer size
//! @returns The next audio period
const SampleFrame* renderNextPeriod();

//! Renders the stereo, interleaved audio buffer into @a dst
//! @note @a dst is expected to have a frame count of `framesPerAudioBuffer()`
//! @note if @a dst is mono, the buffer is converted to mono
//! @note if @a dst is non stereo, the other channels are zero-filled
void renderNextBuffer(AudioBufferView<float> auto dst);

//! Block until a change in model can be done (i.e. wait for audio thread)
void requestChangeInModel();
Expand All @@ -277,32 +285,11 @@ class LMMS_EXPORT AudioEngine : public QObject


private:
using Fifo = FifoBuffer<SampleFrame*>;

class fifoWriter : public QThread
{
public:
fifoWriter( AudioEngine * audioEngine, Fifo * fifo );

void finish();


private:
AudioEngine * m_audioEngine;
Fifo * m_fifo;
volatile bool m_writing;

void run() override;

void write(SampleFrame* buffer);
} ;


AudioEngine( bool renderOnly );
~AudioEngine() override;

void startProcessing(bool needsFifo = true);
void stopProcessing();
void startProcessing() { m_audioDev->startProcessing(); }
void stopProcessing() { m_audioDev->stopProcessing(); }


AudioDevice * tryAudioDevices();
Expand All @@ -313,7 +300,6 @@ class LMMS_EXPORT AudioEngine : public QObject
void renderStageEffects();
void renderStageMix();

const SampleFrame* renderNextBuffer();

void swapBuffers();

Expand All @@ -323,12 +309,13 @@ class LMMS_EXPORT AudioEngine : public QObject

std::vector<AudioBusHandle*> m_audioBusHandles;

fpp_t m_framesPerAudioBuffer;
fpp_t m_framesPerPeriod;
sample_rate_t m_baseSampleRate;

SampleFrame* m_inputBuffer[2];
f_cnt_t m_inputBufferFrames[2];
f_cnt_t m_inputBufferSize[2];
sample_rate_t m_baseSampleRate;
int m_inputBufferRead;
int m_inputBufferWrite;

Expand Down Expand Up @@ -358,10 +345,6 @@ class LMMS_EXPORT AudioEngine : public QObject
MidiClient * m_midiClient;
QString m_midiClientName;

// FIFO stuff
Fifo * m_fifo;
fifoWriter * m_fifoWriter;

AudioEngineProfiler m_profiler;

bool m_clearSignal;
Expand Down
5 changes: 5 additions & 0 deletions include/AudioFileDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class AudioFileDevice : public AudioDevice

OutputSettings const & getOutputSettings() const { return m_outputSettings; }

//! Write `size` sample frames from `buf` into the output file.
virtual void writeBuffer(const SampleFrame* buf, std::size_t size) = 0;

protected:
int writeData( const void* data, int len );
Expand All @@ -64,6 +66,9 @@ class AudioFileDevice : public AudioDevice
}

private:
void startProcessingImpl() override {}
void stopProcessingImpl() override {}

QFile m_outputFile;
OutputSettings m_outputSettings;
} ;
Expand Down
4 changes: 1 addition & 3 deletions include/AudioFileMP3.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@ class AudioFileMP3 : public AudioFileDevice
outputFilename, audioEngine );
}

protected:
void writeBuffer(const SampleFrame* /* _buf*/, const fpp_t /*_frames*/) override;

private:
void writeBuffer(const SampleFrame* /* _buf*/, const fpp_t /*_frames*/) override;
void flushRemainingBuffers();
bool initEncoder();
void tearDownEncoder();
Expand Down
8 changes: 3 additions & 5 deletions include/AudioJack.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ private slots:
bool initJackClient();
void resizeInputBuffer(jack_nframes_t nframes);

void startProcessingImpl() override;
void stopProcessingImpl() override;

void attemptToConnect(size_t index, const char *lmms_port_type, const char *source_port, const char *destination_port);
void attemptToReconnectOutput(size_t outputIndex, const QString& targetPort);
void attemptToReconnectInput(size_t inputIndex, const QString& sourcePort);

void startProcessing() override;
void stopProcessing() override;

void registerPort(AudioBusHandle* port) override;
void unregisterPort(AudioBusHandle* port) override;
void renamePort(AudioBusHandle* port) override;
Expand All @@ -122,14 +122,12 @@ private slots:
jack_client_t* m_client;

bool m_active;
std::atomic<bool> m_stopped;

std::atomic<MidiJack*> m_midiClient;
std::vector<jack_port_t*> m_outputPorts;
std::vector<jack_port_t*> m_inputPorts;
jack_default_audio_sample_t** m_tempOutBufs;
std::vector<SampleFrame> m_inputFrameBuffer;
SampleFrame* m_outBuf;

f_cnt_t m_framesDoneInCurBuf;
f_cnt_t m_framesToDoInCurBuf;
Expand Down
Loading
Loading