summaryrefslogtreecommitdiffstats
path: root/src/audio_core/hle/dsp.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/audio_core/hle/dsp.h595
1 files changed, 0 insertions, 595 deletions
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
deleted file mode 100644
index 94ce48863..000000000
--- a/src/audio_core/hle/dsp.h
+++ /dev/null
@@ -1,595 +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 <cstddef>
-#include <memory>
-#include <type_traits>
-#include "audio_core/hle/common.h"
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#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
-// have Read/Write permissions.
-//
-// First Region: 0x1FF50000 (Size: 0x8000)
-// Second Region: 0x1FF70000 (Size: 0x8000)
-//
-// The DSP reads from each region alternately based on the frame counter for each region much like a
-// double-buffer. The frame counter is located as the very last u16 of each region and is
-// incremented each audio tick.
-
-constexpr u32 region0_offset = 0x50000;
-constexpr u32 region1_offset = 0x70000;
-
-/**
- * The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from
- * its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian
- * layout of the ARM11. Hence from the ARM11's point of view the memory space appears to be
- * middle-endian.
- *
- * Unusually this does not appear to be an issue for floating point numbers. The DSP makes the more
- * sensible choice of keeping that little-endian. There are also some exceptions such as the
- * IntermediateMixSamples structure, which is little-endian.
- *
- * This struct implements the conversion to and from this middle-endianness.
- */
-struct u32_dsp {
- u32_dsp() = default;
- operator u32() const {
- return Convert(storage);
- }
- void operator=(u32 new_value) {
- storage = Convert(new_value);
- }
-
-private:
- static constexpr u32 Convert(u32 value) {
- return (value << 16) | (value >> 16);
- }
- u32_le storage;
-};
-#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
-static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivially copyable");
-#endif
-
-// There are 15 structures in each memory region. A table of them in the order they appear in memory
-// is presented below:
-//
-// # First Region DSP Address Purpose Control
-// 5 0x8400 DSP Status DSP
-// 9 0x8410 DSP Debug Info DSP
-// 6 0x8540 Final Mix Samples DSP
-// 2 0x8680 Source Status [24] DSP
-// 8 0x8710 Compressor Table Application
-// 4 0x9430 DSP Configuration Application
-// 7 0x9492 Intermediate Mix Samples DSP + App
-// 1 0x9E92 Source Configuration [24] Application
-// 3 0xA792 Source ADPCM Coefficients [24] Application
-// 10 0xA912 Surround Sound Related
-// 11 0xAA12 Surround Sound Related
-// 12 0xAAD2 Surround Sound Related
-// 13 0xAC52 Surround Sound Related
-// 14 0xAC5C Surround Sound Related
-// 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.
-//
-// 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
-// implementation does and provides to applications.
-//
-// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
-// the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for
-// the second region via:
-// second_region_dsp_addr = first_region_dsp_addr | 0x10000
-//
-// Applications maintain most of its own audio state, the memory region is used mainly for
-// communication and not storage of state.
-//
-// In the documentation below, filter and effect transfer functions are specified in the z domain.
-// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
-// frequency domain, just like how the s domain is the analog frequency domain.)
-
-#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
-
-// GCC versions < 5.0 do not implement std::is_trivially_copyable.
-// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
-#if (__GNUC__ >= 5) || defined(__clang__)
-#define ASSERT_DSP_STRUCT(name, size) \
- static_assert(std::is_standard_layout<name>::value, \
- "DSP structure " #name " doesn't use standard layout"); \
- static_assert(std::is_trivially_copyable<name>::value, \
- "DSP structure " #name " isn't trivially copyable"); \
- static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
-#else
-#define ASSERT_DSP_STRUCT(name, size) \
- static_assert(std::is_standard_layout<name>::value, \
- "DSP structure " #name " doesn't use standard layout"); \
- static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
-#endif
-
-struct SourceConfiguration {
- struct Configuration {
- /// These dirty flags are set by the application when it updates the fields in this struct.
- /// The DSP clears these each audio frame.
- 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;
- /// Tends to be set when a looped buffer is queued.
- BitField<3, 1, u32_le> partial_embedded_buffer_dirty;
- BitField<4, 1, u32_le> partial_reset_flag;
-
- BitField<16, 1, u32_le> enable_dirty;
- BitField<17, 1, u32_le> interpolation_dirty;
- BitField<18, 1, u32_le> rate_multiplier_dirty;
- BitField<19, 1, u32_le> buffer_queue_dirty;
- BitField<20, 1, u32_le> loop_related_dirty;
- /// Tends to also be set when embedded buffer is updated.
- BitField<21, 1, u32_le> play_position_dirty;
- BitField<22, 1, u32_le> filters_enabled_dirty;
- BitField<23, 1, u32_le> simple_filter_dirty;
- BitField<24, 1, u32_le> biquad_filter_dirty;
- BitField<25, 1, u32_le> gain_0_dirty;
- BitField<26, 1, u32_le> gain_1_dirty;
- BitField<27, 1, u32_le> gain_2_dirty;
- BitField<28, 1, u32_le> sync_dirty;
- BitField<29, 1, u32_le> reset_flag;
- BitField<30, 1, u32_le> embedded_buffer_dirty;
- };
-
- // Gain control
-
- /**
- * Gain is between 0.0-1.0. This determines how much will this source appear on each of the
- * 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers
- * is fed two left and two right channels.
- */
- float_le gain[3][4];
-
- // Interpolation
-
- /// Multiplier for sample rate. Resampling occurs with the selected interpolation method.
- float_le rate_multiplier;
-
- enum class InterpolationMode : u8 {
- Polyphase = 0,
- Linear = 1,
- None = 2,
- };
-
- InterpolationMode interpolation_mode;
- INSERT_PADDING_BYTES(1); ///< Interpolation related
-
- // Filters
-
- /**
- * This is the simplest normalized first-order digital recursive filter.
- * The transfer function of this filter is:
- * H(z) = b0 / (1 - a1 z^-1)
- * Note the feedbackward coefficient is negated.
- * Values are signed fixed point with 15 fractional bits.
- */
- struct SimpleFilter {
- s16_le b0;
- s16_le a1;
- };
-
- /**
- * This is a normalised biquad filter (second-order).
- * The transfer function of this filter is:
- * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
- * Nintendo chose to negate the feedbackward coefficients. This differs from standard
- * notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
- * Values are signed fixed point with 14 fractional bits.
- */
- struct BiquadFilter {
- s16_le a2;
- s16_le a1;
- s16_le b2;
- s16_le b1;
- s16_le b0;
- };
-
- union {
- u16_le filters_enabled;
- BitField<0, 1, u16_le> simple_filter_enabled;
- BitField<1, 1, u16_le> biquad_filter_enabled;
- };
-
- SimpleFilter simple_filter;
- BiquadFilter biquad_filter;
-
- // Buffer Queue
-
- /// A buffer of audio data from the application, along with metadata about it.
- struct Buffer {
- /// Physical memory address of the start of the buffer
- u32_dsp physical_address;
-
- /// This is length in terms of samples.
- /// Note that in different buffer formats a sample takes up different number of bytes.
- u32_dsp length;
-
- /// ADPCM Predictor (4 bits) and Scale (4 bits)
- union {
- u16_le adpcm_ps;
- BitField<0, 4, u16_le> adpcm_scale;
- BitField<4, 4, u16_le> adpcm_predictor;
- };
-
- /// ADPCM Historical Samples (y[n-1] and y[n-2])
- u16_le adpcm_yn[2];
-
- /// This is non-zero when the ADPCM values above are to be updated.
- u8 adpcm_dirty;
-
- /// Is a looping buffer.
- u8 is_looping;
-
- /// This value is shown in SourceStatus::previous_buffer_id when this buffer has
- /// finished. This allows the emulated application to tell what buffer is currently
- /// playing.
- u16_le buffer_id;
-
- INSERT_PADDING_DSPWORDS(1);
- };
-
- u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
- Buffer buffers[4]; ///< Queued Buffers
-
- // Playback controls
-
- u32_dsp loop_related;
- u8 enable;
- INSERT_PADDING_BYTES(1);
- u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
- u32_dsp play_position; ///< Position. (Units: number of samples)
- INSERT_PADDING_DSPWORDS(2);
-
- // Embedded Buffer
- // This buffer is often the first buffer to be used when initiating audio playback,
- // after which the buffer queue is used.
-
- u32_dsp physical_address;
-
- /// This is length in terms of samples.
- /// Note a sample takes up different number of bytes in different buffer formats.
- u32_dsp length;
-
- enum class MonoOrStereo : u16_le {
- Mono = 1,
- Stereo = 2,
- };
-
- enum class Format : u16_le {
- PCM8 = 0,
- PCM16 = 1,
- ADPCM = 2,
- };
-
- union {
- u16_le flags1_raw;
- BitField<0, 2, MonoOrStereo> mono_or_stereo;
- BitField<2, 2, Format> format;
- BitField<5, 1, u16_le> fade_in;
- };
-
- /// ADPCM Predictor (4 bit) and Scale (4 bit)
- union {
- u16_le adpcm_ps;
- BitField<0, 4, u16_le> adpcm_scale;
- BitField<4, 4, u16_le> adpcm_predictor;
- };
-
- /// ADPCM Historical Samples (y[n-1] and y[n-2])
- u16_le adpcm_yn[2];
-
- union {
- u16_le flags2_raw;
- BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
- BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
- };
-
- /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this
- /// buffer).
- u16_le buffer_id;
- };
-
- Configuration config[num_sources];
-};
-ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
-ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
-
-struct SourceStatus {
- struct Status {
- u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
- u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
- u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
- u32_dsp buffer_position; ///< Number of samples into the current buffer
- u16_le current_buffer_id; ///< Updated when a buffer finishes playing
- INSERT_PADDING_DSPWORDS(1);
- };
-
- Status status[num_sources];
-};
-ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
-
-struct DspConfiguration {
- /// These dirty flags are set by the application when it updates the fields in this struct.
- /// The DSP clears these each audio frame.
- union {
- u32_le dirty_raw;
-
- BitField<8, 1, u32_le> mixer1_enabled_dirty;
- BitField<9, 1, u32_le> mixer2_enabled_dirty;
- BitField<10, 1, u32_le> delay_effect_0_dirty;
- BitField<11, 1, u32_le> delay_effect_1_dirty;
- BitField<12, 1, u32_le> reverb_effect_0_dirty;
- BitField<13, 1, u32_le> reverb_effect_1_dirty;
-
- BitField<16, 1, u32_le> volume_0_dirty;
-
- BitField<24, 1, u32_le> volume_1_dirty;
- BitField<25, 1, u32_le> volume_2_dirty;
- BitField<26, 1, u32_le> output_format_dirty;
- BitField<27, 1, u32_le> limiter_enabled_dirty;
- BitField<28, 1, u32_le> headphones_connected_dirty;
- };
-
- /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
- /// each at the final mixer.
- float_le volume[3];
-
- INSERT_PADDING_DSPWORDS(3);
-
- enum class OutputFormat : u16_le {
- Mono = 0,
- Stereo = 1,
- Surround = 2,
- };
-
- OutputFormat output_format;
-
- u16_le limiter_enabled; ///< Not sure of the exact gain equation for the limiter.
- u16_le headphones_connected; ///< Application updates the DSP on headphone status.
- INSERT_PADDING_DSPWORDS(4); ///< TODO: Surround sound related
- INSERT_PADDING_DSPWORDS(2); ///< TODO: Intermediate mixer 1/2 related
- u16_le mixer1_enabled;
- u16_le mixer2_enabled;
-
- /**
- * This is delay with feedback.
- * Transfer function:
- * H(z) = a z^-N / (1 - b z^-1 + a g z^-N)
- * where
- * N = frame_count * samples_per_frame
- * g, a and b are fixed point with 7 fractional bits
- */
- struct DelayEffect {
- /// These dirty flags are set by the application when it updates the fields in this struct.
- /// The DSP clears these each audio frame.
- union {
- u16_le dirty_raw;
- BitField<0, 1, u16_le> enable_dirty;
- BitField<1, 1, u16_le> work_buffer_address_dirty;
- BitField<2, 1, u16_le> other_dirty; ///< Set when anything else has been changed
- };
-
- u16_le enable;
- INSERT_PADDING_DSPWORDS(1);
- u16_le outputs;
- /// The application allocates a block of memory for the DSP to use as a work buffer.
- u32_dsp work_buffer_address;
- /// Frames to delay by
- u16_le frame_count;
-
- // Coefficients
- s16_le g; ///< Fixed point with 7 fractional bits
- s16_le a; ///< Fixed point with 7 fractional bits
- s16_le b; ///< Fixed point with 7 fractional bits
- };
-
- DelayEffect delay_effect[2];
-
- struct ReverbEffect {
- INSERT_PADDING_DSPWORDS(26); ///< TODO
- };
-
- ReverbEffect reverb_effect[2];
-
- INSERT_PADDING_DSPWORDS(4);
-};
-ASSERT_DSP_STRUCT(DspConfiguration, 196);
-ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20);
-ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
-
-struct AdpcmCoefficients {
- /// Coefficients are signed fixed point with 11 fractional bits.
- /// Each source has 16 coefficients associated with it.
- s16_le coeff[num_sources][16];
-};
-ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
-
-struct DspStatus {
- u16_le unknown;
- u16_le dropped_frames;
- INSERT_PADDING_DSPWORDS(0xE);
-};
-ASSERT_DSP_STRUCT(DspStatus, 32);
-
-/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
-/// When the application writes to this region it has no effect.
-struct FinalMixSamples {
- s16_le pcm16[samples_per_frame][2];
-};
-ASSERT_DSP_STRUCT(FinalMixSamples, 640);
-
-/// DSP writes output of intermediate mixers 1 and 2 here.
-/// Writes to this region by the application edits the output of the intermediate mixers.
-/// This seems to be intended to allow the application to do custom effects on the ARM11.
-/// Values that exceed s16 range will be clipped by the DSP after further processing.
-struct IntermediateMixSamples {
- struct Samples {
- s32_le pcm32[4][samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
- };
-
- Samples mix1;
- Samples mix2;
-};
-ASSERT_DSP_STRUCT(IntermediateMixSamples, 5120);
-
-/// Compressor table
-struct Compressor {
- INSERT_PADDING_DSPWORDS(0xD20); ///< TODO
-};
-
-/// There is no easy way to implement this in a HLE implementation.
-struct DspDebug {
- INSERT_PADDING_DSPWORDS(0x130);
-};
-ASSERT_DSP_STRUCT(DspDebug, 0x260);
-
-struct SharedMemory {
- /// Padding
- INSERT_PADDING_DSPWORDS(0x400);
-
- DspStatus dsp_status;
-
- DspDebug dsp_debug;
-
- FinalMixSamples final_samples;
-
- SourceStatus source_statuses;
-
- Compressor compressor;
-
- DspConfiguration dsp_configuration;
-
- IntermediateMixSamples intermediate_mix_samples;
-
- SourceConfiguration source_configurations;
-
- AdpcmCoefficients adpcm_coefficients;
-
- struct {
- INSERT_PADDING_DSPWORDS(0x100);
- } unknown10;
-
- struct {
- INSERT_PADDING_DSPWORDS(0xC0);
- } unknown11;
-
- struct {
- INSERT_PADDING_DSPWORDS(0x180);
- } unknown12;
-
- struct {
- INSERT_PADDING_DSPWORDS(0xA);
- } unknown13;
-
- struct {
- INSERT_PADDING_DSPWORDS(0x13A3);
- } unknown14;
-
- u16_le frame_counter;
-};
-ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
-
-union DspMemory {
- std::array<u8, 0x80000> raw_memory;
- struct {
- u8 unused_0[0x50000];
- SharedMemory region_0;
- u8 unused_1[0x18000];
- SharedMemory region_1;
- u8 unused_2[0x8000];
- };
-};
-static_assert(offsetof(DspMemory, region_0) == region0_offset,
- "DSP region 0 is at the wrong 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");
-static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0,
- "Structures in DSP::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");
-static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0,
- "Structures in DSP::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");
-static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0,
- "Structures in DSP::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");
-static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0,
- "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
-static_assert(offsetof(SharedMemory, compressor) % 2 == 0,
- "Structures in DSP::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");
-static_assert(offsetof(SharedMemory, unknown10) % 2 == 0,
- "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
-static_assert(offsetof(SharedMemory, unknown11) % 2 == 0,
- "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
-static_assert(offsetof(SharedMemory, unknown12) % 2 == 0,
- "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
-static_assert(offsetof(SharedMemory, unknown13) % 2 == 0,
- "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
-static_assert(offsetof(SharedMemory, unknown14) % 2 == 0,
- "Structures in DSP::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