aboutsummaryrefslogtreecommitdiff
path: root/src/audio_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/audio_core.cpp16
-rw-r--r--src/audio_core/hle/source.cpp44
-rw-r--r--src/audio_core/hle/source.h2
-rw-r--r--src/audio_core/null_sink.h6
-rw-r--r--src/audio_core/sdl2_sink.cpp30
-rw-r--r--src/audio_core/sdl2_sink.h5
-rw-r--r--src/audio_core/sink.h9
-rw-r--r--src/audio_core/sink_details.cpp19
-rw-r--r--src/audio_core/sink_details.h2
9 files changed, 104 insertions, 29 deletions
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
index ba6acf28e..84f9c03a7 100644
--- a/src/audio_core/audio_core.cpp
+++ b/src/audio_core/audio_core.cpp
@@ -56,20 +56,8 @@ void AddAddressSpace(Kernel::VMManager& address_space) {
}
void SelectSink(std::string sink_id) {
- auto iter =
- std::find_if(g_sink_details.begin(), g_sink_details.end(),
- [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
-
- if (sink_id == "auto" || iter == g_sink_details.end()) {
- if (sink_id != "auto") {
- LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id %s", sink_id.c_str());
- }
- // Auto-select.
- // g_sink_details is ordered in terms of desirability, with the best choice at the front.
- iter = g_sink_details.begin();
- }
-
- DSP::HLE::SetSink(iter->factory());
+ const SinkDetails& sink_details = GetSinkDetails(sink_id);
+ DSP::HLE::SetSink(sink_details.factory());
}
void EnableStretching(bool enable) {
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp
index 2bbf7146e..92484c526 100644
--- a/src/audio_core/hle/source.cpp
+++ b/src/audio_core/hle/source.cpp
@@ -158,6 +158,14 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
static_cast<size_t>(state.mono_or_stereo));
}
+ u32_dsp play_position = {};
+ if (config.play_position_dirty && config.play_position != 0) {
+ config.play_position_dirty.Assign(0);
+ play_position = config.play_position;
+ // play_position applies only to the embedded buffer, and defaults to 0 w/o a dirty bit
+ // This will be the starting sample for the first time the buffer is played.
+ }
+
if (config.embedded_buffer_dirty) {
config.embedded_buffer_dirty.Assign(0);
state.input_queue.emplace(Buffer{
@@ -171,9 +179,18 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
state.mono_or_stereo,
state.format,
false,
+ play_position,
+ false,
});
- LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu",
- config.physical_address, config.length, config.buffer_id);
+ LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu start=%u",
+ config.physical_address, config.length, config.buffer_id,
+ static_cast<u32>(config.play_position));
+ }
+
+ if (config.loop_related_dirty && config.loop_related != 0) {
+ config.loop_related_dirty.Assign(0);
+ LOG_WARNING(Audio_DSP, "Unhandled complex loop with loop_related=0x%08x",
+ static_cast<u32>(config.loop_related));
}
if (config.buffer_queue_dirty) {
@@ -192,6 +209,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
state.mono_or_stereo,
state.format,
true,
+ {}, // 0 in u32_dsp
+ false,
});
LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i,
b.physical_address, b.length, b.buffer_id);
@@ -247,18 +266,18 @@ bool Source::DequeueBuffer() {
if (state.input_queue.empty())
return false;
- const Buffer buf = state.input_queue.top();
- state.input_queue.pop();
+ Buffer buf = state.input_queue.top();
+
+ // if we're in a loop, the current sound keeps playing afterwards, so leave the queue alone
+ if (!buf.is_looping) {
+ state.input_queue.pop();
+ }
if (buf.adpcm_dirty) {
state.adpcm_state.yn1 = buf.adpcm_yn[0];
state.adpcm_state.yn2 = buf.adpcm_yn[1];
}
- if (buf.is_looping) {
- LOG_ERROR(Audio_DSP, "Looped buffers are unimplemented at the moment");
- }
-
const u8* const memory = Memory::GetPhysicalPointer(buf.physical_address);
if (memory) {
const unsigned num_channels = buf.mono_or_stereo == MonoOrStereo::Stereo ? 2 : 1;
@@ -305,10 +324,13 @@ bool Source::DequeueBuffer() {
break;
}
- state.current_sample_number = 0;
- state.next_sample_number = 0;
+ // the first playthrough starts at play_position, loops start at the beginning of the buffer
+ state.current_sample_number = (!buf.has_played) ? buf.play_position : 0;
+ state.next_sample_number = state.current_sample_number;
state.current_buffer_id = buf.buffer_id;
- state.buffer_update = buf.from_queue;
+ state.buffer_update = buf.from_queue && !buf.has_played;
+
+ buf.has_played = true;
LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu",
source_id, buf.buffer_id, buf.from_queue ? "true" : "false",
diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h
index 3d725f2a3..ccb7f064f 100644
--- a/src/audio_core/hle/source.h
+++ b/src/audio_core/hle/source.h
@@ -76,6 +76,8 @@ private:
Format format;
bool from_queue;
+ u32_dsp play_position; // = 0;
+ bool has_played; // = false;
};
struct BufferOrder {
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index e7668438c..c732926a2 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -23,6 +23,12 @@ public:
size_t SamplesInQueue() const override {
return 0;
}
+
+ void SetDevice(int device_id) override {}
+
+ std::vector<std::string> GetDeviceList() const override {
+ return {};
+ }
};
} // namespace AudioCore
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
index 4b66cd826..933c5f16d 100644
--- a/src/audio_core/sdl2_sink.cpp
+++ b/src/audio_core/sdl2_sink.cpp
@@ -4,12 +4,12 @@
#include <list>
#include <numeric>
-#include <vector>
#include <SDL.h>
#include "audio_core/audio_core.h"
#include "audio_core/sdl2_sink.h"
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/settings.h"
namespace AudioCore {
@@ -42,10 +42,24 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
SDL_AudioSpec obtained_audiospec;
SDL_zero(obtained_audiospec);
- impl->audio_device_id =
- SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
+ int device_count = SDL_GetNumAudioDevices(0);
+ device_list.clear();
+ for (int i = 0; i < device_count; ++i) {
+ device_list.push_back(SDL_GetAudioDeviceName(i, 0));
+ }
+
+ const char* device = nullptr;
+
+ if (device_count >= 1 && Settings::values.audio_device_id != "auto" &&
+ !Settings::values.audio_device_id.empty()) {
+ device = Settings::values.audio_device_id.c_str();
+ }
+
+ impl->audio_device_id = SDL_OpenAudioDevice(device, false, &desired_audiospec,
+ &obtained_audiospec, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (impl->audio_device_id <= 0) {
- LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with: %s", SDL_GetError());
+ LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with code %d for device \"%s\"",
+ impl->audio_device_id, Settings::values.audio_device_id.c_str());
return;
}
@@ -69,6 +83,10 @@ unsigned int SDL2Sink::GetNativeSampleRate() const {
return impl->sample_rate;
}
+std::vector<std::string> SDL2Sink::GetDeviceList() const {
+ return device_list;
+}
+
void SDL2Sink::EnqueueSamples(const s16* samples, size_t sample_count) {
if (impl->audio_device_id <= 0)
return;
@@ -96,6 +114,10 @@ size_t SDL2Sink::SamplesInQueue() const {
return total_size;
}
+void SDL2Sink::SetDevice(int device_id) {
+ this->device_id = device_id;
+}
+
void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
Impl* impl = reinterpret_cast<Impl*>(impl_);
diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h
index ccd0f7c7e..bcc725369 100644
--- a/src/audio_core/sdl2_sink.h
+++ b/src/audio_core/sdl2_sink.h
@@ -21,9 +21,14 @@ public:
size_t SamplesInQueue() const override;
+ std::vector<std::string> GetDeviceList() const override;
+ void SetDevice(int device_id) override;
+
private:
struct Impl;
std::unique_ptr<Impl> impl;
+ int device_id;
+ std::vector<std::string> device_list;
};
} // namespace AudioCore
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index 08f3bab5b..558c8c0fe 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -31,6 +31,15 @@ public:
/// Samples enqueued that have not been played yet.
virtual std::size_t SamplesInQueue() const = 0;
+
+ /**
+ * Sets the desired output device.
+ * @paran device_id Id of the desired device.
+ */
+ virtual void SetDevice(int device_id) = 0;
+
+ /// Returns the list of available devices.
+ virtual std::vector<std::string> GetDeviceList() const = 0;
};
} // namespace
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
index 95ccc9e9d..6972395af 100644
--- a/src/audio_core/sink_details.cpp
+++ b/src/audio_core/sink_details.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <memory>
#include <vector>
#include "audio_core/null_sink.h"
@@ -9,6 +10,7 @@
#ifdef HAVE_SDL2
#include "audio_core/sdl2_sink.h"
#endif
+#include "common/logging/log.h"
namespace AudioCore {
@@ -20,4 +22,21 @@ const std::vector<SinkDetails> g_sink_details = {
{"null", []() { return std::make_unique<NullSink>(); }},
};
+const SinkDetails& GetSinkDetails(std::string sink_id) {
+ auto iter =
+ std::find_if(g_sink_details.begin(), g_sink_details.end(),
+ [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
+
+ if (sink_id == "auto" || iter == g_sink_details.end()) {
+ if (sink_id != "auto") {
+ LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id %s", sink_id.c_str());
+ }
+ // Auto-select.
+ // g_sink_details is ordered in terms of desirability, with the best choice at the front.
+ iter = g_sink_details.begin();
+ }
+
+ return *iter;
+}
+
} // namespace AudioCore
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h
index 4b30cf835..9d3735171 100644
--- a/src/audio_core/sink_details.h
+++ b/src/audio_core/sink_details.h
@@ -24,4 +24,6 @@ struct SinkDetails {
extern const std::vector<SinkDetails> g_sink_details;
+const SinkDetails& GetSinkDetails(std::string sink_id);
+
} // namespace AudioCore