summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/CMakeLists.txt11
-rw-r--r--src/audio_core/audio_out.cpp50
-rw-r--r--src/audio_core/audio_out.h44
-rw-r--r--src/audio_core/buffer.h37
-rw-r--r--src/audio_core/stream.cpp103
-rw-r--r--src/audio_core/stream.h89
-rw-r--r--src/core/CMakeLists.txt2
8 files changed, 336 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 85354f43e..a88551fbc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,6 +3,7 @@ include_directories(.)
3 3
4add_subdirectory(common) 4add_subdirectory(common)
5add_subdirectory(core) 5add_subdirectory(core)
6add_subdirectory(audio_core)
6add_subdirectory(video_core) 7add_subdirectory(video_core)
7add_subdirectory(input_common) 8add_subdirectory(input_common)
8add_subdirectory(tests) 9add_subdirectory(tests)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
new file mode 100644
index 000000000..f00a55994
--- /dev/null
+++ b/src/audio_core/CMakeLists.txt
@@ -0,0 +1,11 @@
1add_library(audio_core STATIC
2 audio_out.cpp
3 audio_out.h
4 buffer.h
5 stream.cpp
6 stream.h
7)
8
9create_target_directory_groups(audio_core)
10
11target_link_libraries(audio_core PUBLIC common core)
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
new file mode 100644
index 000000000..6d418a05b
--- /dev/null
+++ b/src/audio_core/audio_out.cpp
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/audio_out.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8
9namespace AudioCore {
10
11/// Returns the stream format from the specified number of channels
12static Stream::Format ChannelsToStreamFormat(int num_channels) {
13 switch (num_channels) {
14 case 1:
15 return Stream::Format::Mono16;
16 case 2:
17 return Stream::Format::Stereo16;
18 case 6:
19 return Stream::Format::Multi51Channel16;
20 }
21
22 LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
23 UNREACHABLE();
24 return {};
25}
26
27StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels,
28 Stream::ReleaseCallback&& release_callback) {
29 streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
30 std::move(release_callback)));
31 return streams.back();
32}
33
34std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
35 return stream->GetTagsAndReleaseBuffers(max_count);
36}
37
38void AudioOut::StartStream(StreamPtr stream) {
39 stream->Play();
40}
41
42void AudioOut::StopStream(StreamPtr stream) {
43 stream->Stop();
44}
45
46bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
47 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
48}
49
50} // namespace AudioCore
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
new file mode 100644
index 000000000..a86499d10
--- /dev/null
+++ b/src/audio_core/audio_out.h
@@ -0,0 +1,44 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9
10#include "audio_core/buffer.h"
11#include "audio_core/stream.h"
12#include "common/common_types.h"
13
14namespace AudioCore {
15
16using StreamPtr = std::shared_ptr<Stream>;
17
18/**
19 * Represents an audio playback interface, used to open and play audio streams
20 */
21class AudioOut {
22public:
23 /// Opens a new audio stream
24 StreamPtr OpenStream(int sample_rate, int num_channels,
25 Stream::ReleaseCallback&& release_callback);
26
27 /// Returns a vector of recently released buffers specified by tag for the specified stream
28 std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
29
30 /// Starts an audio stream for playback
31 void StartStream(StreamPtr stream);
32
33 /// Stops an audio stream that is currently playing
34 void StopStream(StreamPtr stream);
35
36 /// Queues a buffer into the specified audio stream, returns true on success
37 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
38
39private:
40 /// Active audio streams on the interface
41 std::vector<StreamPtr> streams;
42};
43
44} // namespace AudioCore
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
new file mode 100644
index 000000000..874ec787e
--- /dev/null
+++ b/src/audio_core/buffer.h
@@ -0,0 +1,37 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13/**
14 * Represents a buffer of audio samples to be played in an audio stream
15 */
16class Buffer {
17public:
18 using Tag = u64;
19
20 Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
21
22 /// Returns the raw audio data for the buffer
23 const std::vector<u8>& GetData() const {
24 return data;
25 }
26
27 /// Returns the buffer tag, this is provided by the game to the audout service
28 Tag GetTag() const {
29 return tag;
30 }
31
32private:
33 Tag tag;
34 std::vector<u8> data;
35};
36
37} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
new file mode 100644
index 000000000..82bff4b9e
--- /dev/null
+++ b/src/audio_core/stream.cpp
@@ -0,0 +1,103 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9
10#include "audio_core/stream.h"
11
12namespace AudioCore {
13
14constexpr size_t MaxAudioBufferCount{32};
15
16/// Returns the sample size for the specified audio stream format
17static size_t SampleSizeFromFormat(Stream::Format format) {
18 switch (format) {
19 case Stream::Format::Mono16:
20 return 2;
21 case Stream::Format::Stereo16:
22 return 4;
23 case Stream::Format::Multi51Channel16:
24 return 12;
25 };
26
27 LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
28 UNREACHABLE();
29 return {};
30}
31
32Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback)
33 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} {
34 release_event = CoreTiming::RegisterEvent(
35 "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
36}
37
38void Stream::Play() {
39 state = State::Playing;
40 PlayNextBuffer();
41}
42
43void Stream::Stop() {
44 ASSERT_MSG(false, "Unimplemented");
45}
46
47s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
48 const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)};
49 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
50}
51
52void Stream::PlayNextBuffer() {
53 if (!IsPlaying()) {
54 // Ensure we are in playing state before playing the next buffer
55 return;
56 }
57
58 if (active_buffer) {
59 // Do not queue a new buffer if we are already playing a buffer
60 return;
61 }
62
63 if (queued_buffers.empty()) {
64 // No queued buffers - we are effectively paused
65 return;
66 }
67
68 active_buffer = queued_buffers.front();
69 queued_buffers.pop();
70
71 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
72}
73
74void Stream::ReleaseActiveBuffer() {
75 released_buffers.push(std::move(active_buffer));
76 release_callback();
77 PlayNextBuffer();
78}
79
80bool Stream::QueueBuffer(BufferPtr&& buffer) {
81 if (queued_buffers.size() < MaxAudioBufferCount) {
82 queued_buffers.push(std::move(buffer));
83 PlayNextBuffer();
84 return true;
85 }
86 return false;
87}
88
89bool Stream::ContainsBuffer(Buffer::Tag tag) const {
90 ASSERT_MSG(false, "Unimplemented");
91 return {};
92}
93
94std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
95 std::vector<Buffer::Tag> tags;
96 for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
97 tags.push_back(released_buffers.front()->GetTag());
98 released_buffers.pop();
99 }
100 return tags;
101}
102
103} // namespace AudioCore
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
new file mode 100644
index 000000000..5f43b0798
--- /dev/null
+++ b/src/audio_core/stream.h
@@ -0,0 +1,89 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <vector>
10#include <queue>
11
12#include "audio_core/buffer.h"
13#include "common/assert.h"
14#include "common/common_types.h"
15#include "core/core_timing.h"
16
17namespace AudioCore {
18
19using BufferPtr = std::shared_ptr<Buffer>;
20
21/**
22 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
23 */
24class Stream {
25public:
26 /// Audio format of the stream
27 enum class Format {
28 Mono16,
29 Stereo16,
30 Multi51Channel16,
31 };
32
33 /// Callback function type, used to change guest state on a buffer being released
34 using ReleaseCallback = std::function<void()>;
35
36 Stream(int sample_rate, Format format, ReleaseCallback&& release_callback);
37
38 /// Plays the audio stream
39 void Play();
40
41 /// Stops the audio stream
42 void Stop();
43
44 /// Queues a buffer into the audio stream, returns true on success
45 bool QueueBuffer(BufferPtr&& buffer);
46
47 /// Returns true if the audio stream contains a buffer with the specified tag
48 bool ContainsBuffer(Buffer::Tag tag) const;
49
50 /// Returns a vector of recently released buffers specified by tag
51 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
52
53 /// Returns true if the stream is currently playing
54 bool IsPlaying() const {
55 return state == State::Playing;
56 }
57
58 /// Returns the number of queued buffers
59 size_t GetQueueSize() const {
60 return queued_buffers.size();
61 }
62
63private:
64 /// Current state of the stream
65 enum class State {
66 Stopped,
67 Playing,
68 };
69
70 /// Plays the next queued buffer in the audio stream, starting playback if necessary
71 void PlayNextBuffer();
72
73 /// Releases the actively playing buffer, signalling that it has been completed
74 void ReleaseActiveBuffer();
75
76 /// Gets the number of core cycles when the specified buffer will be released
77 s64 GetBufferReleaseCycles(const Buffer& buffer) const;
78
79 int sample_rate; ///< Sample rate of the stream
80 Format format; ///< Format of the stream
81 ReleaseCallback release_callback; ///< Buffer release callback for the stream
82 State state{State::Stopped}; ///< Playback state of the stream
83 CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
84 BufferPtr active_buffer; ///< Actively playing buffer in the stream
85 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
86 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
87};
88
89} // namespace AudioCore
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b367c2a27..95dbba678 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -305,7 +305,7 @@ add_library(core STATIC
305 305
306create_target_directory_groups(core) 306create_target_directory_groups(core)
307 307
308target_link_libraries(core PUBLIC common PRIVATE video_core) 308target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
309target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) 309target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
310 310
311if (ARCHITECTURE_x86_64) 311if (ARCHITECTURE_x86_64)