mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +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 | ||||
|     audio_core.cpp | ||||
|     audio_core.h | ||||
|     audio_types.h | ||||
|     codec.cpp | ||||
|     codec.h | ||||
|     dsp_interface.cpp | ||||
|     dsp_interface.h | ||||
|     hle/common.h | ||||
|     hle/dsp.cpp | ||||
|     hle/dsp.h | ||||
|     hle/filter.cpp | ||||
|     hle/filter.h | ||||
|     hle/hle.cpp | ||||
|     hle/hle.h | ||||
|     hle/mixers.cpp | ||||
|     hle/mixers.h | ||||
|     hle/pipe.cpp | ||||
|     hle/pipe.h | ||||
|     hle/shared_memory.h | ||||
|     hle/source.cpp | ||||
|     hle/source.h | ||||
|     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 <cstddef> | ||||
| #include <cstring> | ||||
| #include <vector> | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/codec.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| namespace Codec { | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| }; | ||||
| } // namespace Codec
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -5,14 +5,12 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <deque> | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| namespace Codec { | ||||
| 
 | ||||
| /// A variable length buffer of signed PCM16 stereo samples.
 | ||||
| using StereoBuffer16 = std::deque<std::array<s16, 2>>; | ||||
| 
 | ||||
| /// See: Codec::DecodeADPCM
 | ||||
| struct ADPCMState { | ||||
|     // 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, | ||||
|                            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 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <array> | ||||
| #include "common/common_types.h" | ||||
| #include <cstddef> | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace AudioCore { | ||||
| namespace HLE { | ||||
| 
 | ||||
| constexpr int 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>; | ||||
| constexpr size_t num_sources = 24; | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 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 <cstddef> | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/dsp.h" | ||||
| #include "audio_core/hle/filter.h" | ||||
| #include "audio_core/hle/shared_memory.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace AudioCore { | ||||
| namespace HLE { | ||||
| 
 | ||||
| void SourceFilters::Reset() { | ||||
|  | @ -114,4 +114,4 @@ std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s | |||
| } | ||||
| 
 | ||||
| } // namespace HLE
 | ||||
| } // namespace DSP
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -5,11 +5,11 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/dsp.h" | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/hle/shared_memory.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace AudioCore { | ||||
| namespace HLE { | ||||
| 
 | ||||
| /// Preprocessing filters. There is an independent set of filters for each Source.
 | ||||
|  | @ -114,4 +114,4 @@ private: | |||
| }; | ||||
| 
 | ||||
| } // 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 "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/dsp.h" | ||||
| #include "audio_core/hle/mixers.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace AudioCore { | ||||
| namespace HLE { | ||||
| 
 | ||||
| void Mixers::Reset() { | ||||
|  | @ -207,4 +205,4 @@ DspStatus Mixers::GetCurrentStatus() const { | |||
| } | ||||
| 
 | ||||
| } // namespace HLE
 | ||||
| } // namespace DSP
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -5,10 +5,10 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/dsp.h" | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/hle/shared_memory.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace AudioCore { | ||||
| namespace HLE { | ||||
| 
 | ||||
| class Mixers final { | ||||
|  | @ -58,4 +58,4 @@ private: | |||
| }; | ||||
| 
 | ||||
| } // 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 <memory> | ||||
| #include <type_traits> | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_funcs.h" | ||||
|  | @ -15,10 +16,6 @@ | |||
| #include "common/swap.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| class Sink; | ||||
| } | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace HLE { | ||||
| 
 | ||||
| // 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
 | ||||
| //
 | ||||
| // #: 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
 | ||||
| // 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, | ||||
|               "DSP region 1 is at the wrong offset"); | ||||
| 
 | ||||
| extern DspMemory g_dsp_memory; | ||||
| 
 | ||||
| // Structures must have an offset that is a multiple of two.
 | ||||
| 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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "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, | ||||
|               "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||||
|               "Structures in HLE::SharedMemory must be 2-byte aligned"); | ||||
| 
 | ||||
| #undef INSERT_PADDING_DSPWORDS | ||||
| #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 DSP
 | ||||
| } // namespace AudioCore
 | ||||
|  | @ -12,7 +12,7 @@ | |||
| #include "common/logging/log.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace AudioCore { | ||||
| namespace HLE { | ||||
| 
 | ||||
| SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, | ||||
|  | @ -345,4 +345,4 @@ SourceStatus::Status Source::GetCurrentStatus() { | |||
| } | ||||
| 
 | ||||
| } // namespace HLE
 | ||||
| } // namespace DSP
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -7,14 +7,14 @@ | |||
| #include <array> | ||||
| #include <queue> | ||||
| #include <vector> | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/codec.h" | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/dsp.h" | ||||
| #include "audio_core/hle/filter.h" | ||||
| #include "audio_core/interpolate.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace AudioCore { | ||||
| namespace HLE { | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -146,4 +146,4 @@ private: | |||
| }; | ||||
| 
 | ||||
| } // namespace HLE
 | ||||
| } // namespace DSP
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include "common/assert.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| namespace AudioInterp { | ||||
| 
 | ||||
| // 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.
 | ||||
| /// Three adjacent samples are passed to fn each step.
 | ||||
| template <typename Function> | ||||
| static void StepOverSamples(State& state, StereoBuffer16& input, float rate, | ||||
|                             DSP::HLE::StereoFrame16& output, size_t& outputi, Function fn) { | ||||
| static void StepOverSamples(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, | ||||
|                             size_t& outputi, Function fn) { | ||||
|     ASSERT(rate > 0); | ||||
| 
 | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
| void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, | ||||
|           size_t& outputi) { | ||||
| void None(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, size_t& outputi) { | ||||
|     StepOverSamples( | ||||
|         state, input, rate, output, outputi, | ||||
|         [](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) { | ||||
|     // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
 | ||||
|     StepOverSamples(state, input, rate, output, outputi, | ||||
|  | @ -74,3 +74,4 @@ void Linear(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFra | |||
| } | ||||
| 
 | ||||
| } // namespace AudioInterp
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -6,9 +6,10 @@ | |||
| 
 | ||||
| #include <array> | ||||
| #include <deque> | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| namespace AudioInterp { | ||||
| 
 | ||||
| /// A variable length buffer of signed PCM16 stereo samples.
 | ||||
|  | @ -31,8 +32,7 @@ struct State { | |||
|  * @param output The resampled audio buffer. | ||||
|  * @param outputi The index of output to start writing to. | ||||
|  */ | ||||
| void None(State& state, StereoBuffer16& input, float rate, DSP::HLE::StereoFrame16& output, | ||||
|           size_t& outputi); | ||||
| void None(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, size_t& outputi); | ||||
| 
 | ||||
| /**
 | ||||
|  * 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 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); | ||||
| 
 | ||||
| } // namespace AudioInterp
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <cstddef> | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/sink.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include <list> | ||||
| #include <numeric> | ||||
| #include <SDL.h> | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/sdl2_sink.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| #include <algorithm> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include "audio_core/null_sink.h" | ||||
| #include "audio_core/sink_details.h" | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| #include <cmath> | ||||
| #include <vector> | ||||
| #include <SoundTouch.h> | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/time_stretch.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <memory> | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "audio_core/sink.h" | ||||
| #include "audio_core/sink_details.h" | ||||
| #include "citra_qt/configuration/configure_audio.h" | ||||
|  |  | |||
|  | @ -4,7 +4,8 @@ | |||
| 
 | ||||
| #include <memory> | ||||
| #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 "core/arm/arm_interface.h" | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
|  | @ -149,6 +150,8 @@ void System::Reschedule() { | |||
| System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | ||||
|     LOG_DEBUG(HW_Memory, "initialized OK"); | ||||
| 
 | ||||
|     CoreTiming::Init(); | ||||
| 
 | ||||
|     if (Settings::values.use_cpu_jit) { | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
|         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); | ||||
|     } | ||||
| 
 | ||||
|     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>(); | ||||
| 
 | ||||
|     CoreTiming::Init(); | ||||
|     HW::Init(); | ||||
|     Kernel::Init(system_mode); | ||||
|     Service::Init(); | ||||
|     AudioCore::Init(); | ||||
|     GDBStub::Init(); | ||||
|     Movie::GetInstance().Init(); | ||||
| 
 | ||||
|  | @ -196,15 +201,16 @@ void System::Shutdown() { | |||
|     // Shutdown emulation session
 | ||||
|     Movie::GetInstance().Shutdown(); | ||||
|     GDBStub::Shutdown(); | ||||
|     AudioCore::Shutdown(); | ||||
|     VideoCore::Shutdown(); | ||||
|     Service::Shutdown(); | ||||
|     Kernel::Shutdown(); | ||||
|     HW::Shutdown(); | ||||
|     CoreTiming::Shutdown(); | ||||
|     cpu_core = nullptr; | ||||
|     app_loader = nullptr; | ||||
|     telemetry_session = nullptr; | ||||
|     dsp_core = nullptr; | ||||
|     cpu_core = nullptr; | ||||
|     CoreTiming::Shutdown(); | ||||
|     app_loader = nullptr; | ||||
| 
 | ||||
|     if (auto room_member = Network::GetRoomMember().lock()) { | ||||
|         Network::GameInfo game_info{}; | ||||
|         room_member->SendGameInfo(game_info); | ||||
|  |  | |||
|  | @ -15,6 +15,10 @@ | |||
| class EmuWindow; | ||||
| class ARM_Interface; | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| class DspInterface; | ||||
| } | ||||
| 
 | ||||
| namespace Core { | ||||
| 
 | ||||
| class System { | ||||
|  | @ -102,6 +106,14 @@ public: | |||
|         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; | ||||
|     FrameLimiter frame_limiter; | ||||
| 
 | ||||
|  | @ -138,6 +150,9 @@ private: | |||
|     ///< ARM11 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
 | ||||
|     bool reschedule_pending{}; | ||||
| 
 | ||||
|  | @ -154,6 +169,10 @@ inline ARM_Interface& CPU() { | |||
|     return System::GetInstance().CPU(); | ||||
| } | ||||
| 
 | ||||
| inline AudioCore::DspInterface& DSP() { | ||||
|     return System::GetInstance().DSP(); | ||||
| } | ||||
| 
 | ||||
| inline TelemetrySession& Telemetry() { | ||||
|     return System::GetInstance().TelemetrySession(); | ||||
| } | ||||
|  |  | |||
|  | @ -5,10 +5,12 @@ | |||
| #include <algorithm> | ||||
| #include <array> | ||||
| #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/hash.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
|  | @ -16,7 +18,7 @@ | |||
| #include "core/hle/service/dsp_dsp.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| using DspPipe = DSP::HLE::DspPipe; | ||||
| using DspPipe = AudioCore::DspPipe; | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace DSP_DSP { | ||||
|  | @ -44,7 +46,7 @@ public: | |||
|             return one; | ||||
|         case InterruptType::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]; | ||||
|         } | ||||
|         } | ||||
|  | @ -73,7 +75,7 @@ private: | |||
|     /// Currently unknown purpose
 | ||||
|     Kernel::SharedPtr<Kernel::Event> one = nullptr; | ||||
|     /// 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; | ||||
|  | @ -216,7 +218,7 @@ static void RegisterInterruptEvents(Service::Interface* self) { | |||
|     u32 pipe_index = cmd_buff[2]; | ||||
|     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); | ||||
| 
 | ||||
|     InterruptType type = static_cast<InterruptType>(cmd_buff[1]); | ||||
|  | @ -289,7 +291,7 @@ static void WriteProcessPipe(Service::Interface* self) { | |||
|     u32 size = cmd_buff[2]; | ||||
|     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]) { | ||||
|         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
 | ||||
|     // because they read random bytes off the stack.
 | ||||
|     switch (pipe) { | ||||
|     case DSP::HLE::DspPipe::Audio: | ||||
|     case AudioCore::DspPipe::Audio: | ||||
|         ASSERT(message.size() >= 4); | ||||
|         message[2] = 0; | ||||
|         message[3] = 0; | ||||
|         break; | ||||
|     case DSP::HLE::DspPipe::Binary: | ||||
|     case AudioCore::DspPipe::Binary: | ||||
|         ASSERT(message.size() >= 8); | ||||
|         message[4] = 1; | ||||
|         message[5] = 0; | ||||
|  | @ -326,7 +328,7 @@ static void WriteProcessPipe(Service::Interface* self) { | |||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     DSP::HLE::PipeWrite(pipe, message); | ||||
|     Core::DSP().PipeWrite(pipe, message); | ||||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||
|  | @ -338,7 +340,7 @@ static void WriteProcessPipe(Service::Interface* self) { | |||
|  * DSP_DSP::ReadPipeIfPossible service function | ||||
|  *      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. | ||||
|  *      Pipes are used for initialisation. See also DSP::HLE::PipeRead. | ||||
|  *      Pipes are used for initialisation. See also DspInterface::PipeRead. | ||||
|  *  Inputs: | ||||
|  *      1 : Pipe Number | ||||
|  *      2 : Unknown | ||||
|  | @ -356,7 +358,7 @@ static void ReadPipeIfPossible(Service::Interface* self) { | |||
|     u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
 | ||||
|     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), | ||||
|                "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[1] = RESULT_SUCCESS.raw; // No error
 | ||||
|     if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { | ||||
|         std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); | ||||
|     if (Core::DSP().GetPipeReadableSize(pipe) >= size) { | ||||
|         std::vector<u8> response = Core::DSP().PipeRead(pipe, 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
 | ||||
|     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), | ||||
|                "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, | ||||
|                unknown, size, addr); | ||||
| 
 | ||||
|     if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { | ||||
|         std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); | ||||
|     if (Core::DSP().GetPipeReadableSize(pipe) >= size) { | ||||
|         std::vector<u8> response = Core::DSP().PipeRead(pipe, 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 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[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, | ||||
|               unknown, cmd_buff[2]); | ||||
|  | @ -511,12 +513,12 @@ static void RecvData(Service::Interface* self) { | |||
| 
 | ||||
|     cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; | ||||
|     switch (DSP::HLE::GetDspState()) { | ||||
|     case DSP::HLE::DspState::On: | ||||
|     switch (Core::DSP().GetDspState()) { | ||||
|     case AudioCore::DspState::On: | ||||
|         cmd_buff[2] = 0; | ||||
|         break; | ||||
|     case DSP::HLE::DspState::Off: | ||||
|     case DSP::HLE::DspState::Sleeping: | ||||
|     case AudioCore::DspState::Off: | ||||
|     case AudioCore::DspState::Sleeping: | ||||
|         cmd_buff[2] = 1; | ||||
|         break; | ||||
|     default: | ||||
|  |  | |||
|  | @ -7,11 +7,9 @@ | |||
| #include <string> | ||||
| #include "core/hle/service/service.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace HLE { | ||||
| namespace AudioCore { | ||||
| enum class DspPipe; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace DSP_DSP { | ||||
|  | @ -30,7 +28,7 @@ public: | |||
|  * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. | ||||
|  * @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 Service
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| 
 | ||||
| #include <array> | ||||
| #include <cstring> | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "audio_core/dsp_interface.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
|  | @ -311,7 +311,7 @@ u8* GetPhysicalPointer(PAddr address) { | |||
|         target_pointer = vram.data() + offset_into_region; | ||||
|         break; | ||||
|     case DSP_RAM_PADDR: | ||||
|         target_pointer = AudioCore::GetDspMemory().data() + offset_into_region; | ||||
|         target_pointer = Core::DSP().GetDspMemory().data() + offset_into_region; | ||||
|         break; | ||||
|     case FCRAM_PADDR: | ||||
|         for (const auto& region : Kernel::memory_regions) { | ||||
|  |  | |||
|  | @ -2,7 +2,8 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // 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/hle/service/hid/hid.h" | ||||
| #include "core/hle/service/ir/ir.h" | ||||
|  | @ -28,8 +29,10 @@ void Apply() { | |||
|         VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); | ||||
|     } | ||||
| 
 | ||||
|     AudioCore::SelectSink(values.sink_id); | ||||
|     AudioCore::EnableStretching(values.enable_audio_stretching); | ||||
|     if (Core::System::GetInstance().IsPoweredOn()) { | ||||
|         Core::DSP().SetSink(values.sink_id); | ||||
|         Core::DSP().EnableStretching(values.enable_audio_stretching); | ||||
|     } | ||||
| 
 | ||||
|     Service::HID::ReloadInputDevices(); | ||||
|     Service::IR::ReloadInputDevices(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue