Skip to content

Commit c14a327

Browse files
sakertoothmessmerd
andauthored
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 <[email protected]>
1 parent f504204 commit c14a327

File tree

6 files changed

+276
-441
lines changed

6 files changed

+276
-441
lines changed

include/AudioDevice.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ class AudioDevice
113113
m_sampleRate = _new_sr;
114114
}
115115

116+
void setChannels(const ch_cnt_t channels)
117+
{
118+
m_channels = channels;
119+
}
120+
116121
AudioEngine* audioEngine()
117122
{
118123
return m_audioEngine;

include/AudioPortAudio.h

Lines changed: 56 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -25,136 +25,91 @@
2525
#ifndef LMMS_AUDIO_PORTAUDIO_H
2626
#define LMMS_AUDIO_PORTAUDIO_H
2727

28-
#include <QObject>
29-
3028
#include "lmmsconfig.h"
31-
#include "ComboBoxModel.h"
3229

3330
#ifdef LMMS_HAVE_PORTAUDIO
3431

35-
# include <portaudio.h>
36-
37-
# include "AudioDevice.h"
38-
# include "AudioDeviceSetupWidget.h"
39-
40-
# if defined paNeverDropInput || defined paNonInterleaved
41-
# define PORTAUDIO_V19
42-
# else
43-
# define PORTAUDIO_V18
44-
# endif
32+
#include <QComboBox>
33+
#include <QFormLayout>
34+
#include <QString>
35+
#include <QWidget>
36+
#include <portaudio.h>
4537

46-
#endif
38+
#include "AudioDevice.h"
39+
#include "AudioDeviceSetupWidget.h"
4740

41+
namespace lmms {
4842

49-
namespace lmms
43+
namespace detail {
44+
class PortAudioInitializationGuard
5045
{
51-
52-
class AudioPortAudioSetupUtil : public QObject
53-
{
54-
Q_OBJECT
55-
public slots:
56-
void updateBackends();
57-
void updateDevices();
58-
void updateChannels();
59-
6046
public:
61-
ComboBoxModel m_backendModel;
62-
ComboBoxModel m_deviceModel;
63-
};
64-
65-
66-
#ifdef LMMS_HAVE_PORTAUDIO
47+
PortAudioInitializationGuard()
48+
: m_error(Pa_Initialize())
49+
{
50+
if (m_error != paNoError) { throw std::runtime_error{"PortAudio: could not initialize"}; }
51+
}
6752

53+
~PortAudioInitializationGuard()
54+
{
55+
if (m_error == paNoError) { Pa_Terminate(); }
56+
}
6857

69-
namespace gui
70-
{
71-
class ComboBox;
72-
}
58+
PortAudioInitializationGuard(const PortAudioInitializationGuard&) = delete;
59+
PortAudioInitializationGuard(PortAudioInitializationGuard&&) = delete;
60+
PortAudioInitializationGuard& operator=(const PortAudioInitializationGuard&) = delete;
61+
PortAudioInitializationGuard& operator=(PortAudioInitializationGuard&&) = delete;
7362

63+
private:
64+
PaError m_error = paNoError;
65+
};
66+
} // namespace detail
7467

7568
class AudioPortAudio : public AudioDevice
7669
{
7770
public:
78-
AudioPortAudio( bool & _success_ful, AudioEngine* audioEngine );
71+
AudioPortAudio(bool& successful, AudioEngine* engine);
7972
~AudioPortAudio() override;
8073

81-
inline static QString name()
82-
{
83-
return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" );
84-
}
85-
86-
int process_callback(const float* _inputBuffer, float* _outputBuffer, f_cnt_t _framesPerBuffer);
87-
88-
class setupWidget : public gui::AudioDeviceSetupWidget
89-
{
90-
public:
91-
setupWidget( QWidget * _parent );
92-
~setupWidget() override;
93-
94-
void saveSettings() override;
95-
void show() override;
96-
97-
private:
98-
gui::ComboBox * m_backend;
99-
gui::ComboBox * m_device;
100-
AudioPortAudioSetupUtil m_setupUtil;
74+
AudioPortAudio(const AudioPortAudio&) = delete;
75+
AudioPortAudio(AudioPortAudio&&) = delete;
76+
AudioPortAudio& operator=(const AudioPortAudio&) = delete;
77+
AudioPortAudio& operator=(AudioPortAudio&&) = delete;
10178

102-
} ;
103-
104-
private:
10579
void startProcessing() override;
10680
void stopProcessing() override;
10781

108-
#ifdef PORTAUDIO_V19
109-
static int _process_callback( const void *_inputBuffer, void * _outputBuffer,
110-
unsigned long _framesPerBuffer,
111-
const PaStreamCallbackTimeInfo * _timeInfo,
112-
PaStreamCallbackFlags _statusFlags,
113-
void *arg );
114-
115-
#else
82+
static auto name() -> QString { return QT_TRANSLATE_NOOP("AudioDeviceSetupWidget", "PortAudio"); }
11683

117-
#define paContinue 0
118-
#define paComplete 1
119-
#define Pa_GetDeviceCount Pa_CountDevices
120-
#define Pa_GetDefaultInputDevice Pa_GetDefaultInputDeviceID
121-
#define Pa_GetDefaultOutputDevice Pa_GetDefaultOutputDeviceID
122-
#define Pa_IsStreamActive Pa_StreamActive
123-
124-
static int _process_callback( void * _inputBuffer, void * _outputBuffer,
125-
unsigned long _framesPerBuffer, PaTimestamp _outTime, void * _arg );
126-
127-
128-
using PaTime = double;
129-
using PaDeviceIndex = PaDeviceID;
130-
131-
using PaStreamParameters = struct
132-
{
133-
PaDeviceIndex device;
134-
int channelCount;
135-
PaSampleFormat sampleFormat;
136-
PaTime suggestedLatency;
137-
void *hostApiSpecificStreamInfo;
138-
139-
} PaStreamParameters;
140-
#endif // PORTAUDIO_V19
84+
private:
85+
static int processCallback(const void* input, void* output, unsigned long frameCount,
86+
const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData);
14187

142-
PaStream * m_paStream;
143-
PaStreamParameters m_outputParameters;
144-
PaStreamParameters m_inputParameters;
88+
detail::PortAudioInitializationGuard m_initGuard;
14589

146-
bool m_wasPAInitError;
90+
PaStream* m_paStream = nullptr;
91+
std::vector<SampleFrame> m_outBuf;
92+
std::size_t m_outBufPos = 0;
93+
};
94+
} // namespace lmms
14795

148-
SampleFrame* m_outBuf;
149-
std::size_t m_outBufPos;
150-
fpp_t m_outBufSize;
96+
namespace lmms::gui {
97+
class AudioPortAudioSetupWidget : public AudioDeviceSetupWidget
98+
{
99+
public:
100+
AudioPortAudioSetupWidget(QWidget* parent);
151101

152-
bool m_stopped;
102+
void show() override;
103+
void saveSettings() override;
153104

154-
} ;
105+
private:
106+
class DeviceSelectorWidget;
107+
QComboBox* m_backendComboBox = nullptr;
108+
DeviceSelectorWidget* m_inputDevice = nullptr;
109+
DeviceSelectorWidget* m_outputDevice = nullptr;
110+
};
111+
} // namespace lmms::gui
155112

156113
#endif // LMMS_HAVE_PORTAUDIO
157114

158-
} // namespace lmms
159-
160115
#endif // LMMS_AUDIO_PORTAUDIO_H

src/core/AudioEngine.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424

2525
#include "AudioEngine.h"
26+
#include <iostream>
2627

2728
#include "MixHelpers.h"
2829
#include "denormals.h"
@@ -878,7 +879,7 @@ AudioDevice * AudioEngine::tryAudioDevices()
878879

879880

880881
#ifdef LMMS_HAVE_PORTAUDIO
881-
if( dev_name == AudioPortAudio::name() || dev_name == "" )
882+
if (dev_name == AudioPortAudio::name() || dev_name.isEmpty())
882883
{
883884
dev = new AudioPortAudio( success_ful, this );
884885
if( success_ful )

0 commit comments

Comments
 (0)