mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	audio_core: Remove global state
This commit is contained in:
		
							parent
							
								
									dca5fd291f
								
							
						
					
					
						commit
						ab3d53131a
					
				
					 34 changed files with 711 additions and 650 deletions
				
			
		|  | @ -1,17 +1,17 @@ | ||||||
| add_library(audio_core STATIC | add_library(audio_core STATIC | ||||||
|     audio_core.cpp |     audio_types.h | ||||||
|     audio_core.h |  | ||||||
|     codec.cpp |     codec.cpp | ||||||
|     codec.h |     codec.h | ||||||
|  |     dsp_interface.cpp | ||||||
|  |     dsp_interface.h | ||||||
|     hle/common.h |     hle/common.h | ||||||
|     hle/dsp.cpp |  | ||||||
|     hle/dsp.h |  | ||||||
|     hle/filter.cpp |     hle/filter.cpp | ||||||
|     hle/filter.h |     hle/filter.h | ||||||
|  |     hle/hle.cpp | ||||||
|  |     hle/hle.h | ||||||
|     hle/mixers.cpp |     hle/mixers.cpp | ||||||
|     hle/mixers.h |     hle/mixers.h | ||||||
|     hle/pipe.cpp |     hle/shared_memory.h | ||||||
|     hle/pipe.h |  | ||||||
|     hle/source.cpp |     hle/source.cpp | ||||||
|     hle/source.h |     hle/source.h | ||||||
|     interpolate.cpp |     interpolate.cpp | ||||||
|  |  | ||||||
|  | @ -1,61 +0,0 @@ | ||||||
| // Copyright 2016 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #include <array> |  | ||||||
| #include <memory> |  | ||||||
| #include <string> |  | ||||||
| #include "audio_core/audio_core.h" |  | ||||||
| #include "audio_core/hle/dsp.h" |  | ||||||
| #include "audio_core/hle/pipe.h" |  | ||||||
| #include "audio_core/null_sink.h" |  | ||||||
| #include "audio_core/sink.h" |  | ||||||
| #include "audio_core/sink_details.h" |  | ||||||
| #include "common/common_types.h" |  | ||||||
| #include "core/core_timing.h" |  | ||||||
| #include "core/hle/service/dsp_dsp.h" |  | ||||||
| 
 |  | ||||||
| namespace AudioCore { |  | ||||||
| 
 |  | ||||||
| // Audio Ticks occur about every 5 miliseconds.
 |  | ||||||
| static CoreTiming::EventType* tick_event;            ///< CoreTiming event
 |  | ||||||
| static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles
 |  | ||||||
| 
 |  | ||||||
| static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { |  | ||||||
|     if (DSP::HLE::Tick()) { |  | ||||||
|         // TODO(merry): Signal all the other interrupts as appropriate.
 |  | ||||||
|         Service::DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio); |  | ||||||
|         // HACK(merry): Added to prevent regressions. Will remove soon.
 |  | ||||||
|         Service::DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Binary); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Reschedule recurrent event
 |  | ||||||
|     CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Init() { |  | ||||||
|     DSP::HLE::Init(); |  | ||||||
| 
 |  | ||||||
|     tick_event = CoreTiming::RegisterEvent("AudioCore::tick_event", AudioTickCallback); |  | ||||||
|     CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() { |  | ||||||
|     return DSP::HLE::g_dsp_memory.raw_memory; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SelectSink(std::string sink_id) { |  | ||||||
|     const SinkDetails& sink_details = GetSinkDetails(sink_id); |  | ||||||
|     DSP::HLE::SetSink(sink_details.factory()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EnableStretching(bool enable) { |  | ||||||
|     DSP::HLE::EnableStretching(enable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Shutdown() { |  | ||||||
|     CoreTiming::UnscheduleEvent(tick_event, 0); |  | ||||||
|     DSP::HLE::Shutdown(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace AudioCore
 |  | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| // Copyright 2016 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <array> |  | ||||||
| #include <string> |  | ||||||
| #include "common/common_types.h" |  | ||||||
| #include "core/memory.h" |  | ||||||
| 
 |  | ||||||
| namespace AudioCore { |  | ||||||
| 
 |  | ||||||
| constexpr int native_sample_rate = 32728; ///< 32kHz
 |  | ||||||
| 
 |  | ||||||
| /// Initialise Audio Core
 |  | ||||||
| void Init(); |  | ||||||
| 
 |  | ||||||
| /// Returns a reference to the array backing DSP memory
 |  | ||||||
| std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory(); |  | ||||||
| 
 |  | ||||||
| /// Select the sink to use based on sink id.
 |  | ||||||
| void SelectSink(std::string sink_id); |  | ||||||
| 
 |  | ||||||
| /// Enable/Disable stretching.
 |  | ||||||
| void EnableStretching(bool enable); |  | ||||||
| 
 |  | ||||||
| /// Shutdown Audio Core
 |  | ||||||
| void Shutdown(); |  | ||||||
| 
 |  | ||||||
| } // namespace AudioCore
 |  | ||||||
							
								
								
									
										43
									
								
								src/audio_core/audio_types.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/audio_core/audio_types.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <cstddef> | ||||||
|  | #include <deque> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | namespace AudioCore { | ||||||
|  | 
 | ||||||
|  | /// Samples per second which the 3DS's audio hardware natively outputs at
 | ||||||
|  | constexpr int native_sample_rate = 32728; // Hz
 | ||||||
|  | 
 | ||||||
|  | /// Samples per audio frame at native sample rate
 | ||||||
|  | constexpr int samples_per_frame = 160; | ||||||
|  | 
 | ||||||
|  | /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
 | ||||||
|  | using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; | ||||||
|  | 
 | ||||||
|  | /// The DSP is quadraphonic internally.
 | ||||||
|  | using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; | ||||||
|  | 
 | ||||||
|  | /// A variable length buffer of signed PCM16 stereo samples.
 | ||||||
|  | using StereoBuffer16 = std::deque<std::array<s16, 2>>; | ||||||
|  | 
 | ||||||
|  | constexpr size_t num_dsp_pipe = 8; | ||||||
|  | enum class DspPipe { | ||||||
|  |     Debug = 0, | ||||||
|  |     Dma = 1, | ||||||
|  |     Audio = 2, | ||||||
|  |     Binary = 3, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class DspState { | ||||||
|  |     Off, | ||||||
|  |     On, | ||||||
|  |     Sleeping, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace AudioCore
 | ||||||
|  | @ -5,12 +5,13 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <vector> | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/codec.h" | #include "audio_core/codec.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| 
 | 
 | ||||||
|  | namespace AudioCore { | ||||||
| namespace Codec { | namespace Codec { | ||||||
| 
 | 
 | ||||||
| StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, | StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, | ||||||
|  | @ -124,4 +125,5 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| }; | } // namespace Codec
 | ||||||
|  | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -5,14 +5,12 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <deque> | #include "audio_core/audio_types.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
|  | namespace AudioCore { | ||||||
| namespace Codec { | namespace Codec { | ||||||
| 
 | 
 | ||||||
| /// A variable length buffer of signed PCM16 stereo samples.
 |  | ||||||
| using StereoBuffer16 = std::deque<std::array<s16, 2>>; |  | ||||||
| 
 |  | ||||||
| /// See: Codec::DecodeADPCM
 | /// See: Codec::DecodeADPCM
 | ||||||
| struct ADPCMState { | struct ADPCMState { | ||||||
|     // Two historical samples from previous processed buffer,
 |     // Two historical samples from previous processed buffer,
 | ||||||
|  | @ -48,4 +46,5 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, | ||||||
|  */ |  */ | ||||||
| StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, | StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, | ||||||
|                            const size_t sample_count); |                            const size_t sample_count); | ||||||
| }; | } // namespace Codec
 | ||||||
|  | } // namespace AudioCore
 | ||||||
|  |  | ||||||
							
								
								
									
										75
									
								
								src/audio_core/dsp_interface.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/audio_core/dsp_interface.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <cstddef> | ||||||
|  | #include "audio_core/dsp_interface.h" | ||||||
|  | #include "audio_core/sink.h" | ||||||
|  | #include "audio_core/sink_details.h" | ||||||
|  | #include "common/assert.h" | ||||||
|  | 
 | ||||||
|  | namespace AudioCore { | ||||||
|  | 
 | ||||||
|  | DspInterface::DspInterface() = default; | ||||||
|  | 
 | ||||||
|  | DspInterface::~DspInterface() { | ||||||
|  |     if (perform_time_stretching) { | ||||||
|  |         FlushResidualStretcherAudio(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspInterface::SetSink(const std::string& sink_id) { | ||||||
|  |     const SinkDetails& sink_details = GetSinkDetails(sink_id); | ||||||
|  |     sink = sink_details.factory(); | ||||||
|  |     time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Sink& DspInterface::GetSink() { | ||||||
|  |     ASSERT(sink); | ||||||
|  |     return *sink.get(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspInterface::EnableStretching(bool enable) { | ||||||
|  |     if (perform_time_stretching == enable) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (!enable) { | ||||||
|  |         FlushResidualStretcherAudio(); | ||||||
|  |     } | ||||||
|  |     perform_time_stretching = enable; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspInterface::OutputFrame(const StereoFrame16& frame) { | ||||||
|  |     if (!sink) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (perform_time_stretching) { | ||||||
|  |         time_stretcher.AddSamples(&frame[0][0], frame.size()); | ||||||
|  |         std::vector<s16> stretched_samples = time_stretcher.Process(sink->SamplesInQueue()); | ||||||
|  |         sink->EnqueueSamples(stretched_samples.data(), stretched_samples.size() / 2); | ||||||
|  |     } else { | ||||||
|  |         constexpr size_t maximum_sample_latency = 2048; // about 64 miliseconds
 | ||||||
|  |         if (sink->SamplesInQueue() > maximum_sample_latency) { | ||||||
|  |             // This can occur if we're running too fast and samples are starting to back up.
 | ||||||
|  |             // Just drop the samples.
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         sink->EnqueueSamples(&frame[0][0], frame.size()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspInterface::FlushResidualStretcherAudio() { | ||||||
|  |     if (!sink) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     time_stretcher.Flush(); | ||||||
|  |     while (true) { | ||||||
|  |         std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue()); | ||||||
|  |         if (residual_audio.empty()) | ||||||
|  |             break; | ||||||
|  |         sink->EnqueueSamples(residual_audio.data(), residual_audio.size() / 2); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace AudioCore
 | ||||||
							
								
								
									
										81
									
								
								src/audio_core/dsp_interface.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/audio_core/dsp_interface.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,81 @@ | ||||||
|  | // Copyright 2017 Citra 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/audio_types.h" | ||||||
|  | #include "audio_core/time_stretch.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/memory.h" | ||||||
|  | 
 | ||||||
|  | namespace AudioCore { | ||||||
|  | 
 | ||||||
|  | class Sink; | ||||||
|  | 
 | ||||||
|  | class DspInterface { | ||||||
|  | public: | ||||||
|  |     DspInterface(); | ||||||
|  |     virtual ~DspInterface(); | ||||||
|  | 
 | ||||||
|  |     DspInterface(const DspInterface&) = delete; | ||||||
|  |     DspInterface(DspInterface&&) = delete; | ||||||
|  |     DspInterface& operator=(const DspInterface&) = delete; | ||||||
|  |     DspInterface& operator=(DspInterface&&) = delete; | ||||||
|  | 
 | ||||||
|  |     /// Get the state of the DSP
 | ||||||
|  |     virtual DspState GetDspState() const = 0; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Reads `length` bytes from the DSP pipe identified with `pipe_number`. | ||||||
|  |      * @note Can read up to the maximum value of a u16 in bytes (65,535). | ||||||
|  |      * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an | ||||||
|  |      * empty vector will be returned. | ||||||
|  |      * @note IF `length` is set to 0, an empty vector will be returned. | ||||||
|  |      * @note IF `length` is greater than the amount of data available, this function will only read | ||||||
|  |      * the available amount. | ||||||
|  |      * @param pipe_number a `DspPipe` | ||||||
|  |      * @param length the number of bytes to read. The max is 65,535 (max of u16). | ||||||
|  |      * @returns a vector of bytes from the specified pipe. On error, will be empty. | ||||||
|  |      */ | ||||||
|  |     virtual std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) = 0; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * How much data is left in pipe | ||||||
|  |      * @param pipe_number The Pipe ID | ||||||
|  |      * @return The amount of data remaning in the pipe. This is the maximum length PipeRead will | ||||||
|  |      * return. | ||||||
|  |      */ | ||||||
|  |     virtual size_t GetPipeReadableSize(DspPipe pipe_number) const = 0; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Write to a DSP pipe. | ||||||
|  |      * @param pipe_number The Pipe ID | ||||||
|  |      * @param buffer The data to write to the pipe. | ||||||
|  |      */ | ||||||
|  |     virtual void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) = 0; | ||||||
|  | 
 | ||||||
|  |     /// Returns a reference to the array backing DSP memory
 | ||||||
|  |     virtual std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() = 0; | ||||||
|  | 
 | ||||||
|  |     /// Select the sink to use based on sink id.
 | ||||||
|  |     void SetSink(const std::string& sink_id); | ||||||
|  |     /// Get the current sink
 | ||||||
|  |     Sink& GetSink(); | ||||||
|  |     /// Enable/Disable audio stretching.
 | ||||||
|  |     void EnableStretching(bool enable); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     void OutputFrame(const StereoFrame16& frame); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void FlushResidualStretcherAudio(); | ||||||
|  | 
 | ||||||
|  |     std::unique_ptr<Sink> sink; | ||||||
|  |     bool perform_time_stretching = false; | ||||||
|  |     TimeStretcher time_stretcher; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace AudioCore
 | ||||||
|  | @ -5,20 +5,12 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <cstddef> | ||||||
| #include "common/common_types.h" |  | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| constexpr int num_sources = 24; | constexpr size_t num_sources = 24; | ||||||
| constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
 |  | ||||||
| 
 |  | ||||||
| /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
 |  | ||||||
| using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; |  | ||||||
| 
 |  | ||||||
| /// The DSP is quadraphonic internally.
 |  | ||||||
| using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; |  | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. |  * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. | ||||||
|  | @ -31,4 +23,4 @@ void FilterFrame(FrameT& frame, FilterT& filter) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -1,172 +0,0 @@ | ||||||
| // Copyright 2016 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #include <array> |  | ||||||
| #include <memory> |  | ||||||
| #include "audio_core/hle/dsp.h" |  | ||||||
| #include "audio_core/hle/mixers.h" |  | ||||||
| #include "audio_core/hle/pipe.h" |  | ||||||
| #include "audio_core/hle/source.h" |  | ||||||
| #include "audio_core/sink.h" |  | ||||||
| #include "audio_core/time_stretch.h" |  | ||||||
| 
 |  | ||||||
| namespace DSP { |  | ||||||
| namespace HLE { |  | ||||||
| 
 |  | ||||||
| // Region management
 |  | ||||||
| 
 |  | ||||||
| DspMemory g_dsp_memory; |  | ||||||
| 
 |  | ||||||
| static size_t CurrentRegionIndex() { |  | ||||||
|     // The region with the higher frame counter is chosen unless there is wraparound.
 |  | ||||||
|     // This function only returns a 0 or 1.
 |  | ||||||
|     u16 frame_counter_0 = g_dsp_memory.region_0.frame_counter; |  | ||||||
|     u16 frame_counter_1 = g_dsp_memory.region_1.frame_counter; |  | ||||||
| 
 |  | ||||||
|     if (frame_counter_0 == 0xFFFFu && frame_counter_1 != 0xFFFEu) { |  | ||||||
|         // Wraparound has occurred.
 |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (frame_counter_1 == 0xFFFFu && frame_counter_0 != 0xFFFEu) { |  | ||||||
|         // Wraparound has occurred.
 |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return (frame_counter_0 > frame_counter_1) ? 0 : 1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static SharedMemory& ReadRegion() { |  | ||||||
|     return CurrentRegionIndex() == 0 ? g_dsp_memory.region_0 : g_dsp_memory.region_1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static SharedMemory& WriteRegion() { |  | ||||||
|     return CurrentRegionIndex() != 0 ? g_dsp_memory.region_0 : g_dsp_memory.region_1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Audio processing and mixing
 |  | ||||||
| 
 |  | ||||||
| static std::array<Source, num_sources> sources = { |  | ||||||
|     Source(0),  Source(1),  Source(2),  Source(3),  Source(4),  Source(5),  Source(6),  Source(7), |  | ||||||
|     Source(8),  Source(9),  Source(10), Source(11), Source(12), Source(13), Source(14), Source(15), |  | ||||||
|     Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23), |  | ||||||
| }; |  | ||||||
| static Mixers mixers; |  | ||||||
| 
 |  | ||||||
| static StereoFrame16 GenerateCurrentFrame() { |  | ||||||
|     SharedMemory& read = ReadRegion(); |  | ||||||
|     SharedMemory& write = WriteRegion(); |  | ||||||
| 
 |  | ||||||
|     std::array<QuadFrame32, 3> intermediate_mixes = {}; |  | ||||||
| 
 |  | ||||||
|     // Generate intermediate mixes
 |  | ||||||
|     for (size_t i = 0; i < num_sources; i++) { |  | ||||||
|         write.source_statuses.status[i] = |  | ||||||
|             sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); |  | ||||||
|         for (size_t mix = 0; mix < 3; mix++) { |  | ||||||
|             sources[i].MixInto(intermediate_mixes[mix], mix); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Generate final mix
 |  | ||||||
|     write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, |  | ||||||
|                                    write.intermediate_mix_samples, intermediate_mixes); |  | ||||||
| 
 |  | ||||||
|     StereoFrame16 output_frame = mixers.GetOutput(); |  | ||||||
| 
 |  | ||||||
|     // Write current output frame to the shared memory region
 |  | ||||||
|     for (size_t samplei = 0; samplei < output_frame.size(); samplei++) { |  | ||||||
|         for (size_t channeli = 0; channeli < output_frame[0].size(); channeli++) { |  | ||||||
|             write.final_samples.pcm16[samplei][channeli] = s16_le(output_frame[samplei][channeli]); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return output_frame; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Audio output
 |  | ||||||
| 
 |  | ||||||
| static bool perform_time_stretching = true; |  | ||||||
| static std::unique_ptr<AudioCore::Sink> sink; |  | ||||||
| static AudioCore::TimeStretcher time_stretcher; |  | ||||||
| 
 |  | ||||||
| static void FlushResidualStretcherAudio() { |  | ||||||
|     time_stretcher.Flush(); |  | ||||||
|     while (true) { |  | ||||||
|         std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue()); |  | ||||||
|         if (residual_audio.empty()) |  | ||||||
|             break; |  | ||||||
|         sink->EnqueueSamples(residual_audio.data(), residual_audio.size() / 2); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void OutputCurrentFrame(const StereoFrame16& frame) { |  | ||||||
|     if (perform_time_stretching) { |  | ||||||
|         time_stretcher.AddSamples(&frame[0][0], frame.size()); |  | ||||||
|         std::vector<s16> stretched_samples = time_stretcher.Process(sink->SamplesInQueue()); |  | ||||||
|         sink->EnqueueSamples(stretched_samples.data(), stretched_samples.size() / 2); |  | ||||||
|     } else { |  | ||||||
|         constexpr size_t maximum_sample_latency = 2048; // about 64 miliseconds
 |  | ||||||
|         if (sink->SamplesInQueue() > maximum_sample_latency) { |  | ||||||
|             // This can occur if we're running too fast and samples are starting to back up.
 |  | ||||||
|             // Just drop the samples.
 |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         sink->EnqueueSamples(&frame[0][0], frame.size()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void EnableStretching(bool enable) { |  | ||||||
|     if (perform_time_stretching == enable) |  | ||||||
|         return; |  | ||||||
| 
 |  | ||||||
|     if (!enable) { |  | ||||||
|         FlushResidualStretcherAudio(); |  | ||||||
|     } |  | ||||||
|     perform_time_stretching = enable; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Public Interface
 |  | ||||||
| 
 |  | ||||||
| void Init() { |  | ||||||
|     DSP::HLE::ResetPipes(); |  | ||||||
| 
 |  | ||||||
|     for (auto& source : sources) { |  | ||||||
|         source.Reset(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     mixers.Reset(); |  | ||||||
| 
 |  | ||||||
|     time_stretcher.Reset(); |  | ||||||
|     if (sink) { |  | ||||||
|         time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Shutdown() { |  | ||||||
|     if (perform_time_stretching) { |  | ||||||
|         FlushResidualStretcherAudio(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool Tick() { |  | ||||||
|     StereoFrame16 current_frame = {}; |  | ||||||
| 
 |  | ||||||
|     // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
 |  | ||||||
|     // shared memory region)
 |  | ||||||
|     current_frame = GenerateCurrentFrame(); |  | ||||||
| 
 |  | ||||||
|     OutputCurrentFrame(current_frame); |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SetSink(std::unique_ptr<AudioCore::Sink> sink_) { |  | ||||||
|     sink = std::move(sink_); |  | ||||||
|     time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace HLE
 |  | ||||||
| } // namespace DSP
 |  | ||||||
|  | @ -5,12 +5,12 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/dsp.h" |  | ||||||
| #include "audio_core/hle/filter.h" | #include "audio_core/hle/filter.h" | ||||||
|  | #include "audio_core/hle/shared_memory.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| void SourceFilters::Reset() { | void SourceFilters::Reset() { | ||||||
|  | @ -114,4 +114,4 @@ std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -5,11 +5,11 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/shared_memory.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| /// Preprocessing filters. There is an independent set of filters for each Source.
 | /// Preprocessing filters. There is an independent set of filters for each Source.
 | ||||||
|  | @ -114,4 +114,4 @@ private: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
							
								
								
									
										341
									
								
								src/audio_core/hle/hle.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								src/audio_core/hle/hle.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,341 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "audio_core/audio_types.h" | ||||||
|  | #include "audio_core/hle/common.h" | ||||||
|  | #include "audio_core/hle/hle.h" | ||||||
|  | #include "audio_core/hle/mixers.h" | ||||||
|  | #include "audio_core/hle/shared_memory.h" | ||||||
|  | #include "audio_core/hle/source.h" | ||||||
|  | #include "audio_core/sink.h" | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/core_timing.h" | ||||||
|  | #include "core/hle/service/dsp_dsp.h" | ||||||
|  | 
 | ||||||
|  | namespace AudioCore { | ||||||
|  | 
 | ||||||
|  | static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles
 | ||||||
|  | 
 | ||||||
|  | struct DspHle::Impl final { | ||||||
|  | public: | ||||||
|  |     explicit Impl(DspHle& parent); | ||||||
|  |     ~Impl(); | ||||||
|  | 
 | ||||||
|  |     DspState GetDspState() const; | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> PipeRead(DspPipe pipe_number, u32 length); | ||||||
|  |     size_t GetPipeReadableSize(DspPipe pipe_number) const; | ||||||
|  |     void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer); | ||||||
|  | 
 | ||||||
|  |     std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void ResetPipes(); | ||||||
|  |     void WriteU16(DspPipe pipe_number, u16 value); | ||||||
|  |     void AudioPipeWriteStructAddresses(); | ||||||
|  | 
 | ||||||
|  |     size_t CurrentRegionIndex() const; | ||||||
|  |     HLE::SharedMemory& ReadRegion(); | ||||||
|  |     HLE::SharedMemory& WriteRegion(); | ||||||
|  | 
 | ||||||
|  |     StereoFrame16 GenerateCurrentFrame(); | ||||||
|  |     bool Tick(); | ||||||
|  |     void AudioTickCallback(int cycles_late); | ||||||
|  | 
 | ||||||
|  |     DspState dsp_state = DspState::Off; | ||||||
|  |     std::array<std::vector<u8>, num_dsp_pipe> pipe_data; | ||||||
|  | 
 | ||||||
|  |     HLE::DspMemory dsp_memory; | ||||||
|  |     std::array<HLE::Source, HLE::num_sources> sources{{ | ||||||
|  |         HLE::Source(0),  HLE::Source(1),  HLE::Source(2),  HLE::Source(3),  HLE::Source(4), | ||||||
|  |         HLE::Source(5),  HLE::Source(6),  HLE::Source(7),  HLE::Source(8),  HLE::Source(9), | ||||||
|  |         HLE::Source(10), HLE::Source(11), HLE::Source(12), HLE::Source(13), HLE::Source(14), | ||||||
|  |         HLE::Source(15), HLE::Source(16), HLE::Source(17), HLE::Source(18), HLE::Source(19), | ||||||
|  |         HLE::Source(20), HLE::Source(21), HLE::Source(22), HLE::Source(23), | ||||||
|  |     }}; | ||||||
|  |     HLE::Mixers mixers; | ||||||
|  | 
 | ||||||
|  |     DspHle& parent; | ||||||
|  |     CoreTiming::EventType* tick_event; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | DspHle::Impl::Impl(DspHle& parent_) : parent(parent_) { | ||||||
|  |     tick_event = | ||||||
|  |         CoreTiming::RegisterEvent("AudioCore::DspHle::tick_event", [this](u64, int cycles_late) { | ||||||
|  |             this->AudioTickCallback(cycles_late); | ||||||
|  |         }); | ||||||
|  |     CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DspHle::Impl::~Impl() { | ||||||
|  |     CoreTiming::UnscheduleEvent(tick_event, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DspState DspHle::Impl::GetDspState() const { | ||||||
|  |     return dsp_state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<u8> DspHle::Impl::PipeRead(DspPipe pipe_number, u32 length) { | ||||||
|  |     const size_t pipe_index = static_cast<size_t>(pipe_number); | ||||||
|  | 
 | ||||||
|  |     if (pipe_index >= num_dsp_pipe) { | ||||||
|  |         LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (length > UINT16_MAX) { // Can only read at most UINT16_MAX from the pipe
 | ||||||
|  |         LOG_ERROR(Audio_DSP, "length of %u greater than max of %u", length, UINT16_MAX); | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::vector<u8>& data = pipe_data[pipe_index]; | ||||||
|  | 
 | ||||||
|  |     if (length > data.size()) { | ||||||
|  |         LOG_WARNING( | ||||||
|  |             Audio_DSP, | ||||||
|  |             "pipe_number = %zu is out of data, application requested read of %u but %zu remain", | ||||||
|  |             pipe_index, length, data.size()); | ||||||
|  |         length = static_cast<u32>(data.size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (length == 0) | ||||||
|  |         return {}; | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> ret(data.begin(), data.begin() + length); | ||||||
|  |     data.erase(data.begin(), data.begin() + length); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t DspHle::Impl::GetPipeReadableSize(DspPipe pipe_number) const { | ||||||
|  |     const size_t pipe_index = static_cast<size_t>(pipe_number); | ||||||
|  | 
 | ||||||
|  |     if (pipe_index >= num_dsp_pipe) { | ||||||
|  |         LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return pipe_data[pipe_index].size(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | ||||||
|  |     switch (pipe_number) { | ||||||
|  |     case DspPipe::Audio: { | ||||||
|  |         if (buffer.size() != 4) { | ||||||
|  |             LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", | ||||||
|  |                       buffer.size()); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         enum class StateChange { | ||||||
|  |             Initialize = 0, | ||||||
|  |             Shutdown = 1, | ||||||
|  |             Wakeup = 2, | ||||||
|  |             Sleep = 3, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // The difference between Initialize and Wakeup is that Input state is maintained
 | ||||||
|  |         // when sleeping but isn't when turning it off and on again. (TODO: Implement this.)
 | ||||||
|  |         // Waking up from sleep garbles some of the structs in the memory region. (TODO:
 | ||||||
|  |         // Implement this.) Applications store away the state of these structs before
 | ||||||
|  |         // sleeping and reset it back after wakeup on behalf of the DSP.
 | ||||||
|  | 
 | ||||||
|  |         switch (static_cast<StateChange>(buffer[0])) { | ||||||
|  |         case StateChange::Initialize: | ||||||
|  |             LOG_INFO(Audio_DSP, "Application has requested initialization of DSP hardware"); | ||||||
|  |             ResetPipes(); | ||||||
|  |             AudioPipeWriteStructAddresses(); | ||||||
|  |             dsp_state = DspState::On; | ||||||
|  |             break; | ||||||
|  |         case StateChange::Shutdown: | ||||||
|  |             LOG_INFO(Audio_DSP, "Application has requested shutdown of DSP hardware"); | ||||||
|  |             dsp_state = DspState::Off; | ||||||
|  |             break; | ||||||
|  |         case StateChange::Wakeup: | ||||||
|  |             LOG_INFO(Audio_DSP, "Application has requested wakeup of DSP hardware"); | ||||||
|  |             ResetPipes(); | ||||||
|  |             AudioPipeWriteStructAddresses(); | ||||||
|  |             dsp_state = DspState::On; | ||||||
|  |             break; | ||||||
|  |         case StateChange::Sleep: | ||||||
|  |             LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware"); | ||||||
|  |             UNIMPLEMENTED(); | ||||||
|  |             dsp_state = DspState::Sleeping; | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             LOG_ERROR(Audio_DSP, | ||||||
|  |                       "Application has requested unknown state transition of DSP hardware %hhu", | ||||||
|  |                       buffer[0]); | ||||||
|  |             dsp_state = DspState::Off; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |         LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", | ||||||
|  |                      static_cast<size_t>(pipe_number)); | ||||||
|  |         UNIMPLEMENTED(); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::array<u8, Memory::DSP_RAM_SIZE>& DspHle::Impl::GetDspMemory() { | ||||||
|  |     return dsp_memory.raw_memory; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspHle::Impl::ResetPipes() { | ||||||
|  |     for (auto& data : pipe_data) { | ||||||
|  |         data.clear(); | ||||||
|  |     } | ||||||
|  |     dsp_state = DspState::Off; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspHle::Impl::WriteU16(DspPipe pipe_number, u16 value) { | ||||||
|  |     const size_t pipe_index = static_cast<size_t>(pipe_number); | ||||||
|  | 
 | ||||||
|  |     std::vector<u8>& data = pipe_data.at(pipe_index); | ||||||
|  |     // Little endian
 | ||||||
|  |     data.emplace_back(value & 0xFF); | ||||||
|  |     data.emplace_back(value >> 8); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspHle::Impl::AudioPipeWriteStructAddresses() { | ||||||
|  |     // These struct addresses are DSP dram addresses.
 | ||||||
|  |     // See also: DSP_DSP::ConvertProcessAddressFromDspDram
 | ||||||
|  |     static const std::array<u16, 15> struct_addresses = { | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, frame_counter) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, source_configurations) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, source_statuses) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, adpcm_coefficients) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, dsp_configuration) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, dsp_status) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, final_samples) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, intermediate_mix_samples) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, compressor) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, dsp_debug) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, unknown10) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, unknown11) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, unknown12) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, unknown13) / 2, | ||||||
|  |         0x8000 + offsetof(HLE::SharedMemory, unknown14) / 2, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // Begin with a u16 denoting the number of structs.
 | ||||||
|  |     WriteU16(DspPipe::Audio, static_cast<u16>(struct_addresses.size())); | ||||||
|  |     // Then write the struct addresses.
 | ||||||
|  |     for (u16 addr : struct_addresses) { | ||||||
|  |         WriteU16(DspPipe::Audio, addr); | ||||||
|  |     } | ||||||
|  |     // Signal that we have data on this pipe.
 | ||||||
|  |     Service::DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t DspHle::Impl::CurrentRegionIndex() const { | ||||||
|  |     // The region with the higher frame counter is chosen unless there is wraparound.
 | ||||||
|  |     // This function only returns a 0 or 1.
 | ||||||
|  |     const u16 frame_counter_0 = dsp_memory.region_0.frame_counter; | ||||||
|  |     const u16 frame_counter_1 = dsp_memory.region_1.frame_counter; | ||||||
|  | 
 | ||||||
|  |     if (frame_counter_0 == 0xFFFFu && frame_counter_1 != 0xFFFEu) { | ||||||
|  |         // Wraparound has occurred.
 | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (frame_counter_1 == 0xFFFFu && frame_counter_0 != 0xFFFEu) { | ||||||
|  |         // Wraparound has occurred.
 | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return (frame_counter_0 > frame_counter_1) ? 0 : 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HLE::SharedMemory& DspHle::Impl::ReadRegion() { | ||||||
|  |     return CurrentRegionIndex() == 0 ? dsp_memory.region_0 : dsp_memory.region_1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HLE::SharedMemory& DspHle::Impl::WriteRegion() { | ||||||
|  |     return CurrentRegionIndex() != 0 ? dsp_memory.region_0 : dsp_memory.region_1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | StereoFrame16 DspHle::Impl::GenerateCurrentFrame() { | ||||||
|  |     HLE::SharedMemory& read = ReadRegion(); | ||||||
|  |     HLE::SharedMemory& write = WriteRegion(); | ||||||
|  | 
 | ||||||
|  |     std::array<QuadFrame32, 3> intermediate_mixes = {}; | ||||||
|  | 
 | ||||||
|  |     // Generate intermediate mixes
 | ||||||
|  |     for (size_t i = 0; i < HLE::num_sources; i++) { | ||||||
|  |         write.source_statuses.status[i] = | ||||||
|  |             sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); | ||||||
|  |         for (size_t mix = 0; mix < 3; mix++) { | ||||||
|  |             sources[i].MixInto(intermediate_mixes[mix], mix); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Generate final mix
 | ||||||
|  |     write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, | ||||||
|  |                                    write.intermediate_mix_samples, intermediate_mixes); | ||||||
|  | 
 | ||||||
|  |     StereoFrame16 output_frame = mixers.GetOutput(); | ||||||
|  | 
 | ||||||
|  |     // Write current output frame to the shared memory region
 | ||||||
|  |     for (size_t samplei = 0; samplei < output_frame.size(); samplei++) { | ||||||
|  |         for (size_t channeli = 0; channeli < output_frame[0].size(); channeli++) { | ||||||
|  |             write.final_samples.pcm16[samplei][channeli] = s16_le(output_frame[samplei][channeli]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return output_frame; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DspHle::Impl::Tick() { | ||||||
|  |     StereoFrame16 current_frame = {}; | ||||||
|  | 
 | ||||||
|  |     // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
 | ||||||
|  |     // shared memory region)
 | ||||||
|  |     current_frame = GenerateCurrentFrame(); | ||||||
|  | 
 | ||||||
|  |     parent.OutputFrame(current_frame); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspHle::Impl::AudioTickCallback(int cycles_late) { | ||||||
|  |     if (Tick()) { | ||||||
|  |         // TODO(merry): Signal all the other interrupts as appropriate.
 | ||||||
|  |         Service::DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); | ||||||
|  |         // HACK(merry): Added to prevent regressions. Will remove soon.
 | ||||||
|  |         Service::DSP_DSP::SignalPipeInterrupt(DspPipe::Binary); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Reschedule recurrent event
 | ||||||
|  |     CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DspHle::DspHle() : impl(std::make_unique<Impl>(*this)) {} | ||||||
|  | DspHle::~DspHle() = default; | ||||||
|  | 
 | ||||||
|  | DspState DspHle::GetDspState() const { | ||||||
|  |     return impl->GetDspState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<u8> DspHle::PipeRead(DspPipe pipe_number, u32 length) { | ||||||
|  |     return impl->PipeRead(pipe_number, length); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t DspHle::GetPipeReadableSize(DspPipe pipe_number) const { | ||||||
|  |     return impl->GetPipeReadableSize(pipe_number); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DspHle::PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | ||||||
|  |     impl->PipeWrite(pipe_number, buffer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::array<u8, Memory::DSP_RAM_SIZE>& DspHle::GetDspMemory() { | ||||||
|  |     return impl->GetDspMemory(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace AudioCore
 | ||||||
							
								
								
									
										36
									
								
								src/audio_core/hle/hle.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/audio_core/hle/hle.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | // Copyright 2017 Citra Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <memory> | ||||||
|  | #include <vector> | ||||||
|  | #include "audio_core/audio_types.h" | ||||||
|  | #include "audio_core/dsp_interface.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "core/memory.h" | ||||||
|  | 
 | ||||||
|  | namespace AudioCore { | ||||||
|  | 
 | ||||||
|  | class DspHle final : public DspInterface { | ||||||
|  | public: | ||||||
|  |     DspHle(); | ||||||
|  |     ~DspHle(); | ||||||
|  | 
 | ||||||
|  |     DspState GetDspState() const override; | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) override; | ||||||
|  |     size_t GetPipeReadableSize(DspPipe pipe_number) const override; | ||||||
|  |     void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) override; | ||||||
|  | 
 | ||||||
|  |     std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     struct Impl; | ||||||
|  |     friend struct Impl; | ||||||
|  |     std::unique_ptr<Impl> impl; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace AudioCore
 | ||||||
|  | @ -4,14 +4,12 @@ | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| 
 | 
 | ||||||
| #include "audio_core/hle/common.h" |  | ||||||
| #include "audio_core/hle/dsp.h" |  | ||||||
| #include "audio_core/hle/mixers.h" | #include "audio_core/hle/mixers.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| void Mixers::Reset() { | void Mixers::Reset() { | ||||||
|  | @ -207,4 +205,4 @@ DspStatus Mixers::GetCurrentStatus() const { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -5,10 +5,10 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/hle/dsp.h" | #include "audio_core/hle/shared_memory.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| class Mixers final { | class Mixers final { | ||||||
|  | @ -58,4 +58,4 @@ private: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -1,177 +0,0 @@ | ||||||
| // Copyright 2016 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #include <array> |  | ||||||
| #include <vector> |  | ||||||
| #include "audio_core/hle/dsp.h" |  | ||||||
| #include "audio_core/hle/pipe.h" |  | ||||||
| #include "common/assert.h" |  | ||||||
| #include "common/common_types.h" |  | ||||||
| #include "common/logging/log.h" |  | ||||||
| #include "core/hle/service/dsp_dsp.h" |  | ||||||
| 
 |  | ||||||
| namespace DSP { |  | ||||||
| namespace HLE { |  | ||||||
| 
 |  | ||||||
| static DspState dsp_state = DspState::Off; |  | ||||||
| 
 |  | ||||||
| static std::array<std::vector<u8>, NUM_DSP_PIPE> pipe_data; |  | ||||||
| 
 |  | ||||||
| void ResetPipes() { |  | ||||||
|     for (auto& data : pipe_data) { |  | ||||||
|         data.clear(); |  | ||||||
|     } |  | ||||||
|     dsp_state = DspState::Off; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) { |  | ||||||
|     const size_t pipe_index = static_cast<size_t>(pipe_number); |  | ||||||
| 
 |  | ||||||
|     if (pipe_index >= NUM_DSP_PIPE) { |  | ||||||
|         LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); |  | ||||||
|         return {}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (length > UINT16_MAX) { // Can only read at most UINT16_MAX from the pipe
 |  | ||||||
|         LOG_ERROR(Audio_DSP, "length of %u greater than max of %u", length, UINT16_MAX); |  | ||||||
|         return {}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::vector<u8>& data = pipe_data[pipe_index]; |  | ||||||
| 
 |  | ||||||
|     if (length > data.size()) { |  | ||||||
|         LOG_WARNING( |  | ||||||
|             Audio_DSP, |  | ||||||
|             "pipe_number = %zu is out of data, application requested read of %u but %zu remain", |  | ||||||
|             pipe_index, length, data.size()); |  | ||||||
|         length = static_cast<u32>(data.size()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (length == 0) |  | ||||||
|         return {}; |  | ||||||
| 
 |  | ||||||
|     std::vector<u8> ret(data.begin(), data.begin() + length); |  | ||||||
|     data.erase(data.begin(), data.begin() + length); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| size_t GetPipeReadableSize(DspPipe pipe_number) { |  | ||||||
|     const size_t pipe_index = static_cast<size_t>(pipe_number); |  | ||||||
| 
 |  | ||||||
|     if (pipe_index >= NUM_DSP_PIPE) { |  | ||||||
|         LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index); |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return pipe_data[pipe_index].size(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void WriteU16(DspPipe pipe_number, u16 value) { |  | ||||||
|     const size_t pipe_index = static_cast<size_t>(pipe_number); |  | ||||||
| 
 |  | ||||||
|     std::vector<u8>& data = pipe_data.at(pipe_index); |  | ||||||
|     // Little endian
 |  | ||||||
|     data.emplace_back(value & 0xFF); |  | ||||||
|     data.emplace_back(value >> 8); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void AudioPipeWriteStructAddresses() { |  | ||||||
|     // These struct addresses are DSP dram addresses.
 |  | ||||||
|     // See also: DSP_DSP::ConvertProcessAddressFromDspDram
 |  | ||||||
|     static const std::array<u16, 15> struct_addresses = { |  | ||||||
|         0x8000 + offsetof(SharedMemory, frame_counter) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, source_configurations) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, source_statuses) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, adpcm_coefficients) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, dsp_configuration) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, dsp_status) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, final_samples) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, intermediate_mix_samples) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, compressor) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, dsp_debug) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, unknown10) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, unknown11) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, unknown12) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, unknown13) / 2, |  | ||||||
|         0x8000 + offsetof(SharedMemory, unknown14) / 2, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     // Begin with a u16 denoting the number of structs.
 |  | ||||||
|     WriteU16(DspPipe::Audio, static_cast<u16>(struct_addresses.size())); |  | ||||||
|     // Then write the struct addresses.
 |  | ||||||
|     for (u16 addr : struct_addresses) { |  | ||||||
|         WriteU16(DspPipe::Audio, addr); |  | ||||||
|     } |  | ||||||
|     // Signal that we have data on this pipe.
 |  | ||||||
|     Service::DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { |  | ||||||
|     switch (pipe_number) { |  | ||||||
|     case DspPipe::Audio: { |  | ||||||
|         if (buffer.size() != 4) { |  | ||||||
|             LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", |  | ||||||
|                       buffer.size()); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         enum class StateChange { |  | ||||||
|             Initialize = 0, |  | ||||||
|             Shutdown = 1, |  | ||||||
|             Wakeup = 2, |  | ||||||
|             Sleep = 3, |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         // The difference between Initialize and Wakeup is that Input state is maintained
 |  | ||||||
|         // when sleeping but isn't when turning it off and on again. (TODO: Implement this.)
 |  | ||||||
|         // Waking up from sleep garbles some of the structs in the memory region. (TODO:
 |  | ||||||
|         // Implement this.) Applications store away the state of these structs before
 |  | ||||||
|         // sleeping and reset it back after wakeup on behalf of the DSP.
 |  | ||||||
| 
 |  | ||||||
|         switch (static_cast<StateChange>(buffer[0])) { |  | ||||||
|         case StateChange::Initialize: |  | ||||||
|             LOG_INFO(Audio_DSP, "Application has requested initialization of DSP hardware"); |  | ||||||
|             ResetPipes(); |  | ||||||
|             AudioPipeWriteStructAddresses(); |  | ||||||
|             dsp_state = DspState::On; |  | ||||||
|             break; |  | ||||||
|         case StateChange::Shutdown: |  | ||||||
|             LOG_INFO(Audio_DSP, "Application has requested shutdown of DSP hardware"); |  | ||||||
|             dsp_state = DspState::Off; |  | ||||||
|             break; |  | ||||||
|         case StateChange::Wakeup: |  | ||||||
|             LOG_INFO(Audio_DSP, "Application has requested wakeup of DSP hardware"); |  | ||||||
|             ResetPipes(); |  | ||||||
|             AudioPipeWriteStructAddresses(); |  | ||||||
|             dsp_state = DspState::On; |  | ||||||
|             break; |  | ||||||
|         case StateChange::Sleep: |  | ||||||
|             LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware"); |  | ||||||
|             UNIMPLEMENTED(); |  | ||||||
|             dsp_state = DspState::Sleeping; |  | ||||||
|             break; |  | ||||||
|         default: |  | ||||||
|             LOG_ERROR(Audio_DSP, |  | ||||||
|                       "Application has requested unknown state transition of DSP hardware %hhu", |  | ||||||
|                       buffer[0]); |  | ||||||
|             dsp_state = DspState::Off; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     default: |  | ||||||
|         LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", |  | ||||||
|                      static_cast<size_t>(pipe_number)); |  | ||||||
|         UNIMPLEMENTED(); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| DspState GetDspState() { |  | ||||||
|     return dsp_state; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } // namespace HLE
 |  | ||||||
| } // namespace DSP
 |  | ||||||
|  | @ -1,63 +0,0 @@ | ||||||
| // Copyright 2016 Citra Emulator Project
 |  | ||||||
| // Licensed under GPLv2 or any later version
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <cstddef> |  | ||||||
| #include <vector> |  | ||||||
| #include "common/common_types.h" |  | ||||||
| 
 |  | ||||||
| namespace DSP { |  | ||||||
| namespace HLE { |  | ||||||
| 
 |  | ||||||
| /// Reset the pipes by setting pipe positions back to the beginning.
 |  | ||||||
| void ResetPipes(); |  | ||||||
| 
 |  | ||||||
| enum class DspPipe { |  | ||||||
|     Debug = 0, |  | ||||||
|     Dma = 1, |  | ||||||
|     Audio = 2, |  | ||||||
|     Binary = 3, |  | ||||||
| }; |  | ||||||
| constexpr size_t NUM_DSP_PIPE = 8; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Reads `length` bytes from the DSP pipe identified with `pipe_number`. |  | ||||||
|  * @note Can read up to the maximum value of a u16 in bytes (65,535). |  | ||||||
|  * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty |  | ||||||
|  * vector will be returned. |  | ||||||
|  * @note IF `length` is set to 0, an empty vector will be returned. |  | ||||||
|  * @note IF `length` is greater than the amount of data available, this function will only read the |  | ||||||
|  * available amount. |  | ||||||
|  * @param pipe_number a `DspPipe` |  | ||||||
|  * @param length the number of bytes to read. The max is 65,535 (max of u16). |  | ||||||
|  * @returns a vector of bytes from the specified pipe. On error, will be empty. |  | ||||||
|  */ |  | ||||||
| std::vector<u8> PipeRead(DspPipe pipe_number, u32 length); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * How much data is left in pipe |  | ||||||
|  * @param pipe_number The Pipe ID |  | ||||||
|  * @return The amount of data remaning in the pipe. This is the maximum length PipeRead will return. |  | ||||||
|  */ |  | ||||||
| size_t GetPipeReadableSize(DspPipe pipe_number); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Write to a DSP pipe. |  | ||||||
|  * @param pipe_number The Pipe ID |  | ||||||
|  * @param buffer The data to write to the pipe. |  | ||||||
|  */ |  | ||||||
| void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer); |  | ||||||
| 
 |  | ||||||
| enum class DspState { |  | ||||||
|     Off, |  | ||||||
|     On, |  | ||||||
|     Sleeping, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /// Get the state of the DSP
 |  | ||||||
| DspState GetDspState(); |  | ||||||
| 
 |  | ||||||
| } // namespace HLE
 |  | ||||||
| } // namespace DSP
 |  | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
|  | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
|  | @ -15,10 +16,6 @@ | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
| 
 | 
 | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
| class Sink; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| namespace DSP { |  | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| // The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
 | // The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
 | ||||||
|  | @ -86,7 +83,7 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial | ||||||
| //       0           0xBFFF                     Frame Counter                         Application
 | //       0           0xBFFF                     Frame Counter                         Application
 | ||||||
| //
 | //
 | ||||||
| // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
 | // #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
 | ||||||
| //    See also: DSP::HLE::PipeRead.
 | //    See also: HLE::PipeRead.
 | ||||||
| //
 | //
 | ||||||
| // Note that the above addresses do vary slightly between audio firmwares observed; the addresses
 | // Note that the above addresses do vary slightly between audio firmwares observed; the addresses
 | ||||||
| // are not fixed in stone. The addresses above are only an examplar; they're what this
 | // are not fixed in stone. The addresses above are only an examplar; they're what this
 | ||||||
|  | @ -527,69 +524,40 @@ static_assert(offsetof(DspMemory, region_0) == region0_offset, | ||||||
| static_assert(offsetof(DspMemory, region_1) == region1_offset, | static_assert(offsetof(DspMemory, region_1) == region1_offset, | ||||||
|               "DSP region 1 is at the wrong offset"); |               "DSP region 1 is at the wrong offset"); | ||||||
| 
 | 
 | ||||||
| extern DspMemory g_dsp_memory; |  | ||||||
| 
 |  | ||||||
| // Structures must have an offset that is a multiple of two.
 | // Structures must have an offset that is a multiple of two.
 | ||||||
| static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, | static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, | static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, | static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, | static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, | static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, | static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, | static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, | static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, compressor) % 2 == 0, | static_assert(offsetof(SharedMemory, compressor) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, | static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, | static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, | static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, | static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, | static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, | static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, | ||||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); |               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||||
| 
 | 
 | ||||||
| #undef INSERT_PADDING_DSPWORDS | #undef INSERT_PADDING_DSPWORDS | ||||||
| #undef ASSERT_DSP_STRUCT | #undef ASSERT_DSP_STRUCT | ||||||
| 
 | 
 | ||||||
| /// Initialize DSP hardware
 |  | ||||||
| void Init(); |  | ||||||
| 
 |  | ||||||
| /// Shutdown DSP hardware
 |  | ||||||
| void Shutdown(); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Perform processing and updates state of current shared memory buffer. |  | ||||||
|  * This function is called every audio tick before triggering the audio interrupt. |  | ||||||
|  * @return Whether an audio interrupt should be triggered this frame. |  | ||||||
|  */ |  | ||||||
| bool Tick(); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Set the output sink. This must be called before calling Tick(). |  | ||||||
|  * @param sink The sink to which audio will be output to. |  | ||||||
|  */ |  | ||||||
| void SetSink(std::unique_ptr<AudioCore::Sink> sink); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Enables/Disables audio-stretching. |  | ||||||
|  * Audio stretching is an enhancement that stretches audio to match emulation |  | ||||||
|  * speed to prevent stuttering at the cost of some audio latency. |  | ||||||
|  * @param enable true to enable, false to disable. |  | ||||||
|  */ |  | ||||||
| void EnableStretching(bool enable); |  | ||||||
| 
 |  | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, | SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, | ||||||
|  | @ -345,4 +345,4 @@ SourceStatus::Status Source::GetCurrentStatus() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -7,14 +7,14 @@ | ||||||
| #include <array> | #include <array> | ||||||
| #include <queue> | #include <queue> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/codec.h" | #include "audio_core/codec.h" | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/hle/common.h" | ||||||
| #include "audio_core/hle/dsp.h" |  | ||||||
| #include "audio_core/hle/filter.h" | #include "audio_core/hle/filter.h" | ||||||
| #include "audio_core/interpolate.h" | #include "audio_core/interpolate.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { | namespace HLE { | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -146,4 +146,4 @@ private: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace HLE
 | } // namespace HLE
 | ||||||
| } // namespace DSP
 | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| 
 | 
 | ||||||
|  | namespace AudioCore { | ||||||
| namespace AudioInterp { | namespace AudioInterp { | ||||||
| 
 | 
 | ||||||
| // Calculations are done in fixed point with 24 fractional bits.
 | // Calculations are done in fixed point with 24 fractional bits.
 | ||||||
|  | @ -16,8 +17,8 @@ constexpr u64 scale_mask = scale_factor - 1; | ||||||
| /// Here we step over the input in steps of rate, until we consume all of the input.
 | /// Here we step over the input in steps of rate, until we consume all of the input.
 | ||||||
| /// Three adjacent samples are passed to fn each step.
 | /// Three adjacent samples are passed to fn each step.
 | ||||||
| template <typename Function> | template <typename Function> | ||||||
| static void StepOverSamples(State& state, StereoBuffer16& input, float rate, | static void StepOverSamples(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, | ||||||
|                             DSP::HLE::StereoFrame16& output, size_t& outputi, Function fn) { |                             size_t& outputi, Function fn) { | ||||||
|     ASSERT(rate > 0); |     ASSERT(rate > 0); | ||||||
| 
 | 
 | ||||||
|     if (input.empty()) |     if (input.empty()) | ||||||
|  | @ -50,14 +51,13 @@ static void StepOverSamples(State& state, StereoBuffer16& input, float rate, | ||||||
|     input.erase(input.begin(), std::next(input.begin(), inputi + 2)); |     input.erase(input.begin(), std::next(input.begin(), inputi + 2)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, | void None(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, size_t& outputi) { | ||||||
|           size_t& outputi) { |  | ||||||
|     StepOverSamples( |     StepOverSamples( | ||||||
|         state, input, rate, output, outputi, |         state, input, rate, output, outputi, | ||||||
|         [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; }); |         [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, | void Linear(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, | ||||||
|             size_t& outputi) { |             size_t& outputi) { | ||||||
|     // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
 |     // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
 | ||||||
|     StepOverSamples(state, input, rate, output, outputi, |     StepOverSamples(state, input, rate, output, outputi, | ||||||
|  | @ -74,3 +74,4 @@ void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFra | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace AudioInterp
 | } // namespace AudioInterp
 | ||||||
|  | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -6,9 +6,10 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <deque> | #include <deque> | ||||||
| #include "audio_core/hle/common.h" | #include "audio_core/audio_types.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
|  | namespace AudioCore { | ||||||
| namespace AudioInterp { | namespace AudioInterp { | ||||||
| 
 | 
 | ||||||
| /// A variable length buffer of signed PCM16 stereo samples.
 | /// A variable length buffer of signed PCM16 stereo samples.
 | ||||||
|  | @ -31,8 +32,7 @@ struct State { | ||||||
|  * @param output The resampled audio buffer. |  * @param output The resampled audio buffer. | ||||||
|  * @param outputi The index of output to start writing to. |  * @param outputi The index of output to start writing to. | ||||||
|  */ |  */ | ||||||
| void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, | void None(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, size_t& outputi); | ||||||
|           size_t& outputi); |  | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. |  * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. | ||||||
|  | @ -43,7 +43,8 @@ void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame | ||||||
|  * @param output The resampled audio buffer. |  * @param output The resampled audio buffer. | ||||||
|  * @param outputi The index of output to start writing to. |  * @param outputi The index of output to start writing to. | ||||||
|  */ |  */ | ||||||
| void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, | void Linear(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, | ||||||
|             size_t& outputi); |             size_t& outputi); | ||||||
| 
 | 
 | ||||||
| } // namespace AudioInterp
 | } // namespace AudioInterp
 | ||||||
|  | } // namespace AudioCore
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/sink.h" | #include "audio_core/sink.h" | ||||||
| 
 | 
 | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| #include <list> | #include <list> | ||||||
| #include <numeric> | #include <numeric> | ||||||
| #include <SDL.h> | #include <SDL.h> | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/sdl2_sink.h" | #include "audio_core/sdl2_sink.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "audio_core/null_sink.h" | #include "audio_core/null_sink.h" | ||||||
| #include "audio_core/sink_details.h" | #include "audio_core/sink_details.h" | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| #include <cmath> | #include <cmath> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <SoundTouch.h> | #include <SoundTouch.h> | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_types.h" | ||||||
| #include "audio_core/time_stretch.h" | #include "audio_core/time_stretch.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include "audio_core/audio_core.h" |  | ||||||
| #include "audio_core/sink.h" | #include "audio_core/sink.h" | ||||||
| #include "audio_core/sink_details.h" | #include "audio_core/sink_details.h" | ||||||
| #include "citra_qt/configuration/configure_audio.h" | #include "citra_qt/configuration/configure_audio.h" | ||||||
|  |  | ||||||
|  | @ -4,7 +4,8 @@ | ||||||
| 
 | 
 | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/dsp_interface.h" | ||||||
|  | #include "audio_core/hle/hle.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #ifdef ARCHITECTURE_x86_64 | #ifdef ARCHITECTURE_x86_64 | ||||||
|  | @ -149,6 +150,8 @@ void System::Reschedule() { | ||||||
| System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | ||||||
|     LOG_DEBUG(HW_Memory, "initialized OK"); |     LOG_DEBUG(HW_Memory, "initialized OK"); | ||||||
| 
 | 
 | ||||||
|  |     CoreTiming::Init(); | ||||||
|  | 
 | ||||||
|     if (Settings::values.use_cpu_jit) { |     if (Settings::values.use_cpu_jit) { | ||||||
| #ifdef ARCHITECTURE_x86_64 | #ifdef ARCHITECTURE_x86_64 | ||||||
|         cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); |         cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); | ||||||
|  | @ -160,13 +163,15 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | ||||||
|         cpu_core = std::make_unique<ARM_DynCom>(USER32MODE); |         cpu_core = std::make_unique<ARM_DynCom>(USER32MODE); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     dsp_core = std::make_unique<AudioCore::DspHle>(); | ||||||
|  |     dsp_core->SetSink(Settings::values.sink_id); | ||||||
|  |     dsp_core->EnableStretching(Settings::values.enable_audio_stretching); | ||||||
|  | 
 | ||||||
|     telemetry_session = std::make_unique<Core::TelemetrySession>(); |     telemetry_session = std::make_unique<Core::TelemetrySession>(); | ||||||
| 
 | 
 | ||||||
|     CoreTiming::Init(); |  | ||||||
|     HW::Init(); |     HW::Init(); | ||||||
|     Kernel::Init(system_mode); |     Kernel::Init(system_mode); | ||||||
|     Service::Init(); |     Service::Init(); | ||||||
|     AudioCore::Init(); |  | ||||||
|     GDBStub::Init(); |     GDBStub::Init(); | ||||||
|     Movie::GetInstance().Init(); |     Movie::GetInstance().Init(); | ||||||
| 
 | 
 | ||||||
|  | @ -196,15 +201,16 @@ void System::Shutdown() { | ||||||
|     // Shutdown emulation session
 |     // Shutdown emulation session
 | ||||||
|     Movie::GetInstance().Shutdown(); |     Movie::GetInstance().Shutdown(); | ||||||
|     GDBStub::Shutdown(); |     GDBStub::Shutdown(); | ||||||
|     AudioCore::Shutdown(); |  | ||||||
|     VideoCore::Shutdown(); |     VideoCore::Shutdown(); | ||||||
|     Service::Shutdown(); |     Service::Shutdown(); | ||||||
|     Kernel::Shutdown(); |     Kernel::Shutdown(); | ||||||
|     HW::Shutdown(); |     HW::Shutdown(); | ||||||
|     CoreTiming::Shutdown(); |  | ||||||
|     cpu_core = nullptr; |  | ||||||
|     app_loader = nullptr; |  | ||||||
|     telemetry_session = nullptr; |     telemetry_session = nullptr; | ||||||
|  |     dsp_core = nullptr; | ||||||
|  |     cpu_core = nullptr; | ||||||
|  |     CoreTiming::Shutdown(); | ||||||
|  |     app_loader = nullptr; | ||||||
|  | 
 | ||||||
|     if (auto room_member = Network::GetRoomMember().lock()) { |     if (auto room_member = Network::GetRoomMember().lock()) { | ||||||
|         Network::GameInfo game_info{}; |         Network::GameInfo game_info{}; | ||||||
|         room_member->SendGameInfo(game_info); |         room_member->SendGameInfo(game_info); | ||||||
|  |  | ||||||
|  | @ -15,6 +15,10 @@ | ||||||
| class EmuWindow; | class EmuWindow; | ||||||
| class ARM_Interface; | class ARM_Interface; | ||||||
| 
 | 
 | ||||||
|  | namespace AudioCore { | ||||||
|  | class DspInterface; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
| 
 | 
 | ||||||
| class System { | class System { | ||||||
|  | @ -102,6 +106,14 @@ public: | ||||||
|         return *cpu_core; |         return *cpu_core; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Gets a reference to the emulated DSP. | ||||||
|  |      * @returns A reference to the emulated DSP. | ||||||
|  |      */ | ||||||
|  |     AudioCore::DspInterface& DSP() { | ||||||
|  |         return *dsp_core; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     PerfStats perf_stats; |     PerfStats perf_stats; | ||||||
|     FrameLimiter frame_limiter; |     FrameLimiter frame_limiter; | ||||||
| 
 | 
 | ||||||
|  | @ -138,6 +150,9 @@ private: | ||||||
|     ///< ARM11 CPU core
 |     ///< ARM11 CPU core
 | ||||||
|     std::unique_ptr<ARM_Interface> cpu_core; |     std::unique_ptr<ARM_Interface> cpu_core; | ||||||
| 
 | 
 | ||||||
|  |     ///< DSP core
 | ||||||
|  |     std::unique_ptr<AudioCore::DspInterface> dsp_core; | ||||||
|  | 
 | ||||||
|     /// When true, signals that a reschedule should happen
 |     /// When true, signals that a reschedule should happen
 | ||||||
|     bool reschedule_pending{}; |     bool reschedule_pending{}; | ||||||
| 
 | 
 | ||||||
|  | @ -154,6 +169,10 @@ inline ARM_Interface& CPU() { | ||||||
|     return System::GetInstance().CPU(); |     return System::GetInstance().CPU(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | inline AudioCore::DspInterface& DSP() { | ||||||
|  |     return System::GetInstance().DSP(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| inline TelemetrySession& Telemetry() { | inline TelemetrySession& Telemetry() { | ||||||
|     return System::GetInstance().TelemetrySession(); |     return System::GetInstance().TelemetrySession(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -5,10 +5,12 @@ | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
| #include "audio_core/hle/pipe.h" | #include "audio_core/audio_types.h" | ||||||
|  | #include "audio_core/dsp_interface.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/hash.h" | #include "common/hash.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  | #include "core/core.h" | ||||||
| #include "core/hle/ipc.h" | #include "core/hle/ipc.h" | ||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
| #include "core/hle/kernel/handle_table.h" | #include "core/hle/kernel/handle_table.h" | ||||||
|  | @ -16,7 +18,7 @@ | ||||||
| #include "core/hle/service/dsp_dsp.h" | #include "core/hle/service/dsp_dsp.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| 
 | 
 | ||||||
| using DspPipe = DSP::HLE::DspPipe; | using DspPipe = AudioCore::DspPipe; | ||||||
| 
 | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
| namespace DSP_DSP { | namespace DSP_DSP { | ||||||
|  | @ -44,7 +46,7 @@ public: | ||||||
|             return one; |             return one; | ||||||
|         case InterruptType::Pipe: { |         case InterruptType::Pipe: { | ||||||
|             const size_t pipe_index = static_cast<size_t>(dsp_pipe); |             const size_t pipe_index = static_cast<size_t>(dsp_pipe); | ||||||
|             ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE); |             ASSERT(pipe_index < AudioCore::num_dsp_pipe); | ||||||
|             return pipe[pipe_index]; |             return pipe[pipe_index]; | ||||||
|         } |         } | ||||||
|         } |         } | ||||||
|  | @ -73,7 +75,7 @@ private: | ||||||
|     /// Currently unknown purpose
 |     /// Currently unknown purpose
 | ||||||
|     Kernel::SharedPtr<Kernel::Event> one = nullptr; |     Kernel::SharedPtr<Kernel::Event> one = nullptr; | ||||||
|     /// Each DSP pipe has an associated interrupt
 |     /// Each DSP pipe has an associated interrupt
 | ||||||
|     std::array<Kernel::SharedPtr<Kernel::Event>, DSP::HLE::NUM_DSP_PIPE> pipe = {{}}; |     std::array<Kernel::SharedPtr<Kernel::Event>, AudioCore::num_dsp_pipe> pipe = {{}}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static InterruptEvents interrupt_events; | static InterruptEvents interrupt_events; | ||||||
|  | @ -216,7 +218,7 @@ static void RegisterInterruptEvents(Service::Interface* self) { | ||||||
|     u32 pipe_index = cmd_buff[2]; |     u32 pipe_index = cmd_buff[2]; | ||||||
|     u32 event_handle = cmd_buff[4]; |     u32 event_handle = cmd_buff[4]; | ||||||
| 
 | 
 | ||||||
|     ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE, |     ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < AudioCore::num_dsp_pipe, | ||||||
|                "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); |                "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); | ||||||
| 
 | 
 | ||||||
|     InterruptType type = static_cast<InterruptType>(cmd_buff[1]); |     InterruptType type = static_cast<InterruptType>(cmd_buff[1]); | ||||||
|  | @ -289,7 +291,7 @@ static void WriteProcessPipe(Service::Interface* self) { | ||||||
|     u32 size = cmd_buff[2]; |     u32 size = cmd_buff[2]; | ||||||
|     u32 buffer = cmd_buff[4]; |     u32 buffer = cmd_buff[4]; | ||||||
| 
 | 
 | ||||||
|     DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); |     AudioCore::DspPipe pipe = static_cast<AudioCore::DspPipe>(pipe_index); | ||||||
| 
 | 
 | ||||||
|     if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { |     if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { | ||||||
|         LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, " |         LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, " | ||||||
|  | @ -312,12 +314,12 @@ static void WriteProcessPipe(Service::Interface* self) { | ||||||
|     // The likely reason for this is that games tend to pass in garbage at these bytes
 |     // The likely reason for this is that games tend to pass in garbage at these bytes
 | ||||||
|     // because they read random bytes off the stack.
 |     // because they read random bytes off the stack.
 | ||||||
|     switch (pipe) { |     switch (pipe) { | ||||||
|     case DSP::HLE::DspPipe::Audio: |     case AudioCore::DspPipe::Audio: | ||||||
|         ASSERT(message.size() >= 4); |         ASSERT(message.size() >= 4); | ||||||
|         message[2] = 0; |         message[2] = 0; | ||||||
|         message[3] = 0; |         message[3] = 0; | ||||||
|         break; |         break; | ||||||
|     case DSP::HLE::DspPipe::Binary: |     case AudioCore::DspPipe::Binary: | ||||||
|         ASSERT(message.size() >= 8); |         ASSERT(message.size() >= 8); | ||||||
|         message[4] = 1; |         message[4] = 1; | ||||||
|         message[5] = 0; |         message[5] = 0; | ||||||
|  | @ -326,7 +328,7 @@ static void WriteProcessPipe(Service::Interface* self) { | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     DSP::HLE::PipeWrite(pipe, message); |     Core::DSP().PipeWrite(pipe, message); | ||||||
| 
 | 
 | ||||||
|     cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); |     cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 |     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||||
|  | @ -338,7 +340,7 @@ static void WriteProcessPipe(Service::Interface* self) { | ||||||
|  * DSP_DSP::ReadPipeIfPossible service function |  * DSP_DSP::ReadPipeIfPossible service function | ||||||
|  *      A pipe is a means of communication between the ARM11 and DSP that occurs on |  *      A pipe is a means of communication between the ARM11 and DSP that occurs on | ||||||
|  *      hardware by writing to/reading from the DSP registers at 0x10203000. |  *      hardware by writing to/reading from the DSP registers at 0x10203000. | ||||||
|  *      Pipes are used for initialisation. See also DSP::HLE::PipeRead. |  *      Pipes are used for initialisation. See also DspInterface::PipeRead. | ||||||
|  *  Inputs: |  *  Inputs: | ||||||
|  *      1 : Pipe Number |  *      1 : Pipe Number | ||||||
|  *      2 : Unknown |  *      2 : Unknown | ||||||
|  | @ -356,7 +358,7 @@ static void ReadPipeIfPossible(Service::Interface* self) { | ||||||
|     u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
 |     u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
 | ||||||
|     VAddr addr = cmd_buff[0x41]; |     VAddr addr = cmd_buff[0x41]; | ||||||
| 
 | 
 | ||||||
|     DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); |     AudioCore::DspPipe pipe = static_cast<AudioCore::DspPipe>(pipe_index); | ||||||
| 
 | 
 | ||||||
|     ASSERT_MSG(Memory::IsValidVirtualAddress(addr), |     ASSERT_MSG(Memory::IsValidVirtualAddress(addr), | ||||||
|                "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, |                "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, | ||||||
|  | @ -364,8 +366,8 @@ static void ReadPipeIfPossible(Service::Interface* self) { | ||||||
| 
 | 
 | ||||||
|     cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); |     cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 |     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||||
|     if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { |     if (Core::DSP().GetPipeReadableSize(pipe) >= size) { | ||||||
|         std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); |         std::vector<u8> response = Core::DSP().PipeRead(pipe, size); | ||||||
| 
 | 
 | ||||||
|         Memory::WriteBlock(addr, response.data(), response.size()); |         Memory::WriteBlock(addr, response.data(), response.size()); | ||||||
| 
 | 
 | ||||||
|  | @ -400,14 +402,14 @@ static void ReadPipe(Service::Interface* self) { | ||||||
|     u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
 |     u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
 | ||||||
|     VAddr addr = cmd_buff[0x41]; |     VAddr addr = cmd_buff[0x41]; | ||||||
| 
 | 
 | ||||||
|     DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); |     AudioCore::DspPipe pipe = static_cast<AudioCore::DspPipe>(pipe_index); | ||||||
| 
 | 
 | ||||||
|     ASSERT_MSG(Memory::IsValidVirtualAddress(addr), |     ASSERT_MSG(Memory::IsValidVirtualAddress(addr), | ||||||
|                "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, |                "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, | ||||||
|                unknown, size, addr); |                unknown, size, addr); | ||||||
| 
 | 
 | ||||||
|     if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { |     if (Core::DSP().GetPipeReadableSize(pipe) >= size) { | ||||||
|         std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); |         std::vector<u8> response = Core::DSP().PipeRead(pipe, size); | ||||||
| 
 | 
 | ||||||
|         Memory::WriteBlock(addr, response.data(), response.size()); |         Memory::WriteBlock(addr, response.data(), response.size()); | ||||||
| 
 | 
 | ||||||
|  | @ -441,11 +443,11 @@ static void GetPipeReadableSize(Service::Interface* self) { | ||||||
|     u32 pipe_index = cmd_buff[1]; |     u32 pipe_index = cmd_buff[1]; | ||||||
|     u32 unknown = cmd_buff[2]; |     u32 unknown = cmd_buff[2]; | ||||||
| 
 | 
 | ||||||
|     DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); |     AudioCore::DspPipe pipe = static_cast<AudioCore::DspPipe>(pipe_index); | ||||||
| 
 | 
 | ||||||
|     cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); |     cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 |     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||||
|     cmd_buff[2] = static_cast<u32>(DSP::HLE::GetPipeReadableSize(pipe)); |     cmd_buff[2] = static_cast<u32>(Core::DSP().GetPipeReadableSize(pipe)); | ||||||
| 
 | 
 | ||||||
|     LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, |     LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, | ||||||
|               unknown, cmd_buff[2]); |               unknown, cmd_buff[2]); | ||||||
|  | @ -511,12 +513,12 @@ static void RecvData(Service::Interface* self) { | ||||||
| 
 | 
 | ||||||
|     cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); |     cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); | ||||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; |     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||||
|     switch (DSP::HLE::GetDspState()) { |     switch (Core::DSP().GetDspState()) { | ||||||
|     case DSP::HLE::DspState::On: |     case AudioCore::DspState::On: | ||||||
|         cmd_buff[2] = 0; |         cmd_buff[2] = 0; | ||||||
|         break; |         break; | ||||||
|     case DSP::HLE::DspState::Off: |     case AudioCore::DspState::Off: | ||||||
|     case DSP::HLE::DspState::Sleeping: |     case AudioCore::DspState::Sleeping: | ||||||
|         cmd_buff[2] = 1; |         cmd_buff[2] = 1; | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|  |  | ||||||
|  | @ -7,11 +7,9 @@ | ||||||
| #include <string> | #include <string> | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| 
 | 
 | ||||||
| namespace DSP { | namespace AudioCore { | ||||||
| namespace HLE { |  | ||||||
| enum class DspPipe; | enum class DspPipe; | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| namespace Service { | namespace Service { | ||||||
| namespace DSP_DSP { | namespace DSP_DSP { | ||||||
|  | @ -30,7 +28,7 @@ public: | ||||||
|  * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. |  * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. | ||||||
|  * @param pipe The DSP pipe for which to signal an interrupt for. |  * @param pipe The DSP pipe for which to signal an interrupt for. | ||||||
|  */ |  */ | ||||||
| void SignalPipeInterrupt(DSP::HLE::DspPipe pipe); | void SignalPipeInterrupt(AudioCore::DspPipe pipe); | ||||||
| 
 | 
 | ||||||
| } // namespace DSP_DSP
 | } // namespace DSP_DSP
 | ||||||
| } // namespace Service
 | } // namespace Service
 | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/dsp_interface.h" | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  | @ -311,7 +311,7 @@ u8* GetPhysicalPointer(PAddr address) { | ||||||
|         target_pointer = vram.data() + offset_into_region; |         target_pointer = vram.data() + offset_into_region; | ||||||
|         break; |         break; | ||||||
|     case DSP_RAM_PADDR: |     case DSP_RAM_PADDR: | ||||||
|         target_pointer = AudioCore::GetDspMemory().data() + offset_into_region; |         target_pointer = Core::DSP().GetDspMemory().data() + offset_into_region; | ||||||
|         break; |         break; | ||||||
|     case FCRAM_PADDR: |     case FCRAM_PADDR: | ||||||
|         for (const auto& region : Kernel::memory_regions) { |         for (const auto& region : Kernel::memory_regions) { | ||||||
|  |  | ||||||
|  | @ -2,7 +2,8 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/dsp_interface.h" | ||||||
|  | #include "core/core.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/hle/service/hid/hid.h" | #include "core/hle/service/hid/hid.h" | ||||||
| #include "core/hle/service/ir/ir.h" | #include "core/hle/service/ir/ir.h" | ||||||
|  | @ -28,8 +29,10 @@ void Apply() { | ||||||
|         VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); |         VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     AudioCore::SelectSink(values.sink_id); |     if (Core::System::GetInstance().IsPoweredOn()) { | ||||||
|     AudioCore::EnableStretching(values.enable_audio_stretching); |         Core::DSP().SetSink(values.sink_id); | ||||||
|  |         Core::DSP().EnableStretching(values.enable_audio_stretching); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     Service::HID::ReloadInputDevices(); |     Service::HID::ReloadInputDevices(); | ||||||
|     Service::IR::ReloadInputDevices(); |     Service::IR::ReloadInputDevices(); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue