mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #1572 from MerryMage/audio-filter
DSP: Implement audio filters (simple, biquad)
This commit is contained in:
		
						commit
						644fbbeb13
					
				
					 5 changed files with 275 additions and 7 deletions
				
			
		|  | @ -2,13 +2,16 @@ set(SRCS | |||
|             audio_core.cpp | ||||
|             codec.cpp | ||||
|             hle/dsp.cpp | ||||
|             hle/filter.cpp | ||||
|             hle/pipe.cpp | ||||
|             ) | ||||
| 
 | ||||
| set(HEADERS | ||||
|             audio_core.h | ||||
|             codec.h | ||||
|             hle/common.h | ||||
|             hle/dsp.h | ||||
|             hle/filter.h | ||||
|             hle/pipe.h | ||||
|             sink.h | ||||
|             ) | ||||
|  |  | |||
							
								
								
									
										35
									
								
								src/audio_core/hle/common.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/audio_core/hle/common.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <array> | ||||
| 
 | ||||
| #include "audio_core/audio_core.h" | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace HLE { | ||||
| 
 | ||||
| /// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
 | ||||
| using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>; | ||||
| 
 | ||||
| /// The DSP is quadraphonic internally.
 | ||||
| using QuadFrame32   = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>; | ||||
| 
 | ||||
| /**
 | ||||
|  * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. | ||||
|  * FilterT::ProcessSample is called sequentially on the samples. | ||||
|  */ | ||||
| template<typename FrameT, typename FilterT> | ||||
| void FilterFrame(FrameT& frame, FilterT& filter) { | ||||
|     std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const typename FrameT::value_type& sample) { | ||||
|         return filter.ProcessSample(sample); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| } // namespace HLE
 | ||||
| } // namespace DSP
 | ||||
|  | @ -126,8 +126,11 @@ struct SourceConfiguration { | |||
|         union { | ||||
|             u32_le dirty_raw; | ||||
| 
 | ||||
|             BitField<0, 1, u32_le> format_dirty; | ||||
|             BitField<1, 1, u32_le> mono_or_stereo_dirty; | ||||
|             BitField<2, 1, u32_le> adpcm_coefficients_dirty; | ||||
|             BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
 | ||||
|             BitField<4, 1, u32_le> partial_reset_flag; | ||||
| 
 | ||||
|             BitField<16, 1, u32_le> enable_dirty; | ||||
|             BitField<17, 1, u32_le> interpolation_dirty; | ||||
|  | @ -143,8 +146,7 @@ struct SourceConfiguration { | |||
|             BitField<27, 1, u32_le> gain_2_dirty; | ||||
|             BitField<28, 1, u32_le> sync_dirty; | ||||
|             BitField<29, 1, u32_le> reset_flag; | ||||
| 
 | ||||
|             BitField<31, 1, u32_le> embedded_buffer_dirty; | ||||
|             BitField<30, 1, u32_le> embedded_buffer_dirty; | ||||
|         }; | ||||
| 
 | ||||
|         // Gain control
 | ||||
|  | @ -175,7 +177,8 @@ struct SourceConfiguration { | |||
|         /**
 | ||||
|          * This is the simplest normalized first-order digital recursive filter. | ||||
|          * The transfer function of this filter is: | ||||
|          *     H(z) = b0 / (1 + a1 z^-1) | ||||
|          *     H(z) = b0 / (1 - a1 z^-1) | ||||
|          * Note the feedbackward coefficient is negated. | ||||
|          * Values are signed fixed point with 15 fractional bits. | ||||
|          */ | ||||
|         struct SimpleFilter { | ||||
|  | @ -192,11 +195,11 @@ struct SourceConfiguration { | |||
|          * Values are signed fixed point with 14 fractional bits. | ||||
|          */ | ||||
|         struct BiquadFilter { | ||||
|             s16_le b0; | ||||
|             s16_le b1; | ||||
|             s16_le b2; | ||||
|             s16_le a1; | ||||
|             s16_le a2; | ||||
|             s16_le a1; | ||||
|             s16_le b2; | ||||
|             s16_le b1; | ||||
|             s16_le b0; | ||||
|         }; | ||||
| 
 | ||||
|         union { | ||||
|  |  | |||
							
								
								
									
										115
									
								
								src/audio_core/hle/filter.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/audio_core/hle/filter.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <array> | ||||
| #include <cstddef> | ||||
| 
 | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/dsp.h" | ||||
| #include "audio_core/hle/filter.h" | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/math_util.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace HLE { | ||||
| 
 | ||||
| void SourceFilters::Reset() { | ||||
|     Enable(false, false); | ||||
| } | ||||
| 
 | ||||
| void SourceFilters::Enable(bool simple, bool biquad) { | ||||
|     simple_filter_enabled = simple; | ||||
|     biquad_filter_enabled = biquad; | ||||
| 
 | ||||
|     if (!simple) | ||||
|         simple_filter.Reset(); | ||||
|     if (!biquad) | ||||
|         biquad_filter.Reset(); | ||||
| } | ||||
| 
 | ||||
| void SourceFilters::Configure(SourceConfiguration::Configuration::SimpleFilter config) { | ||||
|     simple_filter.Configure(config); | ||||
| } | ||||
| 
 | ||||
| void SourceFilters::Configure(SourceConfiguration::Configuration::BiquadFilter config) { | ||||
|     biquad_filter.Configure(config); | ||||
| } | ||||
| 
 | ||||
| void SourceFilters::ProcessFrame(StereoFrame16& frame) { | ||||
|     if (!simple_filter_enabled && !biquad_filter_enabled) | ||||
|         return; | ||||
| 
 | ||||
|     if (simple_filter_enabled) { | ||||
|         FilterFrame(frame, simple_filter); | ||||
|     } | ||||
| 
 | ||||
|     if (biquad_filter_enabled) { | ||||
|         FilterFrame(frame, biquad_filter); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // SimpleFilter
 | ||||
| 
 | ||||
| void SourceFilters::SimpleFilter::Reset() { | ||||
|     y1.fill(0); | ||||
|     // Configure as passthrough.
 | ||||
|     a1 = 0; | ||||
|     b0 = 1 << 15; | ||||
| } | ||||
| 
 | ||||
| void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { | ||||
|     a1 = config.a1; | ||||
|     b0 = config.b0; | ||||
| } | ||||
| 
 | ||||
| std::array<s16, 2> SourceFilters::SimpleFilter::ProcessSample(const std::array<s16, 2>& x0) { | ||||
|     std::array<s16, 2> y0; | ||||
|     for (size_t i = 0; i < 2; i++) { | ||||
|         const s32 tmp = (b0 * x0[i] + a1 * y1[i]) >> 15; | ||||
|         y0[i] = MathUtil::Clamp(tmp, -32768, 32767); | ||||
|     } | ||||
| 
 | ||||
|     y1 = y0; | ||||
| 
 | ||||
|     return y0; | ||||
| } | ||||
| 
 | ||||
| // BiquadFilter
 | ||||
| 
 | ||||
| void SourceFilters::BiquadFilter::Reset() { | ||||
|     x1.fill(0); | ||||
|     x2.fill(0); | ||||
|     y1.fill(0); | ||||
|     y2.fill(0); | ||||
|     // Configure as passthrough.
 | ||||
|     a1 = a2 = b1 = b2 = 0; | ||||
|     b0 = 1 << 14; | ||||
| } | ||||
| 
 | ||||
| void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { | ||||
|     a1 = config.a1; | ||||
|     a2 = config.a2; | ||||
|     b0 = config.b0; | ||||
|     b1 = config.b1; | ||||
|     b2 = config.b2; | ||||
| } | ||||
| 
 | ||||
| std::array<s16, 2> SourceFilters::BiquadFilter::ProcessSample(const std::array<s16, 2>& x0) { | ||||
|     std::array<s16, 2> y0; | ||||
|     for (size_t i = 0; i < 2; i++) { | ||||
|         const s32 tmp = (b0 * x0[i] + b1 * x1[i] + b2 * x2[i] + a1 * y1[i] + a2 * y2[i]) >> 14; | ||||
|         y0[i] = MathUtil::Clamp(tmp, -32768, 32767); | ||||
|     } | ||||
| 
 | ||||
|     x2 = x1; | ||||
|     x1 = x0; | ||||
|     y2 = y1; | ||||
|     y1 = y0; | ||||
| 
 | ||||
|     return y0; | ||||
| } | ||||
| 
 | ||||
| } // namespace HLE
 | ||||
| } // namespace DSP
 | ||||
							
								
								
									
										112
									
								
								src/audio_core/hle/filter.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/audio_core/hle/filter.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,112 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| 
 | ||||
| #include "audio_core/hle/common.h" | ||||
| #include "audio_core/hle/dsp.h" | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace DSP { | ||||
| namespace HLE { | ||||
| 
 | ||||
| /// Preprocessing filters. There is an independent set of filters for each Source.
 | ||||
| class SourceFilters final { | ||||
|     SourceFilters() { Reset(); } | ||||
| 
 | ||||
|     /// Reset internal state.
 | ||||
|     void Reset(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Enable/Disable filters | ||||
|      * See also: SourceConfiguration::Configuration::simple_filter_enabled, | ||||
|      *           SourceConfiguration::Configuration::biquad_filter_enabled. | ||||
|      * @param simple If true, enables the simple filter. If false, disables it. | ||||
|      * @param simple If true, enables the biquad filter. If false, disables it. | ||||
|      */ | ||||
|     void Enable(bool simple, bool biquad); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Configure simple filter. | ||||
|      * @param config Configuration from DSP shared memory. | ||||
|      */ | ||||
|     void Configure(SourceConfiguration::Configuration::SimpleFilter config); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Configure biquad filter. | ||||
|      * @param config Configuration from DSP shared memory. | ||||
|      */ | ||||
|     void Configure(SourceConfiguration::Configuration::BiquadFilter config); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Processes a frame in-place. | ||||
|      * @param frame Audio samples to process. Modified in-place. | ||||
|      */ | ||||
|     void ProcessFrame(StereoFrame16& frame); | ||||
| 
 | ||||
| private: | ||||
|     bool simple_filter_enabled; | ||||
|     bool biquad_filter_enabled; | ||||
| 
 | ||||
|     struct SimpleFilter { | ||||
|         SimpleFilter() { Reset(); } | ||||
| 
 | ||||
|         /// Resets internal state.
 | ||||
|         void Reset(); | ||||
| 
 | ||||
|         /**
 | ||||
|          * Configures this filter with application settings. | ||||
|          * @param config Configuration from DSP shared memory. | ||||
|          */ | ||||
|         void Configure(SourceConfiguration::Configuration::SimpleFilter config); | ||||
| 
 | ||||
|         /**
 | ||||
|          * Processes a single stereo PCM16 sample. | ||||
|          * @param x0 Input sample | ||||
|          * @return Output sample | ||||
|          */ | ||||
|         std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0); | ||||
| 
 | ||||
|     private: | ||||
|         // Configuration
 | ||||
|         s32 a1, b0; | ||||
|         // Internal state
 | ||||
|         std::array<s16, 2> y1; | ||||
|     } simple_filter; | ||||
| 
 | ||||
|     struct BiquadFilter { | ||||
|         BiquadFilter() { Reset(); } | ||||
| 
 | ||||
|         /// Resets internal state.
 | ||||
|         void Reset(); | ||||
| 
 | ||||
|         /**
 | ||||
|          * Configures this filter with application settings. | ||||
|          * @param config Configuration from DSP shared memory. | ||||
|          */ | ||||
|         void Configure(SourceConfiguration::Configuration::BiquadFilter config); | ||||
| 
 | ||||
|         /**
 | ||||
|          * Processes a single stereo PCM16 sample. | ||||
|          * @param x0 Input sample | ||||
|          * @return Output sample | ||||
|          */ | ||||
|         std::array<s16, 2> ProcessSample(const std::array<s16, 2>& x0); | ||||
| 
 | ||||
|     private: | ||||
|         // Configuration
 | ||||
|         s32 a1, a2, b0, b1, b2; | ||||
|         // Internal state
 | ||||
|         std::array<s16, 2> x1; | ||||
|         std::array<s16, 2> x2; | ||||
|         std::array<s16, 2> y1; | ||||
|         std::array<s16, 2> y2; | ||||
|     } biquad_filter; | ||||
| }; | ||||
| 
 | ||||
| } // namespace HLE
 | ||||
| } // namespace DSP
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue