diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index 92c47a6aceb..cfb4f497f31 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -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 ); @@ -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 diff --git a/include/AudioBufferView.h b/include/AudioBufferView.h index f44437a075d..fd1fc29dba8 100644 --- a/include/AudioBufferView.h +++ b/include/AudioBufferView.h @@ -361,6 +361,12 @@ class InterleavedBufferView : public detail::BufferViewData } } + //! @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()`. @@ -501,6 +507,12 @@ class PlanarBufferView : public detail::BufferViewData 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 { diff --git a/include/AudioDevice.h b/include/AudioDevice.h index dcde7d161dc..230da3c3dc3 100644 --- a/include/AudioDevice.h +++ b/include/AudioDevice.h @@ -28,6 +28,7 @@ #include #include +#include "AudioBufferView.h" #include "LmmsTypes.h" class QThread; @@ -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 diff --git a/include/AudioDummy.h b/include/AudioDummy.h index 607ea40f2f7..9adbf4e75a8 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -78,12 +78,12 @@ class AudioDummy : public QThread, public AudioDevice private: - void startProcessing() override + void startProcessingImpl() override { start(); } - void stopProcessing() override + void stopProcessingImpl() override { stopProcessingThread( this ); } @@ -91,18 +91,10 @@ class AudioDummy : public QThread, public AudioDevice 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( audioEngine()->framesPerPeriod() * 1000000.0f / audioEngine()->outputSampleRate() - timer.elapsed() ); if( microseconds > 0 ) diff --git a/include/AudioEngine.h b/include/AudioEngine.h index 3f736b7614f..ebb35fdd3ec 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -37,7 +37,6 @@ #include "LmmsTypes.h" #include "SampleFrame.h" #include "LocklessList.h" -#include "FifoBuffer.h" #include "AudioEngineProfiler.h" #include "PlayHandle.h" @@ -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 @@ -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() @@ -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() { @@ -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() @@ -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 auto dst); //! Block until a change in model can be done (i.e. wait for audio thread) void requestChangeInModel(); @@ -277,32 +285,11 @@ class LMMS_EXPORT AudioEngine : public QObject private: - using Fifo = FifoBuffer; - - 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(); @@ -313,7 +300,6 @@ class LMMS_EXPORT AudioEngine : public QObject void renderStageEffects(); void renderStageMix(); - const SampleFrame* renderNextBuffer(); void swapBuffers(); @@ -323,12 +309,13 @@ class LMMS_EXPORT AudioEngine : public QObject std::vector 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; @@ -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; diff --git a/include/AudioFileDevice.h b/include/AudioFileDevice.h index dc9a786a4a8..581d425003d 100644 --- a/include/AudioFileDevice.h +++ b/include/AudioFileDevice.h @@ -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 ); @@ -64,6 +66,9 @@ class AudioFileDevice : public AudioDevice } private: + void startProcessingImpl() override {} + void stopProcessingImpl() override {} + QFile m_outputFile; OutputSettings m_outputSettings; } ; diff --git a/include/AudioFileMP3.h b/include/AudioFileMP3.h index e56ac0ba332..8c6ca213174 100644 --- a/include/AudioFileMP3.h +++ b/include/AudioFileMP3.h @@ -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(); diff --git a/include/AudioJack.h b/include/AudioJack.h index dec67eba113..8da7c28f1d4 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -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; @@ -122,14 +122,12 @@ private slots: jack_client_t* m_client; bool m_active; - std::atomic m_stopped; std::atomic m_midiClient; std::vector m_outputPorts; std::vector m_inputPorts; jack_default_audio_sample_t** m_tempOutBufs; std::vector m_inputFrameBuffer; - SampleFrame* m_outBuf; f_cnt_t m_framesDoneInCurBuf; f_cnt_t m_framesToDoInCurBuf; diff --git a/include/AudioOss.h b/include/AudioOss.h index 91d45607344..666fdd54884 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -77,15 +77,12 @@ class setupWidget : public gui::AudioDeviceSetupWidget private: - void startProcessing() override; - void stopProcessing() override; + void startProcessingImpl() override; + void stopProcessingImpl() override; void run() override; int m_audioFD; - - bool m_convertEndian; - -} ; +}; } // namespace lmms diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index 271ce0941af..358858ac36b 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -102,8 +102,8 @@ class AudioPortAudio : public AudioDevice } ; private: - void startProcessing() override; - void stopProcessing() override; + void startProcessingImpl() override; + void stopProcessingImpl() override; #ifdef PORTAUDIO_V19 static int _process_callback( const void *_inputBuffer, void * _outputBuffer, @@ -144,13 +144,7 @@ class AudioPortAudio : public AudioDevice PaStreamParameters m_inputParameters; bool m_wasPAInitError; - - SampleFrame* m_outBuf; - std::size_t m_outBufPos; - fpp_t m_outBufSize; - bool m_stopped; - } ; #endif // LMMS_HAVE_PORTAUDIO diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index db3c566bf53..9759e5ab31b 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -52,7 +52,6 @@ class AudioPulseAudio : public QThread, public AudioDevice Q_OBJECT public: AudioPulseAudio( bool & _success_ful, AudioEngine* audioEngine ); - ~AudioPulseAudio() override; inline static QString name() { @@ -83,17 +82,13 @@ class AudioPulseAudio : public QThread, public AudioDevice pa_stream * m_s; pa_sample_spec m_sampleSpec; - + double m_latency; private: - void startProcessing() override; - void stopProcessing() override; + void startProcessingImpl() override; + void stopProcessingImpl() override; void run() override; - volatile bool m_quit; - - bool m_convertEndian; - bool m_connected; QSemaphore m_connectedSemaphore; diff --git a/include/AudioSampleRecorder.h b/include/AudioSampleRecorder.h index 691196be62c..402cbb8909a 100644 --- a/include/AudioSampleRecorder.h +++ b/include/AudioSampleRecorder.h @@ -48,7 +48,7 @@ class AudioSampleRecorder : public AudioDevice std::shared_ptr createSampleBuffer(); private: - void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override; + void writeBuffer(const SampleFrame* _ab, const fpp_t _frames); using BufferList = QList>; BufferList m_buffers; diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 651ed96be57..9e38ca15d1c 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -73,8 +73,8 @@ class AudioSdl : public AudioDevice private: - void startProcessing() override; - void stopProcessing() override; + void startProcessingImpl() override; + void stopProcessingImpl() override; static void sdlAudioCallback( void * _udata, Uint8 * _buf, int _len ); void sdlAudioCallback( Uint8 * _buf, int _len ); @@ -82,15 +82,10 @@ class AudioSdl : public AudioDevice static void sdlInputAudioCallback( void * _udata, Uint8 * _buf, int _len ); void sdlInputAudioCallback( Uint8 * _buf, int _len ); - SDL_AudioSpec m_audioHandle; - - SampleFrame* m_outBuf; - size_t m_currentBufferFramePos; size_t m_currentBufferFramesCount; - bool m_stopped; - + SDL_AudioSpec m_audioHandle; SDL_AudioDeviceID m_outputDevice; SDL_AudioSpec m_inputAudioHandle; diff --git a/include/AudioSndio.h b/include/AudioSndio.h index beb4913eb4b..f2ea7858fa4 100644 --- a/include/AudioSndio.h +++ b/include/AudioSndio.h @@ -73,16 +73,13 @@ class AudioSndio : public QThread, public AudioDevice } ; private: - void startProcessing() override; - void stopProcessing() override; + void startProcessingImpl() override; + void stopProcessingImpl() override; void run() override; struct sio_hdl *m_hdl; struct sio_par m_par; - - bool m_convertEndian; -} ; - +}; } // namespace lmms diff --git a/include/AudioSoundIo.h b/include/AudioSoundIo.h index 2e785e1c11e..493839dc13f 100644 --- a/include/AudioSoundIo.h +++ b/include/AudioSoundIo.h @@ -103,17 +103,12 @@ class AudioSoundIo : public AudioDevice } ; private: - virtual void startProcessing(); - virtual void stopProcessing(); + void startProcessingImpl() override; + void stopProcessingImpl() override; SoundIo *m_soundio; SoundIoOutStream *m_outstream; - SampleFrame* m_outBuf; - int m_outBufSize; - fpp_t m_outBufFramesTotal; - fpp_t m_outBufFrameIndex; - bool m_stopped; bool m_outstreamStarted; diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index d630e4285d7..08b1d5a12df 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -25,7 +25,6 @@ #include "AudioEngine.h" #include "MixHelpers.h" -#include "denormals.h" #include "lmmsconfig.h" @@ -67,27 +66,32 @@ using LocklessListElement = LocklessList::Element; static thread_local bool s_renderingThread = false; +AudioEngine::AudioEngine(bool renderOnly) + : m_renderOnly(renderOnly) + , m_framesPerAudioBuffer(std::clamp(ConfigManager::inst()->value("audioengine", "framesperaudiobuffer").toULong(), + MINIMUM_BUFFER_SIZE, MAXIMUM_BUFFER_SIZE)) + , m_framesPerPeriod(std::min(m_framesPerAudioBuffer, DEFAULT_BUFFER_SIZE)) + , m_baseSampleRate( + std::max(ConfigManager::inst()->value("audioengine", "samplerate").toInt(), SUPPORTED_SAMPLERATES.front())) + , m_inputBufferRead(0) + , m_inputBufferWrite(1) + , m_outputBufferRead(nullptr) + , m_outputBufferWrite(nullptr) + , m_workers() + , m_numWorkers(QThread::idealThreadCount() - 1) + , m_newPlayHandles(PlayHandle::MaxNumber) + , m_masterGain(1.0f) + , m_audioDev(nullptr) + , m_oldAudioDev(nullptr) + , m_audioDevStartFailed(false) + , m_profiler() + , m_clearSignal(false) +{ + if (m_framesPerAudioBuffer % m_framesPerPeriod != 0) + { + throw std::invalid_argument{"Frames per period is not a multiple of buffer size"}; + } - - -AudioEngine::AudioEngine( bool renderOnly ) : - m_renderOnly( renderOnly ), - m_framesPerPeriod( DEFAULT_BUFFER_SIZE ), - m_baseSampleRate(std::max(ConfigManager::inst()->value("audioengine", "samplerate").toInt(), SUPPORTED_SAMPLERATES.front())), - m_inputBufferRead( 0 ), - m_inputBufferWrite( 1 ), - m_outputBufferRead(nullptr), - m_outputBufferWrite(nullptr), - m_workers(), - m_numWorkers( QThread::idealThreadCount()-1 ), - m_newPlayHandles( PlayHandle::MaxNumber ), - m_masterGain( 1.0f ), - m_audioDev( nullptr ), - m_oldAudioDev( nullptr ), - m_audioDevStartFailed( false ), - m_profiler(), - m_clearSignal(false) -{ for( int i = 0; i < 2; ++i ) { m_inputBufferFrames[i] = 0; @@ -96,43 +100,7 @@ AudioEngine::AudioEngine( bool renderOnly ) : zeroSampleFrames(m_inputBuffer[i], m_inputBufferSize[i]); } - // determine FIFO size and number of frames per period - int fifoSize = 1; - - // if not only rendering (that is, using the GUI), load the buffer - // size from user configuration - if( renderOnly == false ) - { - m_framesPerPeriod = - ( fpp_t ) ConfigManager::inst()->value( "audioengine", "framesperaudiobuffer" ).toInt(); - - // if the value read from user configuration is not set or - // lower than the minimum allowed, use the default value and - // save it to the configuration - if( m_framesPerPeriod < MINIMUM_BUFFER_SIZE ) - { - ConfigManager::inst()->setValue( "audioengine", - "framesperaudiobuffer", - QString::number( DEFAULT_BUFFER_SIZE ) ); - - m_framesPerPeriod = DEFAULT_BUFFER_SIZE; - } - // lmms works with chunks of size DEFAULT_BUFFER_SIZE (256) and only the final mix will use the actual - // buffer size. Plugins don't see a larger buffer size than 256. If m_framesPerPeriod is larger than - // DEFAULT_BUFFER_SIZE, it's set to DEFAULT_BUFFER_SIZE and the rest is handled by an increased fifoSize. - else if( m_framesPerPeriod > DEFAULT_BUFFER_SIZE ) - { - fifoSize = m_framesPerPeriod / DEFAULT_BUFFER_SIZE; - m_framesPerPeriod = DEFAULT_BUFFER_SIZE; - } - } - - // allocate the FIFO from the determined size - m_fifo = new Fifo( fifoSize ); - - // now that framesPerPeriod is fixed initialize global BufferManager BufferManager::init( m_framesPerPeriod ); - m_outputBufferRead = std::make_unique(m_framesPerPeriod); m_outputBufferWrite = std::make_unique(m_framesPerPeriod); @@ -165,12 +133,6 @@ AudioEngine::~AudioEngine() m_workers[w]->wait( 500 ); } - while( m_fifo->available() ) - { - delete[] m_fifo->read(); - } - delete m_fifo; - delete m_midiClient; delete m_audioDev; @@ -200,46 +162,6 @@ void AudioEngine::initDevices() emit sampleRateChanged(); } - - - -void AudioEngine::startProcessing(bool needsFifo) -{ - if (needsFifo) - { - m_fifoWriter = new fifoWriter( this, m_fifo ); - m_fifoWriter->start( QThread::HighPriority ); - } - else - { - m_fifoWriter = nullptr; - } - - m_audioDev->startProcessing(); -} - - - - -void AudioEngine::stopProcessing() -{ - if( m_fifoWriter != nullptr ) - { - m_fifoWriter->finish(); - m_fifoWriter->wait(); - m_audioDev->stopProcessing(); - delete m_fifoWriter; - m_fifoWriter = nullptr; - } - else - { - m_audioDev->stopProcessing(); - } -} - - - - bool AudioEngine::criticalXRuns() const { return cpuLoad() >= 99 && Engine::getSong()->isExporting() == false; @@ -396,7 +318,7 @@ void AudioEngine::renderStageMix() -const SampleFrame* AudioEngine::renderNextBuffer() +const SampleFrame* AudioEngine::renderNextPeriod() { const auto lock = std::lock_guard{m_changeMutex}; @@ -410,10 +332,42 @@ const SampleFrame* AudioEngine::renderNextBuffer() s_renderingThread = false; m_profiler.finishPeriod(outputSampleRate(), m_framesPerPeriod); - return m_outputBufferRead.get(); } +template void AudioEngine::renderNextBuffer>(InterleavedBufferView dst); +template void AudioEngine::renderNextBuffer>(PlanarBufferView dst); +void AudioEngine::renderNextBuffer(AudioBufferView auto dst) +{ + for (auto frame = f_cnt_t{0}; frame < dst.frames(); ++frame) + { + const auto index = frame % m_framesPerPeriod; + if (index == 0) { renderNextPeriod(); } + + switch (dst.channels()) + { + case 0: + assert(false); + break; + case 1: + dst.sample(0, frame) = m_outputBufferRead[index].average(); + break; + case 2: + dst.sample(0, frame) = m_outputBufferRead[index][0]; + dst.sample(1, frame) = m_outputBufferRead[index][1]; + break; + default: + dst.sample(0, frame) = m_outputBufferRead[index][0]; + dst.sample(1, frame) = m_outputBufferRead[index][1]; + for (auto channel = 2; channel < dst.channels(); ++channel) + { + dst.sample(channel, frame) = 0.f; + } + break; + } + } +} + @@ -483,7 +437,7 @@ void AudioEngine::doSetAudioDevice( AudioDevice * _dev ) } } -void AudioEngine::setAudioDevice(AudioDevice* _dev, bool _needs_fifo, bool startNow) +void AudioEngine::setAudioDevice(AudioDevice* _dev, bool startNow) { stopProcessing(); @@ -492,7 +446,7 @@ void AudioEngine::setAudioDevice(AudioDevice* _dev, bool _needs_fifo, bool start emit qualitySettingsChanged(); emit sampleRateChanged(); - if (startNow) {startProcessing( _needs_fifo );} + if (startNow) { startProcessing(); } } @@ -1044,49 +998,4 @@ MidiClient * AudioEngine::tryMidiClients() return new MidiDummy; } - - - - - - - - -AudioEngine::fifoWriter::fifoWriter( AudioEngine* audioEngine, Fifo * fifo ) : - m_audioEngine( audioEngine ), - m_fifo( fifo ), - m_writing( true ) -{ - setObjectName("AudioEngine::fifoWriter"); -} - - - - -void AudioEngine::fifoWriter::finish() -{ - m_writing = false; -} - - - - -void AudioEngine::fifoWriter::run() -{ - disable_denormals(); - - const fpp_t frames = m_audioEngine->framesPerPeriod(); - while( m_writing ) - { - auto buffer = new SampleFrame[frames]; - const SampleFrame* b = m_audioEngine->renderNextBuffer(); - memcpy(buffer, b, frames * sizeof(SampleFrame)); - m_fifo->write(buffer); - } - - // Let audio backend stop processing - m_fifo->write(nullptr); - m_fifo->waitUntilRead(); -} - } // namespace lmms diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 3435f20c25f..f3b551f9336 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -139,7 +139,7 @@ void ProjectRenderer::startProcessing() { // Have to do audio engine stuff with GUI-thread affinity in order to // make slots connected to sampleRateChanged()-signals being called immediately. - Engine::audioEngine()->setAudioDevice(m_fileDev, false, false); + Engine::audioEngine()->setAudioDevice(m_fileDev, false); start( #ifndef LMMS_BUILD_WIN32 @@ -157,17 +157,19 @@ void ProjectRenderer::run() Engine::getSong()->startExport(); // Skip first empty buffer. - Engine::audioEngine()->nextBuffer(); + Engine::audioEngine()->renderNextPeriod(); m_progress = 0; // Now start processing - Engine::audioEngine()->startProcessing(false); + Engine::audioEngine()->startProcessing(); // Continually track and emit progress percentage to listeners. while (!Engine::getSong()->isExportDone() && !m_abort) { - m_fileDev->processNextBuffer(); + const auto buffer = Engine::audioEngine()->renderNextPeriod(); + m_fileDev->writeBuffer(buffer, Engine::audioEngine()->framesPerPeriod()); + const int nprog = Engine::getSong()->getExportProgress(); if (m_progress != nprog) { diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index d8313749268..8b81532cf99 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -41,8 +41,7 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : DEFAULT_CHANNELS), _audioEngine), m_handle( nullptr ), m_hwParams( nullptr ), - m_swParams( nullptr ), - m_convertEndian( false ) + m_swParams(nullptr) { _success_ful = false; @@ -97,7 +96,6 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : AudioAlsa::~AudioAlsa() { - stopProcessing(); if( m_handle != nullptr ) { snd_pcm_close( m_handle ); @@ -223,92 +221,34 @@ int AudioAlsa::handleError( int _err ) -void AudioAlsa::startProcessing() +void AudioAlsa::startProcessingImpl() { - if( !isRunning() ) - { - start( QThread::HighPriority ); - } + start(QThread::HighPriority); } -void AudioAlsa::stopProcessing() +void AudioAlsa::stopProcessingImpl() { stopProcessingThread( this ); } void AudioAlsa::run() { - auto temp = new SampleFrame[audioEngine()->framesPerPeriod()]; - auto outbuf = new int_sample_t[audioEngine()->framesPerPeriod() * channels()]; - auto pcmbuf = new int_sample_t[m_periodSize * channels()]; - - int outbuf_size = audioEngine()->framesPerPeriod() * channels(); - int outbuf_pos = 0; - int pcmbuf_size = m_periodSize * channels(); - - bool quit = false; - while( quit == false ) + const auto framesPerAudioBuffer = audioEngine()->framesPerAudioBuffer(); + auto buf = std::vector(framesPerAudioBuffer * channels()); + while (AudioDevice::isRunning()) { - int_sample_t * ptr = pcmbuf; - int len = pcmbuf_size; - while( len ) - { - if( outbuf_pos == 0 ) - { - // frames depend on the sample rate - const fpp_t frames = getNextBuffer( temp ); - if( !frames ) - { - quit = true; - memset( ptr, 0, len - * sizeof( int_sample_t ) ); - break; - } - outbuf_size = frames * channels(); - - convertToS16(temp, frames, outbuf, m_convertEndian); - } - int min_len = std::min(len, outbuf_size - outbuf_pos); - memcpy( ptr, outbuf + outbuf_pos, - min_len * sizeof( int_sample_t ) ); - ptr += min_len; - len -= min_len; - outbuf_pos += min_len; - outbuf_pos %= outbuf_size; - } + const auto bufferView = InterleavedBufferView{buf.data(), channels(), framesPerAudioBuffer}; + audioEngine()->renderNextBuffer(bufferView); - f_cnt_t frames = m_periodSize; - ptr = pcmbuf; - - while( frames ) + if (const auto framesWritten = snd_pcm_writei(m_handle, buf.data(), framesPerAudioBuffer); framesWritten < 0) { - int err = snd_pcm_writei( m_handle, ptr, frames ); - - if( err == -EAGAIN ) - { - continue; - } - - if( err < 0 ) - { - if( handleError( err ) < 0 ) - { - printf( "Write error: %s\n", - snd_strerror( err ) ); - } - break; // skip this buffer - } - ptr += err * channels(); - frames -= err; + handleError(framesWritten); + continue; } } - - delete[] temp; - delete[] outbuf; - delete[] pcmbuf; } @@ -333,19 +273,10 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) } // set the sample format - if (int err = snd_pcm_hw_params_set_format(m_handle, m_hwParams, SND_PCM_FORMAT_S16_LE); err < 0) - { - if (int err = snd_pcm_hw_params_set_format(m_handle, m_hwParams, SND_PCM_FORMAT_S16_BE); err < 0) - { - printf( "Neither little- nor big-endian available for " - "playback: %s\n", snd_strerror( err ) ); - return err; - } - m_convertEndian = isLittleEndian(); - } - else + if (int err = snd_pcm_hw_params_set_format(m_handle, m_hwParams, SND_PCM_FORMAT_FLOAT); err < 0) { - m_convertEndian = !isLittleEndian(); + printf("Failed to set PCM format: %s", snd_strerror(err)); + return err; } // set the count of channels diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp index 8d46f794f67..38e2db14425 100644 --- a/src/core/audio/AudioDevice.cpp +++ b/src/core/audio/AudioDevice.cpp @@ -30,68 +30,35 @@ namespace lmms { -AudioDevice::AudioDevice( const ch_cnt_t _channels, AudioEngine* _audioEngine ) : - m_supportsCapture( false ), - m_sampleRate( _audioEngine->outputSampleRate() ), - m_channels( _channels ), - m_audioEngine( _audioEngine ), - m_buffer(new SampleFrame[audioEngine()->framesPerPeriod()]) +AudioDevice::AudioDevice(const ch_cnt_t _channels, AudioEngine* _audioEngine) + : m_supportsCapture(false) + , m_sampleRate(_audioEngine->outputSampleRate()) + , m_channels(_channels) + , m_audioEngine(_audioEngine) { } - AudioDevice::~AudioDevice() { - delete[] m_buffer; + assert(m_running.test() && "device should have been stopped before being destroyed"); m_devMutex.tryLock(); unlock(); } - - - -void AudioDevice::processNextBuffer() +void AudioDevice::startProcessing() { - const fpp_t frames = getNextBuffer( m_buffer ); - if (frames) { writeBuffer(m_buffer, frames); } - else - { - m_inProcess = false; - } + m_running.test_and_set(std::memory_order_acquire); + startProcessingImpl(); } -fpp_t AudioDevice::getNextBuffer(SampleFrame* _ab) -{ - fpp_t frames = audioEngine()->framesPerPeriod(); - const SampleFrame* b = audioEngine()->nextBuffer(); - - if (!b) { return 0; } - - memcpy(_ab, b, frames * sizeof(SampleFrame)); - - if (audioEngine()->hasFifoWriter()) { delete[] b; } - return frames; -} - - - - void AudioDevice::stopProcessing() { - if( audioEngine()->hasFifoWriter() ) - { - while( m_inProcess ) - { - processNextBuffer(); - } - } + m_running.clear(std::memory_order_release); + stopProcessingImpl(); } - - - void AudioDevice::stopProcessingThread( QThread * thread ) { if( !thread->wait( 30000 ) ) diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index c07c10ffe36..91a21786085 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -102,12 +102,9 @@ AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam) , m_active(false) , m_midiClient(nullptr) , m_tempOutBufs(new jack_default_audio_sample_t*[channels()]) - , m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) , m_framesDoneInCurBuf(0) , m_framesToDoInCurBuf(0) { - m_stopped = true; - successful = initJackClient(); if (successful) { connect(this, SIGNAL(zombified()), this, SLOT(restartAfterZombified()), Qt::QueuedConnection); @@ -121,7 +118,6 @@ AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam) AudioJack::~AudioJack() { - AudioJack::stopProcessing(); #ifdef AUDIO_BUS_HANDLE_SUPPORT while (m_portMap.size()) { @@ -136,8 +132,6 @@ AudioJack::~AudioJack() } delete[] m_tempOutBufs; - - delete[] m_outBuf; } @@ -292,13 +286,9 @@ void AudioJack::attemptToReconnectInput(size_t inputIndex, const QString& source } -void AudioJack::startProcessing() +void AudioJack::startProcessingImpl() { - if (m_active || m_client == nullptr) - { - m_stopped = false; - return; - } + if (m_active || m_client == nullptr) { return; } if (jack_activate(m_client)) { @@ -309,7 +299,7 @@ void AudioJack::startProcessing() m_active = true; // try to sync JACK's and LMMS's buffer-size - // jack_set_buffer_size( m_client, audioEngine()->framesPerPeriod() ); + jack_set_buffer_size(m_client, audioEngine()->framesPerAudioBuffer()); const auto cm = ConfigManager::inst(); @@ -323,16 +313,13 @@ void AudioJack::startProcessing() { attemptToReconnectInput(i, cm->value(audioJackClass, getInputKeyByChannel(i))); } - - m_stopped = false; } -void AudioJack::stopProcessing() +void AudioJack::stopProcessingImpl() { - m_stopped = true; } void AudioJack::registerPort(AudioBusHandle* port) @@ -410,7 +397,7 @@ int AudioJack::processCallback(jack_nframes_t nframes) } #ifdef AUDIO_BUS_HANDLE_SUPPORT - const int frames = std::min(nframes, audioEngine()->framesPerPeriod()); + const int frames = std::min(nframes, audioEngine()->framesPerAudioBuffer()); for (JackPortMap::iterator it = m_portMap.begin(); it != m_portMap.end(); ++it) { for (ch_cnt_t ch = 0; ch < channels(); ++ch) @@ -426,51 +413,19 @@ int AudioJack::processCallback(jack_nframes_t nframes) } #endif - jack_nframes_t done = 0; - while (done < nframes && !m_stopped) + if (!isRunning()) { - jack_nframes_t todo = std::min(nframes - done, m_framesToDoInCurBuf - m_framesDoneInCurBuf); - for (int c = 0; c < channels(); ++c) + for (int channel = 0; channel < channels(); ++channel) { - jack_default_audio_sample_t* o = m_tempOutBufs[c]; - for (jack_nframes_t frame = 0; frame < todo; ++frame) - { - o[done + frame] = m_outBuf[m_framesDoneInCurBuf + frame][c]; - } - } - done += todo; - m_framesDoneInCurBuf += todo; - if (m_framesDoneInCurBuf == m_framesToDoInCurBuf) - { - m_framesToDoInCurBuf = getNextBuffer(m_outBuf); - m_framesDoneInCurBuf = 0; - if (!m_framesToDoInCurBuf) - { - m_stopped = true; - break; - } + std::fill_n(m_tempOutBufs[channel], nframes * channels(), 0.f); } } - - if (nframes != done) + else { - for (int c = 0; c < channels(); ++c) - { - jack_default_audio_sample_t* b = m_tempOutBufs[c] + done; - memset(b, 0, sizeof(*b) * (nframes - done)); - } + const auto bufferView = PlanarBufferView{m_tempOutBufs, channels(), nframes}; + audioEngine()->renderNextBuffer(bufferView); } - for (int c = 0; c < channels(); ++c) - { - jack_default_audio_sample_t* jack_input_buffer = (jack_default_audio_sample_t*) jack_port_get_buffer(m_inputPorts[c], nframes); - - for (jack_nframes_t frame = 0; frame < nframes; frame++) - { - m_inputFrameBuffer[frame][c] = static_cast(jack_input_buffer[frame]); - } - } - audioEngine()->pushInputFrames (m_inputFrameBuffer.data(), nframes); return 0; } diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index dff41eb2a87..765cc3e5550 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -65,14 +65,10 @@ static const QString PATH_DEV_DSP = "/dev/dsp"; #endif - - -AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : - AudioDevice(std::clamp( - ConfigManager::inst()->value("audiooss", "channels").toInt(), - DEFAULT_CHANNELS, - DEFAULT_CHANNELS), _audioEngine), - m_convertEndian( false ) +AudioOss::AudioOss(bool& _success_ful, AudioEngine* _audioEngine) + : AudioDevice(std::clamp( + ConfigManager::inst()->value("audiooss", "channels").toInt(), DEFAULT_CHANNELS, DEFAULT_CHANNELS), + _audioEngine) { _success_ful = false; @@ -99,7 +95,7 @@ AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : int frag_spec; for (frag_spec = 0; - 1u << frag_spec < audioEngine()->framesPerPeriod() * channels() * BYTES_PER_INT_SAMPLE; + 1u << frag_spec < audioEngine()->framesPerAudioBuffer() * channels() * BYTES_PER_INT_SAMPLE; ++frag_spec) { } @@ -112,39 +108,14 @@ AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : printf( "Warning: Couldn't set audio fragment size\n" ); } - unsigned int value; - // Get a list of supported hardware formats - if ( ioctl( m_audioFD, SNDCTL_DSP_GETFMTS, &value ) < 0 ) - { - perror( "SNDCTL_DSP_GETFMTS" ); - printf( "Couldn't get audio format list\n" ); - return; - } + unsigned int value = AFMT_S16_NE; - // Set the audio format - if( value & AFMT_S16_LE ) - { - value = AFMT_S16_LE; - } - else if( value & AFMT_S16_BE ) - { - value = AFMT_S16_BE; - } - else - { - printf(" Soundcard doesn't support signed 16-bit-data\n"); - } if ( ioctl( m_audioFD, SNDCTL_DSP_SETFMT, &value ) < 0 ) { perror( "SNDCTL_DSP_SETFMT" ); printf( "Couldn't set audio format\n" ); return; } - if( ( isLittleEndian() && ( value == AFMT_S16_BE ) ) || - ( !isLittleEndian() && ( value == AFMT_S16_LE ) ) ) - { - m_convertEndian = true; - } // Set the number of channels of output value = channels(); @@ -188,7 +159,6 @@ AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : AudioOss::~AudioOss() { - stopProcessing(); close( m_audioFD ); } @@ -236,44 +206,37 @@ QString AudioOss::probeDevice() -void AudioOss::startProcessing() +void AudioOss::startProcessingImpl() { - if( !isRunning() ) - { - start( QThread::HighPriority ); - } + start(QThread::HighPriority); } -void AudioOss::stopProcessing() +void AudioOss::stopProcessingImpl() { stopProcessingThread( this ); } void AudioOss::run() { - auto temp = new SampleFrame[audioEngine()->framesPerPeriod()]; - auto outbuf = new int_sample_t[audioEngine()->framesPerPeriod() * channels()]; + auto buf = std::vector(audioEngine()->framesPerAudioBuffer() * channels()); + auto pcmBuf = std::vector(buf.size()); + const auto bytesToWrite = static_cast(pcmBuf.size() * sizeof(int16_t)); - while( true ) + while (AudioDevice::isRunning()) { - const fpp_t frames = getNextBuffer( temp ); - if( !frames ) - { - break; - } + const auto bufferView = InterleavedBufferView{buf.data(), channels(), audioEngine()->framesPerAudioBuffer()}; + audioEngine()->renderNextBuffer(bufferView); - int bytes = convertToS16(temp, frames, outbuf, m_convertEndian); - if( write( m_audioFD, outbuf, bytes ) != bytes ) + for (auto i = std::size_t{0}; i < buf.size(); ++i) { - break; + pcmBuf[i] = static_cast(buf[i] * OUTPUT_SAMPLE_MULTIPLIER); } - } - delete[] temp; - delete[] outbuf; + if (write(m_audioFD, pcmBuf.data(), bytesToWrite) != bytesToWrite) { break; } + } } diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index 1f700906931..63e54a6648e 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -65,14 +65,10 @@ AudioPortAudio::AudioPortAudio( bool & _success_ful, AudioEngine * _audioEngine DEFAULT_CHANNELS, DEFAULT_CHANNELS), _audioEngine), m_paStream( nullptr ), - m_wasPAInitError( false ), - m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]), - m_outBufPos( 0 ) + m_wasPAInitError( false ) { _success_ful = false; - m_outBufSize = audioEngine()->framesPerPeriod(); - PaError err = Pa_Initialize(); if( err != paNoError ) { @@ -117,12 +113,12 @@ AudioPortAudio::AudioPortAudio( bool & _success_ful, AudioEngine * _audioEngine return; } - double inLatency = 0;//(double)audioEngine()->framesPerPeriod() / (double)sampleRate(); - double outLatency = 0;//(double)audioEngine()->framesPerPeriod() / (double)sampleRate(); + double inLatency = 0;//(double)audioEngine()->framesPerAudioBuffer() / (double)sampleRate(); + double outLatency = 0;//(double)audioEngine()->framesPerAudioBuffer() / (double)sampleRate(); //inLatency = Pa_GetDeviceInfo( inDevIdx )->defaultLowInputLatency; //outLatency = Pa_GetDeviceInfo( outDevIdx )->defaultLowOutputLatency; - const int samples = audioEngine()->framesPerPeriod(); + const int samples = audioEngine()->framesPerAudioBuffer(); // Configure output parameters. m_outputParameters.device = outDevIdx; @@ -186,19 +182,16 @@ AudioPortAudio::AudioPortAudio( bool & _success_ful, AudioEngine * _audioEngine AudioPortAudio::~AudioPortAudio() { - stopProcessing(); - if( !m_wasPAInitError ) { Pa_Terminate(); } - delete[] m_outBuf; } -void AudioPortAudio::startProcessing() +void AudioPortAudio::startProcessingImpl() { m_stopped = false; PaError err = Pa_StartStream( m_paStream ); @@ -213,7 +206,7 @@ void AudioPortAudio::startProcessing() -void AudioPortAudio::stopProcessing() +void AudioPortAudio::stopProcessingImpl() { if( m_paStream && Pa_IsStreamActive( m_paStream ) ) { @@ -235,44 +228,14 @@ int AudioPortAudio::process_callback(const float* _inputBuffer, float* _outputBu audioEngine()->pushInputFrames( (SampleFrame*)_inputBuffer, _framesPerBuffer ); } - if( m_stopped ) + if (!isRunning()) { - memset( _outputBuffer, 0, _framesPerBuffer * - channels() * sizeof(float) ); + std::fill_n(_outputBuffer, _framesPerBuffer * channels(), 0.f); return paComplete; } - while( _framesPerBuffer ) - { - if( m_outBufPos == 0 ) - { - // frames depend on the sample rate - const fpp_t frames = getNextBuffer( m_outBuf ); - if( !frames ) - { - m_stopped = true; - memset( _outputBuffer, 0, _framesPerBuffer * - channels() * sizeof(float) ); - return paComplete; - } - m_outBufSize = frames; - } - const auto min_len = std::min(_framesPerBuffer, m_outBufSize - m_outBufPos); - - for( fpp_t frame = 0; frame < min_len; ++frame ) - { - for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) - { - (_outputBuffer + frame * channels())[chnl] = AudioEngine::clip(m_outBuf[frame][chnl]); - } - } - - _outputBuffer += min_len * channels(); - _framesPerBuffer -= min_len; - m_outBufPos += min_len; - m_outBufPos %= m_outBufSize; - } - + const auto bufferView = InterleavedBufferView{_outputBuffer, channels(), _framesPerBuffer}; + audioEngine()->renderNextBuffer(bufferView); return paContinue; } diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index 6ce759ccb4a..e3f7767fd13 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -42,38 +42,22 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) static_cast( userdata )->streamWriteCallback( s, length ); } - - - -AudioPulseAudio::AudioPulseAudio( bool & _success_ful, AudioEngine* _audioEngine ) : - AudioDevice(std::clamp( - ConfigManager::inst()->value("audiopa", "channels").toInt(), - DEFAULT_CHANNELS, - DEFAULT_CHANNELS), _audioEngine), - m_s( nullptr ), - m_quit( false ), - m_convertEndian( false ) +AudioPulseAudio::AudioPulseAudio(bool& _success_ful, AudioEngine* _audioEngine) + : AudioDevice(std::clamp( + ConfigManager::inst()->value("audiopa", "channels").toInt(), DEFAULT_CHANNELS, DEFAULT_CHANNELS), + _audioEngine) + , m_s(nullptr) + , m_latency(static_cast(_audioEngine->framesPerAudioBuffer()) / sampleRate()) { _success_ful = false; - m_sampleSpec.format = PA_SAMPLE_S16LE; + m_sampleSpec.format = PA_SAMPLE_FLOAT32; m_sampleSpec.rate = sampleRate(); m_sampleSpec.channels = channels(); _success_ful = true; } - - - -AudioPulseAudio::~AudioPulseAudio() -{ - stopProcessing(); -} - - - - QString AudioPulseAudio::probeDevice() { QString dev = ConfigManager::inst()->value( "audiopa", "device" ); @@ -91,20 +75,16 @@ QString AudioPulseAudio::probeDevice() -void AudioPulseAudio::startProcessing() +void AudioPulseAudio::startProcessingImpl() { - if( !isRunning() ) - { - start( QThread::HighPriority ); - } + start(QThread::HighPriority); } -void AudioPulseAudio::stopProcessing() +void AudioPulseAudio::stopProcessingImpl() { - m_quit = true; stopProcessingThread( this ); } @@ -160,11 +140,8 @@ static void context_state_callback(pa_context *c, void *userdata) buffer_attr.minreq = (uint32_t)(-1); buffer_attr.fragsize = (uint32_t)(-1); - double latency = (double)( Engine::audioEngine()->framesPerPeriod() ) / (double)_this->sampleRate(); - // ask PulseAudio for the desired latency (which might not be approved) - buffer_attr.tlength = pa_usec_to_bytes( latency * PA_USEC_PER_MSEC, - &_this->m_sampleSpec ); + buffer_attr.tlength = pa_usec_to_bytes(_this->m_latency * PA_USEC_PER_MSEC, &_this->m_sampleSpec); pa_stream_connect_playback( _this->m_s, nullptr, &buffer_attr, PA_STREAM_ADJUST_LATENCY, @@ -218,23 +195,17 @@ void AudioPulseAudio::run() if( m_connected ) { int ret = 0; - m_quit = false; - while( m_quit == false - && pa_mainloop_iterate( mainLoop, 1, &ret ) >= 0 ) - { - } + while (AudioDevice::isRunning() && pa_mainloop_iterate(mainLoop, 1, &ret) >= 0) {} pa_stream_disconnect( m_s ); pa_stream_unref( m_s ); } else { - const fpp_t fpp = audioEngine()->framesPerPeriod(); - auto temp = new SampleFrame[fpp]; - while( getNextBuffer( temp ) ) + while (AudioDevice::isRunning()) { + audioEngine()->renderNextPeriod(); } - delete[] temp; } pa_context_disconnect( context ); @@ -243,35 +214,28 @@ void AudioPulseAudio::run() pa_mainloop_free( mainLoop ); } +void AudioPulseAudio::streamWriteCallback(pa_stream*, size_t) +{ + auto buf = static_cast(nullptr); + auto maxBufSizeInBytes = audioEngine()->framesPerAudioBuffer() * channels() * sizeof(float); + pa_stream_begin_write(m_s, &buf, &maxBufSizeInBytes); + if (!buf) { return; } + const auto numSamples = maxBufSizeInBytes / sizeof(float); + const auto numFrames = numSamples / channels(); -void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length ) -{ - const fpp_t fpp = audioEngine()->framesPerPeriod(); - auto temp = new SampleFrame[fpp]; - auto pcmbuf = (int_sample_t*)pa_xmalloc(fpp * channels() * sizeof(int_sample_t)); - - size_t fd = 0; - while( fd < length/4 && m_quit == false ) + if (!AudioDevice::isRunning()) { - const fpp_t frames = getNextBuffer( temp ); - if( !frames ) - { - m_quit = true; - break; - } - int bytes = convertToS16(temp, frames, pcmbuf, m_convertEndian); - if( bytes > 0 ) - { - pa_stream_write( m_s, pcmbuf, bytes, nullptr, 0, - PA_SEEK_RELATIVE ); - } - fd += frames; + std::fill_n(static_cast(buf), numSamples, 0.f); + } + else + { + const auto bufferView = InterleavedBufferView{reinterpret_cast(buf), channels(), numFrames}; + audioEngine()->renderNextBuffer(bufferView); } - pa_xfree( pcmbuf ); - delete[] temp; + pa_stream_write(m_s, buf, maxBufSizeInBytes, nullptr, 0, PA_SEEK_RELATIVE); } diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index cdd446f2bb6..b18a5c10211 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -41,9 +41,8 @@ constexpr auto SectionSDL = "audiosdl"; constexpr auto PlaybackDeviceSDL = "device"; constexpr auto InputDeviceSDL = "inputdevice"; -AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : - AudioDevice( DEFAULT_CHANNELS, _audioEngine ), - m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) +AudioSdl::AudioSdl(bool& _success_ful, AudioEngine* _audioEngine) + : AudioDevice(DEFAULT_CHANNELS, _audioEngine) { _success_ful = false; @@ -62,7 +61,7 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : // to convert the buffers m_audioHandle.channels = channels(); - m_audioHandle.samples = std::max(f_cnt_t{1024}, audioEngine()->framesPerPeriod() * 2); + m_audioHandle.samples = _audioEngine->framesPerAudioBuffer(); m_audioHandle.callback = sdlAudioCallback; m_audioHandle.userdata = this; @@ -121,25 +120,19 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : AudioSdl::~AudioSdl() { - stopProcessing(); - if (m_inputDevice != 0) SDL_CloseAudioDevice(m_inputDevice); if (m_outputDevice != 0) SDL_CloseAudioDevice(m_outputDevice); SDL_Quit(); - - delete[] m_outBuf; } -void AudioSdl::startProcessing() +void AudioSdl::startProcessingImpl() { - m_stopped = false; - SDL_PauseAudioDevice (m_outputDevice, 0); SDL_PauseAudioDevice (m_inputDevice, 0); } @@ -147,15 +140,13 @@ void AudioSdl::startProcessing() -void AudioSdl::stopProcessing() +void AudioSdl::stopProcessingImpl() { if( SDL_GetAudioDeviceStatus(m_outputDevice) == SDL_AUDIO_PLAYING ) { SDL_LockAudioDevice (m_inputDevice); SDL_LockAudioDevice (m_outputDevice); - m_stopped = true; - SDL_PauseAudioDevice (m_inputDevice, 1); SDL_PauseAudioDevice (m_outputDevice, 1); @@ -176,38 +167,16 @@ void AudioSdl::sdlAudioCallback( void * _udata, Uint8 * _buf, int _len ) void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) { - if( m_stopped ) + const auto frameCount = _len / sizeof(float) / channels(); + + if (!isRunning()) { memset( _buf, 0, _len ); return; } - // SDL2: process float samples - while( _len ) - { - if( m_currentBufferFramePos == 0 ) - { - // frames depend on the sample rate - const fpp_t frames = getNextBuffer( m_outBuf ); - if( !frames ) - { - memset( _buf, 0, _len ); - return; - } - m_currentBufferFramesCount = frames; - - } - const uint min_frames_count = std::min(_len/sizeof(SampleFrame), - m_currentBufferFramesCount - - m_currentBufferFramePos); - - memcpy( _buf, m_outBuf + m_currentBufferFramePos, min_frames_count*sizeof(SampleFrame) ); - _buf += min_frames_count*sizeof(SampleFrame); - _len -= min_frames_count*sizeof(SampleFrame); - m_currentBufferFramePos += min_frames_count; - - m_currentBufferFramePos %= m_currentBufferFramesCount; - } + const auto bufferView = InterleavedBufferView{reinterpret_cast(_buf), channels(), frameCount}; + audioEngine()->renderNextBuffer(bufferView); } void AudioSdl::sdlInputAudioCallback(void *_udata, Uint8 *_buf, int _len) { diff --git a/src/core/audio/AudioSndio.cpp b/src/core/audio/AudioSndio.cpp index 8bd09906eb6..0c3fa2449ab 100644 --- a/src/core/audio/AudioSndio.cpp +++ b/src/core/audio/AudioSndio.cpp @@ -41,12 +41,10 @@ namespace lmms { -AudioSndio::AudioSndio(bool & _success_ful, AudioEngine * _audioEngine) : - AudioDevice(std::clamp( - ConfigManager::inst()->value("audiosndio", "channels").toInt(), - DEFAULT_CHANNELS, - DEFAULT_CHANNELS), _audioEngine), - m_convertEndian ( false ) +AudioSndio::AudioSndio(bool& _success_ful, AudioEngine* _audioEngine) + : AudioDevice(std::clamp(ConfigManager::inst()->value("audiosndio", "channels").toInt(), DEFAULT_CHANNELS, + DEFAULT_CHANNELS), + _audioEngine) { _success_ful = false; @@ -73,14 +71,9 @@ AudioSndio::AudioSndio(bool & _success_ful, AudioEngine * _audioEngine) : m_par.bits = 16; m_par.le = SIO_LE_NATIVE; m_par.rate = sampleRate(); - m_par.round = audioEngine()->framesPerPeriod(); + m_par.round = audioEngine()->framesPerAudioBuffer(); m_par.appbufsz = m_par.round * 2; - if ( (isLittleEndian() && (m_par.le == 0)) || - (!isLittleEndian() && (m_par.le == 1))) { - m_convertEndian = true; - } - struct sio_par reqpar = m_par; if (!sio_setpar(m_hdl, &m_par)) @@ -115,7 +108,6 @@ AudioSndio::AudioSndio(bool & _success_ful, AudioEngine * _audioEngine) : AudioSndio::~AudioSndio() { - stopProcessing(); if (m_hdl != nullptr) { sio_close( m_hdl ); @@ -124,42 +116,28 @@ AudioSndio::~AudioSndio() } -void AudioSndio::startProcessing() +void AudioSndio::startProcessingImpl() { - if( !isRunning() ) - { - start( QThread::HighPriority ); - } + start(QThread::HighPriority); } -void AudioSndio::stopProcessing() +void AudioSndio::stopProcessingImpl() { stopProcessingThread( this ); } void AudioSndio::run() { - SampleFrame* temp = new SampleFrame[audioEngine()->framesPerPeriod()]; - int_sample_t * outbuf = new int_sample_t[audioEngine()->framesPerPeriod() * channels()]; + const auto framesPerAudioBuffer = audioEngine()->framesPerAudioBuffer(); + auto buf = std::vector(framesPerAudioBuffer * channels()); - while( true ) + while (AudioDevice::isRunning()) { - const fpp_t frames = getNextBuffer( temp ); - if( !frames ) - { - break; - } - - uint bytes = convertToS16(temp, frames, outbuf, m_convertEndian); - if( sio_write( m_hdl, outbuf, bytes ) != bytes ) - { - break; - } + const auto bufferView = InterleavedBufferView{buf.data(), channels(), framesPerAudioBuffer}; + audioEngine()->renderNextBuffer(bufferView); + sio_write(m_hdl, buf.data(), buf.size() * sizeof(float)); } - - delete[] temp; - delete[] outbuf; } diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index 01f804935ca..4e6ca762a06 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -46,10 +46,7 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : outSuccessful = false; m_soundio = nullptr; m_outstream = nullptr; - m_outBuf = nullptr; m_disconnectErr = 0; - m_outBufFrameIndex = 0; - m_outBufFramesTotal = 0; m_stopped = true; m_outstreamStarted = false; @@ -164,7 +161,7 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : } m_outstream->name = "LMMS"; - m_outstream->software_latency = (double)audioEngine()->framesPerPeriod() / (double)currentSampleRate; + m_outstream->software_latency = static_cast(audioEngine()->framesPerAudioBuffer()) / currentSampleRate; m_outstream->userdata = this; m_outstream->write_callback = staticWriteCallback; m_outstream->error_callback = staticErrorCallback; @@ -192,8 +189,6 @@ void AudioSoundIo::onBackendDisconnect(int err) AudioSoundIo::~AudioSoundIo() { - stopProcessing(); - if (m_outstream) { soundio_outstream_destroy(m_outstream); @@ -206,14 +201,8 @@ AudioSoundIo::~AudioSoundIo() } } -void AudioSoundIo::startProcessing() +void AudioSoundIo::startProcessingImpl() { - m_outBufFrameIndex = 0; - m_outBufFramesTotal = 0; - m_outBufSize = audioEngine()->framesPerPeriod(); - - m_outBuf = new SampleFrame[m_outBufSize]; - if (! m_outstreamStarted) { if (int err = soundio_outstream_start(m_outstream)) @@ -237,7 +226,7 @@ void AudioSoundIo::startProcessing() } } -void AudioSoundIo::stopProcessing() +void AudioSoundIo::stopProcessingImpl() { m_stopped = true; if (m_outstream) @@ -249,12 +238,6 @@ void AudioSoundIo::stopProcessing() soundio_strerror(err)); } } - - if (m_outBuf) - { - delete[] m_outBuf; - m_outBuf = nullptr; - } } void AudioSoundIo::errorCallback(int err) @@ -269,55 +252,43 @@ void AudioSoundIo::underflowCallback() void AudioSoundIo::writeCallback(int frameCountMin, int frameCountMax) { - if (m_stopped) {return;} - const struct SoundIoChannelLayout *layout = &m_outstream->layout; - SoundIoChannelArea* areas; - int bytesPerSample = m_outstream->bytes_per_sample; - int framesLeft = frameCountMax; + const auto layout = static_cast(&m_outstream->layout); + auto areas = static_cast(nullptr); + auto framesLeft = frameCountMax; while (framesLeft > 0) { - int frameCount = framesLeft; - if (int err = soundio_outstream_begin_write(m_outstream, &areas, &frameCount)) + auto frameCount = framesLeft; + if (int error = soundio_outstream_begin_write(m_outstream, &areas, &frameCount)) { - errorCallback(err); + errorCallback(error); return; } - if (!frameCount) - break; + if (!frameCount) { break; } + + auto buffers = std::array{}; + buffers.fill(nullptr); - - if (m_stopped) + for (auto i = 0; i < layout->channel_count; ++i) { - for (int channel = 0; channel < layout->channel_count; ++channel) - { - memset(areas[channel].ptr, 0, bytesPerSample * frameCount); - areas[channel].ptr += areas[channel].step * frameCount; - } - continue; + buffers[i] = reinterpret_cast(areas[i].ptr); } - for (int frame = 0; frame < frameCount; frame += 1) + if (!isRunning()) { - if (m_outBufFrameIndex >= m_outBufFramesTotal) + for (auto i = 0; i < layout->channel_count; ++i) { - m_outBufFramesTotal = getNextBuffer(m_outBuf); - if (m_outBufFramesTotal == 0) - { - m_stopped = true; - break; - } - m_outBufFrameIndex = 0; + std::fill_n(buffers[i], frameCount * layout->channel_count, 0.f); } - for (int channel = 0; channel < layout->channel_count; channel += 1) - { - float sample = m_outBuf[m_outBufFrameIndex][channel]; - memcpy(areas[channel].ptr, &sample, bytesPerSample); - areas[channel].ptr += areas[channel].step; - } - m_outBufFrameIndex += 1; + break; + } + else + { + const auto bufferView = PlanarBufferView{ + buffers.data(), static_cast(layout->channel_count), static_cast(frameCount)}; + audioEngine()->renderNextBuffer(bufferView); } if (int err = soundio_outstream_end_write(m_outstream))