Merge branch 'feature/oversampling' into develop
This commit is contained in:
commit
8f391b9654
38 changed files with 3182 additions and 406 deletions
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "sfizz.hpp"
|
||||
#include <absl/flags/parse.h>
|
||||
#include <absl/flags/flag.h>
|
||||
#include <absl/types/span.h>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
|
|
@ -156,17 +157,36 @@ static void done(int sig [[maybe_unused]])
|
|||
// exit(0);
|
||||
}
|
||||
|
||||
ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are 1x, 2x, 4x, 8x)");
|
||||
ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded value");
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// std::ios::sync_with_stdio(false);
|
||||
auto arguments = absl::ParseCommandLine(argc, argv);
|
||||
auto filesToParse = absl::MakeConstSpan(arguments).subspan(1);
|
||||
const std::string oversampling = absl::GetFlag(FLAGS_oversampling);
|
||||
const uint32_t preload_size = absl::GetFlag(FLAGS_preload_size);
|
||||
|
||||
std::cout << "Flags" << '\n';
|
||||
std::cout << "- Oversampling: " << oversampling << '\n';
|
||||
std::cout << "- Preloaded Size: " << preload_size << '\n';
|
||||
const auto factor = [&]() {
|
||||
if (oversampling == "x1") return sfz::Oversampling::x1;
|
||||
if (oversampling == "x2") return sfz::Oversampling::x2;
|
||||
if (oversampling == "x4") return sfz::Oversampling::x4;
|
||||
if (oversampling == "x8") return sfz::Oversampling::x8;
|
||||
return sfz::Oversampling::x1;
|
||||
}();
|
||||
|
||||
std::cout << "Positional arguments:";
|
||||
for (auto& file : filesToParse)
|
||||
std::cout << " " << file << ',';
|
||||
std::cout << '\n';
|
||||
|
||||
sfz::Synth synth;
|
||||
synth.setOversamplingFactor(factor);
|
||||
synth.setPreloadSize(preload_size);
|
||||
synth.loadSfzFile(filesToParse[0]);
|
||||
std::cout << "==========" << '\n';
|
||||
std::cout << "Total:" << '\n';
|
||||
|
|
@ -260,4 +280,4 @@ int main(int argc, char** argv)
|
|||
std::cout << "Closing..." << '\n';
|
||||
jack_client_close(client);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ endif()
|
|||
# SIMD checks
|
||||
if (HAVE_X86INTRIN_H AND UNIX)
|
||||
add_compile_options (-DHAVE_X86INTRIN_H)
|
||||
set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp)
|
||||
set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp sfizz/OversamplerSSE.cpp)
|
||||
elseif (HAVE_INTRIN_H AND WIN32)
|
||||
add_compile_options (/DHAVE_INTRIN_H)
|
||||
set (SFIZZ_SIMD_SOURCES sfizz/SIMDSSE.cpp)
|
||||
|
|
@ -20,9 +20,9 @@ elseif (HAVE_ARM_NEON_H AND UNIX)
|
|||
add_compile_options (-march=native)
|
||||
add_compile_options (-mtune=cortex-a53)
|
||||
add_compile_options (-funsafe-math-optimizations)
|
||||
set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp)
|
||||
set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp sfizz/OversamplerDummy.cpp)
|
||||
else()
|
||||
set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp)
|
||||
set (SFIZZ_SIMD_SOURCES sfizz/SIMDDummy.cpp sfizz/OversamplerDummy.cpp)
|
||||
endif()
|
||||
|
||||
set (SFIZZ_SOURCES ${SFIZZ_SOURCES} ${SFIZZ_SIMD_SOURCES})
|
||||
|
|
|
|||
137
lv2/sfizz.c
137
lv2/sfizz.c
|
|
@ -51,6 +51,8 @@
|
|||
#define SFIZZ_PREFIX SFIZZ_URI "#"
|
||||
#define SFIZZ__sfzFile "http://sfztools.github.io/sfizz:sfzfile"
|
||||
#define SFIZZ__numVoices "http://sfztools.github.io/sfizz:numvoices"
|
||||
#define SFIZZ__preloadSize "http://sfztools.github.io/sfizz:preload_size"
|
||||
#define SFIZZ__oversampling "http://sfztools.github.io/sfizz:oversampling"
|
||||
|
||||
#define CHANNEL_MASK 0x0F
|
||||
#define MIDI_CHANNEL(byte) (byte & CHANNEL_MASK)
|
||||
|
|
@ -59,6 +61,8 @@
|
|||
#define MAX_PATH_SIZE 1024
|
||||
#define MAX_VOICES 256
|
||||
#define DEFAULT_VOICES 64
|
||||
#define DEFAULT_OVERSAMPLING SFIZZ_OVERSAMPLING_X1
|
||||
#define DEFAULT_PRELOAD 8192
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
typedef struct
|
||||
|
|
@ -75,6 +79,8 @@ typedef struct
|
|||
float *output_buffers[2];
|
||||
const float *volume_port;
|
||||
const float *polyphony_port;
|
||||
const float *oversampling_port;
|
||||
const float *preload_port;
|
||||
|
||||
// Atom forge
|
||||
LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread
|
||||
|
|
@ -103,13 +109,17 @@ typedef struct
|
|||
LV2_URID state_changed_uri;
|
||||
LV2_URID sfizz_sfz_file_uri;
|
||||
LV2_URID sfizz_num_voices_uri;
|
||||
LV2_URID sfizz_preload_size_uri;
|
||||
LV2_URID sfizz_oversampling_uri;
|
||||
|
||||
// Sfizz related data
|
||||
sfizz_synth_t *synth;
|
||||
bool expect_nominal_block_length;
|
||||
char sfz_file_path[MAX_PATH_SIZE];
|
||||
int num_voices;
|
||||
bool changing_voices;
|
||||
unsigned int preload_size;
|
||||
sfizz_oversampling_factor_t oversampling;
|
||||
bool changing_state;
|
||||
int max_block_size;
|
||||
float sample_rate;
|
||||
} sfizz_plugin_t;
|
||||
|
|
@ -121,7 +131,9 @@ enum
|
|||
SFIZZ_LEFT = 2,
|
||||
SFIZZ_RIGHT = 3,
|
||||
SFIZZ_VOLUME = 4,
|
||||
SFIZZ_POLYPHONY = 5
|
||||
SFIZZ_POLYPHONY = 5,
|
||||
SFIZZ_OVERSAMPLING = 6,
|
||||
SFIZZ_PRELOAD = 7
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -146,6 +158,8 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
|
|||
self->state_changed_uri = map->map(map->handle, LV2_STATE__StateChanged);
|
||||
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
||||
self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices);
|
||||
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
|
||||
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -174,6 +188,12 @@ connect_port(LV2_Handle instance,
|
|||
case SFIZZ_POLYPHONY:
|
||||
self->polyphony_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_OVERSAMPLING:
|
||||
self->oversampling_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_PRELOAD:
|
||||
self->preload_port = (const float *)data;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -203,7 +223,9 @@ instantiate(const LV2_Descriptor *descriptor,
|
|||
self->expect_nominal_block_length = false;
|
||||
self->sfz_file_path[0] = '\0';
|
||||
self->num_voices = DEFAULT_VOICES;
|
||||
self->changing_voices = false;
|
||||
self->oversampling = DEFAULT_OVERSAMPLING;
|
||||
self->preload_size = DEFAULT_PRELOAD;
|
||||
self->changing_state = false;
|
||||
|
||||
// Get the features from the host and populate the structure
|
||||
for (const LV2_Feature *const *f = features; *f; f++)
|
||||
|
|
@ -509,8 +531,7 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
sfizz_set_volume(self->synth, volume);
|
||||
|
||||
int num_voices = (int)*self->polyphony_port;
|
||||
if (num_voices != self->num_voices && !self->changing_voices)
|
||||
{
|
||||
if (num_voices != self->num_voices && !self->changing_state) {
|
||||
lv2_log_note(&self->logger, "[run] Number of voices changed to %d\n", num_voices);
|
||||
LV2_Atom_Int num_voices_atom;
|
||||
num_voices_atom.atom.type = self->sfizz_num_voices_uri;
|
||||
|
|
@ -520,7 +541,37 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
lv2_atom_total_size((LV2_Atom*)&num_voices_atom),
|
||||
&num_voices_atom) == LV2_WORKER_SUCCESS)
|
||||
{
|
||||
self->changing_voices = true;
|
||||
self->changing_state = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int preload_size = (int)*self->preload_port;
|
||||
if (preload_size != self->preload_size && !self->changing_state) {
|
||||
lv2_log_note(&self->logger, "[run] Preload size changed to %d\n", preload_size);
|
||||
LV2_Atom_Int atom;
|
||||
atom.atom.type = self->sfizz_preload_size_uri;
|
||||
atom.atom.size = sizeof(int);
|
||||
atom.body = preload_size;
|
||||
if (self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((LV2_Atom*)&atom),
|
||||
&atom) == LV2_WORKER_SUCCESS)
|
||||
{
|
||||
self->changing_state = true;
|
||||
}
|
||||
}
|
||||
|
||||
sfizz_oversampling_factor_t oversampling = (sfizz_oversampling_factor_t)*self->oversampling_port;
|
||||
if (oversampling != self->oversampling && !self->changing_state) {
|
||||
lv2_log_note(&self->logger, "[run] Oversampling size changed to %d\n", oversampling);
|
||||
LV2_Atom_Int atom;
|
||||
atom.atom.type = self->sfizz_oversampling_uri;
|
||||
atom.atom.size = sizeof(int);
|
||||
atom.body = oversampling;
|
||||
if (self->worker->schedule_work(self->worker->handle,
|
||||
lv2_atom_total_size((LV2_Atom*)&atom),
|
||||
&atom) == LV2_WORKER_SUCCESS)
|
||||
{
|
||||
self->changing_state = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -614,6 +665,30 @@ restore(LV2_Handle instance,
|
|||
self->num_voices = num_voices;
|
||||
}
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_preload_size_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
unsigned int preload_size = *(const unsigned int *)value;
|
||||
if (preload_size != self->preload_size)
|
||||
{
|
||||
lv2_log_note(&self->logger, "Restoring the preload size to %d\n", preload_size);
|
||||
sfizz_set_preload_size(self->synth, preload_size);
|
||||
self->preload_size = preload_size;
|
||||
}
|
||||
}
|
||||
|
||||
value = retrieve(handle, self->sfizz_oversampling_uri, &size, &type, &val_flags);
|
||||
if (value)
|
||||
{
|
||||
sfizz_oversampling_factor_t oversampling = *(const sfizz_oversampling_factor_t *)value;
|
||||
if (oversampling != self->oversampling)
|
||||
{
|
||||
lv2_log_note(&self->logger, "Restoring the oversampling to %d\n", oversampling);
|
||||
sfizz_set_oversampling_factor(self->synth, oversampling);
|
||||
self->oversampling = oversampling;
|
||||
}
|
||||
}
|
||||
return LV2_STATE_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -643,6 +718,22 @@ save(LV2_Handle instance,
|
|||
self->atom_int_uri,
|
||||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
|
||||
|
||||
// Save the preload size
|
||||
store(handle,
|
||||
self->sfizz_preload_size_uri,
|
||||
&self->preload_size,
|
||||
sizeof(unsigned int),
|
||||
self->atom_int_uri,
|
||||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
|
||||
|
||||
// Save the preload size
|
||||
store(handle,
|
||||
self->sfizz_oversampling_uri,
|
||||
&self->oversampling,
|
||||
sizeof(int),
|
||||
self->atom_int_uri,
|
||||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
|
||||
|
||||
return LV2_STATE_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -674,6 +765,19 @@ work(LV2_Handle instance,
|
|||
lv2_log_note(&self->logger, "[work] Changing number of voices to: %d\n", num_voices);
|
||||
sfizz_set_num_voices(self->synth, num_voices);
|
||||
}
|
||||
else if (atom->type == self->sfizz_preload_size_uri)
|
||||
{
|
||||
const unsigned int preload_size = *(const unsigned int *)LV2_ATOM_BODY_CONST(atom);
|
||||
lv2_log_note(&self->logger, "[work] Changing preload size to: %d\n", preload_size);
|
||||
sfizz_set_preload_size(self->synth, preload_size);
|
||||
}
|
||||
else if (atom->type == self->sfizz_oversampling_uri)
|
||||
{
|
||||
const sfizz_oversampling_factor_t oversampling =
|
||||
*(const sfizz_oversampling_factor_t *)LV2_ATOM_BODY_CONST(atom);
|
||||
lv2_log_note(&self->logger, "[work] Changing oversampling to: %d\n", oversampling);
|
||||
sfizz_set_oversampling_factor(self->synth, oversampling);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_log_error(&self->logger, "[worker] Got an unknown atom.\n");
|
||||
|
|
@ -702,18 +806,33 @@ work_response(LV2_Handle instance,
|
|||
|
||||
const LV2_Atom *atom = (const LV2_Atom *)data;
|
||||
if (atom->type == self->sfizz_sfz_file_uri)
|
||||
{
|
||||
{ // TODO: could check that everything is indeed changed here
|
||||
const char *sfz_file_path = LV2_ATOM_BODY_CONST(atom);
|
||||
strcpy(self->sfz_file_path, sfz_file_path);
|
||||
lv2_log_note(&self->logger, "[work_response] File changed to: %s\n", self->sfz_file_path);
|
||||
}
|
||||
else if (atom->type == self->sfizz_num_voices_uri)
|
||||
{
|
||||
{ // TODO: could check that everything is indeed changed here
|
||||
const int num_voices = *(const int *)LV2_ATOM_BODY_CONST(atom);
|
||||
self->num_voices = num_voices;
|
||||
self->changing_voices = false;
|
||||
self->changing_state = false;
|
||||
lv2_log_note(&self->logger, "[work_response] Number of voices changed to: %d\n", self->num_voices);
|
||||
}
|
||||
else if (atom->type == self->sfizz_preload_size_uri)
|
||||
{ // TODO: could check that everything is indeed changed here
|
||||
const unsigned int preload_size = *(const unsigned int *)LV2_ATOM_BODY_CONST(atom);
|
||||
self->preload_size = preload_size;
|
||||
self->changing_state = false;
|
||||
lv2_log_note(&self->logger, "[work_response] Preload size changed to: %d\n", self->preload_size);
|
||||
}
|
||||
else if (atom->type == self->sfizz_oversampling_uri)
|
||||
{ // TODO: could check that everything is indeed changed here
|
||||
const sfizz_oversampling_factor_t oversampling =
|
||||
*(const sfizz_oversampling_factor_t *)LV2_ATOM_BODY_CONST(atom);
|
||||
self->oversampling = oversampling;
|
||||
self->changing_state = false;
|
||||
lv2_log_note(&self->logger, "[work_response] Oversampling changed to: %d\n", self->oversampling);
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_log_error(&self->logger, "[work_response] Got an unknown atom.\n");
|
||||
|
|
|
|||
|
|
@ -30,6 +30,16 @@
|
|||
rdfs:label "Polyphony" ;
|
||||
rdfs:range atom:Int .
|
||||
|
||||
<@LV2PLUGIN_URI@:preload_size>
|
||||
a lv2:Parameter ;
|
||||
rdfs:label "Preload size" ;
|
||||
rdfs:range atom:Int .
|
||||
|
||||
<@LV2PLUGIN_URI@:oversampling>
|
||||
a lv2:Parameter ;
|
||||
rdfs:label "Oversampling" ;
|
||||
rdfs:range atom:Int .
|
||||
|
||||
<@LV2PLUGIN_URI@>
|
||||
a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ;
|
||||
|
||||
|
|
@ -105,4 +115,39 @@
|
|||
lv2:scalePoint [ rdfs:label "64 voices"; rdf:value 64 ] ;
|
||||
lv2:scalePoint [ rdfs:label "128 voices"; rdf:value 128 ] ;
|
||||
lv2:scalePoint [ rdfs:label "256 voices"; rdf:value 256 ] ;
|
||||
] .
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 6 ;
|
||||
lv2:symbol "oversampling" ;
|
||||
lv2:name "Internal oversampling factor" ;
|
||||
pg:group <#config> ;
|
||||
lv2:portProperty pprop:notAutomatic ;
|
||||
lv2:portProperty pprop:expensive ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:portProperty lv2:enumeration ;
|
||||
lv2:default 1 ;
|
||||
lv2:minimum 1 ;
|
||||
lv2:maximum 8 ;
|
||||
lv2:scalePoint [ rdfs:label "x1 oversampling"; rdf:value 1 ] ;
|
||||
lv2:scalePoint [ rdfs:label "x2 oversampling"; rdf:value 2 ] ;
|
||||
lv2:scalePoint [ rdfs:label "x4 oversampling"; rdf:value 4 ] ;
|
||||
lv2:scalePoint [ rdfs:label "x8 oversampling"; rdf:value 8 ] ;
|
||||
], [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 7 ;
|
||||
lv2:symbol "preload_size" ;
|
||||
lv2:name "Preload size" ;
|
||||
pg:group <#config> ;
|
||||
lv2:portProperty pprop:notAutomatic ;
|
||||
lv2:portProperty pprop:expensive ;
|
||||
lv2:portProperty lv2:integer ;
|
||||
lv2:portProperty lv2:enumeration ;
|
||||
lv2:default 8192 ;
|
||||
lv2:minimum 1024 ;
|
||||
lv2:maximum 16384 ;
|
||||
lv2:scalePoint [ rdfs:label "4 Ko"; rdf:value 1024 ] ;
|
||||
lv2:scalePoint [ rdfs:label "8 Ko"; rdf:value 2048 ] ;
|
||||
lv2:scalePoint [ rdfs:label "16 Ko"; rdf:value 4096 ] ;
|
||||
lv2:scalePoint [ rdfs:label "32 Ko"; rdf:value 8192 ] ;
|
||||
lv2:scalePoint [ rdfs:label "64 Ko"; rdf:value 16384 ] ;
|
||||
].
|
||||
|
|
|
|||
95
src/external/hiir/StageDataNeon.h
vendored
Normal file
95
src/external/hiir/StageDataNeon.h
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageDataNeon.h
|
||||
Author: Laurent de Soras, 2016
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#if ! defined (hiir_StageDataNeon_HEADER_INCLUDED)
|
||||
#define hiir_StageDataNeon_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma warning (4 : 4250)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
class StageDataNeon
|
||||
{
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
union
|
||||
{
|
||||
__attribute__ ((aligned (16))) float32x4_t
|
||||
_coef4;
|
||||
__attribute__ ((aligned (16))) float
|
||||
_coef [4]; // a_{4n+1}, a_{4n}, a_{4n+3}, a_{4n+2}
|
||||
};
|
||||
union
|
||||
{
|
||||
__attribute__ ((aligned (16))) float32x4_t
|
||||
_mem4;
|
||||
__attribute__ ((aligned (16))) float
|
||||
_mem [4]; // y of the stage
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
}; // class StageDataNeon
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
//#include "hiir/StageDataNeon.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageDataNeon_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
67
src/external/hiir/StageDataSse.h
vendored
Normal file
67
src/external/hiir/StageDataSse.h
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageDataSse.h
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_StageDataSse_HEADER_INCLUDED)
|
||||
#define hiir_StageDataSse_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma once
|
||||
#pragma warning (4 : 4250) // "Inherits via dominance."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
class StageDataSse
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
union
|
||||
{
|
||||
__m128 _coef4; // Just to ensure alignement
|
||||
float _coef [4]; // a_{4n+1}, a_{4n}, a_{4n+3}, a_{4n+2}
|
||||
};
|
||||
union
|
||||
{
|
||||
__m128 _mem4;
|
||||
float _mem [4]; // y of the stage
|
||||
};
|
||||
|
||||
}; // class StageDataSse
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageDataSse_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
100
src/external/hiir/StageProc4Neon.h
vendored
Normal file
100
src/external/hiir/StageProc4Neon.h
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageProc4Neon.h
|
||||
Author: Laurent de Soras, 2016
|
||||
|
||||
Template parameters:
|
||||
- REMAINING: Number of remaining coefficients to process, >= 0
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#if ! defined (hiir_StageProc4Neon_HEADER_INCLUDED)
|
||||
#define hiir_StageProc4Neon_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma warning (4 : 4250)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/def.h"
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
class StageDataNeon;
|
||||
|
||||
template <int REMAINING>
|
||||
class StageProc4Neon
|
||||
{
|
||||
static_assert (REMAINING >= 0, "REMAINING must be >= 0.");
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
static hiir_FORCEINLINE void
|
||||
process_sample_pos (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr);
|
||||
static hiir_FORCEINLINE void
|
||||
process_sample_neg (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr);
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
StageProc4Neon () = delete;
|
||||
StageProc4Neon (const StageProc4Neon <REMAINING> &other) = delete;
|
||||
~StageProc4Neon () = delete;
|
||||
StageProc4Neon <REMAINING> &
|
||||
operator = (const StageProc4Neon <REMAINING> &other) = delete;
|
||||
bool operator == (const StageProc4Neon <REMAINING> &other) const = delete;
|
||||
bool operator != (const StageProc4Neon <REMAINING> &other) const = delete;
|
||||
|
||||
}; // class StageProc4Neon
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#include "hiir/StageProc4Neon.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageProc4Neon_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
174
src/external/hiir/StageProc4Neon.hpp
vendored
Normal file
174
src/external/hiir/StageProc4Neon.hpp
vendored
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageProc4Neon.hpp
|
||||
Author: Laurent de Soras, 2016
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_StageProc4Neon_CODEHEADER_INCLUDED)
|
||||
#define hiir_StageProc4Neon_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/StageDataNeon.h"
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
inline void StageProc4Neon <1>::process_sample_pos (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - 1;
|
||||
|
||||
const float32x4_t tmp_0 = vmlaq_f32 (
|
||||
stage_arr [cnt - 2]._mem4,
|
||||
spl_0 - stage_arr [cnt ]._mem4,
|
||||
stage_arr [cnt ]._coef4
|
||||
);
|
||||
|
||||
stage_arr [cnt - 2]._mem4 = spl_0;
|
||||
stage_arr [cnt - 1]._mem4 = spl_1;
|
||||
stage_arr [cnt ]._mem4 = tmp_0;
|
||||
|
||||
spl_0 = tmp_0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
inline void StageProc4Neon <0>::process_sample_pos (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2;
|
||||
|
||||
stage_arr [cnt - 2]._mem4 = spl_0;
|
||||
stage_arr [cnt - 1]._mem4 = spl_1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <int REMAINING>
|
||||
void StageProc4Neon <REMAINING>::process_sample_pos (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - REMAINING;
|
||||
|
||||
const float32x4_t tmp_0 = vmlaq_f32 (
|
||||
stage_arr [cnt - 2]._mem4,
|
||||
spl_0 - stage_arr [cnt ]._mem4,
|
||||
stage_arr [cnt ]._coef4
|
||||
);
|
||||
const float32x4_t tmp_1 = vmlaq_f32 (
|
||||
stage_arr [cnt - 1]._mem4,
|
||||
spl_1 - stage_arr [cnt + 1]._mem4,
|
||||
stage_arr [cnt + 1]._coef4
|
||||
);
|
||||
|
||||
stage_arr [cnt - 2]._mem4 = spl_0;
|
||||
stage_arr [cnt - 1]._mem4 = spl_1;
|
||||
|
||||
spl_0 = tmp_0;
|
||||
spl_1 = tmp_1;
|
||||
|
||||
StageProc4Neon <REMAINING - 2>::process_sample_pos (
|
||||
nbr_coefs,
|
||||
spl_0,
|
||||
spl_1,
|
||||
stage_arr
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
inline void StageProc4Neon <1>::process_sample_neg (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - 1;
|
||||
|
||||
float32x4_t tmp_0 = spl_0;
|
||||
tmp_0 += stage_arr [cnt ]._mem4;
|
||||
tmp_0 *= stage_arr [cnt ]._coef4;
|
||||
tmp_0 -= stage_arr [cnt - 2]._mem4;
|
||||
|
||||
stage_arr [cnt - 2]._mem4 = spl_0;
|
||||
stage_arr [cnt - 1]._mem4 = spl_1;
|
||||
stage_arr [cnt ]._mem4 = tmp_0;
|
||||
|
||||
spl_0 = tmp_0;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void StageProc4Neon <0>::process_sample_neg (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2;
|
||||
|
||||
stage_arr [cnt - 2]._mem4 = spl_0;
|
||||
stage_arr [cnt - 1]._mem4 = spl_1;
|
||||
}
|
||||
|
||||
template <int REMAINING>
|
||||
void StageProc4Neon <REMAINING>::process_sample_neg (const int nbr_coefs, float32x4_t &spl_0, float32x4_t &spl_1, StageDataNeon *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - REMAINING;
|
||||
|
||||
float32x4_t tmp_0 = spl_0;
|
||||
tmp_0 += stage_arr [cnt ]._mem4;
|
||||
tmp_0 *= stage_arr [cnt ]._coef4;
|
||||
tmp_0 -= stage_arr [cnt - 2]._mem4;
|
||||
|
||||
float32x4_t tmp_1 = spl_1;
|
||||
tmp_1 += stage_arr [cnt + 1]._mem4;
|
||||
tmp_1 *= stage_arr [cnt + 1]._coef4;
|
||||
tmp_1 -= stage_arr [cnt - 1]._mem4;
|
||||
|
||||
stage_arr [cnt - 2]._mem4 = spl_0;
|
||||
stage_arr [cnt - 1]._mem4 = spl_1;
|
||||
|
||||
spl_0 = tmp_0;
|
||||
spl_1 = tmp_1;
|
||||
|
||||
StageProc4Neon <REMAINING - 2>::process_sample_neg (
|
||||
nbr_coefs,
|
||||
spl_0,
|
||||
spl_1,
|
||||
stage_arr
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageProc4Neon_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
102
src/external/hiir/StageProc4Sse.h
vendored
Normal file
102
src/external/hiir/StageProc4Sse.h
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageProc4Sse.h
|
||||
Author: Laurent de Soras, 2015
|
||||
|
||||
Template parameters:
|
||||
- REMAINING: Number of remaining coefficients to process, >= 0
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_StageProc4Sse_HEADER_INCLUDED)
|
||||
#define hiir_StageProc4Sse_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma once
|
||||
#pragma warning (4 : 4250) // "Inherits via dominance."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/def.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
class StageDataSse;
|
||||
|
||||
template <int REMAINING>
|
||||
class StageProc4Sse
|
||||
{
|
||||
|
||||
static_assert ((REMAINING >= 0), "REMAINING must be >= 0");
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
static hiir_FORCEINLINE void
|
||||
process_sample_pos (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr);
|
||||
static hiir_FORCEINLINE void
|
||||
process_sample_neg (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr);
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
StageProc4Sse ();
|
||||
StageProc4Sse (const StageProc4Sse <REMAINING> &other);
|
||||
StageProc4Sse <REMAINING> &
|
||||
operator = (const StageProc4Sse <REMAINING> &other);
|
||||
bool operator == (const StageProc4Sse <REMAINING> &other);
|
||||
bool operator != (const StageProc4Sse <REMAINING> &other);
|
||||
|
||||
}; // class StageProc4Sse
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#include "hiir/StageProc4Sse.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageProc4Sse_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
205
src/external/hiir/StageProc4Sse.hpp
vendored
Normal file
205
src/external/hiir/StageProc4Sse.hpp
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageProc4Sse.hpp
|
||||
Author: Laurent de Soras, 2015
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if defined (hiir_StageProc4Sse_CURRENT_CODEHEADER)
|
||||
#error Recursive inclusion of StageProc4Sse code header.
|
||||
#endif
|
||||
#define hiir_StageProc4Sse_CURRENT_CODEHEADER
|
||||
|
||||
#if ! defined (hiir_StageProc4Sse_CODEHEADER_INCLUDED)
|
||||
#define hiir_StageProc4Sse_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/StageDataSse.h"
|
||||
|
||||
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma inline_depth (255)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProc4Sse <1>::process_sample_pos (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - 1;
|
||||
|
||||
const __m128 tmp_0 = _mm_add_ps (
|
||||
_mm_mul_ps (
|
||||
_mm_sub_ps (spl_0, _mm_load_ps (stage_arr [cnt ]._mem)),
|
||||
_mm_load_ps (stage_arr [cnt ]._coef)
|
||||
),
|
||||
_mm_load_ps (stage_arr [cnt - 2]._mem)
|
||||
);
|
||||
|
||||
_mm_store_ps (stage_arr [cnt - 2]._mem, spl_0);
|
||||
_mm_store_ps (stage_arr [cnt - 1]._mem, spl_1);
|
||||
_mm_store_ps (stage_arr [cnt ]._mem, tmp_0);
|
||||
|
||||
spl_0 = tmp_0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProc4Sse <0>::process_sample_pos (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2;
|
||||
|
||||
_mm_store_ps (stage_arr [cnt - 2]._mem, spl_0);
|
||||
_mm_store_ps (stage_arr [cnt - 1]._mem, spl_1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <int REMAINING>
|
||||
void StageProc4Sse <REMAINING>::process_sample_pos (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - REMAINING;
|
||||
|
||||
const __m128 tmp_0 = _mm_add_ps (
|
||||
_mm_mul_ps (
|
||||
_mm_sub_ps (spl_0, _mm_load_ps (stage_arr [cnt ]._mem)),
|
||||
_mm_load_ps (stage_arr [cnt ]._coef)
|
||||
),
|
||||
_mm_load_ps (stage_arr [cnt - 2]._mem)
|
||||
);
|
||||
const __m128 tmp_1 = _mm_add_ps (
|
||||
_mm_mul_ps (
|
||||
_mm_sub_ps (spl_1, _mm_load_ps (stage_arr [cnt + 1]._mem)),
|
||||
_mm_load_ps (stage_arr [cnt + 1]._coef)
|
||||
),
|
||||
_mm_load_ps (stage_arr [cnt - 1]._mem)
|
||||
);
|
||||
|
||||
_mm_store_ps (stage_arr [cnt - 2]._mem, spl_0);
|
||||
_mm_store_ps (stage_arr [cnt - 1]._mem, spl_1);
|
||||
|
||||
spl_0 = tmp_0;
|
||||
spl_1 = tmp_1;
|
||||
|
||||
StageProc4Sse <REMAINING - 2>::process_sample_pos (
|
||||
nbr_coefs,
|
||||
spl_0,
|
||||
spl_1,
|
||||
stage_arr
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProc4Sse <1>::process_sample_neg (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - 1;
|
||||
|
||||
const __m128 tmp_0 = _mm_sub_ps (
|
||||
_mm_mul_ps (
|
||||
_mm_add_ps (spl_0, _mm_load_ps (stage_arr [cnt ]._mem)),
|
||||
_mm_load_ps (stage_arr [cnt ]._coef)
|
||||
),
|
||||
_mm_load_ps (stage_arr [cnt - 2]._mem)
|
||||
);
|
||||
|
||||
_mm_store_ps (stage_arr [cnt - 2]._mem, spl_0);
|
||||
_mm_store_ps (stage_arr [cnt - 1]._mem, spl_1);
|
||||
_mm_store_ps (stage_arr [cnt ]._mem, tmp_0);
|
||||
|
||||
spl_0 = tmp_0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProc4Sse <0>::process_sample_neg (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2;
|
||||
|
||||
_mm_store_ps (stage_arr [cnt - 2]._mem, spl_0);
|
||||
_mm_store_ps (stage_arr [cnt - 1]._mem, spl_1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <int REMAINING>
|
||||
void StageProc4Sse <REMAINING>::process_sample_neg (const int nbr_coefs, __m128 &spl_0, __m128 &spl_1, StageDataSse *stage_arr)
|
||||
{
|
||||
const int cnt = nbr_coefs + 2 - REMAINING;
|
||||
|
||||
const __m128 tmp_0 = _mm_sub_ps (
|
||||
_mm_mul_ps (
|
||||
_mm_add_ps (spl_0, _mm_load_ps (stage_arr [cnt ]._mem)),
|
||||
_mm_load_ps (stage_arr [cnt ]._coef)
|
||||
),
|
||||
_mm_load_ps (stage_arr [cnt - 2]._mem)
|
||||
);
|
||||
const __m128 tmp_1 = _mm_sub_ps (
|
||||
_mm_mul_ps (
|
||||
_mm_add_ps (spl_1, _mm_load_ps (stage_arr [cnt + 1]._mem)),
|
||||
_mm_load_ps (stage_arr [cnt + 1]._coef)
|
||||
),
|
||||
_mm_load_ps (stage_arr [cnt - 1]._mem)
|
||||
);
|
||||
|
||||
_mm_store_ps (stage_arr [cnt - 2]._mem, spl_0);
|
||||
_mm_store_ps (stage_arr [cnt - 1]._mem, spl_1);
|
||||
|
||||
spl_0 = tmp_0;
|
||||
spl_1 = tmp_1;
|
||||
|
||||
StageProc4Sse <REMAINING - 2>::process_sample_neg (
|
||||
nbr_coefs,
|
||||
spl_0,
|
||||
spl_1,
|
||||
stage_arr
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageProc4Sse_CODEHEADER_INCLUDED
|
||||
|
||||
#undef hiir_StageProc4Sse_CURRENT_CODEHEADER
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
98
src/external/hiir/StageProcFpu.h
vendored
Normal file
98
src/external/hiir/StageProcFpu.h
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageProcFpu.h
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
Template parameters:
|
||||
- REMAINING: Number of remaining coefficients to process, >= 0
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_StageProc_HEADER_INCLUDED)
|
||||
#define hiir_StageProc_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma once
|
||||
#pragma warning (4 : 4250) // "Inherits via dominance."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/def.h"
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
template <int REMAINING>
|
||||
class StageProcFpu
|
||||
{
|
||||
|
||||
static_assert ((REMAINING >= 0), "REMAINING must be >= 0");
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
static hiir_FORCEINLINE void
|
||||
process_sample_pos (const int nbr_coefs, float &spl_0, float &spl_1, const float coef [], float x [], float y []);
|
||||
static hiir_FORCEINLINE void
|
||||
process_sample_neg (const int nbr_coefs, float &spl_0, float &spl_1, const float coef [], float x [], float y []);
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
StageProcFpu ();
|
||||
StageProcFpu (const StageProcFpu <REMAINING> &other);
|
||||
StageProcFpu <REMAINING> &
|
||||
operator = (const StageProcFpu <REMAINING> &other);
|
||||
bool operator == (const StageProcFpu <REMAINING> &other);
|
||||
bool operator != (const StageProcFpu <REMAINING> &other);
|
||||
|
||||
}; // class StageProcFpu
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#include "hiir/StageProcFpu.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageProc_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
165
src/external/hiir/StageProcFpu.hpp
vendored
Normal file
165
src/external/hiir/StageProcFpu.hpp
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*****************************************************************************
|
||||
|
||||
StageProcFpu.hpp
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if defined (hiir_StageProc_CURRENT_CODEHEADER)
|
||||
#error Recursive inclusion of StageProcFpu code header.
|
||||
#endif
|
||||
#define hiir_StageProc_CURRENT_CODEHEADER
|
||||
|
||||
#if ! defined (hiir_StageProc_CODEHEADER_INCLUDED)
|
||||
#define hiir_StageProc_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma inline_depth (255)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProcFpu <1>::process_sample_pos (const int nbr_coefs, float &spl_0, float &/*spl_1*/, const float coef [], float x [], float y [])
|
||||
{
|
||||
const int last = nbr_coefs - 1;
|
||||
const float temp = (spl_0 - y [last]) * coef [last] + x [last];
|
||||
x [last] = spl_0;
|
||||
y [last] = temp;
|
||||
spl_0 = temp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProcFpu <0>::process_sample_pos (const int /*nbr_coefs*/, float &/*spl_0*/, float &/*spl_1*/, const float /*coef*/ [], float /*x*/ [], float /*y*/ [])
|
||||
{
|
||||
// Nothing (stops recursion)
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <int REMAINING>
|
||||
void StageProcFpu <REMAINING>::process_sample_pos (const int nbr_coefs, float &spl_0, float &spl_1, const float coef [], float x [], float y [])
|
||||
{
|
||||
const int cnt = nbr_coefs - REMAINING;
|
||||
|
||||
const float temp_0 =
|
||||
(spl_0 - y [cnt + 0]) * coef [cnt + 0] + x [cnt + 0];
|
||||
const float temp_1 =
|
||||
(spl_1 - y [cnt + 1]) * coef [cnt + 1] + x [cnt + 1];
|
||||
|
||||
x [cnt + 0] = spl_0;
|
||||
x [cnt + 1] = spl_1;
|
||||
|
||||
y [cnt + 0] = temp_0;
|
||||
y [cnt + 1] = temp_1;
|
||||
|
||||
spl_0 = temp_0;
|
||||
spl_1 = temp_1;
|
||||
|
||||
StageProcFpu <REMAINING - 2>::process_sample_pos (
|
||||
nbr_coefs,
|
||||
spl_0,
|
||||
spl_1,
|
||||
&coef [0],
|
||||
&x [0],
|
||||
&y [0]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProcFpu <1>::process_sample_neg (const int nbr_coefs, float &spl_0, float &/*spl_1*/, const float coef [], float x [], float y [])
|
||||
{
|
||||
const int last = nbr_coefs - 1;
|
||||
const float temp = (spl_0 + y [last]) * coef [last] - x [last];
|
||||
x [last] = spl_0;
|
||||
y [last] = temp;
|
||||
spl_0 = temp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
hiir_FORCEINLINE void StageProcFpu <0>::process_sample_neg (const int /*nbr_coefs*/, float &/*spl_0*/, float &/*spl_1*/, const float /*coef*/ [], float /*x*/ [], float /*y*/ [])
|
||||
{
|
||||
// Nothing (stops recursion)
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <int REMAINING>
|
||||
void StageProcFpu <REMAINING>::process_sample_neg (const int nbr_coefs, float &spl_0, float &spl_1, const float coef [], float x [], float y [])
|
||||
{
|
||||
const int cnt = nbr_coefs - REMAINING;
|
||||
|
||||
const float temp_0 =
|
||||
(spl_0 + y [cnt + 0]) * coef [cnt + 0] - x [cnt + 0];
|
||||
const float temp_1 =
|
||||
(spl_1 + y [cnt + 1]) * coef [cnt + 1] - x [cnt + 1];
|
||||
|
||||
x [cnt + 0] = spl_0;
|
||||
x [cnt + 1] = spl_1;
|
||||
|
||||
y [cnt + 0] = temp_0;
|
||||
y [cnt + 1] = temp_1;
|
||||
|
||||
spl_0 = temp_0;
|
||||
spl_1 = temp_1;
|
||||
|
||||
StageProcFpu <REMAINING - 2>::process_sample_neg (
|
||||
nbr_coefs,
|
||||
spl_0,
|
||||
spl_1,
|
||||
&coef [0],
|
||||
&x [0],
|
||||
&y [0]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_StageProc_CODEHEADER_INCLUDED
|
||||
|
||||
#undef hiir_StageProc_CURRENT_CODEHEADER
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
114
src/external/hiir/Upsampler2x4Neon.h
vendored
Normal file
114
src/external/hiir/Upsampler2x4Neon.h
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Upsampler2x4Neon.h
|
||||
Author: Laurent de Soras, 2016
|
||||
|
||||
Upsamples vectors of 4 float by a factor 2 the input signal, using the NEON
|
||||
instruction set.
|
||||
|
||||
This object must be aligned on a 16-byte boundary!
|
||||
|
||||
Template parameters:
|
||||
- NC: number of coefficients, > 0
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#if ! defined (hiir_Upsampler2x4Neon_HEADER_INCLUDED)
|
||||
#define hiir_Upsampler2x4Neon_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma warning (4 : 4250)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/def.h"
|
||||
#include "hiir/StageDataNeon.h"
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
template <int NC>
|
||||
class Upsampler2x4Neon
|
||||
{
|
||||
|
||||
static_assert ((NC > 0), "Number of coefficient must be positive.");
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
enum { NBR_COEFS = NC };
|
||||
|
||||
Upsampler2x4Neon ();
|
||||
|
||||
void set_coefs (const double coef_arr [NBR_COEFS]);
|
||||
hiir_FORCEINLINE void
|
||||
process_sample (float32x4_t &out_0, float32x4_t &out_1, float32x4_t input);
|
||||
void process_block (float out_ptr [], const float in_ptr [], long nbr_spl);
|
||||
void clear_buffers ();
|
||||
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
typedef std::array <StageDataNeon, NBR_COEFS + 2> Filter; // Stages 0 and 1 contain only input memories
|
||||
|
||||
Filter _filter; // Should be the first member (thus easier to align)
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
bool operator == (const Upsampler2x4Neon <NC> &other) const = delete;
|
||||
bool operator != (const Upsampler2x4Neon <NC> &other) const = delete;
|
||||
|
||||
}; // class Upsampler2x4Neon
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#include "hiir/Upsampler2x4Neon.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_Upsampler2x4Neon_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
205
src/external/hiir/Upsampler2x4Neon.hpp
vendored
Normal file
205
src/external/hiir/Upsampler2x4Neon.hpp
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Upsampler2x4Neon.hpp
|
||||
Author: Laurent de Soras, 2016
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_Upsampler2x4Neon_CODEHEADER_INCLUDED)
|
||||
#define hiir_Upsampler2x4Neon_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/StageProc4Neon.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: ctor
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
Upsampler2x4Neon <NC>::Upsampler2x4Neon ()
|
||||
: _filter ()
|
||||
{
|
||||
for (int i = 0; i < NBR_COEFS + 2; ++i)
|
||||
{
|
||||
_filter [i]._coef4 = vdupq_n_f32 (0);
|
||||
}
|
||||
|
||||
clear_buffers ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: set_coefs
|
||||
Description:
|
||||
Sets filter coefficients. Generate them with the PolyphaseIir2Designer
|
||||
class.
|
||||
Call this function before doing any processing.
|
||||
Input parameters:
|
||||
- coef_arr: Array of coefficients. There should be as many coefficients as
|
||||
mentioned in the class template parameter.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Neon <NC>::set_coefs (const double coef_arr [NBR_COEFS])
|
||||
{
|
||||
assert (coef_arr != 0);
|
||||
|
||||
for (int i = 0; i < NBR_COEFS; ++i)
|
||||
{
|
||||
_filter [i + 2]._coef4 = vdupq_n_f32 (float (coef_arr [i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: process_sample
|
||||
Description:
|
||||
Upsamples (x2) the input vector, generating two output vectors.
|
||||
Input parameters:
|
||||
- input: The input vector.
|
||||
Output parameters:
|
||||
- out_0: First output vector.
|
||||
- out_1: Second output vector.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Neon <NC>::process_sample (float32x4_t &out_0, float32x4_t &out_1, float32x4_t input)
|
||||
{
|
||||
float32x4_t even = input;
|
||||
float32x4_t odd = input;
|
||||
StageProc4Neon <NBR_COEFS>::process_sample_pos (
|
||||
NBR_COEFS,
|
||||
even,
|
||||
odd,
|
||||
&_filter [0]
|
||||
);
|
||||
out_0 = even;
|
||||
out_1 = odd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: process_block
|
||||
Description:
|
||||
Upsamples (x2) the input vector block.
|
||||
Input and output blocks may overlap, see assert() for details.
|
||||
Input parameters:
|
||||
- in_ptr: Input array, containing nbr_spl vector.
|
||||
No alignment constraint.
|
||||
- nbr_spl: Number of input vectors to process, > 0
|
||||
Output parameters:
|
||||
- out_0_ptr: Output vector array, capacity: nbr_spl * 2 vectors.
|
||||
No alignment constraint.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Neon <NC>::process_block (float out_ptr [], const float in_ptr [], long nbr_spl)
|
||||
{
|
||||
assert (out_ptr != 0);
|
||||
assert (in_ptr != 0);
|
||||
assert (out_ptr >= in_ptr + nbr_spl * 4 || in_ptr >= out_ptr + nbr_spl * 4);
|
||||
assert (nbr_spl > 0);
|
||||
|
||||
long pos = 0;
|
||||
do
|
||||
{
|
||||
const float32x4_t src = vreinterpretq_f32_u8 (
|
||||
vld1q_u8 (reinterpret_cast <const uint8_t *> (in_ptr + pos * 4))
|
||||
);
|
||||
float32x4_t dst_0;
|
||||
float32x4_t dst_1;
|
||||
process_sample (dst_0, dst_1, src);
|
||||
vst1q_u8 (
|
||||
reinterpret_cast <uint8_t *> (out_ptr + pos * 8 ),
|
||||
vreinterpretq_u8_f32 (dst_0)
|
||||
);
|
||||
vst1q_u8 (
|
||||
reinterpret_cast <uint8_t *> (out_ptr + pos * 8 + 4),
|
||||
vreinterpretq_u8_f32 (dst_1)
|
||||
);
|
||||
++ pos;
|
||||
}
|
||||
while (pos < nbr_spl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: clear_buffers
|
||||
Description:
|
||||
Clears filter memory, as if it processed silence since an infinite amount
|
||||
of time.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Neon <NC>::clear_buffers ()
|
||||
{
|
||||
for (int i = 0; i < NBR_COEFS + 2; ++i)
|
||||
{
|
||||
_filter [i]._mem4 = vdupq_n_f32 (0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_Upsampler2x4Neon_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
113
src/external/hiir/Upsampler2x4Sse.h
vendored
Normal file
113
src/external/hiir/Upsampler2x4Sse.h
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Upsampler2x4Sse.h
|
||||
Author: Laurent de Soras, 2015
|
||||
|
||||
Upsamples vectors of 4 float by a factor 2 the input signal, using the SSE
|
||||
instruction set.
|
||||
|
||||
This object must be aligned on a 16-byte boundary!
|
||||
|
||||
Template parameters:
|
||||
- NC: number of coefficients, > 0
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#if ! defined (hiir_Upsampler2x4Sse_HEADER_INCLUDED)
|
||||
#define hiir_Upsampler2x4Sse_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma warning (4 : 4250)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/def.h"
|
||||
#include "hiir/StageDataSse.h"
|
||||
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#include <array>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
template <int NC>
|
||||
class Upsampler2x4Sse
|
||||
{
|
||||
|
||||
static_assert ((NC > 0), "Number of coefficient must be positive.");
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
enum { NBR_COEFS = NC };
|
||||
|
||||
Upsampler2x4Sse ();
|
||||
|
||||
void set_coefs (const double coef_arr [NBR_COEFS]);
|
||||
hiir_FORCEINLINE void
|
||||
process_sample (__m128 &out_0, __m128 &out_1, __m128 input);
|
||||
void process_block (float out_ptr [], const float in_ptr [], long nbr_spl);
|
||||
void clear_buffers ();
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
typedef std::array <StageDataSse, NBR_COEFS + 2> Filter; // Stages 0 and 1 contain only input memories
|
||||
|
||||
Filter _filter; // Should be the first member (thus easier to align)
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
bool operator == (const Upsampler2x4Sse <NC> &other) const;
|
||||
bool operator != (const Upsampler2x4Sse <NC> &other) const;
|
||||
|
||||
}; // class Upsampler2x4Sse
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#include "hiir/Upsampler2x4Sse.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_Upsampler2x4Sse_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
197
src/external/hiir/Upsampler2x4Sse.hpp
vendored
Normal file
197
src/external/hiir/Upsampler2x4Sse.hpp
vendored
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Upsampler2x4Sse.hpp
|
||||
Author: Laurent de Soras, 2015
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_Upsampler2x4Sse_CODEHEADER_INCLUDED)
|
||||
#define hiir_Upsampler2x4Sse_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/StageProc4Sse.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: ctor
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
Upsampler2x4Sse <NC>::Upsampler2x4Sse ()
|
||||
: _filter ()
|
||||
{
|
||||
for (int i = 0; i < NBR_COEFS + 2; ++i)
|
||||
{
|
||||
_mm_store_ps (_filter [i]._coef, _mm_setzero_ps ());
|
||||
}
|
||||
|
||||
clear_buffers ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: set_coefs
|
||||
Description:
|
||||
Sets filter coefficients. Generate them with the PolyphaseIir2Designer
|
||||
class.
|
||||
Call this function before doing any processing.
|
||||
Input parameters:
|
||||
- coef_arr: Array of coefficients. There should be as many coefficients as
|
||||
mentioned in the class template parameter.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Sse <NC>::set_coefs (const double coef_arr [NBR_COEFS])
|
||||
{
|
||||
assert (coef_arr != 0);
|
||||
|
||||
for (int i = 0; i < NBR_COEFS; ++i)
|
||||
{
|
||||
_mm_store_ps (_filter [i + 2]._coef, _mm_set1_ps (float (coef_arr [i])));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: process_sample
|
||||
Description:
|
||||
Upsamples (x2) the input vector, generating two output vectors.
|
||||
Input parameters:
|
||||
- input: The input vector.
|
||||
Output parameters:
|
||||
- out_0: First output vector.
|
||||
- out_1: Second output vector.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Sse <NC>::process_sample (__m128 &out_0, __m128 &out_1, __m128 input)
|
||||
{
|
||||
__m128 even = input;
|
||||
__m128 odd = input;
|
||||
StageProc4Sse <NBR_COEFS>::process_sample_pos (
|
||||
NBR_COEFS,
|
||||
even,
|
||||
odd,
|
||||
&_filter [0]
|
||||
);
|
||||
out_0 = even;
|
||||
out_1 = odd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: process_block
|
||||
Description:
|
||||
Upsamples (x2) the input vector block.
|
||||
Input and output blocks may overlap, see assert() for details.
|
||||
Input parameters:
|
||||
- in_ptr: Input array, containing nbr_spl vector.
|
||||
No alignment constraint.
|
||||
- nbr_spl: Number of input vectors to process, > 0
|
||||
Output parameters:
|
||||
- out_0_ptr: Output vector array, capacity: nbr_spl * 2 vectors.
|
||||
No alignment constraint.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Sse <NC>::process_block (float out_ptr [], const float in_ptr [], long nbr_spl)
|
||||
{
|
||||
assert (out_ptr != 0);
|
||||
assert (in_ptr != 0);
|
||||
assert (out_ptr >= in_ptr + nbr_spl * 4 || in_ptr >= out_ptr + nbr_spl * 4);
|
||||
assert (nbr_spl > 0);
|
||||
|
||||
long pos = 0;
|
||||
do
|
||||
{
|
||||
__m128 dst_0;
|
||||
__m128 dst_1;
|
||||
const __m128 src = _mm_loadu_ps (in_ptr + pos * 4);
|
||||
process_sample (dst_0, dst_1, src);
|
||||
_mm_storeu_ps (out_ptr + pos * 8 , dst_0);
|
||||
_mm_storeu_ps (out_ptr + pos * 8 + 4, dst_1);
|
||||
++ pos;
|
||||
}
|
||||
while (pos < nbr_spl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: clear_buffers
|
||||
Description:
|
||||
Clears filter memory, as if it processed silence since an infinite amount
|
||||
of time.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2x4Sse <NC>::clear_buffers ()
|
||||
{
|
||||
for (int i = 0; i < NBR_COEFS + 2; ++i)
|
||||
{
|
||||
_mm_store_ps (_filter [i]._mem, _mm_setzero_ps ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_Upsampler2x4Sse_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
106
src/external/hiir/Upsampler2xFpu.h
vendored
Normal file
106
src/external/hiir/Upsampler2xFpu.h
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Upsampler2xFpu.h
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
Upsamples by a factor 2 the input signal, using FPU.
|
||||
|
||||
Template parameters:
|
||||
- NC: number of coefficients, > 0
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_Upsampler2xFpu_HEADER_INCLUDED)
|
||||
#define hiir_Upsampler2xFpu_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma once
|
||||
#pragma warning (4 : 4250) // "Inherits via dominance."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include <array>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
template <int NC>
|
||||
class Upsampler2xFpu
|
||||
{
|
||||
|
||||
static_assert ((NC > 0), "Number of coefficient must be positive.");
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
public:
|
||||
|
||||
enum { NBR_COEFS = NC };
|
||||
|
||||
Upsampler2xFpu ();
|
||||
|
||||
void set_coefs (const double coef_arr [NBR_COEFS]);
|
||||
inline void process_sample (float &out_0, float &out_1, float input);
|
||||
void process_block (float out_ptr [], const float in_ptr [], long nbr_spl);
|
||||
void clear_buffers ();
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
typedef std::array <float, NBR_COEFS> HyperGluar;
|
||||
|
||||
HyperGluar _coef;
|
||||
HyperGluar _x;
|
||||
HyperGluar _y;
|
||||
|
||||
|
||||
|
||||
/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
private:
|
||||
|
||||
bool operator == (const Upsampler2xFpu <NC> &other);
|
||||
bool operator != (const Upsampler2xFpu <NC> &other);
|
||||
|
||||
}; // class Upsampler2xFpu
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#include "hiir/Upsampler2xFpu.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_Upsampler2xFpu_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
205
src/external/hiir/Upsampler2xFpu.hpp
vendored
Normal file
205
src/external/hiir/Upsampler2xFpu.hpp
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Upsampler2xFpu.hpp
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if defined (hiir_Upsampler2xFpu_CURRENT_CODEHEADER)
|
||||
#error Recursive inclusion of Upsampler2xFpu code header.
|
||||
#endif
|
||||
#define hiir_Upsampler2xFpu_CURRENT_CODEHEADER
|
||||
|
||||
#if ! defined (hiir_Upsampler2xFpu_CODEHEADER_INCLUDED)
|
||||
#define hiir_Upsampler2xFpu_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include "hiir/StageProcFpu.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: ctor
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
Upsampler2xFpu <NC>::Upsampler2xFpu ()
|
||||
: _coef ()
|
||||
, _x ()
|
||||
, _y ()
|
||||
{
|
||||
for (int i = 0; i < NBR_COEFS; ++i)
|
||||
{
|
||||
_coef [i] = 0;
|
||||
}
|
||||
clear_buffers ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: set_coefs
|
||||
Description:
|
||||
Sets filter coefficients. Generate them with the PolyphaseIir2Designer
|
||||
class.
|
||||
Call this function before doing any processing.
|
||||
Input parameters:
|
||||
- coef_arr: Array of coefficients. There should be as many coefficients as
|
||||
mentioned in the class template parameter.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2xFpu <NC>::set_coefs (const double coef_arr [NBR_COEFS])
|
||||
{
|
||||
assert (coef_arr != 0);
|
||||
|
||||
for (int i = 0; i < NBR_COEFS; ++i)
|
||||
{
|
||||
_coef [i] = float (coef_arr [i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: process_sample
|
||||
Description:
|
||||
Upsamples (x2) the input sample, generating two output samples.
|
||||
Input parameters:
|
||||
- input: The input sample.
|
||||
Output parameters:
|
||||
- out_0: First output sample.
|
||||
- out_1: Second output sample.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2xFpu <NC>::process_sample (float &out_0, float &out_1, float input)
|
||||
{
|
||||
float even = input;
|
||||
float odd = input;
|
||||
StageProcFpu <NBR_COEFS>::process_sample_pos (
|
||||
NBR_COEFS,
|
||||
even,
|
||||
odd,
|
||||
&_coef [0],
|
||||
&_x [0],
|
||||
&_y [0]
|
||||
);
|
||||
out_0 = even;
|
||||
out_1 = odd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: process_block
|
||||
Description:
|
||||
Upsamples (x2) the input sample block.
|
||||
Input and output blocks may overlap, see assert() for details.
|
||||
Input parameters:
|
||||
- in_ptr: Input array, containing nbr_spl samples.
|
||||
- nbr_spl: Number of input samples to process, > 0
|
||||
Output parameters:
|
||||
- out_0_ptr: Output sample array, capacity: nbr_spl * 2 samples.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2xFpu <NC>::process_block (float out_ptr [], const float in_ptr [], long nbr_spl)
|
||||
{
|
||||
assert (out_ptr != 0);
|
||||
assert (in_ptr != 0);
|
||||
assert (out_ptr >= in_ptr + nbr_spl || in_ptr >= out_ptr + nbr_spl);
|
||||
assert (nbr_spl > 0);
|
||||
|
||||
long pos = 0;
|
||||
do
|
||||
{
|
||||
process_sample (
|
||||
out_ptr [pos * 2 ],
|
||||
out_ptr [pos * 2 + 1],
|
||||
in_ptr [pos]
|
||||
);
|
||||
++ pos;
|
||||
}
|
||||
while (pos < nbr_spl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Name: clear_buffers
|
||||
Description:
|
||||
Clears filter memory, as if it processed silence since an infinite amount
|
||||
of time.
|
||||
Throws: Nothing
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
template <int NC>
|
||||
void Upsampler2xFpu <NC>::clear_buffers ()
|
||||
{
|
||||
for (int i = 0; i < NBR_COEFS; ++i)
|
||||
{
|
||||
_x [i] = 0;
|
||||
_y [i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*\\\ PROTECTED \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_Upsampler2xFpu_CODEHEADER_INCLUDED
|
||||
|
||||
#undef hiir_Upsampler2xFpu_CURRENT_CODEHEADER
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
61
src/external/hiir/def.h
vendored
Normal file
61
src/external/hiir/def.h
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*****************************************************************************
|
||||
|
||||
def.h
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_def_HEADER_INCLUDED)
|
||||
#define hiir_def_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma once
|
||||
#pragma warning (4 : 4250) // "Inherits via dominance."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
const double PI = 3.1415926535897932384626433832795;
|
||||
|
||||
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
|
||||
#define hiir_FORCEINLINE __forceinline
|
||||
|
||||
#else
|
||||
|
||||
#define hiir_FORCEINLINE inline
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_def_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
57
src/external/hiir/fnc.h
vendored
Normal file
57
src/external/hiir/fnc.h
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*****************************************************************************
|
||||
|
||||
fnc.h
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if ! defined (hiir_fnc_HEADER_INCLUDED)
|
||||
#define hiir_fnc_HEADER_INCLUDED
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#pragma once
|
||||
#pragma warning (4 : 4250) // "Inherits via dominance."
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
inline int round_int (double x);
|
||||
inline int ceil_int (double x);
|
||||
|
||||
template <class T>
|
||||
T ipowp (T x, long n);
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#include "hiir/fnc.hpp"
|
||||
|
||||
|
||||
|
||||
#endif // hiir_fnc_HEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
85
src/external/hiir/fnc.hpp
vendored
Normal file
85
src/external/hiir/fnc.hpp
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*****************************************************************************
|
||||
|
||||
fnc.hpp
|
||||
Author: Laurent de Soras, 2005
|
||||
|
||||
--- Legal stuff ---
|
||||
|
||||
This program is free software. It comes without any warranty, to
|
||||
the extent permitted by applicable law. You can redistribute it
|
||||
and/or modify it under the terms of the Do What The Fuck You Want
|
||||
To Public License, Version 2, as published by Sam Hocevar. See
|
||||
http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
|
||||
*Tab=3***********************************************************************/
|
||||
|
||||
|
||||
|
||||
#if defined (hiir_fnc_CURRENT_CODEHEADER)
|
||||
#error Recursive inclusion of fnc code header.
|
||||
#endif
|
||||
#define hiir_fnc_CURRENT_CODEHEADER
|
||||
|
||||
#if ! defined (hiir_fnc_CODEHEADER_INCLUDED)
|
||||
#define hiir_fnc_CODEHEADER_INCLUDED
|
||||
|
||||
|
||||
|
||||
/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
|
||||
namespace hiir
|
||||
{
|
||||
|
||||
|
||||
|
||||
int round_int (double x)
|
||||
{
|
||||
return int (floor (x + 0.5));
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ceil_int (double x)
|
||||
{
|
||||
return int (ceil (x));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T>
|
||||
T ipowp (T x, long n)
|
||||
{
|
||||
assert (n >= 0);
|
||||
|
||||
T z (1);
|
||||
while (n != 0)
|
||||
{
|
||||
if ((n & 1) != 0)
|
||||
{
|
||||
z *= x;
|
||||
}
|
||||
n >>= 1;
|
||||
x *= x;
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace hiir
|
||||
|
||||
|
||||
|
||||
#endif // hiir_fnc_CODEHEADER_INCLUDED
|
||||
|
||||
#undef hiir_fnc_CURRENT_CODEHEADER
|
||||
|
||||
|
||||
|
||||
/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
|
||||
318
src/sfizz.h
318
src/sfizz.h
|
|
@ -33,220 +33,278 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
typedef struct sfizz_synth_t sfizz_synth_t;
|
||||
typedef enum {
|
||||
SFIZZ_OVERSAMPLING_X1 = 1,
|
||||
SFIZZ_OVERSAMPLING_X2 = 2,
|
||||
SFIZZ_OVERSAMPLING_X4 = 4,
|
||||
SFIZZ_OVERSAMPLING_X8 = 8
|
||||
} sfizz_oversampling_factor_t;
|
||||
|
||||
/**
|
||||
* @brief Creates a sfizz synth.
|
||||
* This object has to be freed by the caller using sfizz_free().
|
||||
*
|
||||
* @return sfizz_synth_t*
|
||||
* @brief Creates a sfizz synth. This object has to be freed by the caller
|
||||
* using sfizz_free().
|
||||
*
|
||||
* @return sfizz_synth_t*
|
||||
*/
|
||||
sfizz_synth_t* sfizz_create_synth();
|
||||
/**
|
||||
* @brief Frees an existing sfizz synth.
|
||||
*
|
||||
* @param synth The synth to destroy
|
||||
* @brief Frees an existing sfizz synth.
|
||||
*
|
||||
* @param synth The synth to destroy
|
||||
*/
|
||||
void sfizz_free(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Loads an SFZ file.
|
||||
* The file path can be absolute or relative. All file operations for this SFZ file will be relative to the parent directory of the SFZ file.
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @param path A null-terminated string representing a path to an SFZ file.
|
||||
* @return true when file loading went OK.
|
||||
* @return false if some error occured while loading.
|
||||
* @brief Loads an SFZ file. The file path can be absolute or relative. All
|
||||
* file operations for this SFZ file will be relative to the parent
|
||||
* directory of the SFZ file.
|
||||
*
|
||||
* @param synth The sfizz synth.
|
||||
* @param path A null-terminated string representing a path to an SFZ
|
||||
* file.
|
||||
*
|
||||
* @return true when file loading went OK.
|
||||
* @return false if some error occured while loading.
|
||||
*/
|
||||
bool sfizz_load_file(sfizz_synth_t* synth, const char* path);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of regions in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth
|
||||
* @return int the number of regions
|
||||
* @brief Returns the number of regions in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of regions
|
||||
*/
|
||||
int sfizz_get_num_regions(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of groups in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth
|
||||
* @return int the number of groups
|
||||
* @brief Returns the number of groups in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of groups
|
||||
*/
|
||||
int sfizz_get_num_groups(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of masters in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth
|
||||
* @return int the number of masters
|
||||
* @brief Returns the number of masters in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of masters
|
||||
*/
|
||||
int sfizz_get_num_masters(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of curves in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth
|
||||
* @return int the number of curves
|
||||
* @brief Returns the number of curves in the currently loaded SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of curves
|
||||
*/
|
||||
int sfizz_get_num_curves(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of preloaded samples for the current SFZ file.
|
||||
*
|
||||
* @param synth
|
||||
* @return int the number of preloaded samples
|
||||
* @brief Returns the number of preloaded samples for the current SFZ file.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of preloaded samples
|
||||
*/
|
||||
int sfizz_get_num_preloaded_samples(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Returns the number of active voices.
|
||||
* Note that this function is a basic indicator and does not aim to be perfect.
|
||||
* In particular, it runs on the calling thread so voices may well start or stop
|
||||
* while the function is checking which voice is active.
|
||||
*
|
||||
* @param synth
|
||||
* @return int the number of playing voices
|
||||
* @brief Returns the number of active voices. Note that this function is a
|
||||
* basic indicator and does not aim to be perfect. In particular, it
|
||||
* runs on the calling thread so voices may well start or stop while
|
||||
* the function is checking which voice is active.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return int the number of playing voices
|
||||
*/
|
||||
int sfizz_get_num_active_voices(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Sets the expected number of samples per block.
|
||||
* If unsure, give an upper bound since right now ugly things may happen if you
|
||||
* go over this number.
|
||||
*
|
||||
* @param synth
|
||||
* @param samples_per_block the number of samples per block
|
||||
* @brief Sets the expected number of samples per block. If unsure, give an
|
||||
* upper bound since right now ugly things may happen if you go over
|
||||
* this number.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param samples_per_block the number of samples per block
|
||||
*/
|
||||
void sfizz_set_samples_per_block(sfizz_synth_t* synth, int samples_per_block);
|
||||
/**
|
||||
* @brief Sets the sample rate for the synth.
|
||||
* This is the output sample rate. This setting does not affect the internal
|
||||
* processing.
|
||||
*
|
||||
* @param synth
|
||||
* @param sample_rate the sample rate
|
||||
* @brief Sets the sample rate for the synth. This is the output sample
|
||||
* rate. This setting does not affect the internal processing.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param sample_rate the sample rate
|
||||
*/
|
||||
void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample_rate);
|
||||
|
||||
/**
|
||||
* @brief Send a note on event to the synth.
|
||||
* As with all MIDI events, this needs to happen before the call to sfizz_render_block
|
||||
* in each block.
|
||||
*
|
||||
* @param synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
* @brief Send a note on event to the synth. As with all MIDI events, this
|
||||
* needs to happen before the call to sfizz_render_block in each
|
||||
* block.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
*/
|
||||
void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int channel, int note_number, char velocity);
|
||||
|
||||
/**
|
||||
* @brief Send a note off event to the synth.
|
||||
* As with all MIDI events, this needs to happen before the call to sfizz_render_block
|
||||
* in each block. As per the SFZ spec the velocity of note-off events is usually replaced
|
||||
* by the note-on velocity.
|
||||
*
|
||||
* @param synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
* @brief Send a note off event to the synth. As with all MIDI events, this
|
||||
* needs to happen before the call to sfizz_render_block in each
|
||||
* block. As per the SFZ spec the velocity of note-off events is
|
||||
* usually replaced by the note-on velocity.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
*/
|
||||
void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int channel, int note_number, char velocity);
|
||||
|
||||
/**
|
||||
* @brief Send a CC event to the synth.
|
||||
* As with all MIDI events, this needs to happen before the call to sfizz_render_block
|
||||
* in each block.
|
||||
*
|
||||
* @param synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param cc_number the MIDI CC number
|
||||
* @param cc_value the MIDI CC value
|
||||
* @brief Send a CC event to the synth. As with all MIDI events, this needs
|
||||
* to happen before the call to sfizz_render_block in each block.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param cc_number the MIDI CC number
|
||||
* @param cc_value the MIDI CC value
|
||||
*/
|
||||
void sfizz_send_cc(sfizz_synth_t* synth, int delay, int channel, int cc_number, char cc_value);
|
||||
/**
|
||||
* @brief Send a pitch wheel event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth
|
||||
* @param delay
|
||||
* @param channel
|
||||
* @param pitch
|
||||
* @brief Send a pitch wheel event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay The delay
|
||||
* @param channel The channel
|
||||
* @param pitch The pitch
|
||||
*/
|
||||
void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int channel, int pitch);
|
||||
|
||||
/**
|
||||
* @brief Send an aftertouch event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth
|
||||
* @param delay
|
||||
* @param channel
|
||||
* @param aftertouch
|
||||
*
|
||||
* @param synth
|
||||
* @param delay
|
||||
* @param channel
|
||||
* @param aftertouch
|
||||
*/
|
||||
void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, int channel, char aftertouch);
|
||||
|
||||
/**
|
||||
* @brief Send a tempo event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth
|
||||
* @param delay
|
||||
* @param seconds_per_quarter
|
||||
* @brief Send a tempo event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay The delay
|
||||
* @param seconds_per_quarter The seconds per quarter
|
||||
*/
|
||||
void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float seconds_per_quarter);
|
||||
|
||||
/**
|
||||
* @brief Render a block audio data into a stereo channel.
|
||||
* No other channel configuration is supported. The synth will gracefully ignore your request if you
|
||||
* provide a value. You should pass all the relevant events for the block (midi notes, CCs, ...) before
|
||||
* rendering each block. The synth will memorize the inputs and render sample accurates envelopes
|
||||
* depending on the input events passed to it.
|
||||
*
|
||||
* @param synth
|
||||
* @param channels pointers to the left and right channel of the output
|
||||
* @param num_channels should be equal to 2 for the time being.
|
||||
* @param num_frames number of frames to fill. This should be less than or equal to the expected
|
||||
* samples_per_block.
|
||||
* @brief Render a block audio data into a stereo channel. No other channel
|
||||
* configuration is supported. The synth will gracefully ignore your
|
||||
* request if you provide a value. You should pass all the relevant
|
||||
* events for the block (midi notes, CCs, ...) before rendering each
|
||||
* block. The synth will memorize the inputs and render sample
|
||||
* accurates envelopes depending on the input events passed to it.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param channels pointers to the left and right channel of the
|
||||
* output
|
||||
* @param num_channels should be equal to 2 for the time being.
|
||||
* @param num_frames number of frames to fill. This should be less than
|
||||
* or equal to the expected samples_per_block.
|
||||
*/
|
||||
void sfizz_render_block(sfizz_synth_t* synth, float** channels, int num_channels, int num_frames);
|
||||
|
||||
/**
|
||||
* @brief Force a memory cleanup of the samples loaded in the background.
|
||||
* This should happen automatically but if you want it done more frequently
|
||||
* this is the way to go.
|
||||
*
|
||||
* @param synth
|
||||
* @brief Get the size of the preloaded data. This returns the number of
|
||||
* floats used in the preloading buffers.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return the preloaded data size in sizeof(floats)
|
||||
*/
|
||||
void sfizz_force_garbage_collection(sfizz_synth_t* synth);
|
||||
unsigned int sfizz_get_preload_size(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Sets the size of the preloaded data in number of floats (not
|
||||
* bytes). This will disable the callbacks for the duration of the
|
||||
* load.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param[in] preload_size The preload size
|
||||
*/
|
||||
void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned int preload_size);
|
||||
|
||||
/**
|
||||
* @brief Set the global instrument volume.
|
||||
*
|
||||
* @param synth
|
||||
* @param volume the new volume
|
||||
* @brief Get the internal oversampling rate. This is the sampling rate of
|
||||
* the engine, not the output or expected rate of the calling
|
||||
* function. For the latter use the `get_sample_rate()` functions.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return The internal sample rate of the engine
|
||||
*/
|
||||
sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t* synth);
|
||||
/**
|
||||
* @brief Set the internal oversampling rate. This is the sampling rate of
|
||||
* the engine, not the output or expected rate of the calling
|
||||
* function. For the latter use the `set_sample_rate()` functions.
|
||||
*
|
||||
* Increasing this value (up to x8 oversampling) improves the
|
||||
* quality of the output at the expense of memory consumption and
|
||||
* background loading speed. The main render path still uses the
|
||||
* same linear interpolation algorithm and should not see its
|
||||
* performance decrease, but the files are oversampled upon loading
|
||||
* which increases the stress on the background loader and reduce
|
||||
* the loading speed. You can tweak the size of the preloaded data
|
||||
* to compensate for the memory increase, but the full loading will
|
||||
* need to take place anyway.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param[in] preload_size The preload size
|
||||
*
|
||||
* @return True if the oversampling factor was correct
|
||||
*/
|
||||
bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t oversampling);
|
||||
|
||||
/**
|
||||
* @brief Set the global instrument volume.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param volume the new volume
|
||||
*/
|
||||
void sfizz_set_volume(sfizz_synth_t* synth, float volume);
|
||||
|
||||
/**
|
||||
* @brief Get the global instrument volume.
|
||||
*
|
||||
* @param synth
|
||||
* @return float the instrument volume
|
||||
* @brief Get the global instrument volume.
|
||||
*
|
||||
* @param synth The synth
|
||||
*
|
||||
* @return float the instrument volume
|
||||
*/
|
||||
float sfizz_get_volume(sfizz_synth_t* synth);
|
||||
|
||||
/**
|
||||
* @brief Sets the number of voices used by the synth
|
||||
*
|
||||
* @param synth
|
||||
* @param num_voices
|
||||
* @brief Sets the number of voices used by the synth
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param num_voices The number voices
|
||||
*/
|
||||
void sfizz_set_num_voices(sfizz_synth_t* synth, int num_voices);
|
||||
/**
|
||||
* @brief Returns the number of voices
|
||||
*
|
||||
* @param synth
|
||||
* @return num_voices
|
||||
*
|
||||
* @param synth
|
||||
* @return num_voices
|
||||
*/
|
||||
int sfizz_get_num_voices(sfizz_synth_t* synth);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@
|
|||
#pragma once
|
||||
|
||||
namespace sfz {
|
||||
enum Oversampling {
|
||||
x1 = 1,
|
||||
x2 = 2,
|
||||
x4 = 4,
|
||||
x8 = 8
|
||||
};
|
||||
|
||||
namespace config {
|
||||
constexpr float defaultSampleRate { 48000 };
|
||||
|
|
@ -37,7 +43,7 @@ namespace config {
|
|||
constexpr float virtuallyZero { 0.00005f };
|
||||
constexpr float fastReleaseDuration { 0.01 };
|
||||
constexpr char defineCharacter { '$' };
|
||||
constexpr int oversamplingFactor { 2 };
|
||||
constexpr Oversampling defaultOversamplingFactor { Oversampling::x1 };
|
||||
constexpr float A440 { 440.0 };
|
||||
constexpr unsigned powerHistoryLength { 16 };
|
||||
constexpr float voiceStealingThreshold { 0.00001 };
|
||||
|
|
@ -65,5 +71,6 @@ namespace SIMDConfig {
|
|||
constexpr bool sfzInterpolationCast { true };
|
||||
constexpr bool mean { false };
|
||||
constexpr bool meanSquared { false };
|
||||
constexpr bool upsampling { false };
|
||||
}
|
||||
} // namespace sfz
|
||||
|
|
|
|||
|
|
@ -25,30 +25,80 @@
|
|||
#include "AudioBuffer.h"
|
||||
#include "Config.h"
|
||||
#include "Debug.h"
|
||||
#include "Oversampler.h"
|
||||
#include "absl/types/span.h"
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sndfile.hh>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template <class T>
|
||||
std::unique_ptr<sfz::AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, int numFrames)
|
||||
std::unique_ptr<sfz::AudioBuffer<T>> upsample2x(const sfz::AudioBuffer<T>& buffer)
|
||||
{
|
||||
auto returnedBuffer = std::make_unique<sfz::AudioBuffer<T>>(sndFile.channels(), numFrames);
|
||||
if (sndFile.channels() == 1) {
|
||||
sndFile.readf(returnedBuffer->channelWriter(0), numFrames);
|
||||
} else if (sndFile.channels() == 2) {
|
||||
auto tempReadBuffer = std::make_unique<sfz::AudioBuffer<float>>(1, 2 * numFrames);
|
||||
sndFile.readf(tempReadBuffer->channelWriter(0), numFrames);
|
||||
sfz::readInterleaved<float>(tempReadBuffer->getSpan(0), returnedBuffer->getSpan(0), returnedBuffer->getSpan(1));
|
||||
// auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
|
||||
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 2);
|
||||
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
|
||||
sfz::upsample2xStage(buffer.getConstSpan(channelIdx), outputBuffer->getSpan(channelIdx));
|
||||
}
|
||||
return returnedBuffer;
|
||||
return outputBuffer;
|
||||
}
|
||||
|
||||
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename, uint32_t offset) noexcept
|
||||
template <class T>
|
||||
std::unique_ptr<sfz::AudioBuffer<T>> upsample4x(const sfz::AudioBuffer<T>& buffer)
|
||||
{
|
||||
auto tempBuffer = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
|
||||
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 4);
|
||||
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
|
||||
sfz::upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer));
|
||||
sfz::upsample4xStage(absl::MakeConstSpan(*tempBuffer), outputBuffer->getSpan(channelIdx));
|
||||
}
|
||||
return outputBuffer;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::unique_ptr<sfz::AudioBuffer<T>> upsample8x(const sfz::AudioBuffer<T>& buffer)
|
||||
{
|
||||
auto tempBuffer2x = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 2);
|
||||
auto tempBuffer4x = std::make_unique<sfz::Buffer<T>>(buffer.getNumFrames() * 4);
|
||||
auto outputBuffer = std::make_unique<sfz::AudioBuffer<T>>(buffer.getNumChannels(), buffer.getNumFrames() * 8);
|
||||
for (int channelIdx = 0; channelIdx < buffer.getNumChannels(); channelIdx++) {
|
||||
sfz::upsample2xStage(buffer.getConstSpan(channelIdx), absl::MakeSpan(*tempBuffer2x));
|
||||
sfz::upsample4xStage(absl::MakeConstSpan(*tempBuffer2x), absl::MakeSpan(*tempBuffer4x));
|
||||
sfz::upsample8xStage(absl::MakeConstSpan(*tempBuffer4x), outputBuffer->getSpan(channelIdx));
|
||||
}
|
||||
return outputBuffer;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::unique_ptr<sfz::AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor)
|
||||
{
|
||||
auto baseBuffer = std::make_unique<sfz::AudioBuffer<T>>(sndFile.channels(), numFrames);
|
||||
if (sndFile.channels() == 1) {
|
||||
sndFile.readf(baseBuffer->channelWriter(0), numFrames);
|
||||
} else if (sndFile.channels() == 2) {
|
||||
auto tempReadBuffer = std::make_unique<sfz::AudioBuffer<T>>(1, 2 * numFrames);
|
||||
sndFile.readf(tempReadBuffer->channelWriter(0), numFrames);
|
||||
auto fileBuffer = std::make_unique<sfz::AudioBuffer<T>>(2, numFrames);
|
||||
sfz::readInterleaved<T>(tempReadBuffer->getSpan(0), baseBuffer->getSpan(0), baseBuffer->getSpan(1));
|
||||
}
|
||||
|
||||
switch (factor) {
|
||||
case sfz::Oversampling::x1:
|
||||
return baseBuffer;
|
||||
case sfz::Oversampling::x2:
|
||||
return upsample2x(*baseBuffer);
|
||||
case sfz::Oversampling::x4:
|
||||
return upsample4x(*baseBuffer);
|
||||
case sfz::Oversampling::x8:
|
||||
return upsample8x(*baseBuffer);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename) noexcept
|
||||
{
|
||||
fs::path file { rootDirectory / filename };
|
||||
if (!fs::exists(file))
|
||||
|
|
@ -63,6 +113,7 @@ absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation
|
|||
FileInformation returnedValue;
|
||||
returnedValue.end = static_cast<uint32_t>(sndFile.frames());
|
||||
returnedValue.sampleRate = static_cast<double>(sndFile.samplerate());
|
||||
returnedValue.numChannels = sndFile.channels();
|
||||
|
||||
SF_INSTRUMENT instrumentInfo;
|
||||
sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo));
|
||||
|
|
@ -71,110 +122,151 @@ absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation
|
|||
returnedValue.loopEnd = instrumentInfo.loops[0].end;
|
||||
}
|
||||
|
||||
// FIXME: Large offsets will require large preloading; is this OK in practice?
|
||||
const auto preloadedSize = [&]() {
|
||||
if (config::preloadSize == 0)
|
||||
return returnedValue.end;
|
||||
else
|
||||
return std::min(returnedValue.end, offset + static_cast<uint32_t>(config::preloadSize));
|
||||
}();
|
||||
|
||||
if (preloadedData.contains(filename)) {
|
||||
auto alreadyPreloaded = preloadedData[filename];
|
||||
if (preloadedSize > alreadyPreloaded->getNumFrames()) {
|
||||
// FIXME: Okay, ideally here you would have a double indirection so that we can update _all_ the preloaded
|
||||
// files in previous regions to account for the new offset
|
||||
//
|
||||
// Before this next command, some old regions and the file pool hold a shared pointer to the same audio buffer.
|
||||
// This audio buffer is OK for the old regions, but too small for the new one.
|
||||
// By resetting the filepool data to a new, longer audiobuffer, we are creating 2 copies of the same audio data.
|
||||
// The filepool and the new regions have the longer copy, and the older regions have the shorter copy.
|
||||
// This is not entirely optimal, but is it better to write a double shared pointer ?
|
||||
// std::shared_ptr<std::shared_ptr<sfz::AudioBuffer>>> is a bit ugly...
|
||||
alreadyPreloaded.reset(readFromFile<float>(sndFile, preloadedSize).release());
|
||||
}
|
||||
returnedValue.preloadedData = alreadyPreloaded;
|
||||
} else {
|
||||
returnedValue.preloadedData = readFromFile<float>(sndFile, preloadedSize);
|
||||
preloadedData[filename] = returnedValue.preloadedData;
|
||||
}
|
||||
|
||||
return returnedValue;
|
||||
}
|
||||
|
||||
void sfz::FilePool::enqueueLoading(Voice* voice, const std::string* sample, int numFrames, unsigned ticket) noexcept
|
||||
bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept
|
||||
{
|
||||
if (!loadingQueue.try_enqueue({ voice, sample, numFrames, ticket })) {
|
||||
DBG("Problem enqueuing a file read for file " << sample);
|
||||
fs::path file { rootDirectory / filename };
|
||||
if (!fs::exists(file))
|
||||
return false;
|
||||
|
||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||
if (sndFile.channels() != 1 && sndFile.channels() != 2)
|
||||
return false;
|
||||
|
||||
// FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same
|
||||
const uint32_t frames = sndFile.frames();
|
||||
const auto framesToLoad = [&]() {
|
||||
if (preloadSize == 0)
|
||||
return frames;
|
||||
else
|
||||
return min(frames, maxOffset + preloadSize);
|
||||
}();
|
||||
|
||||
if (preloadedFiles.contains(filename)) {
|
||||
if (framesToLoad > preloadedFiles[filename].preloadedData->getNumFrames()) {
|
||||
preloadedFiles[filename].preloadedData = readFromFile<float>(sndFile, framesToLoad, oversamplingFactor);
|
||||
}
|
||||
} else {
|
||||
preloadedFiles.insert_or_assign(filename, { readFromFile<float>(sndFile, framesToLoad, oversamplingFactor), static_cast<float>(sndFile.samplerate() * oversamplingFactor) });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) noexcept
|
||||
{
|
||||
auto promise = std::make_shared<FilePromise>();
|
||||
const auto preloaded = preloadedFiles.find(filename);
|
||||
if (preloaded != preloadedFiles.end()) {
|
||||
promise->filename = preloaded->first;
|
||||
promise->preloadedData = preloaded->second.preloadedData;
|
||||
promise->sampleRate = preloaded->second.sampleRate;
|
||||
promise->oversamplingFactor = oversamplingFactor;
|
||||
promiseQueue.enqueue(promise);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept
|
||||
{
|
||||
// Update all the preloaded sizes
|
||||
for (auto& preloadedFile : preloadedFiles) {
|
||||
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames();
|
||||
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
||||
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||
preloadedFile.second.preloadedData = readFromFile<float>(sndFile, preloadSize + maxOffset, oversamplingFactor);
|
||||
}
|
||||
this->preloadSize = preloadSize;
|
||||
}
|
||||
|
||||
void sfz::FilePool::loadingThread() noexcept
|
||||
{
|
||||
FilePromisePtr promise;
|
||||
while (!quitThread) {
|
||||
promisesToClean.clear();
|
||||
|
||||
if (emptyQueue) {
|
||||
while(loadingQueue.pop()) {}
|
||||
while(promiseQueue.pop()) {
|
||||
|
||||
}
|
||||
emptyQueue = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
FileLoadingInformation fileToLoad {};
|
||||
if (!loadingQueue.wait_dequeue_timed(fileToLoad, 200ms)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileToLoad.voice == nullptr) {
|
||||
DBG("Background thread error: voice is null.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileToLoad.sample == nullptr) {
|
||||
DBG("Background thread error: sample is null.");
|
||||
continue;
|
||||
}
|
||||
|
||||
DBG("Background loading of: " << *fileToLoad.sample);
|
||||
fs::path file { rootDirectory / *fileToLoad.sample };
|
||||
if (!fs::exists(file)) {
|
||||
DBG("Background thread: no file " << *fileToLoad.sample << " exists.");
|
||||
if (!promiseQueue.try_dequeue(promise)) {
|
||||
std::this_thread::sleep_for(1ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::path file { rootDirectory / std::string(promise->filename) };
|
||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||
if (sndFile.error() != 0)
|
||||
continue;
|
||||
|
||||
std::lock_guard<std::mutex> guard { fileHandleMutex };
|
||||
fileHandles.emplace_back(readFromFile<float>(sndFile, fileToLoad.numFrames));
|
||||
fileToLoad.voice->setFileData(fileHandles.back(), fileToLoad.ticket);
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::FilePool::garbageThread() noexcept
|
||||
{
|
||||
while (!quitThread) {
|
||||
fileHandleMutex.lock();
|
||||
for (auto handle = fileHandles.begin(); handle < fileHandles.end();) {
|
||||
if (handle->use_count() == 1) {
|
||||
handle->reset();
|
||||
std::iter_swap(handle, fileHandles.end() - 1);
|
||||
fileHandles.pop_back();
|
||||
} else {
|
||||
handle++;
|
||||
}
|
||||
}
|
||||
fileHandleMutex.unlock();
|
||||
std::this_thread::sleep_for(200ms);
|
||||
DBG("Loading file for " << promise->filename << " in the background");
|
||||
const uint32_t frames = sndFile.frames();
|
||||
promise->fileData = readFromFile<float>(sndFile, frames, oversamplingFactor);
|
||||
promise->dataReady = true;
|
||||
temporaryFilePromises.push_back(promise);
|
||||
promise.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::FilePool::clear()
|
||||
{
|
||||
preloadedData.clear();
|
||||
fileHandles.clear();
|
||||
while (loadingQueue.pop()) {
|
||||
// Pop the queue
|
||||
emptyFileLoadingQueue();
|
||||
preloadedFiles.clear();
|
||||
temporaryFilePromises.clear();
|
||||
promisesToClean.clear();
|
||||
}
|
||||
|
||||
void sfz::FilePool::cleanupPromises() noexcept
|
||||
{
|
||||
if (temporaryFilePromises.empty())
|
||||
return;
|
||||
|
||||
auto promise = temporaryFilePromises.begin();
|
||||
auto sentinel = temporaryFilePromises.end() - 1;
|
||||
while (promise != temporaryFilePromises.end()) {
|
||||
if (promise->use_count() == 1) {
|
||||
promisesToClean.push_back(*promise);
|
||||
std::iter_swap(promise, sentinel);
|
||||
sentinel--;
|
||||
temporaryFilePromises.pop_back();
|
||||
} else {
|
||||
promise++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
||||
{
|
||||
float samplerateChange { static_cast<float>(factor) / static_cast<float>(this->oversamplingFactor) };
|
||||
for (auto& preloadedFile : preloadedFiles) {
|
||||
const auto numFrames = preloadedFile.second.preloadedData->getNumFrames();
|
||||
const uint32_t maxOffset = numFrames > this->preloadSize ? numFrames - this->preloadSize : 0;
|
||||
fs::path file { rootDirectory / std::string(preloadedFile.first) };
|
||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||
preloadedFile.second.preloadedData = readFromFile<float>(sndFile, preloadSize + maxOffset, factor);
|
||||
preloadedFile.second.sampleRate *= samplerateChange;
|
||||
}
|
||||
|
||||
this->oversamplingFactor = factor;
|
||||
}
|
||||
|
||||
sfz::Oversampling sfz::FilePool::getOversamplingFactor() const noexcept
|
||||
{
|
||||
return oversamplingFactor;
|
||||
}
|
||||
|
||||
uint32_t sfz::FilePool::getPreloadSize() const noexcept
|
||||
{
|
||||
return preloadSize;
|
||||
}
|
||||
|
||||
void sfz::FilePool::emptyFileLoadingQueue() noexcept
|
||||
{
|
||||
emptyQueue = true;
|
||||
|
|
|
|||
|
|
@ -26,16 +26,36 @@
|
|||
#include "Defaults.h"
|
||||
#include "LeakDetector.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "Voice.h"
|
||||
#include "SIMDHelpers.h"
|
||||
#include "ghc/fs_std.hpp"
|
||||
#include "moodycamel/readerwriterqueue.h"
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <mutex>
|
||||
#include <absl/types/optional.h>
|
||||
#include <string_view>
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "moodycamel/readerwriterqueue.h"
|
||||
#include <thread>
|
||||
#include <sndfile.hh>
|
||||
|
||||
namespace sfz {
|
||||
using AudioBufferPtr = std::shared_ptr<AudioBuffer<float>>;
|
||||
|
||||
|
||||
struct PreloadedFileHandle
|
||||
{
|
||||
std::shared_ptr<AudioBuffer<float>> preloadedData {};
|
||||
float sampleRate { config::defaultSampleRate };
|
||||
};
|
||||
|
||||
struct FilePromise
|
||||
{
|
||||
absl::string_view filename {};
|
||||
AudioBufferPtr preloadedData {};
|
||||
std::unique_ptr<AudioBuffer<float>> fileData {};
|
||||
float sampleRate { config::defaultSampleRate };
|
||||
std::atomic<bool> dataReady { false };
|
||||
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
||||
};
|
||||
|
||||
using FilePromisePtr = std::shared_ptr<FilePromise>;
|
||||
/**
|
||||
* @brief This is a singleton-designed class that holds all the preloaded
|
||||
* data as well as functions to request new file data and collect the file
|
||||
|
|
@ -53,6 +73,8 @@ namespace sfz {
|
|||
* to 1. A garbage collection thread then runs regularly to clear the memory of all file
|
||||
* handles with a reference count of 1.
|
||||
*/
|
||||
|
||||
|
||||
class FilePool {
|
||||
public:
|
||||
FilePool() { }
|
||||
|
|
@ -61,7 +83,6 @@ public:
|
|||
{
|
||||
quitThread = true;
|
||||
fileLoadingThread.join();
|
||||
garbageCollectionThread.join();
|
||||
}
|
||||
/**
|
||||
* @brief Set the root directory from which to search for files to load
|
||||
|
|
@ -74,71 +95,63 @@ public:
|
|||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t getNumPreloadedSamples() const noexcept { return preloadedData.size(); }
|
||||
size_t getNumPreloadedSamples() const noexcept { return preloadedFiles.size(); }
|
||||
|
||||
struct FileInformation {
|
||||
uint32_t end { Default::sampleEndRange.getEnd() };
|
||||
uint32_t loopBegin { Default::loopRange.getStart() };
|
||||
uint32_t loopEnd { Default::loopRange.getEnd() };
|
||||
double sampleRate { config::defaultSampleRate };
|
||||
std::shared_ptr<AudioBuffer<float>> preloadedData;
|
||||
int numChannels { 0 };
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get metadata information about a file as well as the first chunk of data
|
||||
* @brief Get metadata information about a file.
|
||||
*
|
||||
* If the same file was already preloaded and with a compatible offset, the handle
|
||||
* is shared between the regions. Otherwise, a new handle is created (the others keep
|
||||
* the old preloaded file).
|
||||
* @param filename
|
||||
* @return absl::optional<FileInformation>
|
||||
*/
|
||||
absl::optional<FileInformation> getFileInformation(const std::string& filename) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Check that a file is preloaded with the proper offset bounds
|
||||
*
|
||||
* @param filename
|
||||
* @param offset the maximum offset to consider for preloading. The total preloaded
|
||||
* size will be preloadedSize + offset
|
||||
* @return absl::optional<FileInformation>
|
||||
* size will be preloadSize + offset
|
||||
* @return true if the preloading went fine
|
||||
* @return false if something went wrong ()
|
||||
*/
|
||||
absl::optional<FileInformation> getFileInformation(const std::string& filename, uint32_t offset) noexcept;
|
||||
/**
|
||||
* @brief Queue a full loading operation for a given voice.
|
||||
*
|
||||
* The goal of the ticket is to avoid file loading operations that for some reason
|
||||
* finish too late a "replace" a proper sample with an obsolete one for a voice.
|
||||
*
|
||||
* @param voice the voice to give the full file data to
|
||||
* @param sample the sample file
|
||||
* @param numFrames the number of frames to load from the file
|
||||
* @param ticket an ideally unique ticket number for this file.
|
||||
*/
|
||||
void enqueueLoading(Voice* voice, const std::string* sample, int numFrames, unsigned ticket) noexcept;
|
||||
bool preloadFile(const std::string& filename, uint32_t maxOffset) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Clear all preloaded files.
|
||||
*
|
||||
*/
|
||||
void clear();
|
||||
/**
|
||||
* @brief Empty the file loading queue. This function will lock and wait
|
||||
* for the background thread to finish its business, so don't call it from
|
||||
* the audio thread.
|
||||
*
|
||||
*/
|
||||
|
||||
void cleanupPromises() noexcept;
|
||||
FilePromisePtr getFilePromise(const std::string& filename) noexcept;
|
||||
void setPreloadSize(uint32_t preloadSize) noexcept;
|
||||
uint32_t getPreloadSize() const noexcept;
|
||||
void setOversamplingFactor(Oversampling factor) noexcept;
|
||||
Oversampling getOversamplingFactor() const noexcept;
|
||||
void emptyFileLoadingQueue() noexcept;
|
||||
private:
|
||||
fs::path rootDirectory;
|
||||
struct FileLoadingInformation {
|
||||
Voice* voice;
|
||||
const std::string* sample;
|
||||
int numFrames;
|
||||
unsigned ticket;
|
||||
};
|
||||
|
||||
moodycamel::BlockingReaderWriterQueue<FileLoadingInformation> loadingQueue { config::numVoices };
|
||||
void loadingThread() noexcept;
|
||||
void garbageThread() noexcept;
|
||||
|
||||
moodycamel::ReaderWriterQueue<FilePromisePtr> promiseQueue;
|
||||
uint32_t preloadSize { config::preloadSize };
|
||||
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
||||
// Signals
|
||||
bool quitThread { false };
|
||||
bool emptyQueue { false };
|
||||
std::mutex fileHandleMutex;
|
||||
std::vector<std::shared_ptr<AudioBuffer<float>>> fileHandles;
|
||||
absl::flat_hash_map<absl::string_view, std::shared_ptr<AudioBuffer<float>>> preloadedData;
|
||||
|
||||
std::vector<FilePromisePtr> temporaryFilePromises;
|
||||
std::vector<FilePromisePtr> promisesToClean;
|
||||
absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles;
|
||||
std::thread fileLoadingThread { &FilePool::loadingThread, this };
|
||||
std::thread garbageCollectionThread { &FilePool::garbageThread, this };
|
||||
LEAK_DETECTOR(FilePool);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
95
src/sfizz/Oversampler.h
Normal file
95
src/sfizz/Oversampler.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2019, Paul Ferrand
|
||||
// All rights reserved.
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. 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.
|
||||
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include "absl/types/span.h"
|
||||
#include "Debug.h"
|
||||
#include "Config.h"
|
||||
#include "hiir/Upsampler2xFpu.h"
|
||||
|
||||
namespace sfz {
|
||||
|
||||
constexpr std::array<double, 12> coeffsStage2x {
|
||||
0.036681502163648017,
|
||||
0.13654762463195771,
|
||||
0.27463175937945411,
|
||||
0.42313861743656667,
|
||||
0.56109869787919475,
|
||||
0.67754004997416162,
|
||||
0.76974183386322659,
|
||||
0.83988962484963803,
|
||||
0.89226081800387891,
|
||||
0.9315419599631839,
|
||||
0.96209454837808395,
|
||||
0.98781637073289708
|
||||
};
|
||||
|
||||
constexpr std::array<double, 4> coeffsStage4x {
|
||||
0.042448989488488006,
|
||||
0.17072114107630679,
|
||||
0.39329183835224008,
|
||||
0.74569514831986694
|
||||
};
|
||||
|
||||
constexpr std::array<double, 3> coeffsStage8x {
|
||||
0.055748680811302048,
|
||||
0.24305119574153092,
|
||||
0.6466991311926823
|
||||
};
|
||||
|
||||
template<bool SIMD=SIMDConfig::upsampling>
|
||||
inline void upsample2xStage(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
ASSERT(output.size() >= 2 * input.size());
|
||||
hiir::Upsampler2xFpu<coeffsStage2x.size()> upsampler;
|
||||
upsampler.set_coefs(coeffsStage2x.data());
|
||||
upsampler.process_block(output.data(), input.data(), input.size());
|
||||
}
|
||||
|
||||
|
||||
template<bool SIMD=SIMDConfig::upsampling>
|
||||
inline void upsample4xStage(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
ASSERT(output.size() >= 2 * input.size());
|
||||
hiir::Upsampler2xFpu<coeffsStage4x.size()> upsampler;
|
||||
upsampler.set_coefs(coeffsStage4x.data());
|
||||
upsampler.process_block(output.data(), input.data(), input.size());
|
||||
}
|
||||
|
||||
template<bool SIMD=SIMDConfig::upsampling>
|
||||
inline void upsample8xStage(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
ASSERT(output.size() >= 2 * input.size());
|
||||
hiir::Upsampler2xFpu<coeffsStage8x.size()> upsampler;
|
||||
upsampler.set_coefs(coeffsStage8x.data());
|
||||
upsampler.process_block(output.data(), input.data(), input.size());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void upsample2xStage<true>(absl::Span<const float> input, absl::Span<float> output);
|
||||
template<>
|
||||
inline void upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output);
|
||||
template<>
|
||||
inline void upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output);
|
||||
}
|
||||
40
src/sfizz/OversamplerDummy.cpp
Normal file
40
src/sfizz/OversamplerDummy.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2019, Paul Ferrand
|
||||
// All rights reserved.
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. 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.
|
||||
|
||||
// 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 "Oversampler.h"
|
||||
|
||||
template<>
|
||||
inline void sfz::upsample2xStage<true>(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
sfz::upsample2xStage<false>(input, output);
|
||||
}
|
||||
template<>
|
||||
inline void sfz::upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
sfz::upsample4xStage<false>(input, output);
|
||||
}
|
||||
template<>
|
||||
inline void sfz::upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
sfz::upsample8xStage<false>(input, output);
|
||||
}
|
||||
50
src/sfizz/OversamplerSSE.cpp
Normal file
50
src/sfizz/OversamplerSSE.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2019, Paul Ferrand
|
||||
// All rights reserved.
|
||||
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. 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.
|
||||
|
||||
// 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 "Oversampler.h"
|
||||
#include "hiir/Upsampler2x4Sse.h"
|
||||
|
||||
template<>
|
||||
inline void sfz::upsample2xStage<true>(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
ASSERT(output.size() >= 2 * input.size());
|
||||
hiir::Upsampler2x4Sse<sfz::coeffsStage2x.size()> upsampler;
|
||||
upsampler.set_coefs(sfz::coeffsStage2x.data());
|
||||
upsampler.process_block(output.data(), input.data(), input.size());
|
||||
}
|
||||
template<>
|
||||
inline void sfz::upsample4xStage<true>(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
ASSERT(output.size() >= 2 * input.size());
|
||||
hiir::Upsampler2x4Sse<sfz::coeffsStage4x.size()> upsampler;
|
||||
upsampler.set_coefs(sfz::coeffsStage4x.data());
|
||||
upsampler.process_block(output.data(), input.data(), input.size());
|
||||
}
|
||||
template<>
|
||||
inline void sfz::upsample8xStage<true>(absl::Span<const float> input, absl::Span<float> output)
|
||||
{
|
||||
ASSERT(output.size() >= 2 * input.size());
|
||||
hiir::Upsampler2x4Sse<sfz::coeffsStage8x.size()> upsampler;
|
||||
upsampler.set_coefs(sfz::coeffsStage8x.data());
|
||||
upsampler.process_block(output.data(), input.data(), input.size());
|
||||
}
|
||||
|
|
@ -644,9 +644,9 @@ float sfz::Region::getBaseGain() noexcept
|
|||
return normalizePercents(amplitude);
|
||||
}
|
||||
|
||||
uint32_t sfz::Region::getOffset() noexcept
|
||||
uint32_t sfz::Region::getOffset(Oversampling factor) noexcept
|
||||
{
|
||||
return offset + offsetDistribution(Random::randomGenerator);
|
||||
return (offset + offsetDistribution(Random::randomGenerator)) * factor;
|
||||
}
|
||||
|
||||
float sfz::Region::getDelay() noexcept
|
||||
|
|
@ -654,25 +654,19 @@ float sfz::Region::getDelay() noexcept
|
|||
return delay + delayDistribution(Random::randomGenerator);
|
||||
}
|
||||
|
||||
uint32_t sfz::Region::trueSampleEnd() const noexcept
|
||||
uint32_t sfz::Region::trueSampleEnd(Oversampling factor) const noexcept
|
||||
{
|
||||
return min(sampleEnd, loopRange.getEnd());
|
||||
return min(sampleEnd, loopRange.getEnd()) * factor;
|
||||
}
|
||||
|
||||
bool sfz::Region::canUsePreloadedData() const noexcept
|
||||
uint32_t sfz::Region::loopStart(Oversampling factor) const noexcept
|
||||
{
|
||||
if (preloadedData == nullptr)
|
||||
return false;
|
||||
|
||||
return trueSampleEnd() < static_cast<uint32_t>(preloadedData->getNumFrames());
|
||||
return loopRange.getStart() * factor;
|
||||
}
|
||||
|
||||
bool sfz::Region::isStereo() const noexcept
|
||||
uint32_t sfz::Region::loopEnd(Oversampling factor) const noexcept
|
||||
{
|
||||
if (isGenerator())
|
||||
return 1;
|
||||
|
||||
return (this->preloadedData->getNumChannels() == 2);
|
||||
return loopRange.getEnd() * factor;
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
|
|
|
|||
|
|
@ -149,13 +149,7 @@ struct Region {
|
|||
* @return false
|
||||
*/
|
||||
void registerTempo(float secondsPerQuarter) noexcept;
|
||||
/**
|
||||
* @brief Is the underlying region sample a stereo one?
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isStereo() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the base pitch of the region depending on which note has been
|
||||
* pressed and at which velocity.
|
||||
|
|
@ -207,7 +201,7 @@ struct Region {
|
|||
*
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t getOffset() noexcept;
|
||||
uint32_t getOffset(Oversampling factor = x1) noexcept;
|
||||
/**
|
||||
* @brief Get the region delay in seconds
|
||||
*
|
||||
|
|
@ -220,14 +214,7 @@ struct Region {
|
|||
*
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t trueSampleEnd() const noexcept;
|
||||
/**
|
||||
* @brief Can the region use the preloaded data only to play its full range?
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool canUsePreloadedData() const noexcept;
|
||||
uint32_t trueSampleEnd(Oversampling factor = x1) const noexcept;
|
||||
/**
|
||||
* @brief Parse a new opcode into the region to fill in the proper parameters.
|
||||
* This must be called multiple times for each opcode applying to this region.
|
||||
|
|
@ -238,6 +225,9 @@ struct Region {
|
|||
*/
|
||||
bool parseOpcode(const Opcode& opcode);
|
||||
|
||||
uint32_t loopStart(Oversampling factor = x1) const noexcept;
|
||||
uint32_t loopEnd(Oversampling factor = x1) const noexcept;
|
||||
|
||||
// Sound source: sample playback
|
||||
std::string sample {}; // Sample
|
||||
float delay { Default::delay }; // delay
|
||||
|
|
@ -323,8 +313,7 @@ struct Region {
|
|||
EGDescription pitchEG;
|
||||
EGDescription filterEG;
|
||||
|
||||
double sampleRate { config::defaultSampleRate };
|
||||
std::shared_ptr<AudioBuffer<float>> preloadedData { nullptr };
|
||||
bool isStereo { false };
|
||||
private:
|
||||
const MidiState& midiState;
|
||||
bool keySwitched { true };
|
||||
|
|
|
|||
10
src/sfizz/Resources.h
Normal file
10
src/sfizz/Resources.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
#include "FilePool.h"
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
struct Resources
|
||||
{
|
||||
FilePool filePool;
|
||||
};
|
||||
}
|
||||
|
|
@ -123,7 +123,7 @@ void sfz::Synth::clear()
|
|||
for (auto& list: ccActivationLists)
|
||||
list.clear();
|
||||
regions.clear();
|
||||
filePool.clear();
|
||||
resources.filePool.clear();
|
||||
hasGlobal = false;
|
||||
hasControl = false;
|
||||
numGroups = 0;
|
||||
|
|
@ -219,7 +219,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
|||
if (regions.empty())
|
||||
return false;
|
||||
|
||||
filePool.setRootDirectory(this->rootDirectory);
|
||||
resources.filePool.setRootDirectory(this->rootDirectory);
|
||||
|
||||
auto lastRegion = regions.end() - 1;
|
||||
auto currentRegion = regions.begin();
|
||||
|
|
@ -227,7 +227,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
|||
auto region = currentRegion->get();
|
||||
|
||||
if (!region->isGenerator()) {
|
||||
auto fileInformation = filePool.getFileInformation(region->sample, region->offset + region->offsetRandom);
|
||||
auto fileInformation = resources.filePool.getFileInformation(region->sample);
|
||||
if (!fileInformation) {
|
||||
DBG("Removing the region with sample " << region->sample);
|
||||
std::iter_swap(currentRegion, lastRegion);
|
||||
|
|
@ -236,8 +236,12 @@ bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
|||
}
|
||||
region->sampleEnd = std::min(region->sampleEnd, fileInformation->end);
|
||||
region->loopRange.shrinkIfSmaller(fileInformation->loopBegin, fileInformation->loopEnd);
|
||||
region->preloadedData = fileInformation->preloadedData;
|
||||
region->sampleRate = fileInformation->sampleRate;
|
||||
if (fileInformation->numChannels == 2)
|
||||
region->isStereo = true;
|
||||
|
||||
// TODO: adjust with LFO targets
|
||||
const auto maxOffset { region->offset + region->offsetRandom };
|
||||
resources.filePool.preloadFile(region->sample, maxOffset);
|
||||
}
|
||||
|
||||
for (auto note = 0; note < 128; note++) {
|
||||
|
|
@ -312,9 +316,7 @@ int sfz::Synth::getNumActiveVoices() const noexcept
|
|||
|
||||
void sfz::Synth::garbageCollect() noexcept
|
||||
{
|
||||
for (auto& voice : voices) {
|
||||
voice->garbageCollect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||
|
|
@ -348,6 +350,8 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
ScopedFTZ ftz;
|
||||
buffer.fill(0.0f);
|
||||
|
||||
resources.filePool.cleanupPromises();
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
return;
|
||||
|
|
@ -387,10 +391,6 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
|
|||
continue;
|
||||
|
||||
voice->startVoice(region, delay, channel, noteNumber, velocity, Voice::TriggerType::NoteOn);
|
||||
if (!region->isGenerator()) {
|
||||
voice->expectFileData(fileTicket);
|
||||
filePool.enqueueLoading(voice, ®ion->sample, region->trueSampleEnd(), fileTicket++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -420,10 +420,6 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
|
|||
continue;
|
||||
|
||||
voice->startVoice(region, delay, channel, noteNumber, replacedVelocity, Voice::TriggerType::NoteOff);
|
||||
if (!region->isGenerator()) {
|
||||
voice->expectFileData(fileTicket);
|
||||
filePool.enqueueLoading(voice, ®ion->sample, region->trueSampleEnd(), fileTicket++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -449,10 +445,6 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc
|
|||
continue;
|
||||
|
||||
voice->startVoice(region, delay, channel, ccNumber, ccValue, Voice::TriggerType::CC);
|
||||
if (!region->isGenerator()) {
|
||||
voice->expectFileData(fileTicket);
|
||||
filePool.enqueueLoading(voice, ®ion->sample, region->trueSampleEnd(), fileTicket++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -503,7 +495,7 @@ std::set<absl::string_view> sfz::Synth::getUnknownOpcodes() const noexcept
|
|||
}
|
||||
size_t sfz::Synth::getNumPreloadedSamples() const noexcept
|
||||
{
|
||||
return filePool.getNumPreloadedSamples();
|
||||
return resources.filePool.getNumPreloadedSamples();
|
||||
}
|
||||
|
||||
float sfz::Synth::getVolume() const noexcept
|
||||
|
|
@ -533,10 +525,9 @@ void sfz::Synth::resetVoices(int numVoices)
|
|||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
filePool.emptyFileLoadingQueue();
|
||||
voices.clear();
|
||||
for (int i = 0; i < numVoices; ++i)
|
||||
voices.push_back(std::make_unique<Voice>(midiState));
|
||||
voices.push_back(std::make_unique<Voice>(midiState, resources));
|
||||
|
||||
for (auto& voice: voices) {
|
||||
voice->setSampleRate(this->sampleRate);
|
||||
|
|
@ -546,3 +537,38 @@ void sfz::Synth::resetVoices(int numVoices)
|
|||
voiceViewArray.reserve(numVoices);
|
||||
this->numVoices = numVoices;
|
||||
}
|
||||
|
||||
void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
for (auto& voice: voices)
|
||||
voice->reset();
|
||||
|
||||
resources.filePool.emptyFileLoadingQueue();
|
||||
resources.filePool.setOversamplingFactor(factor);
|
||||
oversamplingFactor = factor;
|
||||
}
|
||||
|
||||
sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept
|
||||
{
|
||||
return oversamplingFactor;
|
||||
}
|
||||
|
||||
void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
resources.filePool.setPreloadSize(preloadSize);
|
||||
}
|
||||
|
||||
uint32_t sfz::Synth::getPreloadSize() const noexcept
|
||||
{
|
||||
return resources.filePool.getPreloadSize();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@
|
|||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
#include "FilePool.h"
|
||||
#include "Resources.h"
|
||||
#include "Parser.h"
|
||||
#include "Voice.h"
|
||||
#include "Region.h"
|
||||
#include "LeakDetector.h"
|
||||
#include "MidiState.h"
|
||||
|
|
@ -279,6 +280,36 @@ public:
|
|||
*
|
||||
*/
|
||||
void garbageCollect() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the oversampling factor to a new value. This will disable all callbacks
|
||||
* kill all the voices, and trigger a reloading of every file in the FilePool under
|
||||
* the new oversampling.
|
||||
*
|
||||
* @param factor
|
||||
*/
|
||||
void setOversamplingFactor(Oversampling factor) noexcept;
|
||||
|
||||
/**
|
||||
* @brief get the current oversampling factor
|
||||
*
|
||||
* @return Oversampling
|
||||
*/
|
||||
Oversampling getOversamplingFactor() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Set the preloaded file size. This will disable the callback.
|
||||
*
|
||||
* @param factor
|
||||
*/
|
||||
void setPreloadSize(uint32_t preloadSize) noexcept;
|
||||
|
||||
/**
|
||||
* @brief get the current preloaded file size
|
||||
*
|
||||
* @return Oversampling
|
||||
*/
|
||||
uint32_t getPreloadSize() const noexcept;
|
||||
protected:
|
||||
/**
|
||||
* @brief The parser callback; this is called by the parent object each time
|
||||
|
|
@ -337,11 +368,6 @@ private:
|
|||
std::vector<Opcode> masterOpcodes;
|
||||
std::vector<Opcode> groupOpcodes;
|
||||
|
||||
// Singletons passed as references to the voices
|
||||
// TODO: these should probably go in a global singleton holder along with a buffer distribution and LFO/EG stuff...
|
||||
FilePool filePool;
|
||||
MidiState midiState;
|
||||
|
||||
/**
|
||||
* @brief Find a voice that is not currently playing
|
||||
*
|
||||
|
|
@ -370,6 +396,7 @@ private:
|
|||
float sampleRate { config::defaultSampleRate };
|
||||
float volume { Default::volume };
|
||||
int numVoices { config::numVoices };
|
||||
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
||||
|
||||
// Distribution used to generate random value for the *rand opcodes
|
||||
std::uniform_real_distribution<float> randNoteDistribution { 0, 1 };
|
||||
|
|
@ -379,6 +406,10 @@ private:
|
|||
std::atomic<bool> canEnterCallback { true };
|
||||
std::atomic<bool> inCallback { false };
|
||||
|
||||
// Singletons passed as references to the voices
|
||||
Resources resources;
|
||||
MidiState midiState;
|
||||
|
||||
LEAK_DETECTOR(Synth);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@
|
|||
#include "absl/algorithm/container.h"
|
||||
#include <memory>
|
||||
|
||||
sfz::Voice::Voice(const MidiState& midiState)
|
||||
: midiState(midiState)
|
||||
sfz::Voice::Voice(const sfz::MidiState& midiState, sfz::Resources& resources)
|
||||
: midiState(midiState), resources(resources)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +44,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
|
|||
triggerValue = value;
|
||||
|
||||
this->region = region;
|
||||
state = State::playing;
|
||||
|
||||
ASSERT(delay >= 0);
|
||||
if (delay < 0)
|
||||
|
|
@ -51,9 +52,19 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
|
|||
|
||||
// DBG("Starting voice with " << region->sample);
|
||||
|
||||
state = State::playing;
|
||||
speedRatio = static_cast<float>(region->sampleRate / this->sampleRate);
|
||||
if (!region->isGenerator()) {
|
||||
currentPromise = resources.filePool.getFilePromise(region->sample);
|
||||
if (currentPromise == nullptr) {
|
||||
DBG("[Voice] Could not fetch the file promise for sample " << region->sample);
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
speedRatio = static_cast<float>(currentPromise->sampleRate / this->sampleRate);
|
||||
DBG("[Voice] Sample rate for " << region->sample << " is " << currentPromise->sampleRate);
|
||||
DBG("[Voice] Speed ratio set to " << speedRatio);
|
||||
}
|
||||
pitchRatio = region->getBasePitchVariation(number, value);
|
||||
DBG("[Voice] Pitch ratio set to " << pitchRatio);
|
||||
|
||||
baseVolumedB = region->getBaseVolumedB(number);
|
||||
|
||||
|
|
@ -118,15 +129,6 @@ void sfz::Voice::prepareEGEnvelope(int delay, uint8_t velocity) noexcept
|
|||
normalizePercents(region->amplitudeEG.getStart(midiState.cc, velocity)));
|
||||
}
|
||||
|
||||
void sfz::Voice::setFileData(std::shared_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept
|
||||
{
|
||||
if (ticket != this->ticket)
|
||||
return;
|
||||
|
||||
fileData = std::move(file);
|
||||
dataReady.store(true);
|
||||
}
|
||||
|
||||
bool sfz::Voice::isFree() const noexcept
|
||||
{
|
||||
return (region == nullptr);
|
||||
|
|
@ -245,7 +247,7 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
else
|
||||
fillWithData(delayed_buffer);
|
||||
|
||||
if (region->isStereo())
|
||||
if (region->isStereo)
|
||||
processStereo(buffer);
|
||||
else
|
||||
processMono(buffer);
|
||||
|
|
@ -352,12 +354,13 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
|
|||
return;
|
||||
|
||||
auto source { [&]() {
|
||||
if (region->canUsePreloadedData())
|
||||
return AudioSpan<const float>(*region->preloadedData);
|
||||
else if (!dataReady)
|
||||
return AudioSpan<const float>(*region->preloadedData);
|
||||
// if (region->trueSampleEnd() < currentPromise->preloadedData->getNumFrames())
|
||||
// return AudioSpan<const float>(*currentPromise->preloadedData);
|
||||
// else
|
||||
if (!currentPromise->dataReady)
|
||||
return AudioSpan<const float>(*currentPromise->preloadedData);
|
||||
else
|
||||
return AudioSpan<const float>(*fileData);
|
||||
return AudioSpan<const float>(*currentPromise->fileData);
|
||||
}() };
|
||||
|
||||
auto indices = indexSpan.first(buffer.getNumFrames());
|
||||
|
|
@ -372,9 +375,12 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
|
|||
add<int>(sourcePosition, indices);
|
||||
|
||||
//FIXME : all this casting is driving me crazy
|
||||
const auto sampleEnd = min(static_cast<int>(region->trueSampleEnd()), static_cast<int>(source.getNumFrames())) - 1;
|
||||
if (region->shouldLoop() && region->loopRange.getEnd() <= source.getNumFrames()) {
|
||||
const auto offset = sampleEnd - static_cast<int>(region->loopRange.getStart());
|
||||
const auto sampleEnd = min(
|
||||
static_cast<int>(region->trueSampleEnd(currentPromise->oversamplingFactor)),
|
||||
static_cast<int>(source.getNumFrames())
|
||||
) - 1;
|
||||
if (region->shouldLoop() && region->loopEnd(currentPromise->oversamplingFactor) <= source.getNumFrames()) {
|
||||
const auto offset = sampleEnd - static_cast<int>(region->loopStart(currentPromise->oversamplingFactor));
|
||||
for (auto* index = indices.begin(); index < indices.end(); ++index) {
|
||||
if (*index > sampleEnd) {
|
||||
const auto remainingElements = static_cast<size_t>(std::distance(index, indices.end()));
|
||||
|
|
@ -483,30 +489,17 @@ sfz::Voice::TriggerType sfz::Voice::getTriggerType() const noexcept
|
|||
|
||||
void sfz::Voice::reset() noexcept
|
||||
{
|
||||
dataReady.store(false);
|
||||
fileData.reset();
|
||||
state = State::idle;
|
||||
if (region != nullptr) {
|
||||
DBG("Reset voice with sample " << region->sample);
|
||||
}
|
||||
region = nullptr;
|
||||
currentPromise.reset();
|
||||
sourcePosition = 0;
|
||||
floatPositionOffset = 0.0f;
|
||||
noteIsOff = false;
|
||||
}
|
||||
|
||||
void sfz::Voice::garbageCollect() noexcept
|
||||
{
|
||||
if (state == State::idle && region == nullptr) {
|
||||
fileData.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::Voice::expectFileData(unsigned ticket)
|
||||
{
|
||||
this->ticket = ticket;
|
||||
}
|
||||
|
||||
float sfz::Voice::getMeanSquaredAverage() const noexcept
|
||||
{
|
||||
return powerHistory.getAverage();
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "Region.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "MidiState.h"
|
||||
#include "Resources.h"
|
||||
#include "AudioSpan.h"
|
||||
#include "LeakDetector.h"
|
||||
#include <absl/types/span.h>
|
||||
|
|
@ -50,7 +51,7 @@ public:
|
|||
*
|
||||
* @param midiState
|
||||
*/
|
||||
Voice(const MidiState& midiState);
|
||||
Voice(const MidiState& midiState, Resources& resources);
|
||||
enum class TriggerType {
|
||||
NoteOn,
|
||||
NoteOff,
|
||||
|
|
@ -98,23 +99,6 @@ public:
|
|||
*/
|
||||
void startVoice(Region* region, int delay, int channel, int number, uint8_t value, TriggerType triggerType) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Tells the voice that it should expect to receive a file at some point using the
|
||||
* setFileData() function. The ticket is a unique identifier that will prevent the file data
|
||||
* to be set "too late"; if the voice receives the file for an older ticket, it will discard
|
||||
* it.
|
||||
*
|
||||
* @param ticket
|
||||
*/
|
||||
void expectFileData(unsigned ticket);
|
||||
/**
|
||||
* @brief Sets the file data for a given ticket. The voice can freely release and destroy the
|
||||
* shared pointer, as it will be garbage collected by the file pool afterwards.
|
||||
*
|
||||
* @param file
|
||||
* @param ticket
|
||||
*/
|
||||
void setFileData(std::shared_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept;
|
||||
/**
|
||||
* @brief Register a note-off event; this may trigger a release.
|
||||
*
|
||||
|
|
@ -221,11 +205,6 @@ public:
|
|||
*
|
||||
*/
|
||||
void reset() noexcept;
|
||||
/**
|
||||
* @brief Clear the loaded file data if it's not useful anymore
|
||||
*
|
||||
*/
|
||||
void garbageCollect() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the mean squared power of the last rendered block. This is used
|
||||
|
|
@ -309,9 +288,7 @@ private:
|
|||
int sourcePosition { 0 };
|
||||
int initialDelay { 0 };
|
||||
|
||||
std::atomic<bool> dataReady { false };
|
||||
std::shared_ptr<AudioBuffer<float>> fileData { nullptr };
|
||||
unsigned ticket { 0 };
|
||||
FilePromisePtr currentPromise { nullptr };
|
||||
|
||||
Buffer<float> tempBuffer1;
|
||||
Buffer<float> tempBuffer2;
|
||||
|
|
@ -326,6 +303,8 @@ private:
|
|||
float sampleRate { config::defaultSampleRate };
|
||||
|
||||
const MidiState& midiState;
|
||||
Resources& resources;
|
||||
|
||||
ADSREnvelope<float> egEnvelope;
|
||||
LinearEnvelope<float> volumeEnvelope; // dB events but the envelope output is linear gain
|
||||
LinearEnvelope<float> amplitudeEnvelope; // linear events
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
// (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 "Config.h"
|
||||
#include "Synth.h"
|
||||
#include "sfizz.h"
|
||||
|
||||
|
|
@ -127,10 +128,44 @@ void sfizz_render_block(sfizz_synth_t* synth, float** channels, int num_channels
|
|||
self->renderBlock({{channels[0], channels[1]}, static_cast<size_t>(num_frames)});
|
||||
}
|
||||
|
||||
void sfizz_force_garbage_collection(sfizz_synth_t* synth)
|
||||
unsigned int sfizz_get_preload_size(sfizz_synth_t* synth)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
self->garbageCollect();
|
||||
return self->getPreloadSize();
|
||||
}
|
||||
void sfizz_set_preload_size(sfizz_synth_t* synth, unsigned int preload_size)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
self->setPreloadSize(preload_size);
|
||||
}
|
||||
|
||||
sfizz_oversampling_factor_t sfizz_get_oversampling_factor(sfizz_synth_t* synth)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
return static_cast<sfizz_oversampling_factor_t>(self->getOversamplingFactor());
|
||||
}
|
||||
|
||||
bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfizz_oversampling_factor_t oversampling)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
using sfz::Oversampling;
|
||||
switch(oversampling)
|
||||
{
|
||||
case SFIZZ_OVERSAMPLING_X1:
|
||||
self->setOversamplingFactor(sfz::Oversampling::x1);
|
||||
return true;
|
||||
case SFIZZ_OVERSAMPLING_X2:
|
||||
self->setOversamplingFactor(sfz::Oversampling::x2);
|
||||
return true;
|
||||
case SFIZZ_OVERSAMPLING_X4:
|
||||
self->setOversamplingFactor(sfz::Oversampling::x4);
|
||||
return true;
|
||||
case SFIZZ_OVERSAMPLING_X8:
|
||||
self->setOversamplingFactor(sfz::Oversampling::x8);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void sfizz_set_volume(sfizz_synth_t* synth, float volume)
|
||||
|
|
@ -159,4 +194,4 @@ int sfizz_get_num_voices(sfizz_synth_t* synth)
|
|||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -257,9 +257,9 @@ TEST_CASE("[Files] Channels (channels.sfz)")
|
|||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz");
|
||||
REQUIRE(synth.getNumRegions() == 2);
|
||||
REQUIRE(synth.getRegionView(0)->sample == "mono_sample.wav");
|
||||
REQUIRE(!synth.getRegionView(0)->isStereo());
|
||||
REQUIRE(!synth.getRegionView(0)->isStereo);
|
||||
REQUIRE(synth.getRegionView(1)->sample == "stereo_sample.wav");
|
||||
REQUIRE(synth.getRegionView(1)->isStereo());
|
||||
REQUIRE(synth.getRegionView(1)->isStereo);
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] sw_default")
|
||||
|
|
@ -315,4 +315,4 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines")
|
|||
REQUIRE( synth.getRegionView(2)->amplitudeCC );
|
||||
REQUIRE( synth.getRegionView(2)->amplitudeCC->first == 10 );
|
||||
REQUIRE( synth.getRegionView(2)->amplitudeCC->second == 34.0f );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
// (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 "Config.h"
|
||||
#include "sfizz.hpp"
|
||||
#include "catch2/catch.hpp"
|
||||
using namespace Catch::literals;
|
||||
|
|
@ -89,3 +90,33 @@ TEST_CASE("[Synth] Check that the sample per block and sample rate are actually
|
|||
REQUIRE( synth.getVoiceView(i)->getSampleRate() == 48000.0f );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Check that we can change the size of the preload before and after loading")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.setPreloadSize(512);
|
||||
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
||||
synth.setPreloadSize(1024);
|
||||
|
||||
synth.noteOn(0, 1, 36, 24);
|
||||
synth.noteOn(0, 1, 36, 89);
|
||||
synth.renderBlock(buffer);
|
||||
synth.setPreloadSize(2048);
|
||||
synth.renderBlock(buffer);
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Check that we can change the oversampling factor before and after loading")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.setOversamplingFactor(sfz::x2);
|
||||
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
||||
synth.setOversamplingFactor(sfz::x4);
|
||||
|
||||
synth.noteOn(0, 1, 36, 24);
|
||||
synth.noteOn(0, 1, 36, 89);
|
||||
synth.renderBlock(buffer);
|
||||
synth.setOversamplingFactor(sfz::x2);
|
||||
synth.renderBlock(buffer);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue