Add the tuning control
This commit is contained in:
parent
4b2d265240
commit
dfd10a0977
20 changed files with 751 additions and 13 deletions
|
|
@ -78,6 +78,9 @@ target_include_directories(sfizz-pugixml PUBLIC "src/external/pugixml/src")
|
||||||
add_library(sfizz-spline STATIC "src/external/spline/spline/spline.cpp")
|
add_library(sfizz-spline STATIC "src/external/spline/spline/spline.cpp")
|
||||||
target_include_directories(sfizz-spline PUBLIC "src/external/spline")
|
target_include_directories(sfizz-spline PUBLIC "src/external/spline")
|
||||||
|
|
||||||
|
add_library(sfizz-tunings STATIC "src/external/tunings/src/Tunings.cpp")
|
||||||
|
target_include_directories(sfizz-tunings PUBLIC "src/external/tunings/include")
|
||||||
|
|
||||||
include (CheckLibraryExists)
|
include (CheckLibraryExists)
|
||||||
add_library (sfizz-atomic INTERFACE)
|
add_library (sfizz-atomic INTERFACE)
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
|
|
|
||||||
1
dpf.mk
1
dpf.mk
|
|
@ -99,6 +99,7 @@ SFIZZ_SOURCES = \
|
||||||
src/sfizz/SIMDNEON.cpp \
|
src/sfizz/SIMDNEON.cpp \
|
||||||
src/sfizz/SIMDSSE.cpp \
|
src/sfizz/SIMDSSE.cpp \
|
||||||
src/sfizz/Synth.cpp \
|
src/sfizz/Synth.cpp \
|
||||||
|
src/sfizz/Tuning.cpp \
|
||||||
src/sfizz/Voice.cpp \
|
src/sfizz/Voice.cpp \
|
||||||
src/sfizz/Wavetables.cpp
|
src/sfizz/Wavetables.cpp
|
||||||
|
|
||||||
|
|
|
||||||
70
lv2/sfizz.c
70
lv2/sfizz.c
|
|
@ -61,6 +61,7 @@
|
||||||
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
|
#define SFIZZ_URI "http://sfztools.github.io/sfizz"
|
||||||
#define SFIZZ_PREFIX SFIZZ_URI "#"
|
#define SFIZZ_PREFIX SFIZZ_URI "#"
|
||||||
#define SFIZZ__sfzFile "http://sfztools.github.io/sfizz:sfzfile"
|
#define SFIZZ__sfzFile "http://sfztools.github.io/sfizz:sfzfile"
|
||||||
|
#define SFIZZ__scalaFile "http://sfztools.github.io/sfizz:scalafile"
|
||||||
#define SFIZZ__numVoices "http://sfztools.github.io/sfizz:numvoices"
|
#define SFIZZ__numVoices "http://sfztools.github.io/sfizz:numvoices"
|
||||||
#define SFIZZ__preloadSize "http://sfztools.github.io/sfizz:preload_size"
|
#define SFIZZ__preloadSize "http://sfztools.github.io/sfizz:preload_size"
|
||||||
#define SFIZZ__oversampling "http://sfztools.github.io/sfizz:oversampling"
|
#define SFIZZ__oversampling "http://sfztools.github.io/sfizz:oversampling"
|
||||||
|
|
@ -105,6 +106,8 @@ typedef struct
|
||||||
const float *oversampling_port;
|
const float *oversampling_port;
|
||||||
const float *preload_port;
|
const float *preload_port;
|
||||||
const float *freewheel_port;
|
const float *freewheel_port;
|
||||||
|
const float *scala_root_key_port;
|
||||||
|
const float *tuning_frequency_port;
|
||||||
|
|
||||||
// Atom forge
|
// Atom forge
|
||||||
LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread
|
LV2_Atom_Forge forge; ///< Forge for writing atoms in run thread
|
||||||
|
|
@ -132,6 +135,7 @@ typedef struct
|
||||||
LV2_URID patch_body_uri;
|
LV2_URID patch_body_uri;
|
||||||
LV2_URID state_changed_uri;
|
LV2_URID state_changed_uri;
|
||||||
LV2_URID sfizz_sfz_file_uri;
|
LV2_URID sfizz_sfz_file_uri;
|
||||||
|
LV2_URID sfizz_scala_file_uri;
|
||||||
LV2_URID sfizz_num_voices_uri;
|
LV2_URID sfizz_num_voices_uri;
|
||||||
LV2_URID sfizz_preload_size_uri;
|
LV2_URID sfizz_preload_size_uri;
|
||||||
LV2_URID sfizz_oversampling_uri;
|
LV2_URID sfizz_oversampling_uri;
|
||||||
|
|
@ -142,6 +146,7 @@ typedef struct
|
||||||
sfizz_synth_t *synth;
|
sfizz_synth_t *synth;
|
||||||
bool expect_nominal_block_length;
|
bool expect_nominal_block_length;
|
||||||
char sfz_file_path[MAX_PATH_SIZE];
|
char sfz_file_path[MAX_PATH_SIZE];
|
||||||
|
char scala_file_path[MAX_PATH_SIZE];
|
||||||
int num_voices;
|
int num_voices;
|
||||||
unsigned int preload_size;
|
unsigned int preload_size;
|
||||||
sfizz_oversampling_factor_t oversampling;
|
sfizz_oversampling_factor_t oversampling;
|
||||||
|
|
@ -162,7 +167,9 @@ enum
|
||||||
SFIZZ_POLYPHONY = 5,
|
SFIZZ_POLYPHONY = 5,
|
||||||
SFIZZ_OVERSAMPLING = 6,
|
SFIZZ_OVERSAMPLING = 6,
|
||||||
SFIZZ_PRELOAD = 7,
|
SFIZZ_PRELOAD = 7,
|
||||||
SFIZZ_FREEWHEELING = 8
|
SFIZZ_FREEWHEELING = 8,
|
||||||
|
SFIZZ_SCALA_ROOT_KEY = 9,
|
||||||
|
SFIZZ_TUNING_FREQUENCY = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -186,6 +193,7 @@ sfizz_lv2_map_required_uris(sfizz_plugin_t *self)
|
||||||
self->patch_value_uri = map->map(map->handle, LV2_PATCH__value);
|
self->patch_value_uri = map->map(map->handle, LV2_PATCH__value);
|
||||||
self->state_changed_uri = map->map(map->handle, LV2_STATE__StateChanged);
|
self->state_changed_uri = map->map(map->handle, LV2_STATE__StateChanged);
|
||||||
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
self->sfizz_sfz_file_uri = map->map(map->handle, SFIZZ__sfzFile);
|
||||||
|
self->sfizz_scala_file_uri = map->map(map->handle, SFIZZ__scalaFile);
|
||||||
self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices);
|
self->sfizz_num_voices_uri = map->map(map->handle, SFIZZ__numVoices);
|
||||||
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
|
self->sfizz_preload_size_uri = map->map(map->handle, SFIZZ__preloadSize);
|
||||||
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
|
self->sfizz_oversampling_uri = map->map(map->handle, SFIZZ__oversampling);
|
||||||
|
|
@ -228,6 +236,12 @@ connect_port(LV2_Handle instance,
|
||||||
case SFIZZ_FREEWHEELING:
|
case SFIZZ_FREEWHEELING:
|
||||||
self->freewheel_port = (const float *)data;
|
self->freewheel_port = (const float *)data;
|
||||||
break;
|
break;
|
||||||
|
case SFIZZ_SCALA_ROOT_KEY:
|
||||||
|
self->scala_root_key_port = (const float *)data;
|
||||||
|
break;
|
||||||
|
case SFIZZ_TUNING_FREQUENCY:
|
||||||
|
self->tuning_frequency_port = (const float *)data;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -284,6 +298,7 @@ instantiate(const LV2_Descriptor *descriptor,
|
||||||
self->sample_rate = (float)rate;
|
self->sample_rate = (float)rate;
|
||||||
self->expect_nominal_block_length = false;
|
self->expect_nominal_block_length = false;
|
||||||
self->sfz_file_path[0] = '\0';
|
self->sfz_file_path[0] = '\0';
|
||||||
|
self->scala_file_path[0] = '\0';
|
||||||
self->num_voices = DEFAULT_VOICES;
|
self->num_voices = DEFAULT_VOICES;
|
||||||
self->oversampling = DEFAULT_OVERSAMPLING;
|
self->oversampling = DEFAULT_OVERSAMPLING;
|
||||||
self->preload_size = DEFAULT_PRELOAD;
|
self->preload_size = DEFAULT_PRELOAD;
|
||||||
|
|
@ -420,15 +435,15 @@ deactivate(LV2_Handle instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sfizz_lv2_send_file_path(sfizz_plugin_t *self)
|
sfizz_lv2_send_file_path(sfizz_plugin_t *self, LV2_URID urid, const char *path)
|
||||||
{
|
{
|
||||||
LV2_Atom_Forge_Frame frame;
|
LV2_Atom_Forge_Frame frame;
|
||||||
lv2_atom_forge_frame_time(&self->forge, 0);
|
lv2_atom_forge_frame_time(&self->forge, 0);
|
||||||
lv2_atom_forge_object(&self->forge, &frame, 0, self->patch_set_uri);
|
lv2_atom_forge_object(&self->forge, &frame, 0, self->patch_set_uri);
|
||||||
lv2_atom_forge_key(&self->forge, self->patch_property_uri);
|
lv2_atom_forge_key(&self->forge, self->patch_property_uri);
|
||||||
lv2_atom_forge_urid(&self->forge, self->sfizz_sfz_file_uri);
|
lv2_atom_forge_urid(&self->forge, urid);
|
||||||
lv2_atom_forge_key(&self->forge, self->patch_value_uri);
|
lv2_atom_forge_key(&self->forge, self->patch_value_uri);
|
||||||
lv2_atom_forge_path(&self->forge, self->sfz_file_path, (uint32_t)strlen(self->sfz_file_path));
|
lv2_atom_forge_path(&self->forge, path, (uint32_t)strlen(path));
|
||||||
lv2_atom_forge_pop(&self->forge, &frame);
|
lv2_atom_forge_pop(&self->forge, &frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,6 +492,18 @@ sfizz_lv2_handle_atom_object(sfizz_plugin_t *self, const LV2_Atom_Object *obj)
|
||||||
self->worker->schedule_work(self->worker->handle, null_terminated_atom_size, sfz_file_path);
|
self->worker->schedule_work(self->worker->handle, null_terminated_atom_size, sfz_file_path);
|
||||||
self->check_modification = false;
|
self->check_modification = false;
|
||||||
}
|
}
|
||||||
|
else if (key == self->sfizz_scala_file_uri)
|
||||||
|
{
|
||||||
|
const uint32_t original_atom_size = lv2_atom_total_size((const LV2_Atom *)atom);
|
||||||
|
const uint32_t null_terminated_atom_size = original_atom_size + 1;
|
||||||
|
char atom_buffer[MAX_PATH_SIZE];
|
||||||
|
memcpy(&atom_buffer, atom, original_atom_size);
|
||||||
|
atom_buffer[original_atom_size] = 0; // Null terminate the string for safety
|
||||||
|
LV2_Atom *scala_file_path = (LV2_Atom *)&atom_buffer;
|
||||||
|
scala_file_path->type = self->sfizz_scala_file_uri;
|
||||||
|
self->worker->schedule_work(self->worker->handle, null_terminated_atom_size, scala_file_path);
|
||||||
|
self->check_modification = false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lv2_log_warning(&self->logger, "[sfizz] Unknown or unsupported object\n");
|
lv2_log_warning(&self->logger, "[sfizz] Unknown or unsupported object\n");
|
||||||
|
|
@ -657,11 +684,16 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
lv2_atom_object_get(obj, self->patch_property_uri, &property, 0);
|
||||||
if (!property) // Send the full state
|
if (!property) // Send the full state
|
||||||
{
|
{
|
||||||
sfizz_lv2_send_file_path(self);
|
sfizz_lv2_send_file_path(self, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||||
|
sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||||
}
|
}
|
||||||
else if (property->body == self->sfizz_sfz_file_uri)
|
else if (property->body == self->sfizz_sfz_file_uri)
|
||||||
{
|
{
|
||||||
sfizz_lv2_send_file_path(self);
|
sfizz_lv2_send_file_path(self, self->sfizz_sfz_file_uri, self->sfz_file_path);
|
||||||
|
}
|
||||||
|
else if (property->body == self->sfizz_scala_file_uri)
|
||||||
|
{
|
||||||
|
sfizz_lv2_send_file_path(self, self->sfizz_scala_file_uri, self->scala_file_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -685,6 +717,8 @@ run(LV2_Handle instance, uint32_t sample_count)
|
||||||
// Check and update parameters if needed
|
// Check and update parameters if needed
|
||||||
sfizz_lv2_check_freewheeling(self);
|
sfizz_lv2_check_freewheeling(self);
|
||||||
sfizz_set_volume(self->synth, *(self->volume_port));
|
sfizz_set_volume(self->synth, *(self->volume_port));
|
||||||
|
sfizz_set_scala_root_key(self->synth, *(self->scala_root_key_port));
|
||||||
|
sfizz_set_tuning_frequency(self->synth, *(self->tuning_frequency_port));
|
||||||
sfizz_lv2_check_preload_size(self);
|
sfizz_lv2_check_preload_size(self);
|
||||||
sfizz_lv2_check_oversampling(self);
|
sfizz_lv2_check_oversampling(self);
|
||||||
sfizz_lv2_check_num_voices(self);
|
sfizz_lv2_check_num_voices(self);
|
||||||
|
|
@ -838,6 +872,13 @@ restore(LV2_Handle instance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value = retrieve(handle, self->sfizz_scala_file_uri, &size, &type, &val_flags);
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
lv2_log_note(&self->logger, "[sfizz] Restoring the scale %s\n", (const char *)value);
|
||||||
|
sfizz_load_scala_file(self->synth, (const char *)value);
|
||||||
|
}
|
||||||
|
|
||||||
value = retrieve(handle, self->sfizz_num_voices_uri, &size, &type, &val_flags);
|
value = retrieve(handle, self->sfizz_num_voices_uri, &size, &type, &val_flags);
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
|
|
@ -894,6 +935,14 @@ save(LV2_Handle instance,
|
||||||
self->atom_path_uri,
|
self->atom_path_uri,
|
||||||
LV2_STATE_IS_POD);
|
LV2_STATE_IS_POD);
|
||||||
|
|
||||||
|
// Save the scala file path
|
||||||
|
store(handle,
|
||||||
|
self->sfizz_scala_file_uri,
|
||||||
|
self->scala_file_path,
|
||||||
|
strlen(self->scala_file_path) + 1,
|
||||||
|
self->atom_path_uri,
|
||||||
|
LV2_STATE_IS_POD);
|
||||||
|
|
||||||
// Save the number of voices
|
// Save the number of voices
|
||||||
store(handle,
|
store(handle,
|
||||||
self->sfizz_num_voices_uri,
|
self->sfizz_num_voices_uri,
|
||||||
|
|
@ -952,6 +1001,15 @@ work(LV2_Handle instance,
|
||||||
};
|
};
|
||||||
respond(handle, lv2_atom_total_size(&check_modification_atom), &check_modification_atom);
|
respond(handle, lv2_atom_total_size(&check_modification_atom), &check_modification_atom);
|
||||||
}
|
}
|
||||||
|
else if (atom->type == self->sfizz_scala_file_uri)
|
||||||
|
{
|
||||||
|
const char *scala_file_path = LV2_ATOM_BODY_CONST(atom);
|
||||||
|
if (sfizz_load_scala_file(self->synth, scala_file_path)) {
|
||||||
|
lv2_log_note(&self->logger, "[sfizz] Scala file loaded: %s\n", scala_file_path);
|
||||||
|
} else {
|
||||||
|
lv2_log_error(&self->logger, "[sfizz] Error with %s; no scala file should be loaded\n", scala_file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (atom->type == self->sfizz_num_voices_uri)
|
else if (atom->type == self->sfizz_num_voices_uri)
|
||||||
{
|
{
|
||||||
const int num_voices = *(const int *)LV2_ATOM_BODY_CONST(atom);
|
const int num_voices = *(const int *)LV2_ATOM_BODY_CONST(atom);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,12 @@ midnam:update a lv2:Feature .
|
||||||
"Configuration"@fr ,
|
"Configuration"@fr ,
|
||||||
"Impostazioni"@it .
|
"Impostazioni"@it .
|
||||||
|
|
||||||
|
<@LV2PLUGIN_URI@#tuning>
|
||||||
|
a pg:Group ;
|
||||||
|
lv2:symbol "tuning" ;
|
||||||
|
lv2:name "Tuning",
|
||||||
|
"Accordage"@fr .
|
||||||
|
|
||||||
<@LV2PLUGIN_URI@:sfzfile>
|
<@LV2PLUGIN_URI@:sfzfile>
|
||||||
a lv2:Parameter ;
|
a lv2:Parameter ;
|
||||||
rdfs:label "SFZ file",
|
rdfs:label "SFZ file",
|
||||||
|
|
@ -34,6 +40,14 @@ midnam:update a lv2:Feature .
|
||||||
"File SFZ"@it ;
|
"File SFZ"@it ;
|
||||||
rdfs:range atom:Path .
|
rdfs:range atom:Path .
|
||||||
|
|
||||||
|
<@LV2PLUGIN_URI@:scalafile>
|
||||||
|
a lv2:Parameter ;
|
||||||
|
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||||
|
rdfs:label "Scala file",
|
||||||
|
"Fichier Scala"@fr ,
|
||||||
|
"File Scala"@it ;
|
||||||
|
rdfs:range atom:Path .
|
||||||
|
|
||||||
<@LV2PLUGIN_URI@>
|
<@LV2PLUGIN_URI@>
|
||||||
a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ;
|
a doap:Project, lv2:Plugin, lv2:InstrumentPlugin ;
|
||||||
|
|
||||||
|
|
@ -62,6 +76,7 @@ midnam:update a lv2:Feature .
|
||||||
opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ;
|
opts:supportedOption bufsize:maxBlockLength, bufsize:nominalBlockLength ;
|
||||||
|
|
||||||
patch:writable <@LV2PLUGIN_URI@:sfzfile> ;
|
patch:writable <@LV2PLUGIN_URI@:sfzfile> ;
|
||||||
|
patch:writable <@LV2PLUGIN_URI@:scalafile> ;
|
||||||
|
|
||||||
lv2:port [
|
lv2:port [
|
||||||
a lv2:InputPort, atom:AtomPort ;
|
a lv2:InputPort, atom:AtomPort ;
|
||||||
|
|
@ -242,4 +257,27 @@ midnam:update a lv2:Feature .
|
||||||
lv2:default 0 ;
|
lv2:default 0 ;
|
||||||
lv2:minimum 0 ;
|
lv2:minimum 0 ;
|
||||||
lv2:maximum 1 ;
|
lv2:maximum 1 ;
|
||||||
|
] , [
|
||||||
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
|
lv2:index 9 ;
|
||||||
|
lv2:symbol "scala_root_key" ;
|
||||||
|
lv2:name "Scala root key",
|
||||||
|
"Tonalité de base Scala"@fr ;
|
||||||
|
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||||
|
lv2:portProperty lv2:integer ;
|
||||||
|
lv2:default 60 ;
|
||||||
|
lv2:minimum 0 ;
|
||||||
|
lv2:maximum 127 ;
|
||||||
|
units:unit units:midiNote
|
||||||
|
] , [
|
||||||
|
a lv2:InputPort, lv2:ControlPort ;
|
||||||
|
lv2:index 10 ;
|
||||||
|
lv2:symbol "tuning_frequency" ;
|
||||||
|
lv2:name "Tuning frequency",
|
||||||
|
"Fréquence d'accordage"@fr ;
|
||||||
|
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||||
|
lv2:default 440.0 ;
|
||||||
|
lv2:minimum 300.0 ;
|
||||||
|
lv2:maximum 500.0 ;
|
||||||
|
units:unit units:hz
|
||||||
] .
|
] .
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ set (SFIZZ_SOURCES
|
||||||
sfizz/SfzFilter.cpp
|
sfizz/SfzFilter.cpp
|
||||||
sfizz/Curve.cpp
|
sfizz/Curve.cpp
|
||||||
sfizz/Wavetables.cpp
|
sfizz/Wavetables.cpp
|
||||||
|
sfizz/Tuning.cpp
|
||||||
sfizz/RTSemaphore.cpp
|
sfizz/RTSemaphore.cpp
|
||||||
sfizz/Effects.cpp
|
sfizz/Effects.cpp
|
||||||
sfizz/effects/Nothing.cpp
|
sfizz/effects/Nothing.cpp
|
||||||
|
|
@ -58,7 +59,7 @@ target_sources(sfizz_static PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfi
|
||||||
target_include_directories (sfizz_static PUBLIC .)
|
target_include_directories (sfizz_static PUBLIC .)
|
||||||
target_include_directories (sfizz_static PUBLIC external)
|
target_include_directories (sfizz_static PUBLIC external)
|
||||||
target_link_libraries (sfizz_static PUBLIC absl::strings absl::span)
|
target_link_libraries (sfizz_static PUBLIC absl::strings absl::span)
|
||||||
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid sfizz-atomic)
|
target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-atomic)
|
||||||
set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp")
|
set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp")
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_compile_definitions (sfizz_static PRIVATE _USE_MATH_DEFINES)
|
target_compile_definitions (sfizz_static PRIVATE _USE_MATH_DEFINES)
|
||||||
|
|
@ -95,7 +96,7 @@ if (SFIZZ_SHARED)
|
||||||
target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
|
target_sources(sfizz_shared PRIVATE ${SFIZZ_SOURCES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp)
|
||||||
target_include_directories (sfizz_shared PRIVATE .)
|
target_include_directories (sfizz_shared PRIVATE .)
|
||||||
target_include_directories (sfizz_shared PRIVATE external)
|
target_include_directories (sfizz_shared PRIVATE external)
|
||||||
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-kissfft sfizz-cpuid sfizz-atomic)
|
target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-atomic)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_compile_definitions (sfizz_shared PRIVATE _USE_MATH_DEFINES)
|
target_compile_definitions (sfizz_shared PRIVATE _USE_MATH_DEFINES)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
52
src/sfizz.h
52
src/sfizz.h
|
|
@ -85,6 +85,58 @@ SFIZZ_EXPORTED_API bool sfizz_load_file(sfizz_synth_t* synth, const char* path);
|
||||||
*/
|
*/
|
||||||
SFIZZ_EXPORTED_API bool sfizz_load_string(sfizz_synth_t* synth, const char* path, const char* text);
|
SFIZZ_EXPORTED_API bool sfizz_load_string(sfizz_synth_t* synth, const char* path, const char* text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the tuning from a Scala file loaded from the file system.
|
||||||
|
*
|
||||||
|
* @param synth The sfizz synth.
|
||||||
|
* @param path The path to the file in Scala format.
|
||||||
|
* @return @true when tuning scale loaded OK,
|
||||||
|
* @false if some error occurred.
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API bool sfizz_load_scala_file(sfizz_synth_t* synth, const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the tuning from a Scala file loaded from memory.
|
||||||
|
*
|
||||||
|
* @param synth The sfizz synth.
|
||||||
|
* @param text The contents of the file in Scala format.
|
||||||
|
* @return @true when tuning scale loaded OK,
|
||||||
|
* @false if some error occurred.
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API bool sfizz_load_scala_string(sfizz_synth_t* synth, const char* text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the scala root key.
|
||||||
|
*
|
||||||
|
* @param synth The sfizz synth.
|
||||||
|
* @param root_key The MIDI number of the Scala root key (default 60 for C4).
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API void sfizz_set_scala_root_key(sfizz_synth_t* synth, int root_key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the scala root key.
|
||||||
|
*
|
||||||
|
* @param synth The sfizz synth.
|
||||||
|
* @return The MIDI number of the Scala root key (default 60 for C4).
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API int sfizz_get_scala_root_key(sfizz_synth_t* synth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the reference tuning frequency.
|
||||||
|
*
|
||||||
|
* @param synth The sfizz synth.
|
||||||
|
* @param frequency The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API void sfizz_set_tuning_frequency(sfizz_synth_t* synth, float frequency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the reference tuning frequency.
|
||||||
|
*
|
||||||
|
* @param synth The sfizz synth.
|
||||||
|
* @return The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API float sfizz_get_tuning_frequency(sfizz_synth_t* synth);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the number of regions in the currently loaded SFZ file.
|
* @brief Return the number of regions in the currently loaded SFZ file.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,52 @@ public:
|
||||||
*/
|
*/
|
||||||
bool loadSfzString(const std::string& path, const std::string& text);
|
bool loadSfzString(const std::string& path, const std::string& text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the tuning from a Scala file loaded from the file system.
|
||||||
|
*
|
||||||
|
* @param path The path to the file in Scala format.
|
||||||
|
* @return @true when tuning scale loaded OK,
|
||||||
|
* @false if some error occurred.
|
||||||
|
*/
|
||||||
|
bool loadScalaFile(const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the tuning from a Scala file loaded from memory.
|
||||||
|
*
|
||||||
|
* @param text The contents of the file in Scala format.
|
||||||
|
* @return @true when tuning scale loaded OK,
|
||||||
|
* @false if some error occurred.
|
||||||
|
*/
|
||||||
|
bool loadScalaString(const std::string& text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the scala root key.
|
||||||
|
*
|
||||||
|
* @param rootKey The MIDI number of the Scala root key (default 60 for C4).
|
||||||
|
*/
|
||||||
|
void setScalaRootKey(int rootKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the scala root key.
|
||||||
|
*
|
||||||
|
* @return The MIDI number of the Scala root key (default 60 for C4).
|
||||||
|
*/
|
||||||
|
int getScalaRootKey() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the reference tuning frequency.
|
||||||
|
*
|
||||||
|
* @param frequency The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||||
|
*/
|
||||||
|
void setTuningFrequency(float frequency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the reference tuning frequency.
|
||||||
|
*
|
||||||
|
* @return The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||||
|
*/
|
||||||
|
float getTuningFrequency() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the current number of regions loaded.
|
* @brief Return the current number of regions loaded.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1012,12 +1012,12 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept
|
||||||
bpmSwitched = false;
|
bpmSwitched = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float sfz::Region::getBasePitchVariation(int noteNumber, float velocity) const noexcept
|
float sfz::Region::getBasePitchVariation(float noteNumber, float velocity) const noexcept
|
||||||
{
|
{
|
||||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||||
|
|
||||||
std::uniform_int_distribution<int> pitchDistribution { -pitchRandom, pitchRandom };
|
std::uniform_int_distribution<int> pitchDistribution { -pitchRandom, pitchRandom };
|
||||||
auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center
|
auto pitchVariationInCents = pitchKeytrack * (noteNumber - pitchKeycenter); // note difference with pitch center
|
||||||
pitchVariationInCents += tune; // sample tuning
|
pitchVariationInCents += tune; // sample tuning
|
||||||
pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose
|
pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose
|
||||||
pitchVariationInCents += static_cast<int>(velocity) * pitchVeltrack; // track velocity
|
pitchVariationInCents += static_cast<int>(velocity) * pitchVeltrack; // track velocity
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ struct Region {
|
||||||
* @param velocity
|
* @param velocity
|
||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
float getBasePitchVariation(int noteNumber, float velocity) const noexcept;
|
float getBasePitchVariation(float noteNumber, float velocity) const noexcept;
|
||||||
/**
|
/**
|
||||||
* @brief Get the note-related gain of the region depending on which note has been
|
* @brief Get the note-related gain of the region depending on which note has been
|
||||||
* pressed and at which velocity.
|
* pressed and at which velocity.
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Wavetables.h"
|
#include "Wavetables.h"
|
||||||
#include "Curve.h"
|
#include "Curve.h"
|
||||||
|
#include "Tuning.h"
|
||||||
|
|
||||||
namespace sfz
|
namespace sfz
|
||||||
{
|
{
|
||||||
|
|
@ -27,6 +28,7 @@ struct Resources
|
||||||
FilterPool filterPool { midiState };
|
FilterPool filterPool { midiState };
|
||||||
EQPool eqPool { midiState };
|
EQPool eqPool { midiState };
|
||||||
WavetablePool wavePool;
|
WavetablePool wavePool;
|
||||||
|
Tuning tuning;
|
||||||
|
|
||||||
void setSampleRate(float samplerate)
|
void setSampleRate(float samplerate)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -504,6 +504,36 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sfz::Synth::loadScalaFile(const fs::path& path)
|
||||||
|
{
|
||||||
|
return resources.tuning.loadScalaFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sfz::Synth::loadScalaString(const std::string& text)
|
||||||
|
{
|
||||||
|
return resources.tuning.loadScalaString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::Synth::setScalaRootKey(int rootKey)
|
||||||
|
{
|
||||||
|
resources.tuning.setScalaRootKey(rootKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sfz::Synth::getScalaRootKey() const
|
||||||
|
{
|
||||||
|
return resources.tuning.getScalaRootKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::Synth::setTuningFrequency(float frequency)
|
||||||
|
{
|
||||||
|
resources.tuning.setTuningFrequency(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sfz::Synth::getTuningFrequency() const
|
||||||
|
{
|
||||||
|
return resources.tuning.getTuningFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
sfz::Voice* sfz::Synth::findFreeVoice() noexcept
|
sfz::Voice* sfz::Synth::findFreeVoice() noexcept
|
||||||
{
|
{
|
||||||
auto freeVoice = absl::c_find_if(voices, [](const std::unique_ptr<Voice>& voice) {
|
auto freeVoice = absl::c_find_if(voices, [](const std::unique_ptr<Voice>& voice) {
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,46 @@ public:
|
||||||
* parsing step.
|
* parsing step.
|
||||||
*/
|
*/
|
||||||
void finalizeSfzLoad();
|
void finalizeSfzLoad();
|
||||||
|
/**
|
||||||
|
* @brief Sets the tuning from a Scala file loaded from the file system.
|
||||||
|
*
|
||||||
|
* @param path The path to the file in Scala format.
|
||||||
|
* @return @true when tuning scale loaded OK,
|
||||||
|
* @false if some error occurred.
|
||||||
|
*/
|
||||||
|
bool loadScalaFile(const fs::path& path);
|
||||||
|
/**
|
||||||
|
* @brief Sets the tuning from a Scala file loaded from memory.
|
||||||
|
*
|
||||||
|
* @param text The contents of the file in Scala format.
|
||||||
|
* @return @true when tuning scale loaded OK,
|
||||||
|
* @false if some error occurred.
|
||||||
|
*/
|
||||||
|
bool loadScalaString(const std::string& text);
|
||||||
|
/**
|
||||||
|
* @brief Sets the scala root key.
|
||||||
|
*
|
||||||
|
* @param rootKey The MIDI number of the Scala root key (default 60 for C4).
|
||||||
|
*/
|
||||||
|
void setScalaRootKey(int rootKey);
|
||||||
|
/**
|
||||||
|
* @brief Gets the scala root key.
|
||||||
|
*
|
||||||
|
* @return The MIDI number of the Scala root key (default 60 for C4).
|
||||||
|
*/
|
||||||
|
int getScalaRootKey() const;
|
||||||
|
/**
|
||||||
|
* @brief Sets the reference tuning frequency.
|
||||||
|
*
|
||||||
|
* @param frequency The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||||
|
*/
|
||||||
|
void setTuningFrequency(float frequency);
|
||||||
|
/**
|
||||||
|
* @brief Gets the reference tuning frequency.
|
||||||
|
*
|
||||||
|
* @return The frequency which indicates where standard tuning A4 is (default 440 Hz).
|
||||||
|
*/
|
||||||
|
float getTuningFrequency() const;
|
||||||
/**
|
/**
|
||||||
* @brief Get the current number of regions loaded
|
* @brief Get the current number of regions loaded
|
||||||
*
|
*
|
||||||
|
|
|
||||||
202
src/sfizz/Tuning.cpp
Normal file
202
src/sfizz/Tuning.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||||
|
// license. You should have receive a LICENSE.md file along with the code.
|
||||||
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "Tuning.h"
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "Tunings.h" // Surge tuning library
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace sfz {
|
||||||
|
|
||||||
|
struct Tuning::Impl {
|
||||||
|
public:
|
||||||
|
Impl() { updateKeysFractional12TET(); }
|
||||||
|
|
||||||
|
const Tunings::Tuning& tuning() const { return tuning_; }
|
||||||
|
|
||||||
|
float getKeyFractional12TET(int midiKey) const;
|
||||||
|
|
||||||
|
int rootKey() const { return rootKey_; }
|
||||||
|
float tuningFrequency() const { return tuningFrequency_; }
|
||||||
|
|
||||||
|
void updateScale(const Tunings::Scale& scale);
|
||||||
|
void updateRootKey(int rootKey);
|
||||||
|
void updateTuningFrequency(float tuningFrequency);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateKeysFractional12TET();
|
||||||
|
static Tunings::KeyboardMapping mappingFromParameters(int rootKey, float tuningFrequency);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int defaultRootKey = 60;
|
||||||
|
static constexpr float defaultTuningFrequency = 440.0;
|
||||||
|
int rootKey_ = defaultRootKey;
|
||||||
|
float tuningFrequency_ = defaultTuningFrequency;
|
||||||
|
|
||||||
|
Tunings::Tuning tuning_ {
|
||||||
|
Tunings::evenTemperament12NoteScale(),
|
||||||
|
mappingFromParameters(defaultRootKey, defaultTuningFrequency)
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr int numKeys = Tunings::Tuning::N;
|
||||||
|
std::array<float, numKeys> keysFractional12TET_;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
constexpr int Tuning::Impl::defaultRootKey;
|
||||||
|
constexpr float Tuning::Impl::defaultTuningFrequency;
|
||||||
|
constexpr int Tuning::Impl::numKeys;
|
||||||
|
|
||||||
|
float Tuning::Impl::getKeyFractional12TET(int midiKey) const
|
||||||
|
{
|
||||||
|
return keysFractional12TET_[std::max(0, std::min(numKeys - 1, midiKey))];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tuning::Impl::updateScale(const Tunings::Scale& scale)
|
||||||
|
{
|
||||||
|
tuning_ = Tunings::Tuning(scale, tuning_.keyboardMapping);
|
||||||
|
updateKeysFractional12TET();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tuning::Impl::updateRootKey(int rootKey)
|
||||||
|
{
|
||||||
|
ASSERT(rootKey >= 0);
|
||||||
|
rootKey = std::max(0, rootKey);
|
||||||
|
|
||||||
|
if (rootKey_ == rootKey)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tuning_ = Tunings::Tuning(tuning_.scale, mappingFromParameters(rootKey, tuningFrequency_));
|
||||||
|
rootKey_ = rootKey;
|
||||||
|
updateKeysFractional12TET();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tuning::Impl::updateTuningFrequency(float tuningFrequency)
|
||||||
|
{
|
||||||
|
ASSERT(tuningFrequency >= 0);
|
||||||
|
tuningFrequency = std::max(0.0f, tuningFrequency);
|
||||||
|
|
||||||
|
if (tuningFrequency_ == tuningFrequency)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tuning_ = Tunings::Tuning(tuning_.scale, mappingFromParameters(rootKey_, tuningFrequency));
|
||||||
|
tuningFrequency_ = tuningFrequency;
|
||||||
|
updateKeysFractional12TET();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tuning::Impl::updateKeysFractional12TET()
|
||||||
|
{
|
||||||
|
// mapping of MIDI key to equal temperament key
|
||||||
|
for (int key = 0; key < numKeys; ++key) {
|
||||||
|
double freq = tuning_.frequencyForMidiNote(key);
|
||||||
|
keysFractional12TET_[key] = 12.0 * std::log2(freq / 440.0) + 69.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tunings::KeyboardMapping Tuning::Impl::mappingFromParameters(int rootKey, float tuningFrequency)
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
|
// root note is the start of octave. like Scala
|
||||||
|
#else
|
||||||
|
// root note is the start of next octave. like Sforzando
|
||||||
|
rootKey = std::max(0, rootKey - 12);
|
||||||
|
#endif
|
||||||
|
// fixed frequency of the root note
|
||||||
|
const double rootFrequency = tuningFrequency * std::exp2((rootKey - 69) / 12.0);
|
||||||
|
return Tunings::tuneNoteTo(rootKey, rootFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
Tuning::Tuning()
|
||||||
|
: impl_(new Impl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuning::~Tuning()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tuning::loadScalaFile(const fs::path& path)
|
||||||
|
{
|
||||||
|
const Tunings::KeyboardMapping kbm = impl_->tuning().keyboardMapping;
|
||||||
|
|
||||||
|
fs::ifstream stream(path);
|
||||||
|
if (stream.bad()) {
|
||||||
|
DBG("Cannot open scale file: " << path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tunings::Scale scl;
|
||||||
|
try {
|
||||||
|
scl = Tunings::readSCLStream(stream);
|
||||||
|
}
|
||||||
|
catch (Tunings::TuningError& error) {
|
||||||
|
DBG("Tuning: " << error.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_->updateScale(scl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tuning::loadScalaString(const std::string& text)
|
||||||
|
{
|
||||||
|
const Tunings::KeyboardMapping kbm = impl_->tuning().keyboardMapping;
|
||||||
|
|
||||||
|
std::istringstream stream(text);
|
||||||
|
|
||||||
|
Tunings::Scale scl;
|
||||||
|
try {
|
||||||
|
scl = Tunings::readSCLStream(stream);
|
||||||
|
}
|
||||||
|
catch (Tunings::TuningError& error) {
|
||||||
|
DBG("Tuning: " << error.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_->updateScale(scl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tuning::setScalaRootKey(int rootKey)
|
||||||
|
{
|
||||||
|
impl_->updateRootKey(rootKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Tuning::getScalaRootKey() const
|
||||||
|
{
|
||||||
|
return impl_->rootKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tuning::setTuningFrequency(float frequency)
|
||||||
|
{
|
||||||
|
impl_->updateTuningFrequency(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Tuning::getTuningFrequency() const
|
||||||
|
{
|
||||||
|
return impl_->tuningFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tuning::loadEqualTemperamentScale()
|
||||||
|
{
|
||||||
|
impl_->updateScale(Tunings::evenTemperament12NoteScale());
|
||||||
|
}
|
||||||
|
|
||||||
|
float Tuning::getFrequencyOfKey(int midiKey) const
|
||||||
|
{
|
||||||
|
return impl_->tuning().frequencyForMidiNote(midiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Tuning::getKeyFractional12TET(int midiKey)
|
||||||
|
{
|
||||||
|
return impl_->getKeyFractional12TET(midiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
68
src/sfizz/Tuning.h
Normal file
68
src/sfizz/Tuning.h
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||||
|
// license. You should have receive a LICENSE.md file along with the code.
|
||||||
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "ghc/fs_std.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace sfz {
|
||||||
|
|
||||||
|
class Tuning {
|
||||||
|
public:
|
||||||
|
Tuning();
|
||||||
|
~Tuning();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load a scale from a file in the Scala format.
|
||||||
|
*/
|
||||||
|
bool loadScalaFile(const fs::path& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load a scale from memory in the Scala format.
|
||||||
|
*/
|
||||||
|
bool loadScalaString(const std::string& text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the root key.
|
||||||
|
*/
|
||||||
|
void setScalaRootKey(int rootKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the root key.
|
||||||
|
*/
|
||||||
|
int getScalaRootKey() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the tuning frequency.
|
||||||
|
*/
|
||||||
|
void setTuningFrequency(float frequency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the tuning frequency.
|
||||||
|
*/
|
||||||
|
float getTuningFrequency() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load the equal-temperament scale.
|
||||||
|
*/
|
||||||
|
void loadEqualTemperamentScale();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the MIDI key frequency under the present tuning.
|
||||||
|
*/
|
||||||
|
float getFrequencyOfKey(int midiKey) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the fractional MIDI key reconverted into equal temperament scale.
|
||||||
|
*/
|
||||||
|
float getKeyFractional12TET(int midiKey);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Impl;
|
||||||
|
std::unique_ptr<Impl> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
|
|
@ -81,7 +81,11 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
|
||||||
}
|
}
|
||||||
speedRatio = static_cast<float>(currentPromise->sampleRate / this->sampleRate);
|
speedRatio = static_cast<float>(currentPromise->sampleRate / this->sampleRate);
|
||||||
}
|
}
|
||||||
pitchRatio = region->getBasePitchVariation(number, value);
|
|
||||||
|
// do Scala retuning and reconvert the frequency into a 12TET key number
|
||||||
|
const float numberRetuned = resources.tuning.getKeyFractional12TET(number);
|
||||||
|
|
||||||
|
pitchRatio = region->getBasePitchVariation(numberRetuned, value);
|
||||||
baseVolumedB = region->getBaseVolumedB(number);
|
baseVolumedB = region->getBaseVolumedB(number);
|
||||||
baseGain = region->getBaseGain();
|
baseGain = region->getBaseGain();
|
||||||
if (triggerType != TriggerType::CC)
|
if (triggerType != TriggerType::CC)
|
||||||
|
|
@ -107,7 +111,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
|
||||||
sourcePosition = region->getOffset();
|
sourcePosition = region->getOffset();
|
||||||
triggerDelay = delay;
|
triggerDelay = delay;
|
||||||
initialDelay = delay + static_cast<int>(region->getDelay() * sampleRate);
|
initialDelay = delay + static_cast<int>(region->getDelay() * sampleRate);
|
||||||
baseFrequency = midiNoteFrequency(number);
|
baseFrequency = resources.tuning.getFrequencyOfKey(number);
|
||||||
bendStepFactor = centsFactor(region->bendStep);
|
bendStepFactor = centsFactor(region->bendStep);
|
||||||
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, value, sampleRate);
|
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, value, sampleRate);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,36 @@ bool sfz::Sfizz::loadSfzString(const std::string& path, const std::string& text)
|
||||||
return synth->loadSfzString(path, text);
|
return synth->loadSfzString(path, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sfz::Sfizz::loadScalaFile(const std::string& path)
|
||||||
|
{
|
||||||
|
return synth->loadScalaFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sfz::Sfizz::loadScalaString(const std::string& text)
|
||||||
|
{
|
||||||
|
return synth->loadScalaString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::Sfizz::setScalaRootKey(int rootKey)
|
||||||
|
{
|
||||||
|
return synth->setScalaRootKey(rootKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sfz::Sfizz::getScalaRootKey() const
|
||||||
|
{
|
||||||
|
return synth->getScalaRootKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::Sfizz::setTuningFrequency(float frequency)
|
||||||
|
{
|
||||||
|
return synth->setTuningFrequency(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sfz::Sfizz::getTuningFrequency() const
|
||||||
|
{
|
||||||
|
return synth->getTuningFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
int sfz::Sfizz::getNumRegions() const noexcept
|
int sfz::Sfizz::getNumRegions() const noexcept
|
||||||
{
|
{
|
||||||
return synth->getNumRegions();
|
return synth->getNumRegions();
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,42 @@ bool sfizz_load_string(sfizz_synth_t* synth, const char* path, const char* text)
|
||||||
return self->loadSfzString(path, text);
|
return self->loadSfzString(path, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sfizz_load_scala_file(sfizz_synth_t* synth, const char* path)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->loadScalaFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sfizz_load_scala_string(sfizz_synth_t* synth, const char* text)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->loadScalaString(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfizz_set_scala_root_key(sfizz_synth_t* synth, int root_key)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
self->setScalaRootKey(root_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sfizz_get_scala_root_key(sfizz_synth_t* synth)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->getScalaRootKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfizz_set_tuning_frequency(sfizz_synth_t* synth, float frequency)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
self->setTuningFrequency(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sfizz_get_tuning_frequency(sfizz_synth_t* synth)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->getTuningFrequency();
|
||||||
|
}
|
||||||
|
|
||||||
void sfizz_free(sfizz_synth_t* synth)
|
void sfizz_free(sfizz_synth_t* synth)
|
||||||
{
|
{
|
||||||
delete reinterpret_cast<sfz::Synth*>(synth);
|
delete reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ set(SFIZZ_TEST_SOURCES
|
||||||
FloatHelpersT.cpp
|
FloatHelpersT.cpp
|
||||||
WavetablesT.cpp
|
WavetablesT.cpp
|
||||||
SemaphoreT.cpp
|
SemaphoreT.cpp
|
||||||
|
TuningT.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})
|
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})
|
||||||
|
|
@ -79,4 +80,7 @@ target_link_libraries(sfizz_plot_wavetables PRIVATE sfizz::sfizz)
|
||||||
add_executable(sfizz_file_instrument FileInstrument.cpp)
|
add_executable(sfizz_file_instrument FileInstrument.cpp)
|
||||||
target_link_libraries(sfizz_file_instrument PRIVATE sfizz::sfizz)
|
target_link_libraries(sfizz_file_instrument PRIVATE sfizz::sfizz)
|
||||||
|
|
||||||
|
add_executable(sfizz_tuning Tuning.cpp)
|
||||||
|
target_link_libraries(sfizz_tuning PRIVATE sfizz::sfizz)
|
||||||
|
|
||||||
file(COPY "." DESTINATION ${CMAKE_BINARY_DIR}/tests)
|
file(COPY "." DESTINATION ${CMAKE_BINARY_DIR}/tests)
|
||||||
|
|
|
||||||
106
tests/Tuning.cpp
Normal file
106
tests/Tuning.cpp
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||||
|
// license. You should have receive a LICENSE.md file along with the code.
|
||||||
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "sfizz/Tuning.h"
|
||||||
|
#include "sfizz/SfzHelpers.h"
|
||||||
|
#include "cxxopts.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
static const char *octNoteNames[12] = {
|
||||||
|
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string noteName(int key)
|
||||||
|
{
|
||||||
|
int octNum;
|
||||||
|
int octNoteNum;
|
||||||
|
if (key >= 0) {
|
||||||
|
octNum = key / 12 - 1;
|
||||||
|
octNoteNum = key % 12;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
octNum = -2 - (key + 1) / -12;
|
||||||
|
octNoteNum = (key % 12 + 12) % 12;
|
||||||
|
}
|
||||||
|
return std::string(octNoteNames[octNoteNum]) + std::to_string(octNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
cxxopts::Options options(argv[0], " - command line options");
|
||||||
|
|
||||||
|
options.add_options()
|
||||||
|
("h,help", "Print help")
|
||||||
|
("s,scale", "Path of scala tuning file", cxxopts::value<std::string>())
|
||||||
|
("f,frequency", "Tuning frequency", cxxopts::value<float>()->default_value("440.0"))
|
||||||
|
("r,root-key", "Root key", cxxopts::value<std::string>()->default_value("C5"));
|
||||||
|
|
||||||
|
auto result = options.parse(argc, argv);
|
||||||
|
|
||||||
|
if (result.count("help")) {
|
||||||
|
std::cout << options.help({""}) << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
sfz::Tuning tuning;
|
||||||
|
|
||||||
|
if (result.count("scale")) {
|
||||||
|
if (!tuning.loadScalaFile(result["scale"].as<std::string>())) {
|
||||||
|
fprintf(stderr, "Could not load the scale file.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<uint8_t> noteNumber = sfz::readNoteValue(
|
||||||
|
result["root-key"].as<std::string>());
|
||||||
|
if (!noteNumber) {
|
||||||
|
fprintf(stderr, "The root key is not a valid note name.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tuning.setScalaRootKey(*noteNumber);
|
||||||
|
tuning.setTuningFrequency(result["frequency"].as<float>());
|
||||||
|
|
||||||
|
const int numRows = 3;
|
||||||
|
const int numCols = 4;
|
||||||
|
|
||||||
|
for (int row = 0; row < numRows; ++row) {
|
||||||
|
for (int i = 0; i < 73; ++i)
|
||||||
|
putchar('-');
|
||||||
|
putchar('\n');
|
||||||
|
for (int nthKey = 0; nthKey < 12; ++nthKey) {
|
||||||
|
for (int col = 0; col < numCols; ++col) {
|
||||||
|
const int key = nthKey + (col + row * numCols) * 12;
|
||||||
|
|
||||||
|
std::string label;
|
||||||
|
label.push_back('|');
|
||||||
|
label.append(noteName(key));
|
||||||
|
while (label.size() < 5)
|
||||||
|
label.push_back(' ');
|
||||||
|
label.push_back('|');
|
||||||
|
|
||||||
|
if (col > 0) {
|
||||||
|
for (int i = 0; i < 1; ++i)
|
||||||
|
putchar(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s %10.4f", label.c_str(), tuning.getFrequencyOfKey(key));
|
||||||
|
}
|
||||||
|
putchar(' ');
|
||||||
|
putchar('|');
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 73; ++i)
|
||||||
|
putchar('-');
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
17
tests/TuningT.cpp
Normal file
17
tests/TuningT.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||||
|
// license. You should have receive a LICENSE.md file along with the code.
|
||||||
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
|
#include "sfizz/Tuning.h"
|
||||||
|
#include "sfizz/MathHelpers.h"
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("[Tuning] Default tuning")
|
||||||
|
{
|
||||||
|
sfz::Tuning defaultTuning;
|
||||||
|
|
||||||
|
for (int key = 0; key < 128; ++key)
|
||||||
|
REQUIRE(defaultTuning.getFrequencyOfKey(key) == Approx(midiNoteFrequency(key)));
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue