mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 13:27:45 -07:00
Merge pull request #5235 from LAGonauta/fs-dplii-decoder
Change FFDShow DPL2 decoder to FreeSurround
This commit is contained in:
commit
326d72728c
@ -587,6 +587,11 @@ else()
|
||||
set(PNG png)
|
||||
endif()
|
||||
|
||||
# Using static FreeSurround from Externals
|
||||
# There is no system FreeSurround library.
|
||||
message(STATUS "Using static FreeSurround from Externals")
|
||||
add_subdirectory(Externals/FreeSurround)
|
||||
|
||||
if (APPLE)
|
||||
message(STATUS "Using ed25519 from Externals")
|
||||
add_subdirectory(Externals/ed25519)
|
||||
|
14
Externals/FreeSurround/CMakeLists.txt
vendored
Normal file
14
Externals/FreeSurround/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
set(SRCS
|
||||
source/ChannelMaps.cpp
|
||||
source/KissFFT.cpp
|
||||
source/KissFFTR.cpp
|
||||
source/FreeSurroundDecoder.cpp
|
||||
)
|
||||
|
||||
add_library(FreeSurround STATIC ${SRCS})
|
||||
target_include_directories(FreeSurround PUBLIC include)
|
||||
target_compile_options(FreeSurround PRIVATE -w)
|
53
Externals/FreeSurround/FreeSurround.vcxproj
vendored
Normal file
53
Externals/FreeSurround/FreeSurround.vcxproj
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{8498F2FA-5CA6-4169-9971-DE5B1FE6132C}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\Source\VSProps\Base.props" />
|
||||
<Import Project="..\..\Source\VSProps\ClDisableAllWarnings.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\FreeSurround\ChannelMaps.h" />
|
||||
<ClInclude Include="include\FreeSurround\FreeSurroundDecoder.h" />
|
||||
<ClInclude Include="include\FreeSurround\KissFFT.h" />
|
||||
<ClInclude Include="include\FreeSurround\KissFFTR.h" />
|
||||
<ClInclude Include="include\FreeSurround\_KissFFTGuts.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="source\ChannelMaps.cpp" />
|
||||
<ClCompile Include="source\FreeSurroundDecoder.cpp" />
|
||||
<ClCompile Include="source\KissFFT.cpp" />
|
||||
<ClCompile Include="source\KissFFTR.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
42
Externals/FreeSurround/FreeSurround.vcxproj.filters
vendored
Normal file
42
Externals/FreeSurround/FreeSurround.vcxproj.filters
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="source\ChannelMaps.cpp">
|
||||
<Filter>source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source\FreeSurroundDecoder.cpp">
|
||||
<Filter>source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source\KissFFT.cpp">
|
||||
<Filter>source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="source\KissFFTR.cpp">
|
||||
<Filter>source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\FreeSurround\_KissFFTGuts.h">
|
||||
<Filter>include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\FreeSurround\ChannelMaps.h">
|
||||
<Filter>include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\FreeSurround\FreeSurroundDecoder.h">
|
||||
<Filter>include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\FreeSurround\KissFFT.h">
|
||||
<Filter>include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\FreeSurround\KissFFTR.h">
|
||||
<Filter>include</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="include">
|
||||
<UniqueIdentifier>{776ecb31-6d5e-489f-bac9-b91a1b202345}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="source">
|
||||
<UniqueIdentifier>{11345325-d67c-4a21-b2e9-c7c6c8cfc8b4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
36
Externals/FreeSurround/include/FreeSurround/ChannelMaps.h
vendored
Normal file
36
Externals/FreeSurround/include/FreeSurround/ChannelMaps.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright (C) 2010 Christian Kothe
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef CHANNELMAPS_H
|
||||
#define CHANNELMAPS_H
|
||||
#include "FreeSurroundDecoder.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
const int grid_res = 21; // resolution of the lookup grid
|
||||
|
||||
// channel allocation maps (per setup)
|
||||
typedef std::vector<std::vector<float *>> alloc_lut;
|
||||
extern std::map<unsigned, alloc_lut> chn_alloc;
|
||||
// channel metadata maps (per setup)
|
||||
extern std::map<unsigned, std::vector<float>> chn_angle;
|
||||
extern std::map<unsigned, std::vector<float>> chn_xsf;
|
||||
extern std::map<unsigned, std::vector<float>> chn_ysf;
|
||||
extern std::map<unsigned, std::vector<channel_id>> chn_id;
|
||||
|
||||
#endif
|
209
Externals/FreeSurround/include/FreeSurround/FreeSurroundDecoder.h
vendored
Normal file
209
Externals/FreeSurround/include/FreeSurround/FreeSurroundDecoder.h
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
// Copyright (C) 2007-2010 Christian Kothe
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
// USA.
|
||||
|
||||
#ifndef FREESURROUND_DECODER_H
|
||||
#define FREESURROUND_DECODER_H
|
||||
#include "KissFFTR.h"
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
|
||||
typedef std::complex<double> cplx;
|
||||
|
||||
// Identifiers for the supported output channels (from front to back, left to
|
||||
// right). The ordering here also determines the ordering of interleaved
|
||||
// samples in the output signal.
|
||||
|
||||
typedef enum channel_id {
|
||||
ci_none = 0,
|
||||
ci_front_left = 1 << 1,
|
||||
ci_front_center_left = 1 << 2,
|
||||
ci_front_center = 1 << 3,
|
||||
ci_front_center_right = 1 << 4,
|
||||
ci_front_right = 1 << 5,
|
||||
ci_side_front_left = 1 << 6,
|
||||
ci_side_front_right = 1 << 7,
|
||||
ci_side_center_left = 1 << 8,
|
||||
ci_side_center_right = 1 << 9,
|
||||
ci_side_back_left = 1 << 10,
|
||||
ci_side_back_right = 1 << 11,
|
||||
ci_back_left = 1 << 12,
|
||||
ci_back_center_left = 1 << 13,
|
||||
ci_back_center = 1 << 14,
|
||||
ci_back_center_right = 1 << 15,
|
||||
ci_back_right = 1 << 16,
|
||||
ci_lfe = 1 << 31
|
||||
} channel_id;
|
||||
|
||||
// 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
|
||||
typedef enum channel_setup {
|
||||
cs_5point1 = ci_front_left | ci_front_center | ci_front_right | ci_back_left |
|
||||
ci_back_right | ci_lfe,
|
||||
|
||||
cs_7point1 = ci_front_left | ci_front_center | ci_front_right |
|
||||
ci_side_center_left | ci_side_center_right | ci_back_left |
|
||||
ci_back_right | ci_lfe
|
||||
} channel_setup;
|
||||
|
||||
// The FreeSurround decoder.
|
||||
|
||||
class DPL2FSDecoder {
|
||||
public:
|
||||
// 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.
|
||||
DPL2FSDecoder();
|
||||
~DPL2FSDecoder();
|
||||
|
||||
void Init(channel_setup setup = cs_5point1, unsigned int blocksize = 4096,
|
||||
unsigned int samplerate = 48000);
|
||||
|
||||
// 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();
|
||||
|
||||
// set soundfield & rendering parameters
|
||||
// for more information, see full FreeSurround source code
|
||||
void set_circular_wrap(float v);
|
||||
void set_shift(float v);
|
||||
void set_depth(float v);
|
||||
void set_focus(float v);
|
||||
void set_center_image(float v);
|
||||
void set_front_separation(float v);
|
||||
void set_rear_separation(float v);
|
||||
void set_low_cutoff(float v);
|
||||
void set_high_cutoff(float v);
|
||||
void set_bass_redirection(bool v);
|
||||
|
||||
// number of samples currently held in the buffer
|
||||
unsigned int buffered();
|
||||
|
||||
private:
|
||||
// constants
|
||||
const float pi = 3.141592654f;
|
||||
const float epsilon = 0.000001f;
|
||||
|
||||
// number of samples per input/output block, number of output channels
|
||||
unsigned int N, C;
|
||||
unsigned int samplerate;
|
||||
|
||||
// the channel setup
|
||||
channel_setup setup;
|
||||
bool initialized;
|
||||
|
||||
// parameters
|
||||
// angle of the front soundstage around the listener (90\B0=default)
|
||||
float circular_wrap;
|
||||
|
||||
// forward/backward offset of the soundstage
|
||||
float shift;
|
||||
|
||||
// backward extension of the soundstage
|
||||
float depth;
|
||||
|
||||
// localization of the sound events
|
||||
float focus;
|
||||
|
||||
// presence of the center speaker
|
||||
float center_image;
|
||||
|
||||
// front stereo separation
|
||||
float front_separation;
|
||||
|
||||
// rear stereo separation
|
||||
float rear_separation;
|
||||
|
||||
// LFE cutoff frequencies
|
||||
float lo_cut, hi_cut;
|
||||
|
||||
// whether to use the LFE channel
|
||||
bool use_lfe;
|
||||
|
||||
// FFT data structures
|
||||
// left total, right total (source arrays), time-domain destination buffer
|
||||
// array
|
||||
std::vector<double> lt, rt, dst;
|
||||
|
||||
// left total / right total in frequency domain
|
||||
std::vector<cplx> lf, rf;
|
||||
|
||||
// FFT buffers
|
||||
kiss_fftr_cfg forward, inverse;
|
||||
|
||||
// buffers
|
||||
// whether the buffer is currently empty or dirty
|
||||
bool buffer_empty;
|
||||
|
||||
// stereo input buffer (multiplexed)
|
||||
std::vector<float> inbuf;
|
||||
|
||||
// multichannel output buffer (multiplexed)
|
||||
std::vector<float> outbuf;
|
||||
|
||||
// the window function, precomputed
|
||||
std::vector<double> wnd;
|
||||
|
||||
// the signal to be constructed in every channel, in the frequency domain
|
||||
// instantiate the decoder with a given channel setup and processing block
|
||||
// size (in samples)
|
||||
std::vector<std::vector<cplx>> signal;
|
||||
|
||||
// helper functions
|
||||
inline float sqr(double x);
|
||||
inline double amplitude(const cplx &x);
|
||||
inline double phase(const cplx &x);
|
||||
inline cplx polar(double a, double p);
|
||||
inline float min(double a, double b);
|
||||
inline float max(double a, double b);
|
||||
inline float clamp(double x);
|
||||
inline float sign(double x);
|
||||
|
||||
// get the distance of the soundfield edge, along a given angle
|
||||
inline double edgedistance(double a);
|
||||
|
||||
// get the index (and fractional offset!) in a piecewise-linear channel
|
||||
// allocation grid
|
||||
int map_to_grid(double &x);
|
||||
|
||||
// decode a block of data and overlap-add it into outbuf
|
||||
void buffered_decode(float *input);
|
||||
|
||||
// transform amp/phase difference space into x/y soundfield space
|
||||
void transform_decode(double a, double p, double &x, double &y);
|
||||
|
||||
// apply a circular_wrap transformation to some position
|
||||
void transform_circular_wrap(double &x, double &y, double refangle);
|
||||
|
||||
// apply a focus transformation to some position
|
||||
void transform_focus(double &x, double &y, double focus);
|
||||
};
|
||||
#endif
|
131
Externals/FreeSurround/include/FreeSurround/KissFFT.h
vendored
Normal file
131
Externals/FreeSurround/include/FreeSurround/KissFFT.h
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
#ifndef KISS_FFT_H
|
||||
#define KISS_FFT_H
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// we're using doubles here...
|
||||
#define kiss_fft_scalar double
|
||||
|
||||
/*
|
||||
ATTENTION!
|
||||
If you would like a :
|
||||
-- a utility that will handle the caching of fft objects
|
||||
-- real-only (no imaginary time component ) FFT
|
||||
-- a multi-dimensional FFT
|
||||
-- a command-line utility to perform ffts
|
||||
-- a command-line utility to perform fast-convolution filtering
|
||||
|
||||
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
|
||||
in the tools/ directory.
|
||||
*/
|
||||
|
||||
#ifdef USE_SIMD
|
||||
#include <xmmintrin.h>
|
||||
#define kiss_fft_scalar __m128
|
||||
#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes, 16)
|
||||
#define KISS_FFT_FREE _mm_free
|
||||
#else
|
||||
#define KISS_FFT_MALLOC malloc
|
||||
#define KISS_FFT_FREE free
|
||||
#endif
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#include <sys/types.h>
|
||||
#if (FIXED_POINT == 32)
|
||||
#define kiss_fft_scalar int32_t
|
||||
#else
|
||||
#define kiss_fft_scalar int16_t
|
||||
#endif
|
||||
#else
|
||||
#ifndef kiss_fft_scalar
|
||||
/* default is float */
|
||||
#define kiss_fft_scalar float
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
kiss_fft_scalar r;
|
||||
kiss_fft_scalar i;
|
||||
} kiss_fft_cpx;
|
||||
|
||||
typedef struct kiss_fft_state *kiss_fft_cfg;
|
||||
|
||||
/*
|
||||
* kiss_fft_alloc
|
||||
*
|
||||
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
|
||||
*
|
||||
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
|
||||
*
|
||||
* The return value from fft_alloc is a cfg buffer used internally
|
||||
* by the fft routine or NULL.
|
||||
*
|
||||
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using
|
||||
* malloc.
|
||||
* The returned value should be free()d when done to avoid memory leaks.
|
||||
*
|
||||
* The state can be placed in a user supplied buffer 'mem':
|
||||
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
|
||||
* then the function places the cfg in mem and the size used in *lenmem
|
||||
* and returns mem.
|
||||
*
|
||||
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
|
||||
* then the function returns NULL and places the minimum cfg
|
||||
* buffer size in *lenmem.
|
||||
* */
|
||||
|
||||
kiss_fft_cfg kiss_fft_alloc(int nfft, int inverse_fft, void *mem,
|
||||
size_t *lenmem);
|
||||
|
||||
/*
|
||||
* kiss_fft(cfg,in_out_buf)
|
||||
*
|
||||
* Perform an FFT on a complex input buffer.
|
||||
* for a forward FFT,
|
||||
* fin should be f[0] , f[1] , ... ,f[nfft-1]
|
||||
* fout will be F[0] , F[1] , ... ,F[nfft-1]
|
||||
* Note that each element is complex and can be accessed like
|
||||
f[k].r and f[k].i
|
||||
* */
|
||||
void kiss_fft(kiss_fft_cfg cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout);
|
||||
|
||||
/*
|
||||
A more generic version of the above function. It reads its input from every Nth
|
||||
sample.
|
||||
* */
|
||||
void kiss_fft_stride(kiss_fft_cfg cfg, const kiss_fft_cpx *fin,
|
||||
kiss_fft_cpx *fout, int fin_stride);
|
||||
|
||||
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
|
||||
buffer and can be simply free()d when no longer needed*/
|
||||
#define kiss_fft_free free
|
||||
|
||||
/*
|
||||
Cleans up some memory that gets managed internally. Not necessary to call, but
|
||||
it might clean up
|
||||
your compiler output to call this before you exit.
|
||||
*/
|
||||
void kiss_fft_cleanup(void);
|
||||
|
||||
/*
|
||||
* Returns the smallest integer k, such that k>=n and k has only "fast" factors
|
||||
* (2,3,5)
|
||||
*/
|
||||
int kiss_fft_next_fast_size(int n);
|
||||
|
||||
/* for real ffts, we need an even size */
|
||||
#define kiss_fftr_next_fast_size_real(n) \
|
||||
(kiss_fft_next_fast_size(((n) + 1) >> 1) << 1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
47
Externals/FreeSurround/include/FreeSurround/KissFFTR.h
vendored
Normal file
47
Externals/FreeSurround/include/FreeSurround/KissFFTR.h
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef KISS_FTR_H
|
||||
#define KISS_FTR_H
|
||||
|
||||
#include "KissFFT.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
Real optimized version can save about 45% cpu time vs. complex fft of a real
|
||||
seq.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
typedef struct kiss_fftr_state *kiss_fftr_cfg;
|
||||
|
||||
kiss_fftr_cfg kiss_fftr_alloc(int nfft, int inverse_fft, void *mem,
|
||||
size_t *lenmem);
|
||||
/*
|
||||
nfft must be even
|
||||
|
||||
If you don't care to allocate space, use mem = lenmem = NULL
|
||||
*/
|
||||
|
||||
void kiss_fftr(kiss_fftr_cfg cfg, const kiss_fft_scalar *timedata,
|
||||
kiss_fft_cpx *freqdata);
|
||||
/*
|
||||
input timedata has nfft scalar points
|
||||
output freqdata has nfft/2+1 complex points
|
||||
*/
|
||||
|
||||
void kiss_fftri(kiss_fftr_cfg cfg, const kiss_fft_cpx *freqdata,
|
||||
kiss_fft_scalar *timedata);
|
||||
/*
|
||||
input freqdata has nfft/2+1 complex points
|
||||
output timedata has nfft scalar points
|
||||
*/
|
||||
|
||||
#define kiss_fftr_free free
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
202
Externals/FreeSurround/include/FreeSurround/_KissFFTGuts.h
vendored
Normal file
202
Externals/FreeSurround/include/FreeSurround/_KissFFTGuts.h
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
Copyright (c) 2003-2010, Mark Borgerding
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions
|
||||
and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of
|
||||
conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with
|
||||
the distribution.
|
||||
* Neither the author nor the names of any contributors may be used to
|
||||
endorse or promote
|
||||
products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* kiss_fft.h
|
||||
defines kiss_fft_scalar as either short or a float type
|
||||
and defines
|
||||
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
|
||||
#include "KissFFT.h"
|
||||
#include <limits.h>
|
||||
|
||||
#define MAXFACTORS 32
|
||||
/* e.g. an fft of length 128 has 4 factors
|
||||
as far as kissfft is concerned
|
||||
4*4*4*2
|
||||
*/
|
||||
|
||||
struct kiss_fft_state {
|
||||
int nfft;
|
||||
int inverse;
|
||||
int factors[2 * MAXFACTORS];
|
||||
kiss_fft_cpx twiddles[1];
|
||||
};
|
||||
|
||||
/*
|
||||
Explanation of macros dealing with complex math:
|
||||
|
||||
C_MUL(m,a,b) : m = a*b
|
||||
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
|
||||
C_SUB( res, a,b) : res = a - b
|
||||
C_SUBFROM( res , a) : res -= a
|
||||
C_ADDTO( res , a) : res += a
|
||||
* */
|
||||
#ifdef FIXED_POINT
|
||||
#if (FIXED_POINT == 32)
|
||||
#define FRACBITS 31
|
||||
#define SAMPPROD int64_t
|
||||
#define SAMP_MAX 2147483647
|
||||
#else
|
||||
#define FRACBITS 15
|
||||
#define SAMPPROD int32_t
|
||||
#define SAMP_MAX 32767
|
||||
#endif
|
||||
|
||||
#define SAMP_MIN -SAMP_MAX
|
||||
|
||||
#if defined(CHECK_OVERFLOW)
|
||||
#define CHECK_OVERFLOW_OP(a, op, b) \
|
||||
if ((SAMPPROD)(a)op(SAMPPROD)(b) > SAMP_MAX || \
|
||||
(SAMPPROD)(a)op(SAMPPROD)(b) < SAMP_MIN) { \
|
||||
fprintf(stderr, \
|
||||
"WARNING:overflow @ " __FILE__ "(%d): (%d " #op " %d) = %ld\n", \
|
||||
__LINE__, (a), (b), (SAMPPROD)(a)op(SAMPPROD)(b)); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define smul(a, b) ((SAMPPROD)(a) * (b))
|
||||
#define sround(x) (kiss_fft_scalar)(((x) + (1 << (FRACBITS - 1))) >> FRACBITS)
|
||||
|
||||
#define S_MUL(a, b) sround(smul(a, b))
|
||||
|
||||
#define C_MUL(m, a, b) \
|
||||
do { \
|
||||
(m).r = sround(smul((a).r, (b).r) - smul((a).i, (b).i)); \
|
||||
(m).i = sround(smul((a).r, (b).i) + smul((a).i, (b).r)); \
|
||||
} while (0)
|
||||
|
||||
#define DIVSCALAR(x, k) (x) = sround(smul(x, SAMP_MAX / k))
|
||||
|
||||
#define C_FIXDIV(c, div) \
|
||||
do { \
|
||||
DIVSCALAR((c).r, div); \
|
||||
DIVSCALAR((c).i, div); \
|
||||
} while (0)
|
||||
|
||||
#define C_MULBYSCALAR(c, s) \
|
||||
do { \
|
||||
(c).r = sround(smul((c).r, s)); \
|
||||
(c).i = sround(smul((c).i, s)); \
|
||||
} while (0)
|
||||
|
||||
#else /* not FIXED_POINT*/
|
||||
|
||||
#define S_MUL(a, b) ((a) * (b))
|
||||
#define C_MUL(m, a, b) \
|
||||
do { \
|
||||
(m).r = (a).r * (b).r - (a).i * (b).i; \
|
||||
(m).i = (a).r * (b).i + (a).i * (b).r; \
|
||||
} while (0)
|
||||
#define C_FIXDIV(c, div) /* NOOP */
|
||||
#define C_MULBYSCALAR(c, s) \
|
||||
do { \
|
||||
(c).r *= (s); \
|
||||
(c).i *= (s); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_OVERFLOW_OP
|
||||
#define CHECK_OVERFLOW_OP(a, op, b) /* noop */
|
||||
#endif
|
||||
|
||||
#define C_ADD(res, a, b) \
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r, +, (b).r) \
|
||||
CHECK_OVERFLOW_OP((a).i, +, (b).i) \
|
||||
(res).r = (a).r + (b).r; \
|
||||
(res).i = (a).i + (b).i; \
|
||||
} while (0)
|
||||
#define C_SUB(res, a, b) \
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r, -, (b).r) \
|
||||
CHECK_OVERFLOW_OP((a).i, -, (b).i) \
|
||||
(res).r = (a).r - (b).r; \
|
||||
(res).i = (a).i - (b).i; \
|
||||
} while (0)
|
||||
#define C_ADDTO(res, a) \
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((res).r, +, (a).r) \
|
||||
CHECK_OVERFLOW_OP((res).i, +, (a).i) \
|
||||
(res).r += (a).r; \
|
||||
(res).i += (a).i; \
|
||||
} while (0)
|
||||
|
||||
#define C_SUBFROM(res, a) \
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((res).r, -, (a).r) \
|
||||
CHECK_OVERFLOW_OP((res).i, -, (a).i) \
|
||||
(res).r -= (a).r; \
|
||||
(res).i -= (a).i; \
|
||||
} while (0)
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#define KISS_FFT_COS(phase) floor(.5 + SAMP_MAX * cos(phase))
|
||||
#define KISS_FFT_SIN(phase) floor(.5 + SAMP_MAX * sin(phase))
|
||||
#define HALF_OF(x) ((x) >> 1)
|
||||
#elif defined(USE_SIMD)
|
||||
#define KISS_FFT_COS(phase) _mm_set1_ps(cos(phase))
|
||||
#define KISS_FFT_SIN(phase) _mm_set1_ps(sin(phase))
|
||||
#define HALF_OF(x) ((x)*_mm_set1_ps(.5))
|
||||
#else
|
||||
#define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
|
||||
#define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
|
||||
#define HALF_OF(x) ((x)*.5)
|
||||
#endif
|
||||
|
||||
#define kf_cexp(x, phase) \
|
||||
do { \
|
||||
(x)->r = KISS_FFT_COS(phase); \
|
||||
(x)->i = KISS_FFT_SIN(phase); \
|
||||
} while (0)
|
||||
|
||||
/* a debugging function */
|
||||
#define pcpx(c) \
|
||||
fprintf(stderr, "%g + %gi\n", (double)((c)->r), (double)((c)->i))
|
||||
|
||||
#ifdef KISS_FFT_USE_ALLOCA
|
||||
// define this to allow use of alloca instead of malloc for temporary buffers
|
||||
// Temporary buffers are used in two case:
|
||||
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
|
||||
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an
|
||||
// in-place transform.
|
||||
#include <alloca.h>
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr)
|
||||
#else
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
|
||||
#endif
|
1148
Externals/FreeSurround/source/ChannelMaps.cpp
vendored
Normal file
1148
Externals/FreeSurround/source/ChannelMaps.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
310
Externals/FreeSurround/source/FreeSurroundDecoder.cpp
vendored
Normal file
310
Externals/FreeSurround/source/FreeSurroundDecoder.cpp
vendored
Normal file
@ -0,0 +1,310 @@
|
||||
/*
|
||||
Copyright (C) 2007-2010 Christian Kothe
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "FreeSurround/FreeSurroundDecoder.h"
|
||||
#include "FreeSurround/ChannelMaps.h"
|
||||
#include <cmath>
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
// FreeSurround implementation
|
||||
// DPL2FSDecoder::Init() must be called before using the decoder.
|
||||
DPL2FSDecoder::DPL2FSDecoder() {
|
||||
initialized = false;
|
||||
buffer_empty = true;
|
||||
}
|
||||
|
||||
DPL2FSDecoder::~DPL2FSDecoder() {
|
||||
#pragma warning(suppress : 4150)
|
||||
delete forward;
|
||||
#pragma warning(suppress : 4150)
|
||||
delete inverse;
|
||||
}
|
||||
|
||||
void DPL2FSDecoder::Init(channel_setup chsetup, unsigned int blsize,
|
||||
unsigned int sample_rate) {
|
||||
if (!initialized) {
|
||||
setup = chsetup;
|
||||
N = blsize;
|
||||
samplerate = sample_rate;
|
||||
|
||||
// Initialize the parameters
|
||||
wnd = std::vector<double>(N);
|
||||
inbuf = std::vector<float>(3 * N);
|
||||
lt = std::vector<double>(N);
|
||||
rt = std::vector<double>(N);
|
||||
dst = std::vector<double>(N);
|
||||
lf = std::vector<cplx>(N / 2 + 1);
|
||||
rf = std::vector<cplx>(N / 2 + 1);
|
||||
forward = kiss_fftr_alloc(N, 0, 0, 0);
|
||||
inverse = kiss_fftr_alloc(N, 1, 0, 0);
|
||||
C = static_cast<unsigned int>(chn_alloc[setup].size());
|
||||
|
||||
// Allocate per-channel buffers
|
||||
outbuf.resize((N + N / 2) * C);
|
||||
signal.resize(C, std::vector<cplx>(N));
|
||||
|
||||
// Init the window function
|
||||
for (unsigned int k = 0; k < N; k++)
|
||||
wnd[k] = sqrt(0.5 * (1 - cos(2 * pi * k / N)) / N);
|
||||
|
||||
// set default parameters
|
||||
set_circular_wrap(90);
|
||||
set_shift(0);
|
||||
set_depth(1);
|
||||
set_focus(0);
|
||||
set_center_image(1);
|
||||
set_front_separation(1);
|
||||
set_rear_separation(1);
|
||||
set_low_cutoff(40.0f / samplerate * 2);
|
||||
set_high_cutoff(90.0f / samplerate * 2);
|
||||
set_bass_redirection(false);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
// decode a stereo chunk, produces a multichannel chunk of the same size
|
||||
// (lagged)
|
||||
float *DPL2FSDecoder::decode(float *input) {
|
||||
if (initialized) {
|
||||
// append incoming data to the end of the input buffer
|
||||
memcpy(&inbuf[N], &input[0], 8 * N);
|
||||
// process first and second half, overlapped
|
||||
buffered_decode(&inbuf[0]);
|
||||
buffered_decode(&inbuf[N]);
|
||||
// shift last half of the input to the beginning (for overlapping with a
|
||||
// future block)
|
||||
memcpy(&inbuf[0], &inbuf[2 * N], 4 * N);
|
||||
buffer_empty = false;
|
||||
return &outbuf[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// flush the internal buffers
|
||||
void DPL2FSDecoder::flush() {
|
||||
memset(&outbuf[0], 0, outbuf.size() * 4);
|
||||
memset(&inbuf[0], 0, inbuf.size() * 4);
|
||||
buffer_empty = true;
|
||||
}
|
||||
|
||||
// number of samples currently held in the buffer
|
||||
unsigned int DPL2FSDecoder::buffered() { return buffer_empty ? 0 : N / 2; }
|
||||
|
||||
// set soundfield & rendering parameters
|
||||
void DPL2FSDecoder::set_circular_wrap(float v) { circular_wrap = v; }
|
||||
void DPL2FSDecoder::set_shift(float v) { shift = v; }
|
||||
void DPL2FSDecoder::set_depth(float v) { depth = v; }
|
||||
void DPL2FSDecoder::set_focus(float v) { focus = v; }
|
||||
void DPL2FSDecoder::set_center_image(float v) { center_image = v; }
|
||||
void DPL2FSDecoder::set_front_separation(float v) { front_separation = v; }
|
||||
void DPL2FSDecoder::set_rear_separation(float v) { rear_separation = v; }
|
||||
void DPL2FSDecoder::set_low_cutoff(float v) { lo_cut = v * (N / 2); }
|
||||
void DPL2FSDecoder::set_high_cutoff(float v) { hi_cut = v * (N / 2); }
|
||||
void DPL2FSDecoder::set_bass_redirection(bool v) { use_lfe = v; }
|
||||
|
||||
// helper functions
|
||||
inline float DPL2FSDecoder::sqr(double x) { return static_cast<float>(x * x); }
|
||||
inline double DPL2FSDecoder::amplitude(const cplx &x) {
|
||||
return sqrt(sqr(x.real()) + sqr(x.imag()));
|
||||
}
|
||||
inline double DPL2FSDecoder::phase(const cplx &x) {
|
||||
return atan2(x.imag(), x.real());
|
||||
}
|
||||
inline cplx DPL2FSDecoder::polar(double a, double p) {
|
||||
return cplx(a * cos(p), a * sin(p));
|
||||
}
|
||||
inline float DPL2FSDecoder::min(double a, double b) {
|
||||
return static_cast<float>(a < b ? a : b);
|
||||
}
|
||||
inline float DPL2FSDecoder::max(double a, double b) {
|
||||
return static_cast<float>(a > b ? a : b);
|
||||
}
|
||||
inline float DPL2FSDecoder::clamp(double x) { return max(-1, min(1, x)); }
|
||||
inline float DPL2FSDecoder::sign(double x) {
|
||||
return static_cast<float>(x < 0 ? -1 : (x > 0 ? 1 : 0));
|
||||
}
|
||||
// get the distance of the soundfield edge, along a given angle
|
||||
inline double DPL2FSDecoder::edgedistance(double a) {
|
||||
return min(sqrt(1 + sqr(tan(a))), sqrt(1 + sqr(1 / tan(a))));
|
||||
}
|
||||
// get the index (and fractional offset!) in a piecewise-linear channel
|
||||
// allocation grid
|
||||
int DPL2FSDecoder::map_to_grid(double &x) {
|
||||
double gp = ((x + 1) * 0.5) * (grid_res - 1),
|
||||
i = min(grid_res - 2, floor(gp));
|
||||
x = gp - i;
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
// decode a block of data and overlap-add it into outbuf
|
||||
void DPL2FSDecoder::buffered_decode(float *input) {
|
||||
// demultiplex and apply window function
|
||||
for (unsigned int k = 0; k < N; k++) {
|
||||
lt[k] = wnd[k] * input[k * 2 + 0];
|
||||
rt[k] = wnd[k] * input[k * 2 + 1];
|
||||
}
|
||||
|
||||
// map into spectral domain
|
||||
kiss_fftr(forward, <[0], (kiss_fft_cpx *)&lf[0]);
|
||||
kiss_fftr(forward, &rt[0], (kiss_fft_cpx *)&rf[0]);
|
||||
|
||||
// compute multichannel output signal in the spectral domain
|
||||
for (unsigned int f = 1; f < N / 2; f++) {
|
||||
// get Lt/Rt amplitudes & phases
|
||||
double ampL = amplitude(lf[f]), ampR = amplitude(rf[f]);
|
||||
double phaseL = phase(lf[f]), phaseR = phase(rf[f]);
|
||||
// calculate the amplitude & phase differences
|
||||
double ampDiff =
|
||||
clamp((ampL + ampR < epsilon) ? 0 : (ampR - ampL) / (ampR + ampL));
|
||||
double phaseDiff = abs(phaseL - phaseR);
|
||||
if (phaseDiff > pi)
|
||||
phaseDiff = 2 * pi - phaseDiff;
|
||||
|
||||
// decode into x/y soundfield position
|
||||
double x, y;
|
||||
transform_decode(ampDiff, phaseDiff, x, y);
|
||||
// add wrap control
|
||||
transform_circular_wrap(x, y, circular_wrap);
|
||||
// add shift control
|
||||
y = clamp(y - shift);
|
||||
// add depth control
|
||||
y = clamp(1 - (1 - y) * depth);
|
||||
// add focus control
|
||||
transform_focus(x, y, focus);
|
||||
// add crossfeed control
|
||||
x = clamp(x *
|
||||
(front_separation * (1 + y) / 2 + rear_separation * (1 - y) / 2));
|
||||
|
||||
// get total signal amplitude
|
||||
double amp_total = sqrt(ampL * ampL + ampR * ampR);
|
||||
// and total L/C/R signal phases
|
||||
double phase_of[] = {
|
||||
phaseL, atan2(lf[f].imag() + rf[f].imag(), lf[f].real() + rf[f].real()),
|
||||
phaseR};
|
||||
// compute 2d channel map indexes p/q and update x/y to fractional offsets
|
||||
// in the map grid
|
||||
int p = map_to_grid(x), q = map_to_grid(y);
|
||||
// map position to channel volumes
|
||||
for (unsigned int c = 0; c < C - 1; c++) {
|
||||
// look up channel map at respective position (with bilinear
|
||||
// interpolation) and build the
|
||||
// signal
|
||||
std::vector<float *> &a = chn_alloc[setup][c];
|
||||
signal[c][f] = polar(
|
||||
amp_total * ((1 - x) * (1 - y) * a[q][p] + x * (1 - y) * a[q][p + 1] +
|
||||
(1 - x) * y * a[q + 1][p] + x * y * a[q + 1][p + 1]),
|
||||
phase_of[1 + static_cast<int>(sign(chn_xsf[setup][c]))]);
|
||||
}
|
||||
|
||||
// optionally redirect bass
|
||||
if (use_lfe && f < hi_cut) {
|
||||
// level of LFE channel according to normalized frequency
|
||||
double lfe_level =
|
||||
f < lo_cut ? 1
|
||||
: 0.5 * (1 + cos(pi * (f - lo_cut) / (hi_cut - lo_cut)));
|
||||
// assign LFE channel
|
||||
signal[C - 1][f] = lfe_level * polar(amp_total, phase_of[1]);
|
||||
// subtract the signal from the other channels
|
||||
for (unsigned int c = 0; c < C - 1; c++)
|
||||
signal[c][f] *= (1 - lfe_level);
|
||||
}
|
||||
}
|
||||
|
||||
// shift the last 2/3 to the first 2/3 of the output buffer
|
||||
memcpy(&outbuf[0], &outbuf[C * N / 2], N * C * 4);
|
||||
// and clear the rest
|
||||
memset(&outbuf[C * N], 0, C * 4 * N / 2);
|
||||
// backtransform each channel and overlap-add
|
||||
for (unsigned int c = 0; c < C; c++) {
|
||||
// back-transform into time domain
|
||||
kiss_fftri(inverse, (kiss_fft_cpx *)&signal[c][0], &dst[0]);
|
||||
// add the result to the last 2/3 of the output buffer, windowed (and
|
||||
// remultiplex)
|
||||
for (unsigned int k = 0; k < N; k++)
|
||||
outbuf[C * (k + N / 2) + c] += static_cast<float>(wnd[k] * dst[k]);
|
||||
}
|
||||
}
|
||||
|
||||
// transform amp/phase difference space into x/y soundfield space
|
||||
void DPL2FSDecoder::transform_decode(double a, double p, double &x, double &y) {
|
||||
x = clamp(1.0047 * a + 0.46804 * a * p * p * p - 0.2042 * a * p * p * p * p +
|
||||
0.0080586 * a * p * p * p * p * p * p * p -
|
||||
0.0001526 * a * p * p * p * p * p * p * p * p * p * p -
|
||||
0.073512 * a * a * a * p - 0.2499 * a * a * a * p * p * p * p +
|
||||
0.016932 * a * a * a * p * p * p * p * p * p * p -
|
||||
0.00027707 * a * a * a * p * p * p * p * p * p * p * p * p * p +
|
||||
0.048105 * a * a * a * a * a * p * p * p * p * p * p * p -
|
||||
0.0065947 * a * a * a * a * a * p * p * p * p * p * p * p * p * p *
|
||||
p +
|
||||
0.0016006 * a * a * a * a * a * p * p * p * p * p * p * p * p * p *
|
||||
p * p -
|
||||
0.0071132 * a * a * a * a * a * a * a * p * p * p * p * p * p * p *
|
||||
p * p +
|
||||
0.0022336 * a * a * a * a * a * a * a * p * p * p * p * p * p * p *
|
||||
p * p * p * p -
|
||||
0.0004804 * a * a * a * a * a * a * a * p * p * p * p * p * p * p *
|
||||
p * p * p * p * p);
|
||||
y = clamp(
|
||||
0.98592 - 0.62237 * p + 0.077875 * p * p - 0.0026929 * p * p * p * p * p +
|
||||
0.4971 * a * a * p - 0.00032124 * a * a * p * p * p * p * p * p +
|
||||
9.2491e-006 * a * a * a * a * p * p * p * p * p * p * p * p * p * p +
|
||||
0.051549 * a * a * a * a * a * a * a * a +
|
||||
1.0727e-014 * a * a * a * a * a * a * a * a * a * a);
|
||||
}
|
||||
|
||||
// apply a circular_wrap transformation to some position
|
||||
void DPL2FSDecoder::transform_circular_wrap(double &x, double &y,
|
||||
double refangle) {
|
||||
if (refangle == 90)
|
||||
return;
|
||||
refangle = refangle * pi / 180;
|
||||
double baseangle = 90 * pi / 180;
|
||||
// translate into edge-normalized polar coordinates
|
||||
double ang = atan2(x, y), len = sqrt(x * x + y * y);
|
||||
len = len / edgedistance(ang);
|
||||
// apply circular_wrap transform
|
||||
if (abs(ang) < baseangle / 2)
|
||||
// angle falls within the front region (to be enlarged)
|
||||
ang *= refangle / baseangle;
|
||||
else
|
||||
// angle falls within the rear region (to be shrunken)
|
||||
ang = pi - (-(((refangle - 2 * pi) * (pi - abs(ang)) * sign(ang)) /
|
||||
(2 * pi - baseangle)));
|
||||
// translate back into soundfield position
|
||||
len = len * edgedistance(ang);
|
||||
x = clamp(sin(ang) * len);
|
||||
y = clamp(cos(ang) * len);
|
||||
}
|
||||
|
||||
// apply a focus transformation to some position
|
||||
void DPL2FSDecoder::transform_focus(double &x, double &y, double focus) {
|
||||
if (focus == 0)
|
||||
return;
|
||||
// translate into edge-normalized polar coordinates
|
||||
double ang = atan2(x, y),
|
||||
len = clamp(sqrt(x * x + y * y) / edgedistance(ang));
|
||||
// apply focus
|
||||
len = focus > 0 ? 1 - pow(1 - len, 1 + focus * 20) : pow(len, 1 - focus * 20);
|
||||
// back-transform into euclidian soundfield position
|
||||
len = len * edgedistance(ang);
|
||||
x = clamp(sin(ang) * len);
|
||||
y = clamp(cos(ang) * len);
|
||||
}
|
444
Externals/FreeSurround/source/KissFFT.cpp
vendored
Normal file
444
Externals/FreeSurround/source/KissFFT.cpp
vendored
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
Copyright (c) 2003-2010, Mark Borgerding
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions
|
||||
and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of
|
||||
conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with
|
||||
the distribution.
|
||||
* Neither the author nor the names of any contributors may be used to
|
||||
endorse or promote
|
||||
products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "FreeSurround/_KissFFTGuts.h"
|
||||
/* The guts header contains all the multiplication and addition macros that are
|
||||
defined for
|
||||
fixed or floating point complex numbers. It also delares the kf_ internal
|
||||
functions.
|
||||
*/
|
||||
|
||||
static void kf_bfly2(kiss_fft_cpx *Fout, const size_t fstride,
|
||||
const kiss_fft_cfg st, int m) {
|
||||
kiss_fft_cpx *Fout2;
|
||||
kiss_fft_cpx *tw1 = st->twiddles;
|
||||
kiss_fft_cpx t;
|
||||
Fout2 = Fout + m;
|
||||
do {
|
||||
C_FIXDIV(*Fout, 2);
|
||||
C_FIXDIV(*Fout2, 2);
|
||||
|
||||
C_MUL(t, *Fout2, *tw1);
|
||||
tw1 += fstride;
|
||||
C_SUB(*Fout2, *Fout, t);
|
||||
C_ADDTO(*Fout, t);
|
||||
++Fout2;
|
||||
++Fout;
|
||||
} while (--m);
|
||||
}
|
||||
|
||||
static void kf_bfly4(kiss_fft_cpx *Fout, const size_t fstride,
|
||||
const kiss_fft_cfg st, const size_t m) {
|
||||
kiss_fft_cpx *tw1, *tw2, *tw3;
|
||||
kiss_fft_cpx scratch[6];
|
||||
size_t k = m;
|
||||
const size_t m2 = 2 * m;
|
||||
const size_t m3 = 3 * m;
|
||||
|
||||
tw3 = tw2 = tw1 = st->twiddles;
|
||||
|
||||
do {
|
||||
C_FIXDIV(*Fout, 4);
|
||||
C_FIXDIV(Fout[m], 4);
|
||||
C_FIXDIV(Fout[m2], 4);
|
||||
C_FIXDIV(Fout[m3], 4);
|
||||
|
||||
C_MUL(scratch[0], Fout[m], *tw1);
|
||||
C_MUL(scratch[1], Fout[m2], *tw2);
|
||||
C_MUL(scratch[2], Fout[m3], *tw3);
|
||||
|
||||
C_SUB(scratch[5], *Fout, scratch[1]);
|
||||
C_ADDTO(*Fout, scratch[1]);
|
||||
C_ADD(scratch[3], scratch[0], scratch[2]);
|
||||
C_SUB(scratch[4], scratch[0], scratch[2]);
|
||||
C_SUB(Fout[m2], *Fout, scratch[3]);
|
||||
tw1 += fstride;
|
||||
tw2 += fstride * 2;
|
||||
tw3 += fstride * 3;
|
||||
C_ADDTO(*Fout, scratch[3]);
|
||||
|
||||
if (st->inverse) {
|
||||
Fout[m].r = scratch[5].r - scratch[4].i;
|
||||
Fout[m].i = scratch[5].i + scratch[4].r;
|
||||
Fout[m3].r = scratch[5].r + scratch[4].i;
|
||||
Fout[m3].i = scratch[5].i - scratch[4].r;
|
||||
} else {
|
||||
Fout[m].r = scratch[5].r + scratch[4].i;
|
||||
Fout[m].i = scratch[5].i - scratch[4].r;
|
||||
Fout[m3].r = scratch[5].r - scratch[4].i;
|
||||
Fout[m3].i = scratch[5].i + scratch[4].r;
|
||||
}
|
||||
++Fout;
|
||||
} while (--k);
|
||||
}
|
||||
|
||||
static void kf_bfly3(kiss_fft_cpx *Fout, const size_t fstride,
|
||||
const kiss_fft_cfg st, size_t m) {
|
||||
size_t k = m;
|
||||
const size_t m2 = 2 * m;
|
||||
kiss_fft_cpx *tw1, *tw2;
|
||||
kiss_fft_cpx scratch[5];
|
||||
kiss_fft_cpx epi3;
|
||||
epi3 = st->twiddles[fstride * m];
|
||||
|
||||
tw1 = tw2 = st->twiddles;
|
||||
|
||||
do {
|
||||
C_FIXDIV(*Fout, 3);
|
||||
C_FIXDIV(Fout[m], 3);
|
||||
C_FIXDIV(Fout[m2], 3);
|
||||
|
||||
C_MUL(scratch[1], Fout[m], *tw1);
|
||||
C_MUL(scratch[2], Fout[m2], *tw2);
|
||||
|
||||
C_ADD(scratch[3], scratch[1], scratch[2]);
|
||||
C_SUB(scratch[0], scratch[1], scratch[2]);
|
||||
tw1 += fstride;
|
||||
tw2 += fstride * 2;
|
||||
|
||||
Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
|
||||
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
|
||||
|
||||
C_MULBYSCALAR(scratch[0], epi3.i);
|
||||
|
||||
C_ADDTO(*Fout, scratch[3]);
|
||||
|
||||
Fout[m2].r = Fout[m].r + scratch[0].i;
|
||||
Fout[m2].i = Fout[m].i - scratch[0].r;
|
||||
|
||||
Fout[m].r -= scratch[0].i;
|
||||
Fout[m].i += scratch[0].r;
|
||||
|
||||
++Fout;
|
||||
} while (--k);
|
||||
}
|
||||
|
||||
static void kf_bfly5(kiss_fft_cpx *Fout, const size_t fstride,
|
||||
const kiss_fft_cfg st, int m) {
|
||||
kiss_fft_cpx *Fout0, *Fout1, *Fout2, *Fout3, *Fout4;
|
||||
int u;
|
||||
kiss_fft_cpx scratch[13];
|
||||
kiss_fft_cpx *twiddles = st->twiddles;
|
||||
kiss_fft_cpx *tw;
|
||||
kiss_fft_cpx ya, yb;
|
||||
ya = twiddles[fstride * m];
|
||||
yb = twiddles[fstride * 2 * m];
|
||||
|
||||
Fout0 = Fout;
|
||||
Fout1 = Fout0 + m;
|
||||
Fout2 = Fout0 + 2 * m;
|
||||
Fout3 = Fout0 + 3 * m;
|
||||
Fout4 = Fout0 + 4 * m;
|
||||
|
||||
tw = st->twiddles;
|
||||
for (u = 0; u < m; ++u) {
|
||||
C_FIXDIV(*Fout0, 5);
|
||||
C_FIXDIV(*Fout1, 5);
|
||||
C_FIXDIV(*Fout2, 5);
|
||||
C_FIXDIV(*Fout3, 5);
|
||||
C_FIXDIV(*Fout4, 5);
|
||||
scratch[0] = *Fout0;
|
||||
|
||||
C_MUL(scratch[1], *Fout1, tw[u * fstride]);
|
||||
C_MUL(scratch[2], *Fout2, tw[2 * u * fstride]);
|
||||
C_MUL(scratch[3], *Fout3, tw[3 * u * fstride]);
|
||||
C_MUL(scratch[4], *Fout4, tw[4 * u * fstride]);
|
||||
|
||||
C_ADD(scratch[7], scratch[1], scratch[4]);
|
||||
C_SUB(scratch[10], scratch[1], scratch[4]);
|
||||
C_ADD(scratch[8], scratch[2], scratch[3]);
|
||||
C_SUB(scratch[9], scratch[2], scratch[3]);
|
||||
|
||||
Fout0->r += scratch[7].r + scratch[8].r;
|
||||
Fout0->i += scratch[7].i + scratch[8].i;
|
||||
|
||||
scratch[5].r =
|
||||
scratch[0].r + S_MUL(scratch[7].r, ya.r) + S_MUL(scratch[8].r, yb.r);
|
||||
scratch[5].i =
|
||||
scratch[0].i + S_MUL(scratch[7].i, ya.r) + S_MUL(scratch[8].i, yb.r);
|
||||
|
||||
scratch[6].r = S_MUL(scratch[10].i, ya.i) + S_MUL(scratch[9].i, yb.i);
|
||||
scratch[6].i = -S_MUL(scratch[10].r, ya.i) - S_MUL(scratch[9].r, yb.i);
|
||||
|
||||
C_SUB(*Fout1, scratch[5], scratch[6]);
|
||||
C_ADD(*Fout4, scratch[5], scratch[6]);
|
||||
|
||||
scratch[11].r =
|
||||
scratch[0].r + S_MUL(scratch[7].r, yb.r) + S_MUL(scratch[8].r, ya.r);
|
||||
scratch[11].i =
|
||||
scratch[0].i + S_MUL(scratch[7].i, yb.r) + S_MUL(scratch[8].i, ya.r);
|
||||
scratch[12].r = -S_MUL(scratch[10].i, yb.i) + S_MUL(scratch[9].i, ya.i);
|
||||
scratch[12].i = S_MUL(scratch[10].r, yb.i) - S_MUL(scratch[9].r, ya.i);
|
||||
|
||||
C_ADD(*Fout2, scratch[11], scratch[12]);
|
||||
C_SUB(*Fout3, scratch[11], scratch[12]);
|
||||
|
||||
++Fout0;
|
||||
++Fout1;
|
||||
++Fout2;
|
||||
++Fout3;
|
||||
++Fout4;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the butterfly for one stage of a mixed radix FFT */
|
||||
static void kf_bfly_generic(kiss_fft_cpx *Fout, const size_t fstride,
|
||||
const kiss_fft_cfg st, int m, int p) {
|
||||
int u, k, q1, q;
|
||||
kiss_fft_cpx *twiddles = st->twiddles;
|
||||
kiss_fft_cpx t;
|
||||
int Norig = st->nfft;
|
||||
|
||||
kiss_fft_cpx *scratch =
|
||||
(kiss_fft_cpx *)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx) * p);
|
||||
|
||||
for (u = 0; u < m; ++u) {
|
||||
k = u;
|
||||
for (q1 = 0; q1 < p; ++q1) {
|
||||
scratch[q1] = Fout[k];
|
||||
C_FIXDIV(scratch[q1], p);
|
||||
k += m;
|
||||
}
|
||||
|
||||
k = u;
|
||||
for (q1 = 0; q1 < p; ++q1) {
|
||||
int twidx = 0;
|
||||
Fout[k] = scratch[0];
|
||||
for (q = 1; q < p; ++q) {
|
||||
twidx += static_cast<int>(fstride) * k;
|
||||
if (twidx >= Norig)
|
||||
twidx -= Norig;
|
||||
C_MUL(t, scratch[q], twiddles[twidx]);
|
||||
C_ADDTO(Fout[k], t);
|
||||
}
|
||||
k += m;
|
||||
}
|
||||
}
|
||||
KISS_FFT_TMP_FREE(scratch);
|
||||
}
|
||||
|
||||
static void kf_work(kiss_fft_cpx *Fout, const kiss_fft_cpx *f,
|
||||
const size_t fstride, int in_stride, int *factors,
|
||||
const kiss_fft_cfg st) {
|
||||
kiss_fft_cpx *Fout_beg = Fout;
|
||||
const int p = *factors++; /* the radix */
|
||||
const int m = *factors++; /* stage's fft length/p */
|
||||
const kiss_fft_cpx *Fout_end = Fout + p * m;
|
||||
|
||||
#ifdef _OPENMP
|
||||
// use openmp extensions at the
|
||||
// top-level (not recursive)
|
||||
if (fstride == 1 && p <= 5) {
|
||||
int k;
|
||||
|
||||
// execute the p different work units in different threads
|
||||
#pragma omp parallel for
|
||||
for (k = 0; k < p; ++k)
|
||||
kf_work(Fout + k * m, f + fstride * in_stride * k, fstride * p, in_stride,
|
||||
factors, st);
|
||||
// all threads have joined by this point
|
||||
|
||||
switch (p) {
|
||||
case 2:
|
||||
kf_bfly2(Fout, fstride, st, m);
|
||||
break;
|
||||
case 3:
|
||||
kf_bfly3(Fout, fstride, st, m);
|
||||
break;
|
||||
case 4:
|
||||
kf_bfly4(Fout, fstride, st, m);
|
||||
break;
|
||||
case 5:
|
||||
kf_bfly5(Fout, fstride, st, m);
|
||||
break;
|
||||
default:
|
||||
kf_bfly_generic(Fout, fstride, st, m, p);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m == 1) {
|
||||
do {
|
||||
*Fout = *f;
|
||||
f += fstride * in_stride;
|
||||
} while (++Fout != Fout_end);
|
||||
} else {
|
||||
do {
|
||||
// recursive call:
|
||||
// DFT of size m*p performed by doing
|
||||
// p instances of smaller DFTs of size m,
|
||||
// each one takes a decimated version of the input
|
||||
kf_work(Fout, f, fstride * p, in_stride, factors, st);
|
||||
f += fstride * in_stride;
|
||||
} while ((Fout += m) != Fout_end);
|
||||
}
|
||||
|
||||
Fout = Fout_beg;
|
||||
|
||||
// recombine the p smaller DFTs
|
||||
switch (p) {
|
||||
case 2:
|
||||
kf_bfly2(Fout, fstride, st, m);
|
||||
break;
|
||||
case 3:
|
||||
kf_bfly3(Fout, fstride, st, m);
|
||||
break;
|
||||
case 4:
|
||||
kf_bfly4(Fout, fstride, st, m);
|
||||
break;
|
||||
case 5:
|
||||
kf_bfly5(Fout, fstride, st, m);
|
||||
break;
|
||||
default:
|
||||
kf_bfly_generic(Fout, fstride, st, m, p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* facbuf is populated by p1,m1,p2,m2, ...
|
||||
where
|
||||
p[i] * m[i] = m[i-1]
|
||||
m0 = n */
|
||||
static void kf_factor(int n, int *facbuf) {
|
||||
int p = 4;
|
||||
double floor_sqrt;
|
||||
floor_sqrt = floor(sqrt((double)n));
|
||||
|
||||
/*factor out powers of 4, powers of 2, then any remaining primes */
|
||||
do {
|
||||
while (n % p) {
|
||||
switch (p) {
|
||||
case 4:
|
||||
p = 2;
|
||||
break;
|
||||
case 2:
|
||||
p = 3;
|
||||
break;
|
||||
default:
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
if (p > floor_sqrt)
|
||||
p = n; /* no more factors, skip to end */
|
||||
}
|
||||
n /= p;
|
||||
*facbuf++ = p;
|
||||
*facbuf++ = n;
|
||||
} while (n > 1);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* User-callable function to allocate all necessary storage space for the fft.
|
||||
*
|
||||
* The return value is a contiguous block of memory, allocated with malloc. As
|
||||
* such,
|
||||
* It can be freed with free(), rather than a kiss_fft-specific function.
|
||||
* */
|
||||
kiss_fft_cfg kiss_fft_alloc(int nfft, int inverse_fft, void *mem,
|
||||
size_t *lenmem) {
|
||||
kiss_fft_cfg st = NULL;
|
||||
size_t memneeded = sizeof(struct kiss_fft_state) +
|
||||
sizeof(kiss_fft_cpx) * (nfft - 1); /* twiddle factors*/
|
||||
|
||||
if (lenmem == NULL) {
|
||||
st = (kiss_fft_cfg) new char[memneeded];
|
||||
} else {
|
||||
if (mem != NULL && *lenmem >= memneeded)
|
||||
st = (kiss_fft_cfg)mem;
|
||||
*lenmem = memneeded;
|
||||
}
|
||||
if (st) {
|
||||
int i;
|
||||
st->nfft = nfft;
|
||||
st->inverse = inverse_fft;
|
||||
|
||||
for (i = 0; i < nfft; ++i) {
|
||||
const double pi =
|
||||
3.141592653589793238462643383279502884197169399375105820974944;
|
||||
double phase = -2 * pi * i / nfft;
|
||||
if (st->inverse)
|
||||
phase *= -1;
|
||||
kf_cexp(st->twiddles + i, phase);
|
||||
}
|
||||
|
||||
kf_factor(nfft, st->factors);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
void kiss_fft_stride(kiss_fft_cfg st, const kiss_fft_cpx *fin,
|
||||
kiss_fft_cpx *fout, int in_stride) {
|
||||
if (fin == fout) {
|
||||
// NOTE: this is not really an in-place FFT algorithm.
|
||||
// It just performs an out-of-place FFT into a temp buffer
|
||||
kiss_fft_cpx *tmpbuf =
|
||||
(kiss_fft_cpx *)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx) * st->nfft);
|
||||
kf_work(tmpbuf, fin, 1, in_stride, st->factors, st);
|
||||
memcpy(fout, tmpbuf, sizeof(kiss_fft_cpx) * st->nfft);
|
||||
KISS_FFT_TMP_FREE(tmpbuf);
|
||||
} else {
|
||||
kf_work(fout, fin, 1, in_stride, st->factors, st);
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_fft(kiss_fft_cfg cfg, const kiss_fft_cpx *fin, kiss_fft_cpx *fout) {
|
||||
kiss_fft_stride(cfg, fin, fout, 1);
|
||||
}
|
||||
|
||||
void kiss_fft_cleanup(void) {
|
||||
// nothing needed any more
|
||||
}
|
||||
|
||||
int kiss_fft_next_fast_size(int n) {
|
||||
while (1) {
|
||||
int m = n;
|
||||
while ((m % 2) == 0)
|
||||
m /= 2;
|
||||
while ((m % 3) == 0)
|
||||
m /= 3;
|
||||
while ((m % 5) == 0)
|
||||
m /= 5;
|
||||
if (m <= 1)
|
||||
break; /* n is completely factorable by twos, threes, and fives */
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
185
Externals/FreeSurround/source/KissFFTR.cpp
vendored
Normal file
185
Externals/FreeSurround/source/KissFFTR.cpp
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
Copyright (c) 2003-2004, Mark Borgerding
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions
|
||||
and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of
|
||||
conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with
|
||||
the distribution.
|
||||
* Neither the author nor the names of any contributors may be used to
|
||||
endorse or promote
|
||||
products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF
|
||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "FreeSurround/KissFFTR.h"
|
||||
#include "FreeSurround/_KissFFTGuts.h"
|
||||
|
||||
struct kiss_fftr_state {
|
||||
kiss_fft_cfg substate;
|
||||
kiss_fft_cpx *tmpbuf;
|
||||
kiss_fft_cpx *super_twiddles;
|
||||
#ifdef USE_SIMD
|
||||
void *pad;
|
||||
#endif
|
||||
};
|
||||
|
||||
kiss_fftr_cfg kiss_fftr_alloc(int nfft, int inverse_fft, void *mem,
|
||||
size_t *lenmem) {
|
||||
int i;
|
||||
kiss_fftr_cfg st = NULL;
|
||||
size_t subsize = 65536 * 4, memneeded = 0;
|
||||
|
||||
if (nfft & 1) {
|
||||
fprintf(stderr, "Real FFT optimization must be even.\n");
|
||||
return NULL;
|
||||
}
|
||||
nfft >>= 1;
|
||||
|
||||
kiss_fft_alloc(nfft, inverse_fft, NULL, &subsize);
|
||||
memneeded = sizeof(struct kiss_fftr_state) + subsize +
|
||||
sizeof(kiss_fft_cpx) * (nfft * 3 / 2);
|
||||
|
||||
if (lenmem == NULL) {
|
||||
st = (kiss_fftr_cfg) new char[memneeded];
|
||||
} else {
|
||||
if (*lenmem >= memneeded)
|
||||
st = (kiss_fftr_cfg)mem;
|
||||
*lenmem = memneeded;
|
||||
}
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
st->substate = (kiss_fft_cfg)(st + 1); /*just beyond kiss_fftr_state struct */
|
||||
st->tmpbuf = (kiss_fft_cpx *)(((char *)st->substate) + subsize);
|
||||
st->super_twiddles = st->tmpbuf + nfft;
|
||||
kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
|
||||
|
||||
for (i = 0; i < nfft / 2; ++i) {
|
||||
double phase =
|
||||
-3.14159265358979323846264338327 * ((double)(i + 1) / nfft + .5);
|
||||
if (inverse_fft)
|
||||
phase *= -1;
|
||||
kf_cexp(st->super_twiddles + i, phase);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
void kiss_fftr(kiss_fftr_cfg st, const kiss_fft_scalar *timedata,
|
||||
kiss_fft_cpx *freqdata) {
|
||||
/* input buffer timedata is stored row-wise */
|
||||
int k, ncfft;
|
||||
kiss_fft_cpx fpnk, fpk, f1k, f2k, tw, tdc;
|
||||
|
||||
if (st->substate->inverse) {
|
||||
fprintf(stderr, "kiss fft usage error: improper alloc\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ncfft = st->substate->nfft;
|
||||
|
||||
/*perform the parallel fft of two real signals packed in real,imag*/
|
||||
kiss_fft(st->substate, (const kiss_fft_cpx *)timedata, st->tmpbuf);
|
||||
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
|
||||
* contains the sum of the even-numbered elements of the input time sequence
|
||||
* The imag part is the sum of the odd-numbered elements
|
||||
*
|
||||
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
|
||||
* yielding DC of input time sequence
|
||||
* The difference of tdc.r - tdc.i is the sum of the input (dot product)
|
||||
* [1,-1,1,-1...
|
||||
* yielding Nyquist bin of input time sequence
|
||||
*/
|
||||
|
||||
tdc.r = st->tmpbuf[0].r;
|
||||
tdc.i = st->tmpbuf[0].i;
|
||||
C_FIXDIV(tdc, 2);
|
||||
CHECK_OVERFLOW_OP(tdc.r, +, tdc.i);
|
||||
CHECK_OVERFLOW_OP(tdc.r, -, tdc.i);
|
||||
freqdata[0].r = tdc.r + tdc.i;
|
||||
freqdata[ncfft].r = tdc.r - tdc.i;
|
||||
#ifdef USE_SIMD
|
||||
freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
|
||||
#else
|
||||
freqdata[ncfft].i = freqdata[0].i = 0;
|
||||
#endif
|
||||
|
||||
for (k = 1; k <= ncfft / 2; ++k) {
|
||||
fpk = st->tmpbuf[k];
|
||||
fpnk.r = st->tmpbuf[ncfft - k].r;
|
||||
fpnk.i = -st->tmpbuf[ncfft - k].i;
|
||||
C_FIXDIV(fpk, 2);
|
||||
C_FIXDIV(fpnk, 2);
|
||||
|
||||
C_ADD(f1k, fpk, fpnk);
|
||||
C_SUB(f2k, fpk, fpnk);
|
||||
C_MUL(tw, f2k, st->super_twiddles[k - 1]);
|
||||
|
||||
freqdata[k].r = HALF_OF(f1k.r + tw.r);
|
||||
freqdata[k].i = HALF_OF(f1k.i + tw.i);
|
||||
freqdata[ncfft - k].r = HALF_OF(f1k.r - tw.r);
|
||||
freqdata[ncfft - k].i = HALF_OF(tw.i - f1k.i);
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_fftri(kiss_fftr_cfg st, const kiss_fft_cpx *freqdata,
|
||||
kiss_fft_scalar *timedata) {
|
||||
/* input buffer timedata is stored row-wise */
|
||||
int k, ncfft;
|
||||
|
||||
if (st->substate->inverse == 0) {
|
||||
fprintf(stderr, "kiss fft usage error: improper alloc\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ncfft = st->substate->nfft;
|
||||
|
||||
st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
|
||||
st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
|
||||
C_FIXDIV(st->tmpbuf[0], 2);
|
||||
|
||||
for (k = 1; k <= ncfft / 2; ++k) {
|
||||
kiss_fft_cpx fk, fnkc, fek, fok, tmp;
|
||||
fk = freqdata[k];
|
||||
fnkc.r = freqdata[ncfft - k].r;
|
||||
fnkc.i = -freqdata[ncfft - k].i;
|
||||
C_FIXDIV(fk, 2);
|
||||
C_FIXDIV(fnkc, 2);
|
||||
|
||||
C_ADD(fek, fk, fnkc);
|
||||
C_SUB(tmp, fk, fnkc);
|
||||
C_MUL(fok, tmp, st->super_twiddles[k - 1]);
|
||||
C_ADD(st->tmpbuf[k], fek, fok);
|
||||
C_SUB(st->tmpbuf[ncfft - k], fek, fok);
|
||||
#ifdef USE_SIMD
|
||||
st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
|
||||
#else
|
||||
st->tmpbuf[ncfft - k].i *= -1;
|
||||
#endif
|
||||
}
|
||||
kiss_fft(st->substate, st->tmpbuf, (kiss_fft_cpx *)timedata);
|
||||
}
|
@ -40,11 +40,11 @@
|
||||
<ClCompile Include="AudioStretcher.cpp" />
|
||||
<ClCompile Include="CubebStream.cpp" />
|
||||
<ClCompile Include="CubebUtils.cpp" />
|
||||
<ClCompile Include="DPL2Decoder.cpp" />
|
||||
<ClCompile Include="Mixer.cpp" />
|
||||
<ClCompile Include="NullSoundStream.cpp" />
|
||||
<ClCompile Include="OpenALStream.cpp" />
|
||||
<ClCompile Include="WASAPIStream.cpp" />
|
||||
<ClCompile Include="SurroundDecoder.cpp" />
|
||||
<ClCompile Include="WaveFile.cpp" />
|
||||
<ClCompile Include="XAudio2Stream.cpp" />
|
||||
<ClCompile Include="XAudio2_7Stream.cpp">
|
||||
@ -57,7 +57,6 @@
|
||||
<ClInclude Include="AudioStretcher.h" />
|
||||
<ClInclude Include="CubebStream.h" />
|
||||
<ClInclude Include="CubebUtils.h" />
|
||||
<ClInclude Include="DPL2Decoder.h" />
|
||||
<ClInclude Include="Mixer.h" />
|
||||
<ClInclude Include="NullSoundStream.h" />
|
||||
<ClInclude Include="OpenALStream.h" />
|
||||
@ -65,6 +64,7 @@
|
||||
<ClInclude Include="PulseAudioStream.h" />
|
||||
<ClInclude Include="SoundStream.h" />
|
||||
<ClInclude Include="WASAPIStream.h" />
|
||||
<ClInclude Include="SurroundDecoder.h" />
|
||||
<ClInclude Include="WaveFile.h" />
|
||||
<ClInclude Include="XAudio2Stream.h" />
|
||||
<ClInclude Include="XAudio2_7Stream.h" />
|
||||
@ -79,6 +79,9 @@
|
||||
<ProjectReference Include="$(CoreDir)Common\Common.vcxproj">
|
||||
<Project>{2e6c348c-c75c-4d94-8d1e-9c1fcbf3efe4}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(ExternalsDir)FreeSurround\FreeSurround.vcxproj">
|
||||
<Project>{8498f2fa-5ca6-4169-9971-de5b1fe6132c}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
@ -9,7 +9,6 @@
|
||||
<ClCompile Include="AudioCommon.cpp" />
|
||||
<ClCompile Include="AudioStretcher.cpp" />
|
||||
<ClCompile Include="CubebUtils.cpp" />
|
||||
<ClCompile Include="DPL2Decoder.cpp" />
|
||||
<ClCompile Include="Mixer.cpp" />
|
||||
<ClCompile Include="WaveFile.cpp" />
|
||||
<ClCompile Include="NullSoundStream.cpp">
|
||||
@ -30,12 +29,12 @@
|
||||
<ClCompile Include="WASAPIStream.cpp">
|
||||
<Filter>SoundStreams</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SurroundDecoder.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AudioCommon.h" />
|
||||
<ClInclude Include="AudioStretcher.h" />
|
||||
<ClInclude Include="CubebUtils.h" />
|
||||
<ClInclude Include="DPL2Decoder.h" />
|
||||
<ClInclude Include="Mixer.h" />
|
||||
<ClInclude Include="WaveFile.h" />
|
||||
<ClInclude Include="NullSoundStream.h">
|
||||
@ -68,6 +67,7 @@
|
||||
<ClInclude Include="WASAPIStream.h">
|
||||
<Filter>SoundStreams</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SurroundDecoder.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
@ -3,8 +3,8 @@ add_library(audiocommon
|
||||
AudioStretcher.cpp
|
||||
CubebStream.cpp
|
||||
CubebUtils.cpp
|
||||
DPL2Decoder.cpp
|
||||
Mixer.cpp
|
||||
SurroundDecoder.cpp
|
||||
NullSoundStream.cpp
|
||||
WaveFile.cpp
|
||||
)
|
||||
@ -69,4 +69,4 @@ if(WIN32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(audiocommon PRIVATE cubeb SoundTouch)
|
||||
target_link_libraries(audiocommon PRIVATE cubeb SoundTouch FreeSurround)
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
#include "AudioCommon/CubebStream.h"
|
||||
#include "AudioCommon/CubebUtils.h"
|
||||
#include "AudioCommon/DPL2Decoder.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Thread.h"
|
||||
|
@ -1,364 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
// Dolby Pro Logic 2 decoder from ffdshow-tryout
|
||||
// * Copyright 2001 Anders Johansson ajh@atri.curtin.edu.au
|
||||
// * Copyright (c) 2004-2006 Milan Cutka
|
||||
// * based on mplayer HRTF plugin by ylai
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "AudioCommon/DPL2Decoder.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#ifndef M_SQRT1_2
|
||||
#define M_SQRT1_2 0.70710678118654752440
|
||||
#endif
|
||||
|
||||
static int olddelay = -1;
|
||||
static unsigned int oldfreq = 0;
|
||||
static unsigned int dlbuflen;
|
||||
static int cyc_pos;
|
||||
static float l_fwr, r_fwr, lpr_fwr, lmr_fwr;
|
||||
static std::vector<float> fwrbuf_l, fwrbuf_r;
|
||||
static float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain;
|
||||
static std::vector<float> lf, rf, lr, rr, cf, cr;
|
||||
static float LFE_buf[256];
|
||||
static unsigned int lfe_pos;
|
||||
static std::vector<float> filter_coefs_lfe;
|
||||
static unsigned int len125;
|
||||
|
||||
template <class T>
|
||||
static float DotProduct(int count, const T* buf, const std::vector<float>& coeffs, int offset)
|
||||
{
|
||||
return std::inner_product(buf, buf + count, coeffs.begin() + offset, T(0));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static T FIRFilter(const T* buf, int pos, int len, int count, const std::vector<float>& coeffs)
|
||||
{
|
||||
int count1, count2;
|
||||
|
||||
if (pos >= count)
|
||||
{
|
||||
pos -= count;
|
||||
count1 = count;
|
||||
count2 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
count2 = pos;
|
||||
count1 = count - pos;
|
||||
pos = len - count1;
|
||||
}
|
||||
|
||||
// high part of window
|
||||
const T* ptr = &buf[pos];
|
||||
|
||||
float r1 = DotProduct(count1, ptr, coeffs, 0);
|
||||
float r2 = DotProduct(count2, buf, coeffs, count1);
|
||||
return T(r1 + r2);
|
||||
}
|
||||
|
||||
/*
|
||||
// Hamming
|
||||
// 2*pi*k
|
||||
// w(k) = 0.54 - 0.46*cos(------), where 0 <= k < N
|
||||
// N-1
|
||||
//
|
||||
// n window length
|
||||
// returns buffer with the window parameters
|
||||
*/
|
||||
static std::vector<float> Hamming(int n)
|
||||
{
|
||||
std::vector<float> w(n);
|
||||
|
||||
float k = static_cast<float>(2.0 * M_PI / (n - 1));
|
||||
|
||||
// Calculate window coefficients
|
||||
for (int i = 0; i < n; i++)
|
||||
w[i] = static_cast<float>(0.54 - 0.46 * cos(k * i));
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
// FIR filter design
|
||||
|
||||
/* Design FIR filter using the Window method
|
||||
|
||||
n filter length must be odd for HP and BS filters
|
||||
fc cutoff frequencies (1 for LP and HP, 2 for BP and BS)
|
||||
0 < fc < 1 where 1 <=> Fs/2
|
||||
flags window and filter type as defined in filter.h
|
||||
variables are ored together: i.e. LP|HAMMING will give a
|
||||
low pass filter designed using a hamming window
|
||||
opt beta constant used only when designing using kaiser windows
|
||||
|
||||
returns buffer for the filter taps (will be n long)
|
||||
*/
|
||||
static std::vector<float> DesignFIR(unsigned int n, float fc, float opt)
|
||||
{
|
||||
const unsigned int o = n & 1; // Indicator for odd filter length
|
||||
const unsigned int end = ((n + 1) >> 1) - o; // Loop end
|
||||
|
||||
// Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
|
||||
const float fc1 = MathUtil::Clamp(fc, 0.001f, 1.0f) / 2;
|
||||
|
||||
const float k1 = 2 * static_cast<float>(M_PI) * fc1; // Cutoff frequency in rad/s
|
||||
const float k2 = 0.5f * static_cast<float>(1 - o); // Time offset if filter has even length
|
||||
float g = 0.0f; // Gain
|
||||
|
||||
// Sanity check
|
||||
if (n == 0)
|
||||
return {};
|
||||
|
||||
// Get window coefficients
|
||||
std::vector<float> w = Hamming(n);
|
||||
|
||||
// Low pass filter
|
||||
|
||||
// If the filter length is odd, there is one point which is exactly
|
||||
// in the middle. The value at this point is 2*fCutoff*sin(x)/x,
|
||||
// where x is zero. To make sure nothing strange happens, we set this
|
||||
// value separately.
|
||||
if (o)
|
||||
{
|
||||
w[end] = fc1 * w[end] * 2.0f;
|
||||
g = w[end];
|
||||
}
|
||||
|
||||
// Create filter
|
||||
for (u32 i = 0; i < end; i++)
|
||||
{
|
||||
float t1 = static_cast<float>(i + 1) - k2;
|
||||
w[end - i - 1] = w[n - end + i] =
|
||||
static_cast<float>(w[end - i - 1] * sin(k1 * t1) / (M_PI * t1)); // Sinc
|
||||
g += 2 * w[end - i - 1]; // Total gain in filter
|
||||
}
|
||||
|
||||
// Normalize gain
|
||||
g = 1 / g;
|
||||
for (u32 i = 0; i < n; i++)
|
||||
w[i] *= g;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
static void OnSeek()
|
||||
{
|
||||
l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0;
|
||||
std::fill(fwrbuf_l.begin(), fwrbuf_l.end(), 0.0f);
|
||||
std::fill(fwrbuf_r.begin(), fwrbuf_r.end(), 0.0f);
|
||||
adapt_l_gain = adapt_r_gain = adapt_lpr_gain = adapt_lmr_gain = 0;
|
||||
std::fill(lf.begin(), lf.end(), 0.0f);
|
||||
std::fill(rf.begin(), rf.end(), 0.0f);
|
||||
std::fill(lr.begin(), lr.end(), 0.0f);
|
||||
std::fill(rr.begin(), rr.end(), 0.0f);
|
||||
std::fill(cf.begin(), cf.end(), 0.0f);
|
||||
std::fill(cr.begin(), cr.end(), 0.0f);
|
||||
lfe_pos = 0;
|
||||
memset(LFE_buf, 0, sizeof(LFE_buf));
|
||||
}
|
||||
|
||||
static void Done()
|
||||
{
|
||||
OnSeek();
|
||||
|
||||
filter_coefs_lfe.clear();
|
||||
}
|
||||
|
||||
static std::vector<float> CalculateCoefficients125HzLowpass(int rate)
|
||||
{
|
||||
len125 = 256;
|
||||
float f = 125.0f / (rate / 2);
|
||||
std::vector<float> coeffs = DesignFIR(len125, f, 0);
|
||||
static const float M3_01DB = 0.7071067812f;
|
||||
for (unsigned int i = 0; i < len125; i++)
|
||||
{
|
||||
coeffs[i] *= M3_01DB;
|
||||
}
|
||||
return coeffs;
|
||||
}
|
||||
|
||||
static float PassiveLock(float x)
|
||||
{
|
||||
static const float MATAGCLOCK =
|
||||
0.2f; /* AGC range (around 1) where the matrix behaves passively */
|
||||
const float x1 = x - 1;
|
||||
const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK);
|
||||
return x1 - x1 / (1 + ax1s * ax1s) + 1;
|
||||
}
|
||||
|
||||
static void MatrixDecode(const float* in, const int k, const int il, const int ir, bool decode_rear,
|
||||
const int _dlbuflen, float _l_fwr, float _r_fwr, float _lpr_fwr,
|
||||
float _lmr_fwr, float* _adapt_l_gain, float* _adapt_r_gain,
|
||||
float* _adapt_lpr_gain, float* _adapt_lmr_gain, float* _lf, float* _rf,
|
||||
float* _lr, float* _rr, float* _cf)
|
||||
{
|
||||
static const float M9_03DB = 0.3535533906f;
|
||||
static const float MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */
|
||||
static const float MATAGCDECAY = 1.0f; /* AGC baseline decay rate (1/samp.) */
|
||||
static const float MATCOMPGAIN =
|
||||
0.37f; /* Cross talk compensation gain, 0.50 - 0.55 is full cancellation. */
|
||||
|
||||
const int kr = (k + olddelay) % _dlbuflen;
|
||||
float l_gain = (_l_fwr + _r_fwr) / (1 + _l_fwr + _l_fwr);
|
||||
float r_gain = (_l_fwr + _r_fwr) / (1 + _r_fwr + _r_fwr);
|
||||
// The 2nd axis has strong gain fluctuations, and therefore require
|
||||
// limits. The factor corresponds to the 1 / amplification of (Lt
|
||||
// - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
|
||||
// dialogues). It should be bigger than -12 dB to prevent
|
||||
// distortion.
|
||||
float lmr_lim_fwr = _lmr_fwr > M9_03DB * _lpr_fwr ? _lmr_fwr : M9_03DB * _lpr_fwr;
|
||||
float lpr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + _lpr_fwr + _lpr_fwr);
|
||||
float lmr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + lmr_lim_fwr + lmr_lim_fwr);
|
||||
float lmr_unlim_gain = (_lpr_fwr + _lmr_fwr) / (1 + _lmr_fwr + _lmr_fwr);
|
||||
float lpr, lmr;
|
||||
float l_agc, r_agc, lpr_agc, lmr_agc;
|
||||
float f, d_gain, c_gain, c_agc_cfk;
|
||||
|
||||
/*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
|
||||
/* AGC adaption */
|
||||
d_gain = (fabs(l_gain - *_adapt_l_gain) + fabs(r_gain - *_adapt_r_gain)) * 0.5f;
|
||||
f = d_gain * (1.0f / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*_adapt_l_gain = (1 - f) * *_adapt_l_gain + f * l_gain;
|
||||
*_adapt_r_gain = (1 - f) * *_adapt_r_gain + f * r_gain;
|
||||
/* Matrix */
|
||||
l_agc = in[il] * PassiveLock(*_adapt_l_gain);
|
||||
r_agc = in[ir] * PassiveLock(*_adapt_r_gain);
|
||||
_cf[k] = (l_agc + r_agc) * static_cast<float>(M_SQRT1_2);
|
||||
if (decode_rear)
|
||||
{
|
||||
_lr[kr] = _rr[kr] = (l_agc - r_agc) * static_cast<float>(M_SQRT1_2);
|
||||
// Stereo rear channel is steered with the same AGC steering as
|
||||
// the decoding matrix. Note this requires a fast updating AGC
|
||||
// at the order of 20 ms (which is the case here).
|
||||
_lr[kr] *= (_l_fwr + _l_fwr) / (1 + _l_fwr + _r_fwr);
|
||||
_rr[kr] *= (_r_fwr + _r_fwr) / (1 + _l_fwr + _r_fwr);
|
||||
}
|
||||
|
||||
/*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
|
||||
lpr = (in[il] + in[ir]) * static_cast<float>(M_SQRT1_2);
|
||||
lmr = (in[il] - in[ir]) * static_cast<float>(M_SQRT1_2);
|
||||
/* AGC adaption */
|
||||
d_gain = fabs(lmr_unlim_gain - *_adapt_lmr_gain);
|
||||
f = d_gain * (1.0f / MATAGCTRIG);
|
||||
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
|
||||
*_adapt_lpr_gain = (1 - f) * *_adapt_lpr_gain + f * lpr_gain;
|
||||
*_adapt_lmr_gain = (1 - f) * *_adapt_lmr_gain + f * lmr_gain;
|
||||
/* Matrix */
|
||||
lpr_agc = lpr * PassiveLock(*_adapt_lpr_gain);
|
||||
lmr_agc = lmr * PassiveLock(*_adapt_lmr_gain);
|
||||
_lf[k] = (lpr_agc + lmr_agc) * static_cast<float>(M_SQRT1_2);
|
||||
_rf[k] = (lpr_agc - lmr_agc) * static_cast<float>(M_SQRT1_2);
|
||||
|
||||
/*** CENTER FRONT CANCELLATION ***/
|
||||
// A heuristic approach exploits that Lt + Rt gain contains the
|
||||
// information about Lt, Rt correlation. This effectively reshapes
|
||||
// the front and rear "cones" to concentrate Lt + Rt to C and
|
||||
// introduce Lt - Rt in L, R.
|
||||
/* 0.67677 is the empirical lower bound for lpr_gain. */
|
||||
c_gain = 8 * (*_adapt_lpr_gain - 0.67677f);
|
||||
c_gain = c_gain > 0 ? c_gain : 0;
|
||||
// c_gain should not be too high, not even reaching full
|
||||
// cancellation (~ 0.50 - 0.55 at current AGC implementation), or
|
||||
// the center will sound too narrow. */
|
||||
c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
|
||||
c_agc_cfk = c_gain * _cf[k];
|
||||
_lf[k] -= c_agc_cfk;
|
||||
_rf[k] -= c_agc_cfk;
|
||||
_cf[k] += c_agc_cfk + c_agc_cfk;
|
||||
}
|
||||
|
||||
void DPL2Decode(float* samples, int numsamples, float* out)
|
||||
{
|
||||
static const unsigned int FWRDURATION = 240; // FWR average duration (samples)
|
||||
static const int cfg_delay = 0;
|
||||
static const unsigned int fmt_freq = 48000;
|
||||
static const unsigned int fmt_nchannels = 2; // input channels
|
||||
|
||||
int cur = 0;
|
||||
|
||||
if (olddelay != cfg_delay || oldfreq != fmt_freq)
|
||||
{
|
||||
Done();
|
||||
olddelay = cfg_delay;
|
||||
oldfreq = fmt_freq;
|
||||
dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1);
|
||||
cyc_pos = dlbuflen - 1;
|
||||
fwrbuf_l.resize(dlbuflen);
|
||||
fwrbuf_r.resize(dlbuflen);
|
||||
lf.resize(dlbuflen);
|
||||
rf.resize(dlbuflen);
|
||||
lr.resize(dlbuflen);
|
||||
rr.resize(dlbuflen);
|
||||
cf.resize(dlbuflen);
|
||||
cr.resize(dlbuflen);
|
||||
filter_coefs_lfe = CalculateCoefficients125HzLowpass(fmt_freq);
|
||||
lfe_pos = 0;
|
||||
memset(LFE_buf, 0, sizeof(LFE_buf));
|
||||
}
|
||||
|
||||
float* in = samples; // Input audio data
|
||||
float* end = in + numsamples * fmt_nchannels; // Loop end
|
||||
|
||||
while (in < end)
|
||||
{
|
||||
const int k = cyc_pos;
|
||||
|
||||
const int fwr_pos = (k + FWRDURATION) % dlbuflen;
|
||||
/* Update the full wave rectified total amplitude */
|
||||
/* Input matrix decoder */
|
||||
l_fwr += fabs(in[0]) - fabs(fwrbuf_l[fwr_pos]);
|
||||
r_fwr += fabs(in[1]) - fabs(fwrbuf_r[fwr_pos]);
|
||||
lpr_fwr += fabs(in[0] + in[1]) - fabs(fwrbuf_l[fwr_pos] + fwrbuf_r[fwr_pos]);
|
||||
lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]);
|
||||
|
||||
/* Matrix encoded 2 channel sources */
|
||||
fwrbuf_l[k] = in[0];
|
||||
fwrbuf_r[k] = in[1];
|
||||
MatrixDecode(in, k, 0, 1, true, dlbuflen, l_fwr, r_fwr, lpr_fwr, lmr_fwr, &adapt_l_gain,
|
||||
&adapt_r_gain, &adapt_lpr_gain, &adapt_lmr_gain, &lf[0], &rf[0], &lr[0], &rr[0],
|
||||
&cf[0]);
|
||||
|
||||
out[cur + 0] = lf[k];
|
||||
out[cur + 1] = rf[k];
|
||||
out[cur + 2] = cf[k];
|
||||
LFE_buf[lfe_pos] = (lf[k] + rf[k] + 2.0f * cf[k] + lr[k] + rr[k]) / 2.0f;
|
||||
out[cur + 3] = FIRFilter(LFE_buf, lfe_pos, len125, len125, filter_coefs_lfe);
|
||||
lfe_pos++;
|
||||
if (lfe_pos == len125)
|
||||
{
|
||||
lfe_pos = 0;
|
||||
}
|
||||
out[cur + 4] = lr[k];
|
||||
out[cur + 5] = rr[k];
|
||||
// Next sample...
|
||||
in += 2;
|
||||
cur += 6;
|
||||
cyc_pos--;
|
||||
if (cyc_pos < 0)
|
||||
{
|
||||
cyc_pos += dlbuflen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DPL2Reset()
|
||||
{
|
||||
olddelay = -1;
|
||||
oldfreq = 0;
|
||||
filter_coefs_lfe.clear();
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
void DPL2Decode(float* samples, int numsamples, float* out);
|
||||
void DPL2Reset();
|
@ -7,7 +7,6 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "AudioCommon/DPL2Decoder.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
@ -16,10 +15,10 @@
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
Mixer::Mixer(unsigned int BackendSampleRate)
|
||||
: m_sampleRate(BackendSampleRate), m_stretcher(BackendSampleRate)
|
||||
: m_sampleRate(BackendSampleRate), m_stretcher(BackendSampleRate),
|
||||
m_surround_decoder(BackendSampleRate, SURROUND_BLOCK_SIZE)
|
||||
{
|
||||
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
|
||||
DPL2Reset();
|
||||
}
|
||||
|
||||
Mixer::~Mixer()
|
||||
@ -167,20 +166,23 @@ unsigned int Mixer::MixSurround(float* samples, unsigned int num_samples)
|
||||
if (!num_samples)
|
||||
return 0;
|
||||
|
||||
memset(samples, 0, num_samples * 6 * sizeof(float));
|
||||
memset(samples, 0, num_samples * SURROUND_CHANNELS * sizeof(float));
|
||||
|
||||
// Mix() may also use m_scratch_buffer internally, but is safe because it alternates reads and
|
||||
// writes.
|
||||
unsigned int available_samples = Mix(m_scratch_buffer.data(), num_samples);
|
||||
for (size_t i = 0; i < static_cast<size_t>(available_samples) * 2; ++i)
|
||||
size_t needed_frames = m_surround_decoder.QueryFramesNeededForSurroundOutput(num_samples);
|
||||
|
||||
// Mix() may also use m_scratch_buffer internally, but is safe because it alternates reads
|
||||
// and writes.
|
||||
size_t available_frames = Mix(m_scratch_buffer.data(), static_cast<u32>(needed_frames));
|
||||
if (available_frames != needed_frames)
|
||||
{
|
||||
m_float_conversion_buffer[i] =
|
||||
m_scratch_buffer[i] / static_cast<float>(std::numeric_limits<short>::max());
|
||||
ERROR_LOG(AUDIO, "Error decoding surround frames.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DPL2Decode(m_float_conversion_buffer.data(), available_samples, samples);
|
||||
m_surround_decoder.PutFrames(m_scratch_buffer.data(), needed_frames);
|
||||
m_surround_decoder.ReceiveFrames(samples, num_samples);
|
||||
|
||||
return available_samples;
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
void Mixer::MixerFifo::PushSamples(const short* samples, unsigned int num_samples)
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "AudioCommon/AudioStretcher.h"
|
||||
#include "AudioCommon/SurroundDecoder.h"
|
||||
#include "AudioCommon/WaveFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
@ -52,6 +53,9 @@ private:
|
||||
static constexpr float CONTROL_FACTOR = 0.2f;
|
||||
static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset
|
||||
|
||||
const unsigned int SURROUND_CHANNELS = 6;
|
||||
const unsigned int SURROUND_BLOCK_SIZE = 512;
|
||||
|
||||
class MixerFifo final
|
||||
{
|
||||
public:
|
||||
@ -86,8 +90,8 @@ private:
|
||||
|
||||
bool m_is_stretching = false;
|
||||
AudioCommon::AudioStretcher m_stretcher;
|
||||
AudioCommon::SurroundDecoder m_surround_decoder;
|
||||
std::array<short, MAX_SAMPLES * 2> m_scratch_buffer;
|
||||
std::array<float, MAX_SAMPLES * 2> m_float_conversion_buffer;
|
||||
|
||||
WaveFileWriter m_wave_writer_dtk;
|
||||
WaveFileWriter m_wave_writer_dsp;
|
||||
|
@ -246,12 +246,6 @@ void OpenALStream::SoundLoop()
|
||||
frames_per_buffer = OAL_MAX_FRAMES;
|
||||
}
|
||||
|
||||
// DPL2 needs a minimum number of samples to work (FWRDURATION)
|
||||
if (use_surround && frames_per_buffer < 240)
|
||||
{
|
||||
frames_per_buffer = 240;
|
||||
}
|
||||
|
||||
INFO_LOG(AUDIO, "Using %d buffers, each with %d audio frames for a total of %d.", OAL_BUFFERS,
|
||||
frames_per_buffer, frames_per_buffer * OAL_BUFFERS);
|
||||
|
||||
@ -312,15 +306,6 @@ void OpenALStream::SoundLoop()
|
||||
if (rendered_frames < min_frames)
|
||||
continue;
|
||||
|
||||
// zero-out the subwoofer channel - DPL2Decode generates a pretty
|
||||
// good 5.0 but not a good 5.1 output. Sadly there is not a 5.0
|
||||
// AL_FORMAT_50CHN32 to make this super-explicit.
|
||||
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
|
||||
for (u32 i = 0; i < rendered_frames; ++i)
|
||||
{
|
||||
dpl2[i * SURROUND_CHANNELS + 3 /*sub/lfe*/] = 0.0f;
|
||||
}
|
||||
|
||||
if (float32_capable)
|
||||
{
|
||||
palBufferData(m_buffers[next_buffer], AL_FORMAT_51CHN32, dpl2.data(),
|
||||
@ -332,14 +317,11 @@ void OpenALStream::SoundLoop()
|
||||
|
||||
for (u32 i = 0; i < rendered_frames * SURROUND_CHANNELS; ++i)
|
||||
{
|
||||
// For some reason the ffdshow's DPL2 decoder outputs samples bigger than 1.
|
||||
// Most are close to 2.5 and some go up to 8. Hard clamping here, we need to
|
||||
// fix the decoder or implement a limiter.
|
||||
dpl2[i] = dpl2[i] * (INT64_C(1) << 31);
|
||||
if (dpl2[i] > INT_MAX)
|
||||
surround_int32[i] = INT_MAX;
|
||||
else if (dpl2[i] < INT_MIN)
|
||||
surround_int32[i] = INT_MIN;
|
||||
dpl2[i] = dpl2[i] * std::numeric_limits<int>::max();
|
||||
if (dpl2[i] > std::numeric_limits<int>::max())
|
||||
surround_int32[i] = std::numeric_limits<int>::max();
|
||||
else if (dpl2[i] < std::numeric_limits<int>::min())
|
||||
surround_int32[i] = std::numeric_limits<int>::min();
|
||||
else
|
||||
surround_int32[i] = static_cast<int>(dpl2[i]);
|
||||
}
|
||||
@ -353,13 +335,13 @@ void OpenALStream::SoundLoop()
|
||||
|
||||
for (u32 i = 0; i < rendered_frames * SURROUND_CHANNELS; ++i)
|
||||
{
|
||||
dpl2[i] = dpl2[i] * (1 << 15);
|
||||
if (dpl2[i] > SHRT_MAX)
|
||||
surround_short[i] = SHRT_MAX;
|
||||
else if (dpl2[i] < SHRT_MIN)
|
||||
surround_short[i] = SHRT_MIN;
|
||||
dpl2[i] = dpl2[i] * std::numeric_limits<short>::max();
|
||||
if (dpl2[i] > std::numeric_limits<short>::max())
|
||||
surround_short[i] = std::numeric_limits<short>::max();
|
||||
else if (dpl2[i] < std::numeric_limits<short>::min())
|
||||
surround_short[i] = std::numeric_limits<short>::min();
|
||||
else
|
||||
surround_short[i] = static_cast<int>(dpl2[i]);
|
||||
surround_short[i] = static_cast<short>(dpl2[i]);
|
||||
}
|
||||
|
||||
palBufferData(m_buffers[next_buffer], AL_FORMAT_51CHN16, surround_short.data(),
|
||||
|
@ -22,7 +22,7 @@ PulseAudio::PulseAudio() : m_thread(), m_run_thread()
|
||||
bool PulseAudio::Init()
|
||||
{
|
||||
m_stereo = !SConfig::GetInstance().bDPL2Decoder;
|
||||
m_channels = m_stereo ? 2 : 5; // will tell PA we use a Stereo or 5.0 channel setup
|
||||
m_channels = m_stereo ? 2 : 6; // will tell PA we use a Stereo or 5.0 channel setup
|
||||
|
||||
NOTICE_LOG(AUDIO, "PulseAudio backend using %d channels", m_channels);
|
||||
|
||||
@ -96,12 +96,13 @@ bool PulseAudio::PulseInit()
|
||||
m_bytespersample = sizeof(float);
|
||||
|
||||
channel_map_p = &channel_map; // explicit channel map:
|
||||
channel_map.channels = 5;
|
||||
channel_map.channels = 6;
|
||||
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
|
||||
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
|
||||
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
|
||||
channel_map.map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
|
||||
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
|
||||
channel_map.map[3] = PA_CHANNEL_POSITION_LFE;
|
||||
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
|
||||
channel_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
|
||||
}
|
||||
ss.channels = m_channels;
|
||||
ss.rate = m_mixer->GetSampleRate();
|
||||
@ -185,22 +186,9 @@ void PulseAudio::WriteCallback(pa_stream* s, size_t length)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_channels == 5) // Extract dpl2/5.0 Surround
|
||||
if (m_channels == 6) // Extract dpl2/5.1 Surround
|
||||
{
|
||||
float floatbuffer_6chan[frames * 6];
|
||||
m_mixer->MixSurround(floatbuffer_6chan, frames);
|
||||
|
||||
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
|
||||
// Discard the subwoofer channel - DPL2Decode generates a pretty
|
||||
// good 5.0 but not a good 5.1 output.
|
||||
const int dpl2_to_5chan[] = {0, 1, 2, 4, 5};
|
||||
for (int i = 0; i < frames; ++i)
|
||||
{
|
||||
for (int j = 0; j < m_channels; ++j)
|
||||
{
|
||||
((float*)buffer)[m_channels * i + j] = floatbuffer_6chan[6 * i + dpl2_to_5chan[j]];
|
||||
}
|
||||
}
|
||||
m_mixer->MixSurround((float*)buffer, frames);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
93
Source/Core/AudioCommon/SurroundDecoder.cpp
Normal file
93
Source/Core/AudioCommon/SurroundDecoder.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <FreeSurround/FreeSurroundDecoder.h>
|
||||
#include <limits>
|
||||
|
||||
#include "AudioCommon/SurroundDecoder.h"
|
||||
|
||||
namespace AudioCommon
|
||||
{
|
||||
constexpr size_t STEREO_CHANNELS = 2;
|
||||
constexpr size_t SURROUND_CHANNELS = 6;
|
||||
|
||||
SurroundDecoder::SurroundDecoder(u32 sample_rate, u32 frame_block_size)
|
||||
: m_sample_rate(sample_rate), m_frame_block_size(frame_block_size)
|
||||
{
|
||||
m_fsdecoder = std::make_unique<DPL2FSDecoder>();
|
||||
m_fsdecoder->Init(cs_5point1, m_frame_block_size, m_sample_rate);
|
||||
}
|
||||
|
||||
SurroundDecoder::~SurroundDecoder() = default;
|
||||
|
||||
void SurroundDecoder::Clear()
|
||||
{
|
||||
m_fsdecoder->flush();
|
||||
m_decoded_fifo.clear();
|
||||
}
|
||||
|
||||
// Currently only 6 channels are supported.
|
||||
size_t SurroundDecoder::QueryFramesNeededForSurroundOutput(const size_t output_frames) const
|
||||
{
|
||||
if (m_decoded_fifo.size() < output_frames * SURROUND_CHANNELS)
|
||||
{
|
||||
// Output stereo frames needed to have at least the desired number of surround frames
|
||||
size_t frames_needed = output_frames - m_decoded_fifo.size() / SURROUND_CHANNELS;
|
||||
return frames_needed + m_frame_block_size - frames_needed % m_frame_block_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Receive and decode samples
|
||||
void SurroundDecoder::PutFrames(const short* in, const size_t num_frames_in)
|
||||
{
|
||||
// Maybe check if it is really power-of-2?
|
||||
s64 remaining_frames = static_cast<s64>(num_frames_in);
|
||||
size_t frame_index = 0;
|
||||
|
||||
while (remaining_frames > 0)
|
||||
{
|
||||
// Convert to float
|
||||
for (size_t i = 0, end = m_frame_block_size * STEREO_CHANNELS; i < end; ++i)
|
||||
{
|
||||
m_float_conversion_buffer[i] = in[i + frame_index * STEREO_CHANNELS] /
|
||||
static_cast<float>(std::numeric_limits<short>::max());
|
||||
}
|
||||
|
||||
// Decode
|
||||
const float* dpl2_fs = m_fsdecoder->decode(m_float_conversion_buffer.data());
|
||||
|
||||
// Add to ring buffer and fix channel mapping
|
||||
// Maybe modify FreeSurround to output the correct mapping?
|
||||
// FreeSurround:
|
||||
// FL | FC | FR | BL | BR | LFE
|
||||
// Most backends:
|
||||
// FL | FR | FC | LFE | BL | BR
|
||||
for (size_t i = 0; i < m_frame_block_size; ++i)
|
||||
{
|
||||
m_decoded_fifo.push(dpl2_fs[i * SURROUND_CHANNELS + 0]); // LEFTFRONT
|
||||
m_decoded_fifo.push(dpl2_fs[i * SURROUND_CHANNELS + 2]); // RIGHTFRONT
|
||||
m_decoded_fifo.push(dpl2_fs[i * SURROUND_CHANNELS + 1]); // CENTREFRONT
|
||||
m_decoded_fifo.push(dpl2_fs[i * SURROUND_CHANNELS + 5]); // sub/lfe
|
||||
m_decoded_fifo.push(dpl2_fs[i * SURROUND_CHANNELS + 3]); // LEFTREAR
|
||||
m_decoded_fifo.push(dpl2_fs[i * SURROUND_CHANNELS + 4]); // RIGHTREAR
|
||||
}
|
||||
|
||||
remaining_frames = remaining_frames - static_cast<int>(m_frame_block_size);
|
||||
frame_index = frame_index + m_frame_block_size;
|
||||
}
|
||||
}
|
||||
|
||||
void SurroundDecoder::ReceiveFrames(float* out, const size_t num_frames_out)
|
||||
{
|
||||
// Copy to output array with desired num_frames_out
|
||||
for (size_t i = 0, num_samples_output = num_frames_out * SURROUND_CHANNELS;
|
||||
i < num_samples_output; ++i)
|
||||
{
|
||||
out[i] = m_decoded_fifo.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCommon
|
36
Source/Core/AudioCommon/SurroundDecoder.h
Normal file
36
Source/Core/AudioCommon/SurroundDecoder.h
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FixedSizeQueue.h"
|
||||
|
||||
class DPL2FSDecoder;
|
||||
|
||||
namespace AudioCommon
|
||||
{
|
||||
class SurroundDecoder
|
||||
{
|
||||
public:
|
||||
explicit SurroundDecoder(u32 sample_rate, u32 frame_block_size);
|
||||
~SurroundDecoder();
|
||||
size_t QueryFramesNeededForSurroundOutput(const size_t output_frames) const;
|
||||
void PutFrames(const short* in, const size_t num_frames_in);
|
||||
void ReceiveFrames(float* out, const size_t num_frames_out);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
u32 m_sample_rate;
|
||||
u32 m_frame_block_size;
|
||||
|
||||
std::unique_ptr<DPL2FSDecoder> m_fsdecoder;
|
||||
std::array<float, 32768> m_float_conversion_buffer;
|
||||
FixedSizeQueue<float, 32768> m_decoded_fifo;
|
||||
};
|
||||
|
||||
} // AudioCommon
|
@ -36,6 +36,7 @@
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)Bochs_disasm;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)cpp-optparse;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)FreeSurround\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)cubeb\include;$(ExternalsDir)cubeb\msvc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ExternalsDir)enet\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
|
@ -83,6 +83,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ed25519", "..\externals\ed2
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "Core\Updater\Updater.vcxproj", "{E4BECBAB-9C6E-41AB-BB56-F9D70AB6BE03}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FreeSurround", "..\Externals\FreeSurround\FreeSurround.vcxproj", "{8498F2FA-5CA6-4169-9971-DE5B1FE6132C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "discord-rpc", "..\Externals\discord-rpc\src\discord-rpc.vcxproj", "{4482FD2A-EC43-3FFB-AC20-2E5C54B05EAD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "minizip", "..\Externals\minizip\minizip.vcxproj", "{23114507-079A-4418-9707-CFA81A03CA99}"
|
||||
@ -247,6 +249,10 @@ Global
|
||||
{E4BECBAB-9C6E-41AB-BB56-F9D70AB6BE03}.Debug|x64.Build.0 = Debug|x64
|
||||
{E4BECBAB-9C6E-41AB-BB56-F9D70AB6BE03}.Release|x64.ActiveCfg = Release|x64
|
||||
{E4BECBAB-9C6E-41AB-BB56-F9D70AB6BE03}.Release|x64.Build.0 = Release|x64
|
||||
{8498F2FA-5CA6-4169-9971-DE5B1FE6132C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8498F2FA-5CA6-4169-9971-DE5B1FE6132C}.Debug|x64.Build.0 = Debug|x64
|
||||
{8498F2FA-5CA6-4169-9971-DE5B1FE6132C}.Release|x64.ActiveCfg = Release|x64
|
||||
{8498F2FA-5CA6-4169-9971-DE5B1FE6132C}.Release|x64.Build.0 = Release|x64
|
||||
{4482FD2A-EC43-3FFB-AC20-2E5C54B05EAD}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4482FD2A-EC43-3FFB-AC20-2E5C54B05EAD}.Debug|x64.Build.0 = Debug|x64
|
||||
{4482FD2A-EC43-3FFB-AC20-2E5C54B05EAD}.Release|x64.ActiveCfg = Release|x64
|
||||
@ -296,6 +302,7 @@ Global
|
||||
{38FEE76F-F347-484B-949C-B4649381CFFB} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
|
||||
{2C0D058E-DE35-4471-AD99-E68A2CAF9E18} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
|
||||
{5BDF4B91-1491-4FB0-BC27-78E9A8E97DC3} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
|
||||
{8498F2FA-5CA6-4169-9971-DE5B1FE6132C} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
|
||||
{4482FD2A-EC43-3FFB-AC20-2E5C54B05EAD} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
|
||||
{23114507-079A-4418-9707-CFA81A03CA99} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
|
||||
{4C3B2264-EA73-4A7B-9CFE-65B0FD635EBB} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
|
||||
|
Loading…
Reference in New Issue
Block a user