Refactor PortAudio backend (#7444)
Refactors the PortAudio backend to fix issues with DirectSound and MME crackling and not loading properly, as well as to improve code quality and maintainability. --------- Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
This commit is contained in:
@@ -113,6 +113,11 @@ protected:
|
||||
m_sampleRate = _new_sr;
|
||||
}
|
||||
|
||||
void setChannels(const ch_cnt_t channels)
|
||||
{
|
||||
m_channels = channels;
|
||||
}
|
||||
|
||||
AudioEngine* audioEngine()
|
||||
{
|
||||
return m_audioEngine;
|
||||
|
||||
@@ -25,136 +25,91 @@
|
||||
#ifndef LMMS_AUDIO_PORTAUDIO_H
|
||||
#define LMMS_AUDIO_PORTAUDIO_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
#include "ComboBoxModel.h"
|
||||
|
||||
#ifdef LMMS_HAVE_PORTAUDIO
|
||||
|
||||
# include <portaudio.h>
|
||||
#include <QComboBox>
|
||||
#include <QFormLayout>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <portaudio.h>
|
||||
|
||||
# include "AudioDevice.h"
|
||||
# include "AudioDeviceSetupWidget.h"
|
||||
#include "AudioDevice.h"
|
||||
#include "AudioDeviceSetupWidget.h"
|
||||
|
||||
# if defined paNeverDropInput || defined paNonInterleaved
|
||||
# define PORTAUDIO_V19
|
||||
# else
|
||||
# define PORTAUDIO_V18
|
||||
# endif
|
||||
namespace lmms {
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace lmms
|
||||
namespace detail {
|
||||
class PortAudioInitializationGuard
|
||||
{
|
||||
|
||||
class AudioPortAudioSetupUtil : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void updateBackends();
|
||||
void updateDevices();
|
||||
void updateChannels();
|
||||
|
||||
public:
|
||||
ComboBoxModel m_backendModel;
|
||||
ComboBoxModel m_deviceModel;
|
||||
PortAudioInitializationGuard()
|
||||
: m_error(Pa_Initialize())
|
||||
{
|
||||
if (m_error != paNoError) { throw std::runtime_error{"PortAudio: could not initialize"}; }
|
||||
}
|
||||
|
||||
~PortAudioInitializationGuard()
|
||||
{
|
||||
if (m_error == paNoError) { Pa_Terminate(); }
|
||||
}
|
||||
|
||||
PortAudioInitializationGuard(const PortAudioInitializationGuard&) = delete;
|
||||
PortAudioInitializationGuard(PortAudioInitializationGuard&&) = delete;
|
||||
PortAudioInitializationGuard& operator=(const PortAudioInitializationGuard&) = delete;
|
||||
PortAudioInitializationGuard& operator=(PortAudioInitializationGuard&&) = delete;
|
||||
|
||||
private:
|
||||
PaError m_error = paNoError;
|
||||
};
|
||||
|
||||
|
||||
#ifdef LMMS_HAVE_PORTAUDIO
|
||||
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class ComboBox;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class AudioPortAudio : public AudioDevice
|
||||
{
|
||||
public:
|
||||
AudioPortAudio( bool & _success_ful, AudioEngine* audioEngine );
|
||||
AudioPortAudio(bool& successful, AudioEngine* engine);
|
||||
~AudioPortAudio() override;
|
||||
|
||||
inline static QString name()
|
||||
{
|
||||
return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" );
|
||||
}
|
||||
AudioPortAudio(const AudioPortAudio&) = delete;
|
||||
AudioPortAudio(AudioPortAudio&&) = delete;
|
||||
AudioPortAudio& operator=(const AudioPortAudio&) = delete;
|
||||
AudioPortAudio& operator=(AudioPortAudio&&) = delete;
|
||||
|
||||
int process_callback(const float* _inputBuffer, float* _outputBuffer, f_cnt_t _framesPerBuffer);
|
||||
|
||||
class setupWidget : public gui::AudioDeviceSetupWidget
|
||||
{
|
||||
public:
|
||||
setupWidget( QWidget * _parent );
|
||||
~setupWidget() override;
|
||||
|
||||
void saveSettings() override;
|
||||
void show() override;
|
||||
|
||||
private:
|
||||
gui::ComboBox * m_backend;
|
||||
gui::ComboBox * m_device;
|
||||
AudioPortAudioSetupUtil m_setupUtil;
|
||||
|
||||
} ;
|
||||
|
||||
private:
|
||||
void startProcessing() override;
|
||||
void stopProcessing() override;
|
||||
|
||||
#ifdef PORTAUDIO_V19
|
||||
static int _process_callback( const void *_inputBuffer, void * _outputBuffer,
|
||||
unsigned long _framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo * _timeInfo,
|
||||
PaStreamCallbackFlags _statusFlags,
|
||||
void *arg );
|
||||
static auto name() -> QString { return QT_TRANSLATE_NOOP("AudioDeviceSetupWidget", "PortAudio"); }
|
||||
|
||||
#else
|
||||
private:
|
||||
static int processCallback(const void* input, void* output, unsigned long frameCount,
|
||||
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData);
|
||||
|
||||
#define paContinue 0
|
||||
#define paComplete 1
|
||||
#define Pa_GetDeviceCount Pa_CountDevices
|
||||
#define Pa_GetDefaultInputDevice Pa_GetDefaultInputDeviceID
|
||||
#define Pa_GetDefaultOutputDevice Pa_GetDefaultOutputDeviceID
|
||||
#define Pa_IsStreamActive Pa_StreamActive
|
||||
detail::PortAudioInitializationGuard m_initGuard;
|
||||
|
||||
static int _process_callback( void * _inputBuffer, void * _outputBuffer,
|
||||
unsigned long _framesPerBuffer, PaTimestamp _outTime, void * _arg );
|
||||
PaStream* m_paStream = nullptr;
|
||||
std::vector<SampleFrame> m_outBuf;
|
||||
std::size_t m_outBufPos = 0;
|
||||
};
|
||||
} // namespace lmms
|
||||
|
||||
namespace lmms::gui {
|
||||
class AudioPortAudioSetupWidget : public AudioDeviceSetupWidget
|
||||
{
|
||||
public:
|
||||
AudioPortAudioSetupWidget(QWidget* parent);
|
||||
|
||||
using PaTime = double;
|
||||
using PaDeviceIndex = PaDeviceID;
|
||||
void show() override;
|
||||
void saveSettings() override;
|
||||
|
||||
using PaStreamParameters = struct
|
||||
{
|
||||
PaDeviceIndex device;
|
||||
int channelCount;
|
||||
PaSampleFormat sampleFormat;
|
||||
PaTime suggestedLatency;
|
||||
void *hostApiSpecificStreamInfo;
|
||||
|
||||
} PaStreamParameters;
|
||||
#endif // PORTAUDIO_V19
|
||||
|
||||
PaStream * m_paStream;
|
||||
PaStreamParameters m_outputParameters;
|
||||
PaStreamParameters m_inputParameters;
|
||||
|
||||
bool m_wasPAInitError;
|
||||
|
||||
SampleFrame* m_outBuf;
|
||||
std::size_t m_outBufPos;
|
||||
fpp_t m_outBufSize;
|
||||
|
||||
bool m_stopped;
|
||||
|
||||
} ;
|
||||
private:
|
||||
class DeviceSelectorWidget;
|
||||
QComboBox* m_backendComboBox = nullptr;
|
||||
DeviceSelectorWidget* m_inputDevice = nullptr;
|
||||
DeviceSelectorWidget* m_outputDevice = nullptr;
|
||||
};
|
||||
} // namespace lmms::gui
|
||||
|
||||
#endif // LMMS_HAVE_PORTAUDIO
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_AUDIO_PORTAUDIO_H
|
||||
|
||||
Reference in New Issue
Block a user