mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-02-16 02:30:52 +00:00
Remove libsoundio audio backend
SDL has been the default audio backend for years now, and libsoundio has not been well-maintained upstream.
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -7,9 +7,6 @@
|
|||||||
[submodule "app/SDL_GameControllerDB"]
|
[submodule "app/SDL_GameControllerDB"]
|
||||||
path = app/SDL_GameControllerDB
|
path = app/SDL_GameControllerDB
|
||||||
url = https://github.com/gabomdq/SDL_GameControllerDB.git
|
url = https://github.com/gabomdq/SDL_GameControllerDB.git
|
||||||
[submodule "soundio/libsoundio"]
|
|
||||||
path = soundio/libsoundio
|
|
||||||
url = https://github.com/cgutman/libsoundio.git
|
|
||||||
[submodule "h264bitstream/h264bitstream"]
|
[submodule "h264bitstream/h264bitstream"]
|
||||||
path = h264bitstream/h264bitstream
|
path = h264bitstream/h264bitstream
|
||||||
url = https://github.com/aizvorski/h264bitstream.git
|
url = https://github.com/aizvorski/h264bitstream.git
|
||||||
|
|||||||
24
app/app.pro
24
app/app.pro
@@ -157,7 +157,7 @@ win32 {
|
|||||||
CONFIG += ffmpeg libplacebo
|
CONFIG += ffmpeg libplacebo
|
||||||
}
|
}
|
||||||
win32:!winrt {
|
win32:!winrt {
|
||||||
CONFIG += soundio discord-rpc
|
CONFIG += discord-rpc
|
||||||
}
|
}
|
||||||
macx {
|
macx {
|
||||||
!disable-prebuilts {
|
!disable-prebuilts {
|
||||||
@@ -166,11 +166,7 @@ macx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LIBS += -lobjc -framework VideoToolbox -framework AVFoundation -framework CoreVideo -framework CoreGraphics -framework CoreMedia -framework AppKit -framework Metal -framework QuartzCore
|
LIBS += -lobjc -framework VideoToolbox -framework AVFoundation -framework CoreVideo -framework CoreGraphics -framework CoreMedia -framework AppKit -framework Metal -framework QuartzCore
|
||||||
|
CONFIG += ffmpeg
|
||||||
# For libsoundio
|
|
||||||
LIBS += -framework CoreAudio -framework AudioUnit
|
|
||||||
|
|
||||||
CONFIG += ffmpeg soundio
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
@@ -414,13 +410,6 @@ macx {
|
|||||||
HEADERS += \
|
HEADERS += \
|
||||||
streaming/video/ffmpeg-renderers/vt.h
|
streaming/video/ffmpeg-renderers/vt.h
|
||||||
}
|
}
|
||||||
soundio {
|
|
||||||
message(libsoundio audio renderer selected)
|
|
||||||
|
|
||||||
DEFINES += HAVE_SOUNDIO SOUNDIO_STATIC_LIBRARY
|
|
||||||
SOURCES += streaming/audio/renderers/soundioaudiorenderer.cpp
|
|
||||||
HEADERS += streaming/audio/renderers/soundioaudiorenderer.h
|
|
||||||
}
|
|
||||||
discord-rpc {
|
discord-rpc {
|
||||||
message(Discord integration enabled)
|
message(Discord integration enabled)
|
||||||
|
|
||||||
@@ -511,15 +500,6 @@ else:unix: LIBS += -L$$OUT_PWD/../qmdnsengine/ -lqmdnsengine
|
|||||||
INCLUDEPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
|
INCLUDEPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
|
||||||
DEPENDPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
|
DEPENDPATH += $$PWD/../qmdnsengine/qmdnsengine/src/include $$PWD/../qmdnsengine
|
||||||
|
|
||||||
soundio {
|
|
||||||
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../soundio/release/ -lsoundio
|
|
||||||
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../soundio/debug/ -lsoundio
|
|
||||||
else:unix: LIBS += -L$$OUT_PWD/../soundio/ -lsoundio
|
|
||||||
|
|
||||||
INCLUDEPATH += $$PWD/../soundio/libsoundio
|
|
||||||
DEPENDPATH += $$PWD/../soundio/libsoundio
|
|
||||||
}
|
|
||||||
|
|
||||||
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/release/ -lh264bitstream
|
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/release/ -lh264bitstream
|
||||||
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/debug/ -lh264bitstream
|
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../h264bitstream/debug/ -lh264bitstream
|
||||||
else:unix: LIBS += -L$$OUT_PWD/../h264bitstream/ -lh264bitstream
|
else:unix: LIBS += -L$$OUT_PWD/../h264bitstream/ -lh264bitstream
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
#include "../session.h"
|
#include "../session.h"
|
||||||
#include "renderers/renderer.h"
|
#include "renderers/renderer.h"
|
||||||
|
|
||||||
#ifdef HAVE_SOUNDIO
|
|
||||||
#include "renderers/soundioaudiorenderer.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SLAUDIO
|
#ifdef HAVE_SLAUDIO
|
||||||
#include "renderers/slaud.h"
|
#include "renderers/slaud.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -29,12 +25,6 @@ IAudioRenderer* Session::createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATI
|
|||||||
TRY_INIT_RENDERER(SdlAudioRenderer, opusConfig)
|
TRY_INIT_RENDERER(SdlAudioRenderer, opusConfig)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
#ifdef HAVE_SOUNDIO
|
|
||||||
else if (mlAudio == "libsoundio") {
|
|
||||||
TRY_INIT_RENDERER(SoundIoAudioRenderer, opusConfig)
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined(HAVE_SLAUDIO)
|
#if defined(HAVE_SLAUDIO)
|
||||||
else if (mlAudio == "slaudio") {
|
else if (mlAudio == "slaudio") {
|
||||||
TRY_INIT_RENDERER(SLAudioRenderer, opusConfig)
|
TRY_INIT_RENDERER(SLAudioRenderer, opusConfig)
|
||||||
@@ -55,11 +45,8 @@ IAudioRenderer* Session::createAudioRenderer(const POPUS_MULTISTREAM_CONFIGURATI
|
|||||||
TRY_INIT_RENDERER(SLAudioRenderer, opusConfig)
|
TRY_INIT_RENDERER(SLAudioRenderer, opusConfig)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Default to SDL and use libsoundio as a fallback
|
// Default to SDL
|
||||||
TRY_INIT_RENDERER(SdlAudioRenderer, opusConfig)
|
TRY_INIT_RENDERER(SdlAudioRenderer, opusConfig)
|
||||||
#ifdef HAVE_SOUNDIO
|
|
||||||
TRY_INIT_RENDERER(SoundIoAudioRenderer, opusConfig)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,487 +0,0 @@
|
|||||||
#include "soundioaudiorenderer.h"
|
|
||||||
|
|
||||||
#include "SDL_compat.h"
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
SoundIoAudioRenderer::SoundIoAudioRenderer()
|
|
||||||
: m_OpusChannelCount(0),
|
|
||||||
m_SoundIo(nullptr),
|
|
||||||
m_Device(nullptr),
|
|
||||||
m_OutputStream(nullptr),
|
|
||||||
m_RingBuffer(nullptr),
|
|
||||||
m_AudioPacketDuration(0),
|
|
||||||
m_Latency(0),
|
|
||||||
m_Errored(false)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SoundIoAudioRenderer::~SoundIoAudioRenderer()
|
|
||||||
{
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio latency: %f",
|
|
||||||
m_Latency);
|
|
||||||
|
|
||||||
if (m_OutputStream != nullptr) {
|
|
||||||
soundio_outstream_destroy(m_OutputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be destroyed after the stream is stopped
|
|
||||||
// or we could still get sioWriteCallback() calls.
|
|
||||||
if (m_RingBuffer != nullptr) {
|
|
||||||
soundio_ring_buffer_destroy(m_RingBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Device != nullptr) {
|
|
||||||
soundio_device_unref(m_Device);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_SoundIo != nullptr) {
|
|
||||||
soundio_destroy(m_SoundIo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SoundIoAudioRenderer::scoreChannelLayout(const struct SoundIoChannelLayout* layout, const OPUS_MULTISTREAM_CONFIGURATION* opusConfig)
|
|
||||||
{
|
|
||||||
int score = 50;
|
|
||||||
|
|
||||||
// Compute a score for this layout based on how many matching channels
|
|
||||||
// we find (or acceptable alternatives).
|
|
||||||
for (int i = 0; i < layout->channel_count; i++) {
|
|
||||||
if (opusConfig->channelCount >= 2) {
|
|
||||||
switch (layout->channels[i]) {
|
|
||||||
case SoundIoChannelIdFrontLeft:
|
|
||||||
case SoundIoChannelIdFrontRight:
|
|
||||||
score += 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opusConfig->channelCount >= 6) {
|
|
||||||
switch (layout->channels[i]) {
|
|
||||||
case SoundIoChannelIdFrontCenter:
|
|
||||||
case SoundIoChannelIdLfe:
|
|
||||||
score += 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SoundIoChannelIdSideLeft:
|
|
||||||
case SoundIoChannelIdSideRight:
|
|
||||||
score++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SoundIoChannelIdBackLeft:
|
|
||||||
case SoundIoChannelIdBackRight:
|
|
||||||
if (opusConfig->channelCount == 6) {
|
|
||||||
// Score back channels higher in 5.1 mode to
|
|
||||||
// discourage selection of side channel layouts.
|
|
||||||
score += 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Score back channels normally for 7.1 mode
|
|
||||||
score++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now subtract the difference between the desired and actual channel count
|
|
||||||
// to punish layouts that have extra unused speakers.
|
|
||||||
if (opusConfig->channelCount < layout->channel_count) {
|
|
||||||
score -= layout->channel_count - opusConfig->channelCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoundIoAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig)
|
|
||||||
{
|
|
||||||
m_SoundIo = soundio_create();
|
|
||||||
if (m_SoundIo == nullptr) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_create() failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_SoundIo->app_name = "Moonlight";
|
|
||||||
m_SoundIo->userdata = this;
|
|
||||||
m_SoundIo->on_backend_disconnect = sioBackendDisconnect;
|
|
||||||
m_SoundIo->on_devices_change = sioDevicesChanged;
|
|
||||||
|
|
||||||
int err = soundio_connect(m_SoundIo);
|
|
||||||
if (err != SoundIoErrorNone) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_connect() failed: %s",
|
|
||||||
soundio_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio backend: %s",
|
|
||||||
soundio_backend_name(m_SoundIo->current_backend));
|
|
||||||
|
|
||||||
// Don't continue if we could only open the dummy backend
|
|
||||||
if (m_SoundIo->current_backend == SoundIoBackendDummy) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush events to update with new device arrivals
|
|
||||||
soundio_flush_events(m_SoundIo);
|
|
||||||
|
|
||||||
// Remember the actual channel count for later
|
|
||||||
m_OpusChannelCount = opusConfig->channelCount;
|
|
||||||
|
|
||||||
int outputDeviceIndex = soundio_default_output_device_index(m_SoundIo);
|
|
||||||
if (outputDeviceIndex < 0) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"No output device found");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Device = soundio_get_output_device(m_SoundIo, outputDeviceIndex);
|
|
||||||
if (m_Device == nullptr) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_get_output_device() failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Selected audio device: %s",
|
|
||||||
m_Device->name);
|
|
||||||
|
|
||||||
m_OutputStream = soundio_outstream_create(m_Device);
|
|
||||||
if (m_OutputStream == nullptr) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_outstream_create() failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_AudioPacketDuration = (opusConfig->samplesPerFrame / (opusConfig->sampleRate / 1000)) / 1000.0;
|
|
||||||
|
|
||||||
m_OutputStream->format = SoundIoFormatFloat32NE;
|
|
||||||
m_OutputStream->sample_rate = opusConfig->sampleRate;
|
|
||||||
m_OutputStream->software_latency = m_AudioPacketDuration;
|
|
||||||
m_OutputStream->name = "Moonlight";
|
|
||||||
m_OutputStream->userdata = this;
|
|
||||||
m_OutputStream->error_callback = sioErrorCallback;
|
|
||||||
m_OutputStream->write_callback = sioWriteCallback;
|
|
||||||
|
|
||||||
SoundIoChannelLayout bestLayout = m_Device->current_layout;
|
|
||||||
for (int i = 0; i < m_Device->layout_count; i++) {
|
|
||||||
if (scoreChannelLayout(&bestLayout, opusConfig) <
|
|
||||||
scoreChannelLayout(&m_Device->layouts[i], opusConfig)) {
|
|
||||||
bestLayout = m_Device->layouts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestLayout.channel_count < opusConfig->channelCount) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"No compatible channel layouts found. Some channels may not be played!");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_OutputStream->layout = bestLayout;
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Native layout: %s (%d channels)",
|
|
||||||
m_OutputStream->layout.name ?
|
|
||||||
m_OutputStream->layout.name : "<UNKNOWN>",
|
|
||||||
m_OutputStream->layout.channel_count);
|
|
||||||
|
|
||||||
err = soundio_outstream_open(m_OutputStream);
|
|
||||||
if (err != SoundIoErrorNone) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_outstream_open() failed: %s",
|
|
||||||
soundio_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_OutputStream->layout_error) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Channel layout failed: %s",
|
|
||||||
soundio_strerror(m_OutputStream->layout_error));
|
|
||||||
|
|
||||||
// ALSA through PulseAudio appears to fail snd_pcm_set_chmap()
|
|
||||||
// even after claiming the layout is supported (and even on totally
|
|
||||||
// standard layouts like Stereo). We'll just ignore this for ALSA
|
|
||||||
// and only bail if we get an actual failure out of one of these APIs.
|
|
||||||
if (m_SoundIo->current_backend != SoundIoBackendAlsa) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_EffectiveLayout = m_OutputStream->layout;
|
|
||||||
for (int i = 0; i < m_EffectiveLayout.channel_count; i++) {
|
|
||||||
if (opusConfig->channelCount == 6) {
|
|
||||||
// For 5.1, replace side L/R with back L/R so our channel position
|
|
||||||
// logic in sioWriteCallback() works.
|
|
||||||
if (m_EffectiveLayout.channels[i] == SoundIoChannelIdSideLeft) {
|
|
||||||
m_EffectiveLayout.channels[i] = SoundIoChannelIdBackLeft;
|
|
||||||
}
|
|
||||||
if (m_EffectiveLayout.channels[i] == SoundIoChannelIdSideRight) {
|
|
||||||
m_EffectiveLayout.channels[i] = SoundIoChannelIdBackRight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (opusConfig->channelCount == 8) {
|
|
||||||
// For 5.1, replace side L/R with LOC/ROC so our channel position
|
|
||||||
// logic in sioWriteCallback() works.
|
|
||||||
if (m_EffectiveLayout.channels[i] == SoundIoChannelIdSideLeft) {
|
|
||||||
m_EffectiveLayout.channels[i] = SoundIoChannelIdFrontLeftCenter;
|
|
||||||
}
|
|
||||||
if (m_EffectiveLayout.channels[i] == SoundIoChannelIdSideRight) {
|
|
||||||
m_EffectiveLayout.channels[i] = SoundIoChannelIdFrontRightCenter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int packetsToBuffer;
|
|
||||||
|
|
||||||
if (m_SoundIo->current_backend == SoundIoBackendWasapi) {
|
|
||||||
// 15 ms buffer seems to be fine for WASAPI
|
|
||||||
packetsToBuffer = (int)ceil(0.015 / m_AudioPacketDuration);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// 30 ms buffer on CoreAudio to avoid glitching on macOS
|
|
||||||
packetsToBuffer = (int)ceil(0.030 / m_AudioPacketDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always buffer at least 2 packets
|
|
||||||
packetsToBuffer = qMax(2, packetsToBuffer);
|
|
||||||
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio buffer size: %f seconds",
|
|
||||||
packetsToBuffer * m_AudioPacketDuration);
|
|
||||||
|
|
||||||
m_RingBuffer = soundio_ring_buffer_create(m_SoundIo,
|
|
||||||
m_OutputStream->bytes_per_sample *
|
|
||||||
m_OpusChannelCount *
|
|
||||||
opusConfig->samplesPerFrame *
|
|
||||||
packetsToBuffer);
|
|
||||||
if (m_RingBuffer == nullptr) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_ring_buffer_create() failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = soundio_outstream_start(m_OutputStream);
|
|
||||||
if (err != SoundIoErrorNone) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_outstream_start() failed: %s",
|
|
||||||
soundio_strerror(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: For some reason, a constant latency hangs around in the audio pipeline
|
|
||||||
// unless we wait for the audio stream to drain before actually submitting any samples.
|
|
||||||
// This is a gross hack, but it works remarkably well.
|
|
||||||
SDL_Delay(500);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* SoundIoAudioRenderer::getAudioBuffer(int* size)
|
|
||||||
{
|
|
||||||
// We must always write a full frame of audio. If we don't,
|
|
||||||
// the reader will get out of sync with the writer and our
|
|
||||||
// channels will get all mixed up. To ensure this is always
|
|
||||||
// the case, round our bytes free down to the next multiple
|
|
||||||
// of our frame size.
|
|
||||||
int bytesFree = soundio_ring_buffer_free_count(m_RingBuffer);
|
|
||||||
int bytesPerFrame = m_OpusChannelCount * m_OutputStream->bytes_per_sample;
|
|
||||||
*size = qMin(*size, (bytesFree / bytesPerFrame) * bytesPerFrame);
|
|
||||||
return soundio_ring_buffer_write_ptr(m_RingBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoundIoAudioRenderer::submitAudio(int bytesWritten)
|
|
||||||
{
|
|
||||||
if (m_Errored) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytesWritten == 0) {
|
|
||||||
// Nothing to do
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush events to update with new device arrivals
|
|
||||||
soundio_flush_events(m_SoundIo);
|
|
||||||
|
|
||||||
// Advance the write pointer
|
|
||||||
soundio_ring_buffer_advance_write_ptr(m_RingBuffer, bytesWritten);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SoundIoAudioRenderer::getCapabilities()
|
|
||||||
{
|
|
||||||
// TODO: Tweak buffer sizes then re-enable arbitrary audio duration
|
|
||||||
return CAPABILITY_DIRECT_SUBMIT /* | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION */;
|
|
||||||
}
|
|
||||||
|
|
||||||
IAudioRenderer::AudioFormat SoundIoAudioRenderer::getAudioBufferFormat()
|
|
||||||
{
|
|
||||||
return AudioFormat::Float32NE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundIoAudioRenderer::sioErrorCallback(SoundIoOutStream* stream, int err)
|
|
||||||
{
|
|
||||||
auto me = reinterpret_cast<SoundIoAudioRenderer*>(stream->userdata);
|
|
||||||
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio rendering error: %s",
|
|
||||||
soundio_strerror(err));
|
|
||||||
|
|
||||||
// Trigger reinitialization
|
|
||||||
me->m_Errored = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundIoAudioRenderer::sioBackendDisconnect(SoundIo* soundio, int err)
|
|
||||||
{
|
|
||||||
auto me = reinterpret_cast<SoundIoAudioRenderer*>(soundio->userdata);
|
|
||||||
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Audio backend disconnected: %s",
|
|
||||||
soundio_strerror(err));
|
|
||||||
|
|
||||||
// Trigger reinitialization
|
|
||||||
me->m_Errored = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundIoAudioRenderer::sioDevicesChanged(SoundIo* soundio)
|
|
||||||
{
|
|
||||||
auto me = reinterpret_cast<SoundIoAudioRenderer*>(soundio->userdata);
|
|
||||||
|
|
||||||
if (me->m_Device == nullptr) {
|
|
||||||
// Ignore calls that take place during initialization
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int outputDeviceIndex = soundio_default_output_device_index(soundio);
|
|
||||||
if (outputDeviceIndex >= 0) {
|
|
||||||
struct SoundIoDevice* outputDevice = soundio_get_output_device(soundio, outputDeviceIndex);
|
|
||||||
if (outputDevice == nullptr) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_get_output_device() failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!soundio_device_equal(outputDevice, me->m_Device)) {
|
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"Default audio output device changed");
|
|
||||||
|
|
||||||
// Trigger reinitialization
|
|
||||||
me->m_Errored = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
soundio_device_unref(outputDevice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytes_per_frame should never be used on the ring buffer! It's not always
|
|
||||||
// the same number of bytes per frames as the output stream!
|
|
||||||
void SoundIoAudioRenderer::sioWriteCallback(SoundIoOutStream* stream, int frameCountMin, int frameCountMax)
|
|
||||||
{
|
|
||||||
auto me = reinterpret_cast<SoundIoAudioRenderer*>(stream->userdata);
|
|
||||||
char* readPtr = soundio_ring_buffer_read_ptr(me->m_RingBuffer);
|
|
||||||
int framesLeft = soundio_ring_buffer_fill_count(me->m_RingBuffer) /
|
|
||||||
(me->m_OpusChannelCount * stream->bytes_per_sample);
|
|
||||||
int bytesRead = 0;
|
|
||||||
|
|
||||||
// Ensure we always write at least a buffer, even if it's silence, to avoid
|
|
||||||
// busy looping when no audio data is available while libsoundio tries to keep
|
|
||||||
// us from starving the output device.
|
|
||||||
frameCountMin = qMax(frameCountMin, (int)(stream->sample_rate * me->m_AudioPacketDuration));
|
|
||||||
|
|
||||||
// Clamp frameCountMax to at least 2 packets or 20 ms to stop our latency from growing if audio packets lag.
|
|
||||||
// This makes sure that we never increase our latency far beyond what the sink is consuming.
|
|
||||||
frameCountMax = qMin(frameCountMax, (int)(stream->sample_rate * qMax(me->m_AudioPacketDuration * 2, 0.020)));
|
|
||||||
frameCountMin = qMin(frameCountMin, frameCountMax);
|
|
||||||
|
|
||||||
// Clamp framesLeft to frameCountMax
|
|
||||||
framesLeft = qMin(framesLeft, frameCountMax);
|
|
||||||
|
|
||||||
// Track latency on queueing-based backends
|
|
||||||
if (me->m_SoundIo->current_backend != SoundIoBackendCoreAudio && me->m_SoundIo->current_backend != SoundIoBackendJack) {
|
|
||||||
soundio_outstream_get_latency(stream, &me->m_Latency);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
int frameCount;
|
|
||||||
int err;
|
|
||||||
struct SoundIoChannelArea* areas;
|
|
||||||
|
|
||||||
// Always meet the minimum but don't write more than that
|
|
||||||
// if we'll have to insert silence
|
|
||||||
frameCount = qMax(framesLeft, frameCountMin);
|
|
||||||
|
|
||||||
if (frameCount == 0) {
|
|
||||||
// Nothing more to write
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = soundio_outstream_begin_write(stream, &areas, &frameCount);
|
|
||||||
if (err != SoundIoErrorNone) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_outstream_begin_write() failed: %s",
|
|
||||||
soundio_strerror(err));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int frame = 0; frame < frameCount; frame++) {
|
|
||||||
for (int ch = 0; ch < me->m_EffectiveLayout.channel_count; ch++) {
|
|
||||||
// SoundIoChannelId - 1 happens to match Moonlight's channel layout
|
|
||||||
// after we've applied our fixups to m_EffectiveLayout for 5.1 and 7.1.
|
|
||||||
int readPtrChannel = me->m_EffectiveLayout.channels[ch] - 1;
|
|
||||||
|
|
||||||
if (frame >= framesLeft || readPtrChannel >= me->m_OpusChannelCount) {
|
|
||||||
// Write silence if we have no buffered frames left or
|
|
||||||
// nothing in the audio stream for this channel
|
|
||||||
memset(areas[ch].ptr, 0, stream->bytes_per_sample);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Write audio data from our ring buffer
|
|
||||||
memcpy(areas[ch].ptr,
|
|
||||||
&readPtr[readPtrChannel * stream->bytes_per_sample],
|
|
||||||
stream->bytes_per_sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
areas[ch].ptr += areas[ch].step;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move on to the next frame if we aren't inserting silence
|
|
||||||
if (frame < framesLeft) {
|
|
||||||
readPtr += stream->bytes_per_sample * me->m_OpusChannelCount;
|
|
||||||
bytesRead += stream->bytes_per_sample * me->m_OpusChannelCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = soundio_outstream_end_write(stream);
|
|
||||||
if (err != SoundIoErrorNone && err != SoundIoErrorUnderflow) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"soundio_outstream_end_write() failed: %s",
|
|
||||||
soundio_strerror(err));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (framesLeft >= frameCount) {
|
|
||||||
framesLeft -= frameCount;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
framesLeft = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frameCountMin >= frameCount) {
|
|
||||||
frameCountMin -= frameCount;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
frameCountMin = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
soundio_ring_buffer_advance_read_ptr(me->m_RingBuffer, bytesRead);
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "renderer.h"
|
|
||||||
|
|
||||||
#include <soundio/soundio.h>
|
|
||||||
|
|
||||||
class SoundIoAudioRenderer : public IAudioRenderer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SoundIoAudioRenderer();
|
|
||||||
|
|
||||||
~SoundIoAudioRenderer();
|
|
||||||
|
|
||||||
virtual bool prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
|
|
||||||
|
|
||||||
virtual void* getAudioBuffer(int* size);
|
|
||||||
|
|
||||||
virtual bool submitAudio(int bytesWritten);
|
|
||||||
|
|
||||||
virtual int getCapabilities();
|
|
||||||
|
|
||||||
virtual AudioFormat getAudioBufferFormat();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int scoreChannelLayout(const struct SoundIoChannelLayout* layout, const OPUS_MULTISTREAM_CONFIGURATION* opusConfig);
|
|
||||||
|
|
||||||
static void sioErrorCallback(struct SoundIoOutStream* stream, int err);
|
|
||||||
|
|
||||||
static void sioWriteCallback(struct SoundIoOutStream* stream, int frameCountMin, int frameCountMax);
|
|
||||||
|
|
||||||
static void sioBackendDisconnect(struct SoundIo* soundio, int err);
|
|
||||||
|
|
||||||
static void sioDevicesChanged(SoundIo* soundio);
|
|
||||||
|
|
||||||
int m_OpusChannelCount;
|
|
||||||
struct SoundIo* m_SoundIo;
|
|
||||||
struct SoundIoDevice* m_Device;
|
|
||||||
struct SoundIoOutStream* m_OutputStream;
|
|
||||||
struct SoundIoRingBuffer* m_RingBuffer;
|
|
||||||
struct SoundIoChannelLayout m_EffectiveLayout;
|
|
||||||
double m_AudioPacketDuration;
|
|
||||||
double m_Latency;
|
|
||||||
bool m_Errored;
|
|
||||||
};
|
|
||||||
@@ -11,10 +11,6 @@ win32:!winrt {
|
|||||||
SUBDIRS += AntiHooking
|
SUBDIRS += AntiHooking
|
||||||
app.depends += AntiHooking
|
app.depends += AntiHooking
|
||||||
}
|
}
|
||||||
!winrt:win32|macx {
|
|
||||||
SUBDIRS += soundio
|
|
||||||
app.depends += soundio
|
|
||||||
}
|
|
||||||
|
|
||||||
# Support debug and release builds from command line for CI
|
# Support debug and release builds from command line for CI
|
||||||
CONFIG += debug_and_release
|
CONFIG += debug_and_release
|
||||||
|
|||||||
Submodule soundio/libsoundio deleted from 34bbab80bd
@@ -1,103 +0,0 @@
|
|||||||
#-------------------------------------------------
|
|
||||||
#
|
|
||||||
# Project created by QtCreator 2018-10-02T15:27:07
|
|
||||||
#
|
|
||||||
#-------------------------------------------------
|
|
||||||
|
|
||||||
QT -= core gui
|
|
||||||
|
|
||||||
TARGET = soundio
|
|
||||||
TEMPLATE = lib
|
|
||||||
|
|
||||||
# Build a static library
|
|
||||||
CONFIG += staticlib
|
|
||||||
|
|
||||||
# Disable warnings
|
|
||||||
CONFIG += warn_off
|
|
||||||
|
|
||||||
# Include global qmake defs
|
|
||||||
include(../globaldefs.pri)
|
|
||||||
|
|
||||||
# Force MSVC to compile C as C++ for atomic support
|
|
||||||
*-msvc* {
|
|
||||||
QMAKE_CFLAGS += /TP
|
|
||||||
}
|
|
||||||
|
|
||||||
# Older GCC versions defaulted to GNU89
|
|
||||||
*-g++ {
|
|
||||||
QMAKE_CFLAGS += -std=gnu99
|
|
||||||
}
|
|
||||||
|
|
||||||
unix:!macx {
|
|
||||||
CONFIG += link_pkgconfig
|
|
||||||
|
|
||||||
packagesExist(libpulse) {
|
|
||||||
PKGCONFIG += libpulse
|
|
||||||
CONFIG += pulseaudio
|
|
||||||
}
|
|
||||||
packagesExist(alsa) {
|
|
||||||
PKGCONFIG += alsa
|
|
||||||
CONFIG += alsa
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINES += \
|
|
||||||
SOUNDIO_STATIC_LIBRARY \
|
|
||||||
SOUNDIO_VERSION_MAJOR=1 \
|
|
||||||
SOUNDIO_VERSION_MINOR=1 \
|
|
||||||
SOUNDIO_VERSION_PATCH=0 \
|
|
||||||
SOUNDIO_VERSION_STRING=\\\"1.1.0\\\"
|
|
||||||
|
|
||||||
SRC_DIR = $$PWD/libsoundio/src
|
|
||||||
INC_DIR = $$PWD/libsoundio
|
|
||||||
|
|
||||||
SOURCES += \
|
|
||||||
$$SRC_DIR/channel_layout.c \
|
|
||||||
$$SRC_DIR/dummy.c \
|
|
||||||
$$SRC_DIR/os.c \
|
|
||||||
$$SRC_DIR/ring_buffer.c \
|
|
||||||
$$SRC_DIR/soundio.c \
|
|
||||||
$$SRC_DIR/util.c
|
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
$$SRC_DIR/atomics.h \
|
|
||||||
$$SRC_DIR/dummy.h \
|
|
||||||
$$SRC_DIR/list.h \
|
|
||||||
$$SRC_DIR/os.h \
|
|
||||||
$$SRC_DIR/ring_buffer.h \
|
|
||||||
$$SRC_DIR/soundio_internal.h \
|
|
||||||
$$SRC_DIR/soundio_private.h \
|
|
||||||
$$SRC_DIR/util.h \
|
|
||||||
$$INC_DIR/soundio/soundio.h \
|
|
||||||
$$INC_DIR/soundio/endian.h
|
|
||||||
|
|
||||||
INCLUDEPATH += $$INC_DIR
|
|
||||||
|
|
||||||
win32 {
|
|
||||||
message(WASAPI backend selected)
|
|
||||||
|
|
||||||
DEFINES += SOUNDIO_HAVE_WASAPI
|
|
||||||
SOURCES += $$SRC_DIR/wasapi.c
|
|
||||||
HEADERS += $$SRC_DIR/wasapi.h
|
|
||||||
}
|
|
||||||
macx {
|
|
||||||
message(CoreAudio backend selected)
|
|
||||||
|
|
||||||
DEFINES += SOUNDIO_HAVE_COREAUDIO
|
|
||||||
SOURCES += $$SRC_DIR/coreaudio.c
|
|
||||||
HEADERS += $$SRC_DIR/coreaudio.h
|
|
||||||
}
|
|
||||||
pulseaudio {
|
|
||||||
message(PulseAudio backend selected)
|
|
||||||
|
|
||||||
DEFINES += SOUNDIO_HAVE_PULSEAUDIO
|
|
||||||
SOURCES += $$SRC_DIR/pulseaudio.c
|
|
||||||
HEADERS += $$SRC_DIR/pulseaudio.h
|
|
||||||
}
|
|
||||||
alsa {
|
|
||||||
message(ALSA backend selected)
|
|
||||||
|
|
||||||
DEFINES += SOUNDIO_HAVE_ALSA
|
|
||||||
SOURCES += $$SRC_DIR/alsa.c
|
|
||||||
HEADERS += $$SRC_DIR/alsa.h
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user