9#include "pa_ringbuffer.h"
17#include <shared_mutex>
25#define ASSERT_PA_OK(err, message) if ((err) != paNoError) { \
26qWarning() << "Error" << Pa_GetErrorText(err); \
27ILLEGAL_STATE(message); \
43 PaStreamParameters *param{};
44 qreal m_volume, m_pitch;
46 std::atomic<bool> m_pauseRequested =
false;
48 QList<QString> devicesList;
49 QString selectedOutputDevice;
50 inline static std::mutex paStreamLock;
51 inline static std::atomic_bool paInitialized =
false;
55 size_t m_bufferMaxBytes;
56 size_t m_sonicBufferMaxBytes;
58 PaUtilRingBuffer m_ringBuffer{};
59 std::byte *m_ringBufferData;
63 std::byte *sonicBuffer =
nullptr;
65 PaTime m_startPoint = 0.0;
66 std::atomic<int64_t> m_dataWritten = 0;
67 std::atomic<int64_t> m_dataLastWrote = 0;
70 std::atomic<bool> m_blockingState =
false;
71 std::mutex m_waitCompleteMutex;
72 std::condition_variable m_waitCompleteCond;
74 void m_paStreamFinishedCallback() {
75 qDebug() <<
"Stream finished callback.";
80 m_waitCompleteCond.notify_all();
85 int m_paCallback(
const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
86 const PaStreamCallbackTimeInfo *timeInfo) {
87 ring_buffer_size_t bytesAvailCount = PaUtil_GetRingBufferReadAvailable(&m_ringBuffer);
88 auto bytesNeeded =
static_cast<ring_buffer_size_t
>(framesPerBuffer *
90 if (m_blockingState) {
91 memset(outputBuffer, 0,
static_cast<size_t>(bytesNeeded));
92 }
else if (bytesAvailCount == 0) {
93 memset(outputBuffer, 0,
static_cast<size_t>(bytesNeeded));
94 if (m_pauseRequested) {
97 qWarning() <<
"paAbort bytesAvailCount == 0";
100 ring_buffer_size_t timeAlignedByteWritten = 0;
101 ring_buffer_size_t byteToBeWritten = std::min(bytesNeeded, bytesAvailCount);
102 ring_buffer_size_t byteRemainToAlign = byteToBeWritten;
103 while (byteRemainToAlign) {
104 auto *audioDataInfo = dataInfoQueue.
peek();
105 if (audioDataInfo->processedLength < byteRemainToAlign) {
106 timeAlignedByteWritten += audioDataInfo->origLength;
107 byteRemainToAlign -= audioDataInfo->processedLength;
110 ring_buffer_size_t origLengthReduced = std::max(
static_cast<ring_buffer_size_t
>(1),
111 static_cast<ring_buffer_size_t
>(
112 static_cast<double>(byteRemainToAlign) *
113 audioDataInfo->speedUpRate));
114 audioDataInfo->processedLength -=
static_cast<qint32
>(byteRemainToAlign);
115 audioDataInfo->origLength -=
static_cast<qint32
>(origLengthReduced);
116 timeAlignedByteWritten += origLengthReduced;
117 byteRemainToAlign = 0;
120 if (bytesNeeded > bytesAvailCount) {
121 qWarning() <<
"paAbort bytesAvailCount < bytesNeeded";
122 PaUtil_ReadRingBuffer(&m_ringBuffer, outputBuffer, bytesAvailCount);
123 memset(
static_cast<std::byte *
>(outputBuffer) + byteToBeWritten, 0,
124 static_cast<size_t>(bytesNeeded - byteToBeWritten));
126 PaUtil_ReadRingBuffer(&m_ringBuffer, outputBuffer, byteToBeWritten);
128 m_dataWritten += timeAlignedByteWritten;
129 m_dataLastWrote = timeAlignedByteWritten;
134 PaError startStreamSafe() {
135 PaError err = Pa_StopStream(m_stream);
136 if (err != paStreamIsStopped && err != paNoError) {
139 return Pa_StartStream(m_stream);
143 static void printError(PaError error) {
144 qDebug() <<
"Error" << Pa_GetErrorText(error);
147 PaDeviceIndex getCurrentOutputDeviceIndex() {
148 int deviceCount = Pa_GetDeviceCount();
149 for (
auto index = 0; index < deviceCount; index++) {
150 auto *device = Pa_GetDeviceInfo(index);
151 if (strcmp(device->name, selectedOutputDevice.toStdString().data()) == 0 && device->maxOutputChannels > 0) {
155 return Pa_GetDefaultOutputDevice();
159 void initializeStream() {
160 if (!paInitialized) {
162 paInitialized =
true;
163 qDebug() <<
"Initialize PonyAudioSink backend.";
165 param =
new PaStreamParameters;
166 if (selectedOutputDevice.isNull()) {
168 selectedOutputDevice = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->name;
170 param->device = getCurrentOutputDeviceIndex();
171 selectedOutputDevice = Pa_GetDeviceInfo(param->device)->name;
173 if (param->device == paNoDevice)
177 param->suggestedLatency = Pa_GetDeviceInfo(param->device)->defaultLowOutputLatency;
178 param->hostApiSpecificStreamInfo =
nullptr;
180 Pa_OpenStream(&m_stream,
nullptr, param, m_format.
getSampleRate(), paFramesPerBufferUnspecified,
183 const void *inputBuffer,
185 unsigned long framesPerBuffer,
186 const PaStreamCallbackTimeInfo *timeInfo,
187 PaStreamCallbackFlags statusFlags,
190 return static_cast<PonyAudioSink *>(userData)->m_paCallback(inputBuffer, outputBuffer,
194 "Can not open audio stream!"
196 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
198 param->channelCount);
199 ASSERT_PA_OK(Pa_SetStreamFinishedCallback(m_stream, [](
void *userData) {
200 static_cast<PonyAudioSink *
>(userData)->m_paStreamFinishedCallback();
201 }),
"Can not set stream callback!")
205 QString stateToStr() {
217 static unsigned nextPowerOf2(
unsigned val) {
219 val = (val >> 1) | val;
220 val = (val >> 2) | val;
221 val = (val >> 4) | val;
222 val = (val >> 8) | val;
223 val = (val >> 16) | val;
238 m_format(std::move(format)),
244 paStreamLock.unlock();
246 m_sonicBufferMaxBytes = m_bufferMaxBytes * 4;
247 m_ringBufferData =
static_cast<std::byte *
>(PaUtil_AllocateMemory(
static_cast<long>(m_bufferMaxBytes)));
248 if (PaUtil_InitializeRingBuffer(&m_ringBuffer,
250 static_cast<ring_buffer_size_t
>(m_bufferMaxBytes),
251 m_ringBufferData) < 0)
252 throw std::runtime_error(
"can not initialize ring buffer!");
253 sonicBuffer =
new std::byte[m_sonicBufferMaxBytes];
267 std::lock_guard lock(paStreamLock);
269 PaError err = Pa_CloseStream(m_stream);
271 if (err != paNoError) {
272 qWarning() <<
"Error at Destroying PonyAudioSink" << Pa_GetErrorText(err);
282 std::lock_guard lock(paStreamLock);
283 m_pauseRequested =
false;
284 qDebug() <<
"Audio start.";
286 qDebug() <<
"AudioSink already started.";
289 PaError err = startStreamSafe();
290 if (err != paNoError) {
291 qWarning() <<
"Error at starting stream:" << Pa_GetErrorText(err);
295 qDebug() <<
"Pa stream started";
304 std::lock_guard lock(paStreamLock);
305 qDebug() <<
"Audio requesting pause. Current state is " << stateToStr();
307 Pa_StopStream(m_stream);
308 qDebug() <<
"Stream Stopped";
313 qWarning() <<
"AudioSink already paused.";
318 m_pauseRequested =
true;
319 std::unique_lock lock(m_waitCompleteMutex);
320 m_waitCompleteCond.wait(lock);
327 std::lock_guard lock(paStreamLock);
328 qDebug() <<
"Audio stateStop.";
330 Pa_AbortStream(m_stream);
333 qWarning() <<
"AudioSink already stopped.";
338 m_blockingState =
state;
342 return m_blockingState;
358 return static_cast<int64_t
>(PaUtil_GetRingBufferWriteAvailable(&m_ringBuffer))
359 -
static_cast<int64_t
>((
MAX_SPEED_FACTOR - m_speedFactor) *
static_cast<qreal
>(m_bufferMaxBytes) /
369 bool write(
const char *buf, qint32 origLen) {
372 throw std::runtime_error(
"Only support Int16!");
379 reinterpret_cast<short *
>
387 ring_buffer_size_t bufAvailCount = PaUtil_GetRingBufferWriteAvailable(&m_ringBuffer);
389 if (bufAvailCount < len)
return false;
390 void *ptr[2] = {
nullptr};
391 ring_buffer_size_t sizes[2] = {0};
393 PaUtil_GetRingBufferWriteRegions(&m_ringBuffer,
static_cast<ring_buffer_size_t
>(len), &ptr[0], &sizes[0],
396 memcpy(ptr[0], sonicBuffer,
static_cast<size_t>(sizes[0]));
397 memcpy(ptr[1], sonicBuffer + sizes[0],
static_cast<size_t>(sizes[1]));
398 dataInfoQueue.
enqueue({origLen, len,
static_cast<qreal
>(origLen) / len});
399 PaUtil_AdvanceRingBufferWriteIndex(&m_ringBuffer,
static_cast<ring_buffer_size_t
>(len));
409 qWarning() <<
"clear make no effect when state != STOPPED.";
412 PaUtil_FlushRingBuffer(&m_ringBuffer);
423 auto processSec =
static_cast<double>(m_format.
durationOfBytes(m_dataWritten - m_dataLastWrote));
425 return m_startPoint - processSec;
427 return m_startPoint + processSec;
437 qWarning() <<
"Trying set start point to NaN";
443 qWarning() <<
"setTimeBase make no effect when state != STOPPED";
453 m_volume = qBound(0.0, newVolume, 1.0);
462 m_pitch = qBound(0.0, newPitch, 16.0);
484 return m_speedFactor;
493 int devicesCount = Pa_GetDeviceCount();
494 for (
auto index = 0; index < devicesCount; index++) {
495 auto deviceInfo = Pa_GetDeviceInfo(index);
496 QString deviceName = Pa_GetDeviceInfo(index)->name;
497 if (deviceInfo->maxOutputChannels < 2)
continue;
499 if (deviceInfo->hostApi != PaHostApiTypeId::paDirectSound)
continue;
501 devicesList.push_back(deviceName);
502 qDebug() << deviceName;
509 std::lock_guard lock(paStreamLock);
511 Pa_AbortStream(m_stream);
515 paInitialized =
true;
516 if (betweenInitAndOpen) betweenInitAndOpen();
524 qDebug() <<
"Refreshing Devices list...";
525 auto middleFunc = [
this] {
538 std::unique_lock lock(paStreamLock);
568 qDebug() <<
"change audio output device to " << device;
569 selectedOutputDevice = device;
570 std::unique_lock lock(paStreamLock);
572 Pa_AbortStream(m_stream);
575 paInitialized =
false;
585 return m_deviceFormat;
#define ASSERT_PA_OK(err, message)
Definition: audiosink.hpp:25
PlaybackState
Definition: audiosink.hpp:19
Definition: hotplug.hpp:7
void audioOutputsChanged()
播放音频裸流, 用于代替QAudioSink.
Definition: audiosink.hpp:38
void pause()
Definition: audiosink.hpp:303
void onAudioOutputDevicesChanged()
Definition: audiosink.hpp:563
void setVolume(qreal newVolume)
Definition: audiosink.hpp:452
void setBlockState(bool state)
Definition: audiosink.hpp:337
void setSpeed(qreal newSpeed)
Definition: audiosink.hpp:470
qreal pitch() const
Definition: audiosink.hpp:487
int64_t freeByte() const
Definition: audiosink.hpp:357
PonyAudioFormat getCurrentDeviceFormat()
Definition: audiosink.hpp:584
void resourceInsufficient()
void stop()
Definition: audiosink.hpp:326
QStringList getAudioDeviceList()
Definition: audiosink.hpp:533
void requestDeviceSwitch(const QString &device)
Definition: audiosink.hpp:567
~PonyAudioSink() override
Definition: audiosink.hpp:266
void start()
Definition: audiosink.hpp:281
void refreshDevicesList()
Definition: audiosink.hpp:523
void signalDeviceSwitched()
void _getDeviceList()
Definition: audiosink.hpp:491
qreal speed() const
Definition: audiosink.hpp:483
void setPitch(qreal newPitch)
Definition: audiosink.hpp:461
void waitComplete()
Definition: audiosink.hpp:317
qreal getProcessSecs(bool backward) const
Definition: audiosink.hpp:421
size_t clear()
Definition: audiosink.hpp:407
bool isBlock()
Definition: audiosink.hpp:341
constexpr static const qreal MAX_SPEED_FACTOR
Definition: audiosink.hpp:230
void setStartPoint(double t=0.0)
Definition: audiosink.hpp:435
void setFormat(const PonyAudioFormat &format)
Definition: audiosink.hpp:537
PonyAudioSink(PonyAudioFormat format)
Definition: audiosink.hpp:237
void restartStream(const std::function< void()> &betweenInitAndOpen)
Definition: audiosink.hpp:508
qreal volume() const
Definition: audiosink.hpp:479
void signalAudioOutputDeviceListChanged()
QString getSelectedOutputDevice()
Definition: audiosink.hpp:506
PlaybackState state() const
Definition: audiosink.hpp:349
bool write(const char *buf, qint32 origLen)
Definition: audiosink.hpp:369
Definition: readerwriterqueue.h:77
T * peek() const AE_NO_TSAN
Definition: readerwriterqueue.h:384
AE_FORCEINLINE bool enqueue(T const &element) AE_NO_TSAN
Definition: readerwriterqueue.h:259
bool pop() AE_NO_TSAN
Definition: readerwriterqueue.h:426
Definition: audioformat.hpp:155
const PonySampleFormat Int16
Definition: audioformat.hpp:160
const PonySampleFormat Unknown
Definition: audioformat.hpp:158
#define ILLEGAL_STATE(msg)
Definition: ponyplayer.h:40
void sonicSetPitch(sonicStream stream, float pitch)
Definition: sonic.c:267
sonicStream sonicCreateStream(int sampleRate, int numChannels)
Definition: sonic.c:395
void sonicSetVolume(sonicStream stream, float volume)
Definition: sonic.c:302
int sonicWriteShortToStream(sonicStream stream, const short *samples, int numSamples)
Definition: sonic.c:1210
void sonicSetChordPitch(sonicStream stream, int useChordPitch)
Definition: sonic.c:285
int sonicReadShortFromStream(sonicStream stream, short *samples, int maxSamples)
Definition: sonic.c:600
void sonicSetSpeed(sonicStream stream, float speed)
Definition: sonic.c:261