PonyPlayer
audiosink.hpp
浏览该文件的文档.
1#pragma once
2
3#include "portaudio.h"
4#include "ponyplayer.h"
5#include <utility>
6#include <vector>
7#include <QBuffer>
8#include <QDebug>
9#include "pa_ringbuffer.h"
10#include "pa_util.h"
11#include "readerwriterqueue.h"
12#include "sonic.h"
13#include "audioformat.hpp"
14#include "private/hotplug.hpp"
15#include "ponyplayer.h"
16#include <mutex>
17#include <shared_mutex>
18
19enum class PlaybackState {
20 PLAYING,
21 STOPPED,
22 PAUSED,
23};
24
25#define ASSERT_PA_OK(err, message) if ((err) != paNoError) { \
26qWarning() << "Error" << Pa_GetErrorText(err); \
27ILLEGAL_STATE(message); \
28}
29
38class PonyAudioSink : public QObject {
39 Q_OBJECT
40private:
41
42 PaStream *m_stream{};
43 PaStreamParameters *param{};
44 qreal m_volume, m_pitch;
45 PlaybackState m_state;
46 std::atomic<bool> m_pauseRequested = false; // 当播放完缓存的音频后停止
47 HotPlugDetector *hotPlugDetector;
48 QList<QString> devicesList;
49 QString selectedOutputDevice;
50 inline static std::mutex paStreamLock;
51 inline static std::atomic_bool paInitialized = false;
52
53 PonyAudioFormat m_format;
54 PonyAudioFormat m_deviceFormat;
55 size_t m_bufferMaxBytes;
56 size_t m_sonicBufferMaxBytes;
57 qreal m_speedFactor;
58 PaUtilRingBuffer m_ringBuffer{};
59 std::byte *m_ringBufferData;
61
62 sonicStream sonStream;
63 std::byte *sonicBuffer = nullptr;
64
65 PaTime m_startPoint = 0.0;
66 std::atomic<int64_t> m_dataWritten = 0;
67 std::atomic<int64_t> m_dataLastWrote = 0;
68
69
70 std::atomic<bool> m_blockingState = false;
71 std::mutex m_waitCompleteMutex;
72 std::condition_variable m_waitCompleteCond;
73
74 void m_paStreamFinishedCallback() {
75 qDebug() << "Stream finished callback.";
76 m_state = PlaybackState::PAUSED;
77 if (m_state == PlaybackState::PLAYING) {
79 }
80 m_waitCompleteCond.notify_all();
81 emit stateChanged();
82
83 }
84
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 *
89 static_cast<unsigned long>(m_format.getBytesPerSampleChannels()));
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) {
95 return paComplete;
96 } else {
97 qWarning() << "paAbort bytesAvailCount == 0";
98 }
99 } else {
100 ring_buffer_size_t timeAlignedByteWritten = 0; // 透明化加速的影响,表示在1x速度下,理应有多少个Byte被写入
101 ring_buffer_size_t byteToBeWritten = std::min(bytesNeeded, bytesAvailCount); // 实际要往PortAudio的Buffer里写多少Byte
102 ring_buffer_size_t byteRemainToAlign = byteToBeWritten; // 当前还需要处理多少个timeAlignedByteWritten
103 while (byteRemainToAlign) {
104 auto *audioDataInfo = dataInfoQueue.peek();
105 if (audioDataInfo->processedLength < byteRemainToAlign) {
106 timeAlignedByteWritten += audioDataInfo->origLength;
107 byteRemainToAlign -= audioDataInfo->processedLength;
108 dataInfoQueue.pop();
109 } else {
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;
118 }
119 }
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));
125 } else {
126 PaUtil_ReadRingBuffer(&m_ringBuffer, outputBuffer, byteToBeWritten);
127 }
128 m_dataWritten += timeAlignedByteWritten;
129 m_dataLastWrote = timeAlignedByteWritten;
130 }
131 return paContinue;
132 }
133
134 PaError startStreamSafe() {
135 PaError err = Pa_StopStream(m_stream);
136 if (err != paStreamIsStopped && err != paNoError) {
137 return err;
138 }
139 return Pa_StartStream(m_stream);
140 }
141
142
143 static void printError(PaError error) {
144 qDebug() << "Error" << Pa_GetErrorText(error);
145 }
146
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) {
152 return index;
153 }
154 }
155 return Pa_GetDefaultOutputDevice();
156 }
157
158 // this should be guarded by paStreamLock
159 void initializeStream() {
160 if (!paInitialized) {
161 Pa_Initialize();
162 paInitialized = true;
163 qDebug() << "Initialize PonyAudioSink backend.";
164 }
165 param = new PaStreamParameters;
166 if (selectedOutputDevice.isNull()) {
168 selectedOutputDevice = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->name;
169 }
170 param->device = getCurrentOutputDeviceIndex();
171 selectedOutputDevice = Pa_GetDeviceInfo(param->device)->name;
172 param->channelCount = m_format.getChannelCount();
173 if (param->device == paNoDevice)
174 ILLEGAL_STATE("no audio device!");
175 param->channelCount = m_format.getChannelCount();
176 param->sampleFormat = m_format.getSampleFormatForPA();
177 param->suggestedLatency = Pa_GetDeviceInfo(param->device)->defaultLowOutputLatency;
178 param->hostApiSpecificStreamInfo = nullptr;
180 Pa_OpenStream(&m_stream, nullptr, param, m_format.getSampleRate(), paFramesPerBufferUnspecified,
181 paClipOff,
182 [](
183 const void *inputBuffer,
184 void *outputBuffer,
185 unsigned long framesPerBuffer,
186 const PaStreamCallbackTimeInfo *timeInfo,
187 PaStreamCallbackFlags statusFlags,
188 void *userData
189 ) {
190 return static_cast<PonyAudioSink *>(userData)->m_paCallback(inputBuffer, outputBuffer,
191 framesPerBuffer,
192 timeInfo);
193 }, this),
194 "Can not open audio stream!"
195 )
196 const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
197 m_deviceFormat = PonyAudioFormat(PonyPlayer::Int16, static_cast<int>(info->sampleRate),
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!")
202
203 }
204
205 QString stateToStr() {
206 switch (m_state) {
208 return "PLAYING";
210 return "PAUSED";
212 return "STOPPED";
213 }
214 return "UNKNOWN";
215 }
216
217 static unsigned nextPowerOf2(unsigned val) {
218 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;
224 return ++val;
225 }
226
227
228
229public:
230 constexpr const static qreal MAX_SPEED_FACTOR = 4;
231
237 explicit PonyAudioSink(PonyAudioFormat format) : m_volume(0.5), m_pitch(1.0), m_state(PlaybackState::STOPPED),
238 m_format(std::move(format)),
239 m_deviceFormat(PonyPlayer::Unknown, 0, 0), m_speedFactor(1.0) {
240
241
242 paStreamLock.lock();
243 initializeStream();
244 paStreamLock.unlock();
245 m_bufferMaxBytes = nextPowerOf2(static_cast<unsigned>(m_format.suggestedRingBuffer(MAX_SPEED_FACTOR)));
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,
249 sizeof(std::byte),
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];
254 sonStream = sonicCreateStream(m_format.getSampleRate(), m_format.getChannelCount());
255 sonicSetChordPitch(sonStream, 1);
256 sonicSetSpeed(sonStream, static_cast<float>(m_speedFactor));
257 // HotPlugDetector should be created after PA stream open
258 hotPlugDetector = new HotPlugDetector(this);
259 connect(hotPlugDetector, &HotPlugDetector::audioOutputsChanged, this,
261 }
262
266 ~PonyAudioSink() override {
267 std::lock_guard lock(paStreamLock);
268 m_state = PlaybackState::STOPPED;
269 PaError err = Pa_CloseStream(m_stream);
270 m_stream = nullptr;
271 if (err != paNoError) {
272 qWarning() << "Error at Destroying PonyAudioSink" << Pa_GetErrorText(err);
273 }
274 }
275
281 void start() {
282 std::lock_guard lock(paStreamLock);
283 m_pauseRequested = false;
284 qDebug() << "Audio start.";
285 if (m_state == PlaybackState::PLAYING) {
286 qDebug() << "AudioSink already started.";
287 return;
288 }
289 PaError err = startStreamSafe();
290 if (err != paNoError) {
291 qWarning() << "Error at starting stream:" << Pa_GetErrorText(err);
292 ILLEGAL_STATE("Can not start stream!.");
293 }
294 m_state = PlaybackState::PLAYING;
295 qDebug() << "Pa stream started";
296
297 emit stateChanged();
298 }
299
303 void pause() {
304 std::lock_guard lock(paStreamLock);
305 qDebug() << "Audio requesting pause. Current state is " << stateToStr();
306 if (m_state == PlaybackState::PLAYING) {
307 Pa_StopStream(m_stream);
308 qDebug() << "Stream Stopped";
309 m_state = PlaybackState::PAUSED;
310 } else if (m_state == PlaybackState::STOPPED) {
311 // ignore
312 } else {
313 qWarning() << "AudioSink already paused.";
314 }
315 }
316
318 m_pauseRequested = true;
319 std::unique_lock lock(m_waitCompleteMutex);
320 m_waitCompleteCond.wait(lock);
321 }
322
326 void stop() {
327 std::lock_guard lock(paStreamLock);
328 qDebug() << "Audio stateStop.";
329 if (m_state == PlaybackState::PLAYING || m_state == PlaybackState::PAUSED) {
330 Pa_AbortStream(m_stream);
331 m_state = PlaybackState::STOPPED;
332 } else {
333 qWarning() << "AudioSink already stopped.";
334 }
335 }
336
337 void setBlockState(bool state) {
338 m_blockingState = state;
339 }
340
341 bool isBlock() {
342 return m_blockingState;
343 }
344
349 [[nodiscard]] PlaybackState state() const {
350 return m_state;
351 }
352
357 [[nodiscard]] int64_t freeByte() const {
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) /
361 }
362
369 bool write(const char *buf, qint32 origLen) {
370 int len = 0;
371 if (m_format.getSampleFormat() != PonyPlayer::Int16) {
372 throw std::runtime_error("Only support Int16!");
373 }
374 if (origLen % m_format.getBytesPerSampleChannels() == 0) {
375 sonicWriteShortToStream(sonStream, reinterpret_cast<const short *>(buf),
376 static_cast<int>(origLen) / m_format.getBytesPerSampleChannels());
377 int currentLen;
378 while ((currentLen = sonicReadShortFromStream(sonStream,
379 reinterpret_cast<short *>
380 (sonicBuffer + len * m_format.getChannelCount()),
381 0x7fffffff))) {
382 len += currentLen;
383 }
384 } else
385 ILLEGAL_STATE("Incomplete Int16!");
386 len *= m_format.getBytesPerSampleChannels();
387 ring_buffer_size_t bufAvailCount = PaUtil_GetRingBufferWriteAvailable(&m_ringBuffer);
388
389 if (bufAvailCount < len) return false;
390 void *ptr[2] = {nullptr};
391 ring_buffer_size_t sizes[2] = {0};
392
393 PaUtil_GetRingBufferWriteRegions(&m_ringBuffer, static_cast<ring_buffer_size_t>(len), &ptr[0], &sizes[0],
394 &ptr[1],
395 &sizes[1]);
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));
400 return true;
401 }
402
407 size_t clear() {
408 if (m_state != PlaybackState::STOPPED) {
409 qWarning() << "clear make no effect when state != STOPPED.";
410 }
411 // 需要保证此刻没有读写操作
412 PaUtil_FlushRingBuffer(&m_ringBuffer);
413 return 0;
414 }
415
416
421 [[nodiscard]] qreal getProcessSecs(bool backward) const {
422 if (m_state == PlaybackState::STOPPED) { return m_startPoint; }
423 auto processSec = static_cast<double>(m_format.durationOfBytes(m_dataWritten - m_dataLastWrote));
424 if (backward) {
425 return m_startPoint - processSec;
426 } else {
427 return m_startPoint + processSec;
428 }
429 }
430
435 void setStartPoint(double t = 0.0) {
436 if (std::isnan(t)) {
437 qWarning() << "Trying set start point to NaN";
438 }
439 if (m_state == PlaybackState::STOPPED) {
440 m_startPoint = t;
441 m_dataWritten = 0;
442 } else {
443 qWarning() << "setTimeBase make no effect when state != STOPPED";
444 }
445 }
446
447
452 void setVolume(qreal newVolume) {
453 m_volume = qBound(0.0, newVolume, 1.0);
454 sonicSetVolume(sonStream, static_cast<float>(newVolume));
455 }
456
461 void setPitch(qreal newPitch) {
462 m_pitch = qBound(0.0, newPitch, 16.0);
463 sonicSetPitch(sonStream, static_cast<float>(newPitch));
464 }
465
470 void setSpeed(qreal newSpeed) {
471 m_speedFactor = qBound(0.0, newSpeed, MAX_SPEED_FACTOR);
472 sonicSetSpeed(sonStream, static_cast<float>(newSpeed));
473 }
474
479 [[nodiscard]] qreal volume() const {
480 return m_volume;
481 }
482
483 [[nodiscard]] qreal speed() const {
484 return m_speedFactor;
485 }
486
487 [[nodiscard]] qreal pitch() const {
488 return m_pitch;
489 }
490
492 devicesList.clear();
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;
498#ifdef WIN32
499 if (deviceInfo->hostApi != PaHostApiTypeId::paDirectSound) continue;
500#endif
501 devicesList.push_back(deviceName);
502 qDebug() << deviceName;
503 }
504 }
505
506 QString getSelectedOutputDevice() { return selectedOutputDevice; }
507
508 void restartStream(const std::function<void()> &betweenInitAndOpen) {
509 std::lock_guard lock(paStreamLock);
510 if (paInitialized) {
511 Pa_AbortStream(m_stream);
512 Pa_Terminate();
513 }
514 Pa_Initialize();
515 paInitialized = true;
516 if (betweenInitAndOpen) betweenInitAndOpen();
517 initializeStream();
518 if (m_state == PlaybackState::PLAYING) {
519 startStreamSafe();
520 }
521 }
522
524 qDebug() << "Refreshing Devices list...";
525 auto middleFunc = [this] {
527 };
528 restartStream(middleFunc);
531 }
532
533 QStringList getAudioDeviceList() {
534 return devicesList;
535 }
536
537 void setFormat(const PonyAudioFormat &format) {
538 std::unique_lock lock(paStreamLock);
539 m_format = {PonyPlayer::Int16, format.getSampleRate(), format.getChannelCount()};
540 lock.unlock();
541 restartStream(nullptr);
542 }
543
544
545signals:
546
551
556
558
560
561public slots:
562
565 }
566
567 void requestDeviceSwitch(const QString &device) {
568 qDebug() << "change audio output device to " << device;
569 selectedOutputDevice = device;
570 std::unique_lock lock(paStreamLock);
571 if (paInitialized) {
572 Pa_AbortStream(m_stream);
573 Pa_Terminate();
574 }
575 paInitialized = false;
576 initializeStream();
577 if (m_state == PlaybackState::PLAYING) {
578 startStreamSafe();
579 }
580 lock.unlock();
582 }
583
585 return m_deviceFormat;
586 }
587};
#define ASSERT_PA_OK(err, message)
Definition: audiosink.hpp:25
PlaybackState
Definition: audiosink.hpp:19
@ STOPPED
停止状态
@ PLAYING
正在播放
@ PAUSED
暂停状态
Definition: hotplug.hpp:7
void audioOutputsChanged()
Definition: audioformat.hpp:97
int getChannelCount() const
Definition: audioformat.hpp:142
int getBytesPerSampleChannels() const
Definition: audioformat.hpp:134
int64_t suggestedRingBuffer(qreal speedFactor) const
Definition: audioformat.hpp:146
const PonySampleFormat & getSampleFormat() const
Definition: audioformat.hpp:112
int getSampleRate() const
Definition: audioformat.hpp:138
qreal durationOfBytes(int64_t bytes) const
Definition: audioformat.hpp:122
PaSampleFormat getSampleFormatForPA() const
Definition: audioformat.hpp:114
播放音频裸流, 用于代替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
void stateChanged()
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
Definition: sonic.c:192