audio_core: Add initial code for keeping track of audout state.
This commit is contained in:
		| @@ -3,6 +3,7 @@ include_directories(.) | |||||||
|  |  | ||||||
| add_subdirectory(common) | add_subdirectory(common) | ||||||
| add_subdirectory(core) | add_subdirectory(core) | ||||||
|  | add_subdirectory(audio_core) | ||||||
| add_subdirectory(video_core) | add_subdirectory(video_core) | ||||||
| add_subdirectory(input_common) | add_subdirectory(input_common) | ||||||
| add_subdirectory(tests) | add_subdirectory(tests) | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								src/audio_core/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/audio_core/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | add_library(audio_core STATIC | ||||||
|  |     audio_out.cpp | ||||||
|  |     audio_out.h | ||||||
|  |     buffer.h | ||||||
|  |     stream.cpp | ||||||
|  |     stream.h | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | create_target_directory_groups(audio_core) | ||||||
|  |  | ||||||
|  | target_link_libraries(audio_core PUBLIC common core) | ||||||
							
								
								
									
										50
									
								
								src/audio_core/audio_out.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/audio_core/audio_out.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | // Copyright 2018 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "audio_core/audio_out.h" | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | namespace AudioCore { | ||||||
|  |  | ||||||
|  | /// Returns the stream format from the specified number of channels | ||||||
|  | static Stream::Format ChannelsToStreamFormat(int num_channels) { | ||||||
|  |     switch (num_channels) { | ||||||
|  |     case 1: | ||||||
|  |         return Stream::Format::Mono16; | ||||||
|  |     case 2: | ||||||
|  |         return Stream::Format::Stereo16; | ||||||
|  |     case 6: | ||||||
|  |         return Stream::Format::Multi51Channel16; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels); | ||||||
|  |     UNREACHABLE(); | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | StreamPtr AudioOut::OpenStream(int sample_rate, int num_channels, | ||||||
|  |                                Stream::ReleaseCallback&& release_callback) { | ||||||
|  |     streams.push_back(std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels), | ||||||
|  |                                                std::move(release_callback))); | ||||||
|  |     return streams.back(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::vector<u64> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { | ||||||
|  |     return stream->GetTagsAndReleaseBuffers(max_count); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AudioOut::StartStream(StreamPtr stream) { | ||||||
|  |     stream->Play(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AudioOut::StopStream(StreamPtr stream) { | ||||||
|  |     stream->Stop(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) { | ||||||
|  |     return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data))); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace AudioCore | ||||||
							
								
								
									
										44
									
								
								src/audio_core/audio_out.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/audio_core/audio_out.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | // Copyright 2018 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "audio_core/buffer.h" | ||||||
|  | #include "audio_core/stream.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | namespace AudioCore { | ||||||
|  |  | ||||||
|  | using StreamPtr = std::shared_ptr<Stream>; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Represents an audio playback interface, used to open and play audio streams | ||||||
|  |  */ | ||||||
|  | class AudioOut { | ||||||
|  | public: | ||||||
|  |     /// Opens a new audio stream | ||||||
|  |     StreamPtr OpenStream(int sample_rate, int num_channels, | ||||||
|  |                          Stream::ReleaseCallback&& release_callback); | ||||||
|  |  | ||||||
|  |     /// Returns a vector of recently released buffers specified by tag for the specified stream | ||||||
|  |     std::vector<u64> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count); | ||||||
|  |  | ||||||
|  |     /// Starts an audio stream for playback | ||||||
|  |     void StartStream(StreamPtr stream); | ||||||
|  |  | ||||||
|  |     /// Stops an audio stream that is currently playing | ||||||
|  |     void StopStream(StreamPtr stream); | ||||||
|  |  | ||||||
|  |     /// Queues a buffer into the specified audio stream, returns true on success | ||||||
|  |     bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data); | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     /// Active audio streams on the interface | ||||||
|  |     std::vector<StreamPtr> streams; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace AudioCore | ||||||
							
								
								
									
										37
									
								
								src/audio_core/buffer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/audio_core/buffer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | // Copyright 2018 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | namespace AudioCore { | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Represents a buffer of audio samples to be played in an audio stream | ||||||
|  |  */ | ||||||
|  | class Buffer { | ||||||
|  | public: | ||||||
|  |     using Tag = u64; | ||||||
|  |  | ||||||
|  |     Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {} | ||||||
|  |  | ||||||
|  |     /// Returns the raw audio data for the buffer | ||||||
|  |     const std::vector<u8>& GetData() const { | ||||||
|  |         return data; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the buffer tag, this is provided by the game to the audout service | ||||||
|  |     Tag GetTag() const { | ||||||
|  |         return tag; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     Tag tag; | ||||||
|  |     std::vector<u8> data; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace AudioCore | ||||||
							
								
								
									
										103
									
								
								src/audio_core/stream.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/audio_core/stream.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | // Copyright 2018 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/core_timing.h" | ||||||
|  | #include "core/core_timing_util.h" | ||||||
|  |  | ||||||
|  | #include "audio_core/stream.h" | ||||||
|  |  | ||||||
|  | namespace AudioCore { | ||||||
|  |  | ||||||
|  | constexpr size_t MaxAudioBufferCount{32}; | ||||||
|  |  | ||||||
|  | /// Returns the sample size for the specified audio stream format | ||||||
|  | static size_t SampleSizeFromFormat(Stream::Format format) { | ||||||
|  |     switch (format) { | ||||||
|  |     case Stream::Format::Mono16: | ||||||
|  |         return 2; | ||||||
|  |     case Stream::Format::Stereo16: | ||||||
|  |         return 4; | ||||||
|  |     case Stream::Format::Multi51Channel16: | ||||||
|  |         return 12; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format)); | ||||||
|  |     UNREACHABLE(); | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Stream::Stream(int sample_rate, Format format, ReleaseCallback&& release_callback) | ||||||
|  |     : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)} { | ||||||
|  |     release_event = CoreTiming::RegisterEvent( | ||||||
|  |         "Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Stream::Play() { | ||||||
|  |     state = State::Playing; | ||||||
|  |     PlayNextBuffer(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Stream::Stop() { | ||||||
|  |     ASSERT_MSG(false, "Unimplemented"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { | ||||||
|  |     const size_t num_samples{buffer.GetData().size() / SampleSizeFromFormat(format)}; | ||||||
|  |     return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Stream::PlayNextBuffer() { | ||||||
|  |     if (!IsPlaying()) { | ||||||
|  |         // Ensure we are in playing state before playing the next buffer | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (active_buffer) { | ||||||
|  |         // Do not queue a new buffer if we are already playing a buffer | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (queued_buffers.empty()) { | ||||||
|  |         // No queued buffers - we are effectively paused | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     active_buffer = queued_buffers.front(); | ||||||
|  |     queued_buffers.pop(); | ||||||
|  |  | ||||||
|  |     CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Stream::ReleaseActiveBuffer() { | ||||||
|  |     released_buffers.push(std::move(active_buffer)); | ||||||
|  |     release_callback(); | ||||||
|  |     PlayNextBuffer(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Stream::QueueBuffer(BufferPtr&& buffer) { | ||||||
|  |     if (queued_buffers.size() < MaxAudioBufferCount) { | ||||||
|  |         queued_buffers.push(std::move(buffer)); | ||||||
|  |         PlayNextBuffer(); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool Stream::ContainsBuffer(Buffer::Tag tag) const { | ||||||
|  |     ASSERT_MSG(false, "Unimplemented"); | ||||||
|  |     return {}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) { | ||||||
|  |     std::vector<Buffer::Tag> tags; | ||||||
|  |     for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { | ||||||
|  |         tags.push_back(released_buffers.front()->GetTag()); | ||||||
|  |         released_buffers.pop(); | ||||||
|  |     } | ||||||
|  |     return tags; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace AudioCore | ||||||
							
								
								
									
										89
									
								
								src/audio_core/stream.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/audio_core/stream.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | // Copyright 2018 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <functional> | ||||||
|  | #include <memory> | ||||||
|  | #include <vector> | ||||||
|  | #include <queue> | ||||||
|  |  | ||||||
|  | #include "audio_core/buffer.h" | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/core_timing.h" | ||||||
|  |  | ||||||
|  | namespace AudioCore { | ||||||
|  |  | ||||||
|  | using BufferPtr = std::shared_ptr<Buffer>; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut | ||||||
|  |  */ | ||||||
|  | class Stream { | ||||||
|  | public: | ||||||
|  |     /// Audio format of the stream | ||||||
|  |     enum class Format { | ||||||
|  |         Mono16, | ||||||
|  |         Stereo16, | ||||||
|  |         Multi51Channel16, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /// Callback function type, used to change guest state on a buffer being released | ||||||
|  |     using ReleaseCallback = std::function<void()>; | ||||||
|  |  | ||||||
|  |     Stream(int sample_rate, Format format, ReleaseCallback&& release_callback); | ||||||
|  |  | ||||||
|  |     /// Plays the audio stream | ||||||
|  |     void Play(); | ||||||
|  |  | ||||||
|  |     /// Stops the audio stream | ||||||
|  |     void Stop(); | ||||||
|  |  | ||||||
|  |     /// Queues a buffer into the audio stream, returns true on success | ||||||
|  |     bool QueueBuffer(BufferPtr&& buffer); | ||||||
|  |  | ||||||
|  |     /// Returns true if the audio stream contains a buffer with the specified tag | ||||||
|  |     bool ContainsBuffer(Buffer::Tag tag) const; | ||||||
|  |  | ||||||
|  |     /// Returns a vector of recently released buffers specified by tag | ||||||
|  |     std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count); | ||||||
|  |  | ||||||
|  |     /// Returns true if the stream is currently playing | ||||||
|  |     bool IsPlaying() const { | ||||||
|  |         return state == State::Playing; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the number of queued buffers | ||||||
|  |     size_t GetQueueSize() const { | ||||||
|  |         return queued_buffers.size(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     /// Current state of the stream | ||||||
|  |     enum class State { | ||||||
|  |         Stopped, | ||||||
|  |         Playing, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /// Plays the next queued buffer in the audio stream, starting playback if necessary | ||||||
|  |     void PlayNextBuffer(); | ||||||
|  |  | ||||||
|  |     /// Releases the actively playing buffer, signalling that it has been completed | ||||||
|  |     void ReleaseActiveBuffer(); | ||||||
|  |  | ||||||
|  |     /// Gets the number of core cycles when the specified buffer will be released | ||||||
|  |     s64 GetBufferReleaseCycles(const Buffer& buffer) const; | ||||||
|  |  | ||||||
|  |     int sample_rate;                        ///< Sample rate of the stream | ||||||
|  |     Format format;                          ///< Format of the stream | ||||||
|  |     ReleaseCallback release_callback;       ///< Buffer release callback for the stream | ||||||
|  |     State state{State::Stopped};            ///< Playback state of the stream | ||||||
|  |     CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream | ||||||
|  |     BufferPtr active_buffer;                ///< Actively playing buffer in the stream | ||||||
|  |     std::queue<BufferPtr> queued_buffers;   ///< Buffers queued to be played in the stream | ||||||
|  |     std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace AudioCore | ||||||
| @@ -305,7 +305,7 @@ add_library(core STATIC | |||||||
|  |  | ||||||
| create_target_directory_groups(core) | create_target_directory_groups(core) | ||||||
|  |  | ||||||
| target_link_libraries(core PUBLIC common PRIVATE video_core) | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | ||||||
| target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) | ||||||
|  |  | ||||||
| if (ARCHITECTURE_x86_64) | if (ARCHITECTURE_x86_64) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user