mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 23:55:40 +00:00
192 lines
6.8 KiB
C
192 lines
6.8 KiB
C
|
// SPDX-FileCopyrightText: 2007-2010 Christian Kothe, 2024 Connor McLaughlin <stenzek@gmail.com>
|
||
|
// SPDX-License-Identifier: GPL-2.0+
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "kiss_fftr.h"
|
||
|
|
||
|
#include <array>
|
||
|
#include <cmath>
|
||
|
#include <complex>
|
||
|
#include <span>
|
||
|
#include <vector>
|
||
|
|
||
|
/**
|
||
|
* The FreeSurround decoder.
|
||
|
*/
|
||
|
class FreeSurroundDecoder
|
||
|
{
|
||
|
public:
|
||
|
/**
|
||
|
* The supported output channel setups.
|
||
|
* A channel setup is defined by the set of channels that are present. Here is a graphic
|
||
|
* of the cs_5point1 setup: http://en.wikipedia.org/wiki/File:5_1_channels_(surround_sound)_label.svg
|
||
|
*/
|
||
|
enum class ChannelSetup
|
||
|
{
|
||
|
Stereo,
|
||
|
Surround41,
|
||
|
Surround51,
|
||
|
Surround71,
|
||
|
Legacy, // same channels as cs_5point1 but different upmixing transform; does not support the focus control
|
||
|
MaxCount
|
||
|
};
|
||
|
|
||
|
static constexpr int grid_res = 21; // resolution of the lookup grid
|
||
|
using LUT = const float (*)[grid_res];
|
||
|
|
||
|
/**
|
||
|
* Create an instance of the decoder.
|
||
|
* @param setup The output channel setup -- determines the number of output channels
|
||
|
* and their place in the sound field.
|
||
|
* @param blocksize Granularity at which data is processed by the decode() function.
|
||
|
* Must be a power of two and should correspond to ca. 10ms worth of single-channel
|
||
|
* samples (default is 4096 for 44.1Khz data). Do not make it shorter or longer
|
||
|
* than 5ms to 20ms since the granularity at which locations are decoded
|
||
|
* changes with this.
|
||
|
*/
|
||
|
FreeSurroundDecoder(ChannelSetup setup = ChannelSetup::Surround51, unsigned blocksize = 4096);
|
||
|
~FreeSurroundDecoder();
|
||
|
|
||
|
/**
|
||
|
* Decode a chunk of stereo sound. The output is delayed by half of the blocksize.
|
||
|
* This function is the only one needed for straightforward decoding.
|
||
|
* @param input Contains exactly blocksize (multiplexed) stereo samples, i.e. 2*blocksize numbers.
|
||
|
* @return A pointer to an internal buffer of exactly blocksize (multiplexed) multichannel samples.
|
||
|
* The actual number of values depends on the number of output channels in the chosen
|
||
|
* channel setup.
|
||
|
*/
|
||
|
float* Decode(float* input);
|
||
|
|
||
|
/**
|
||
|
* Flush the internal buffer.
|
||
|
*/
|
||
|
void Flush();
|
||
|
|
||
|
// --- soundfield transformations
|
||
|
// These functions allow to set up geometric transformations of the sound field after it has been decoded.
|
||
|
// The sound field is best pictured as a 2-dimensional square with the listener in its
|
||
|
// center which can be shifted or stretched in various ways before it is sent to the
|
||
|
// speakers. The order in which these transformations are applied is as listed below.
|
||
|
|
||
|
/**
|
||
|
* Allows to wrap the soundfield around the listener in a circular manner.
|
||
|
* Determines the angle of the frontal sound stage relative to the listener, in degrees.
|
||
|
* A setting of 90° corresponds to standard surround decoding, 180° stretches the front stage from
|
||
|
* ear to ear, 270° wraps it around most of the head. The side and rear content of the sound
|
||
|
* field is compressed accordingly behind the listerer. (default: 90, range: [0°..360°])
|
||
|
*/
|
||
|
void SetCircularWrap(float v);
|
||
|
|
||
|
/**
|
||
|
* Allows to shift the soundfield forward or backward.
|
||
|
* Value range: [-1.0..+1.0]. 0 is no offset, positive values move the sound
|
||
|
* forward, negative values move it backwards. (default: 0)
|
||
|
*/
|
||
|
void SetShift(float v);
|
||
|
|
||
|
/**
|
||
|
* Allows to scale the soundfield backwards.
|
||
|
* Value range: [0.0..+5.0] -- 0 is all compressed to the front, 1 is no change, 5 is scaled 5x backwards (default: 1)
|
||
|
*/
|
||
|
void SetDepth(float v);
|
||
|
|
||
|
/**
|
||
|
* Allows to control the localization (i.e., focality) of sources.
|
||
|
* Value range: [-1.0..+1.0] -- 0 means unchanged, positive means more localized, negative means more ambient
|
||
|
* (default: 0)
|
||
|
*/
|
||
|
void SetFocus(float v);
|
||
|
|
||
|
// --- rendering parameters
|
||
|
// These parameters control how the sound field is mapped onto speakers.
|
||
|
|
||
|
/**
|
||
|
* Set the presence of the front center channel(s).
|
||
|
* Value range: [0.0..1.0] -- fully present at 1.0, fully replaced by left/right at 0.0 (default: 1).
|
||
|
* The default of 1.0 results in spec-conformant decoding ("movie mode") while a value of 0.7 is
|
||
|
* better suited for music reproduction (which is usually mixed without a center channel).
|
||
|
*/
|
||
|
void SetCenterImage(float v);
|
||
|
|
||
|
/**
|
||
|
* Set the front stereo separation.
|
||
|
* Value range: [0.0..inf] -- 1.0 is default, 0.0 is mono.
|
||
|
*/
|
||
|
void SetFrontSeparation(float v);
|
||
|
|
||
|
/**
|
||
|
* Set the rear stereo separation.
|
||
|
* Value range: [0.0..inf] -- 1.0 is default, 0.0 is mono.
|
||
|
*/
|
||
|
void SetRearSeparation(float v);
|
||
|
|
||
|
// --- bass redirection (to LFE)
|
||
|
|
||
|
/**
|
||
|
* Enable/disable LFE channel (default: false = disabled)
|
||
|
*/
|
||
|
void SetBassRedirection(bool v);
|
||
|
|
||
|
/**
|
||
|
* Set the lower end of the transition band, in Hz/Nyquist (default: 40/22050).
|
||
|
*/
|
||
|
void SetLowCutoff(float v);
|
||
|
|
||
|
/**
|
||
|
* Set the upper end of the transition band, in Hz/Nyquist (default: 90/22050).
|
||
|
*/
|
||
|
void SetHighCutoff(float v);
|
||
|
|
||
|
// --- info
|
||
|
|
||
|
/**
|
||
|
* Number of samples currently held in the buffer.
|
||
|
*/
|
||
|
unsigned GetSamplesBuffered();
|
||
|
|
||
|
private:
|
||
|
using cplx = std::complex<double>;
|
||
|
|
||
|
struct ChannelMap
|
||
|
{
|
||
|
std::span<const LUT> luts;
|
||
|
const float* xsf;
|
||
|
};
|
||
|
|
||
|
static const std::array<ChannelMap, static_cast<size_t>(ChannelSetup::MaxCount)> s_channel_maps;
|
||
|
|
||
|
void BufferedDecode(float* input);
|
||
|
|
||
|
// get the index (and fractional offset!) in a piecewise-linear channel allocation grid
|
||
|
static int MapToGrid(double& x);
|
||
|
|
||
|
// constants
|
||
|
const ChannelMap& cmap; // the channel setup
|
||
|
unsigned N, C; // number of samples per input/output block, number of output channels
|
||
|
|
||
|
// parameters
|
||
|
float circular_wrap; // angle of the front soundstage around the listener (90°=default)
|
||
|
float shift; // forward/backward offset of the soundstage
|
||
|
float depth; // backward extension of the soundstage
|
||
|
float focus; // localization of the sound events
|
||
|
float center_image; // presence of the center speaker
|
||
|
float front_separation; // front stereo separation
|
||
|
float rear_separation; // rear stereo separation
|
||
|
float lo_cut, hi_cut; // LFE cutoff frequencies
|
||
|
bool use_lfe; // whether to use the LFE channel
|
||
|
|
||
|
// FFT data structures
|
||
|
std::vector<double> lt, rt, dst; // left total, right total (source arrays), time-domain destination buffer array
|
||
|
std::vector<cplx> lf, rf; // left total / right total in frequency domain
|
||
|
kiss_fftr_cfg forward = nullptr;
|
||
|
kiss_fftr_cfg inverse = nullptr; // FFT buffers
|
||
|
|
||
|
// buffers
|
||
|
bool buffer_empty = true; // whether the buffer is currently empty or dirty
|
||
|
std::vector<float> inbuf; // stereo input buffer (multiplexed)
|
||
|
std::vector<float> outbuf; // multichannel output buffer (multiplexed)
|
||
|
std::vector<double> wnd; // the window function, precomputed
|
||
|
std::vector<std::vector<cplx>> signal; // the signal to be constructed in every channel, in the frequency domain
|
||
|
};
|