Sample quality controls in UI
This commit is contained in:
parent
4481bc33b0
commit
94be9902c1
27 changed files with 422 additions and 31 deletions
|
|
@ -3,7 +3,7 @@ version 1.0305
|
|||
header_name {.h}
|
||||
code_name {.cxx}
|
||||
widget_class mainView {open
|
||||
xywh {353 221 800 475} type Double
|
||||
xywh {439 94 800 475} type Double
|
||||
class LogicalGroup visible
|
||||
} {
|
||||
Fl_Box imageContainer_ {
|
||||
|
|
@ -20,7 +20,7 @@ widget_class mainView {open
|
|||
class RoundedGroup
|
||||
} {
|
||||
Fl_Box {} {
|
||||
comment {tag=kTagAbout} selected
|
||||
comment {tag=kTagAbout}
|
||||
image {../resources/logo_text_shaded.png} xywh {35 9 120 60}
|
||||
class AboutButton
|
||||
}
|
||||
|
|
@ -330,6 +330,32 @@ widget_class mainView {open
|
|||
class ValueButton
|
||||
}
|
||||
}
|
||||
Fl_Group {} {
|
||||
label Quality open
|
||||
xywh {530 135 195 100} box ROUNDED_BOX labelsize 12 align 17
|
||||
class TitleGroup
|
||||
} {
|
||||
Fl_Spinner sampleQualitySlider_ {
|
||||
comment {tag=kTagSetSampleQuality}
|
||||
xywh {545 195 80 25} labelsize 12 textsize 12
|
||||
class ValueMenu
|
||||
}
|
||||
Fl_Box {} {
|
||||
label Sample
|
||||
xywh {545 155 80 25} labelsize 12
|
||||
class ValueLabel
|
||||
}
|
||||
Fl_Box {} {
|
||||
label Oscillator
|
||||
xywh {630 155 80 25} labelsize 12
|
||||
class ValueLabel
|
||||
}
|
||||
Fl_Spinner oscillatorQualitySlider_ {
|
||||
comment {tag=kTagSetOscillatorQuality} selected
|
||||
xywh {630 195 80 25} labelsize 12 textsize 12
|
||||
class ValueMenu
|
||||
}
|
||||
}
|
||||
}
|
||||
Fl_Box piano_ {
|
||||
xywh {5 400 790 70} labelsize 12
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ EditRange EditRange::get(EditId id)
|
|||
return { 440, 300, 500 };
|
||||
case EditId::StretchTuning:
|
||||
return { 0, 0, 1 };
|
||||
case EditId::SampleQuality:
|
||||
return { 1, 0, 10 };
|
||||
case EditId::OscillatorQuality:
|
||||
return { 1, 0, 3 };
|
||||
case EditId::UIActivePanel:
|
||||
return { 0, 0, 255 };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ enum class EditId : int {
|
|||
ScalaRootKey,
|
||||
TuningFrequency,
|
||||
StretchTuning,
|
||||
SampleQuality,
|
||||
OscillatorQuality,
|
||||
CanEditUserFilesDir,
|
||||
UserFilesDir,
|
||||
FallbackFilesDir,
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
|||
kTagSetScalaRootKey,
|
||||
kTagSetTuningFrequency,
|
||||
kTagSetStretchedTuning,
|
||||
kTagSetSampleQuality,
|
||||
kTagSetOscillatorQuality,
|
||||
kTagSetCCVolume,
|
||||
kTagSetCCPan,
|
||||
kTagChooseUserFilesDir,
|
||||
|
|
@ -112,6 +114,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
|
|||
CTextLabel* tuningFrequencyLabel_ = nullptr;
|
||||
CControl *stretchedTuningSlider_ = nullptr;
|
||||
CTextLabel* stretchedTuningLabel_ = nullptr;
|
||||
SValueMenu *sampleQualitySlider_ = nullptr;
|
||||
SValueMenu *oscillatorQualitySlider_ = nullptr;
|
||||
CTextLabel* keyswitchLabel_ = nullptr;
|
||||
CTextLabel* keyswitchInactiveLabel_ = nullptr;
|
||||
CTextLabel* keyswitchBadge_ = nullptr;
|
||||
|
|
@ -390,6 +394,24 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
|
|||
updateStretchedTuningLabel(value);
|
||||
}
|
||||
break;
|
||||
case EditId::SampleQuality:
|
||||
{
|
||||
const int value = static_cast<int>(v.to_float());
|
||||
if (CControl* slider = sampleQualitySlider_) {
|
||||
slider->setValue(float(value));
|
||||
slider->invalid();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EditId::OscillatorQuality:
|
||||
{
|
||||
const int value = static_cast<int>(v.to_float());
|
||||
if (CControl* slider = oscillatorQualitySlider_) {
|
||||
slider->setValue(float(value));
|
||||
slider->invalid();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EditId::CanEditUserFilesDir:
|
||||
{
|
||||
if (STitleContainer* group = userFilesGroup_)
|
||||
|
|
@ -920,6 +942,8 @@ void Editor::Impl::createFrameContents()
|
|||
adjustMinMaxToEditRange(tuningFrequencySlider_, EditId::TuningFrequency);
|
||||
tuningFrequencySlider_->setWheelInc(0.1f / EditRange::get(EditId::TuningFrequency).extent());
|
||||
adjustMinMaxToEditRange(stretchedTuningSlider_, EditId::StretchTuning);
|
||||
adjustMinMaxToEditRange(sampleQualitySlider_, EditId::SampleQuality);
|
||||
adjustMinMaxToEditRange(oscillatorQualitySlider_, EditId::OscillatorQuality);
|
||||
|
||||
for (int value : {1, 2, 4, 8, 16, 32, 64, 96, 128, 160, 192, 224, 256})
|
||||
numVoicesSlider_->addEntry(std::to_string(value), value);
|
||||
|
|
@ -1000,6 +1024,37 @@ void Editor::Impl::createFrameContents()
|
|||
menu->addEntry("Open SFZ folder", kTagOpenSfzFolder);
|
||||
}
|
||||
|
||||
if (SValueMenu *menu = sampleQualitySlider_) {
|
||||
static const std::array<const char*, 11> labels {{
|
||||
"Nearest", "Linear", "Polynomial",
|
||||
"Sinc 8", "Sinc 12", "Sinc 16", "Sinc 24",
|
||||
"Sinc 36", "Sinc 48", "Sinc 60", "Sinc 72",
|
||||
}};
|
||||
for (size_t i = 0; i < labels.size(); ++i)
|
||||
menu->addEntry(labels[i], float(i));
|
||||
menu->setValueToStringFunction2([](float value, std::string& result, CParamDisplay*) -> bool {
|
||||
int index = int(value);
|
||||
if (index < 0 || unsigned(index) >= labels.size())
|
||||
return false;
|
||||
result = labels[unsigned(index)];
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (SValueMenu *menu = oscillatorQualitySlider_) {
|
||||
static const std::array<const char*, 4> labels {{
|
||||
"Nearest", "Linear", "High", "Dual-High",
|
||||
}};
|
||||
for (size_t i = 0; i < labels.size(); ++i)
|
||||
menu->addEntry(labels[i], float(i));
|
||||
menu->setValueToStringFunction2([](float value, std::string& result, CParamDisplay*) -> bool {
|
||||
int index = int(value);
|
||||
if (index < 0 || unsigned(index) >= labels.size())
|
||||
return false;
|
||||
result = labels[unsigned(index)];
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (SPiano* piano = piano_) {
|
||||
piano->onKeyPressed = [this](unsigned key, float vel) {
|
||||
uint8_t msg[3];
|
||||
|
|
@ -1765,6 +1820,14 @@ void Editor::Impl::valueChanged(CControl* ctl)
|
|||
updateTuningFrequencyLabel(value);
|
||||
break;
|
||||
|
||||
case kTagSetSampleQuality:
|
||||
ctrl.uiSendValue(EditId::SampleQuality, value);
|
||||
break;
|
||||
|
||||
case kTagSetOscillatorQuality:
|
||||
ctrl.uiSendValue(EditId::OscillatorQuality, value);
|
||||
break;
|
||||
|
||||
case kTagSetStretchedTuning:
|
||||
ctrl.uiSendValue(EditId::StretchTuning, value);
|
||||
updateStretchedTuningLabel(value);
|
||||
|
|
|
|||
|
|
@ -169,6 +169,18 @@ view__63->addView(view__64);
|
|||
auto* const view__65 = createValueButton(CRect(20, 60, 120, 85), kTagChooseUserFilesDir, "DefaultPath", kCenterText, 12);
|
||||
userFilesDirButton_ = view__65;
|
||||
view__63->addView(view__65);
|
||||
auto* const view__66 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
|
||||
piano_ = view__66;
|
||||
view__0->addView(view__66);
|
||||
auto* const view__66 = createTitleGroup(CRect(525, 26, 720, 126), -1, "Quality", kCenterText, 12);
|
||||
view__46->addView(view__66);
|
||||
auto* const view__67 = createValueMenu(CRect(15, 60, 95, 85), kTagSetSampleQuality, "", kCenterText, 12);
|
||||
sampleQualitySlider_ = view__67;
|
||||
view__66->addView(view__67);
|
||||
auto* const view__68 = createValueLabel(CRect(15, 20, 95, 45), -1, "Sample", kCenterText, 12);
|
||||
view__66->addView(view__68);
|
||||
auto* const view__69 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oscillator", kCenterText, 12);
|
||||
view__66->addView(view__69);
|
||||
auto* const view__70 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12);
|
||||
oscillatorQualitySlider_ = view__70;
|
||||
view__66->addView(view__70);
|
||||
auto* const view__71 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
|
||||
piano_ = view__71;
|
||||
view__0->addView(view__71);
|
||||
|
|
|
|||
|
|
@ -236,6 +236,12 @@ connect_port(LV2_Handle instance,
|
|||
case SFIZZ_STRETCH_TUNING:
|
||||
self->stretch_tuning_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_SAMPLE_QUALITY:
|
||||
self->sample_quality_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_OSCILLATOR_QUALITY:
|
||||
self->oscillator_quality_port = (const float *)data;
|
||||
break;
|
||||
case SFIZZ_ACTIVE_VOICES:
|
||||
self->active_voices_port = (float *)data;
|
||||
break;
|
||||
|
|
@ -976,6 +982,8 @@ run(LV2_Handle instance, uint32_t sample_count)
|
|||
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_set_sample_quality(self->synth, SFIZZ_PROCESS_LIVE, (int)(*self->sample_quality_port));
|
||||
sfizz_set_oscillator_quality(self->synth, SFIZZ_PROCESS_LIVE, (int)(*self->oscillator_quality_port));
|
||||
sfizz_lv2_check_stretch_tuning(self);
|
||||
sfizz_lv2_check_preload_size(self);
|
||||
sfizz_lv2_check_oversampling(self);
|
||||
|
|
|
|||
|
|
@ -315,8 +315,30 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 1.0 ;
|
||||
units:unit units:coef
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 13 ;
|
||||
lv2:symbol "sample_quality" ;
|
||||
lv2:name "Sample quality",
|
||||
"Qualité des échantillons"@fr ,
|
||||
"Qualità del campione"@it ;
|
||||
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||
lv2:default 1.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 10.0
|
||||
] , [
|
||||
a lv2:InputPort, lv2:ControlPort ;
|
||||
lv2:index 14 ;
|
||||
lv2:symbol "oscillator_quality" ;
|
||||
lv2:name "Oscillator quality",
|
||||
"Qualité des oscillateurs"@fr ,
|
||||
"Qualità dell'oscillatore"@it ;
|
||||
pg:group <@LV2PLUGIN_URI@#tuning> ;
|
||||
lv2:default 1.0 ;
|
||||
lv2:minimum 0.0 ;
|
||||
lv2:maximum 3.0
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 15 ;
|
||||
lv2:symbol "active_voices" ;
|
||||
lv2:name "Active voices",
|
||||
"Voix utilisées"@fr ;
|
||||
|
|
@ -327,7 +349,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 256 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 14 ;
|
||||
lv2:index 16 ;
|
||||
lv2:symbol "num_curves" ;
|
||||
lv2:name "Number of curves",
|
||||
"Nombre de courbes"@fr ;
|
||||
|
|
@ -338,7 +360,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 15 ;
|
||||
lv2:index 17 ;
|
||||
lv2:symbol "num_masters" ;
|
||||
lv2:name "Number of masters",
|
||||
"Nombre de maîtres"@fr ;
|
||||
|
|
@ -349,7 +371,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 16 ;
|
||||
lv2:index 18 ;
|
||||
lv2:symbol "num_groups" ;
|
||||
lv2:name "Number of groups",
|
||||
"Nombre de groupes"@fr ;
|
||||
|
|
@ -360,7 +382,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 17 ;
|
||||
lv2:index 19 ;
|
||||
lv2:symbol "num_regions" ;
|
||||
lv2:name "Number of regions",
|
||||
"Nombre de régions"@fr ;
|
||||
|
|
@ -371,7 +393,7 @@ midnam:update a lv2:Feature .
|
|||
lv2:maximum 65535 ;
|
||||
] , [
|
||||
a lv2:OutputPort, lv2:ControlPort ;
|
||||
lv2:index 18 ;
|
||||
lv2:index 20 ;
|
||||
lv2:symbol "num_samples" ;
|
||||
lv2:name "Number of samples",
|
||||
"Nombre d'échantillons"@fr ;
|
||||
|
|
|
|||
|
|
@ -60,12 +60,14 @@ enum
|
|||
SFIZZ_SCALA_ROOT_KEY = 10,
|
||||
SFIZZ_TUNING_FREQUENCY = 11,
|
||||
SFIZZ_STRETCH_TUNING = 12,
|
||||
SFIZZ_ACTIVE_VOICES = 13,
|
||||
SFIZZ_NUM_CURVES = 14,
|
||||
SFIZZ_NUM_MASTERS = 15,
|
||||
SFIZZ_NUM_GROUPS = 16,
|
||||
SFIZZ_NUM_REGIONS = 17,
|
||||
SFIZZ_NUM_SAMPLES = 18,
|
||||
SFIZZ_SAMPLE_QUALITY = 13,
|
||||
SFIZZ_OSCILLATOR_QUALITY = 14,
|
||||
SFIZZ_ACTIVE_VOICES = 15,
|
||||
SFIZZ_NUM_CURVES = 16,
|
||||
SFIZZ_NUM_MASTERS = 17,
|
||||
SFIZZ_NUM_GROUPS = 18,
|
||||
SFIZZ_NUM_REGIONS = 19,
|
||||
SFIZZ_NUM_SAMPLES = 20,
|
||||
};
|
||||
|
||||
// For use with instance-access
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ struct sfizz_plugin_t
|
|||
const float *scala_root_key_port {};
|
||||
const float *tuning_frequency_port {};
|
||||
const float *stretch_tuning_port {};
|
||||
const float *sample_quality_port {};
|
||||
const float *oscillator_quality_port {};
|
||||
float *active_voices_port {};
|
||||
float *num_curves_port {};
|
||||
float *num_masters_port {};
|
||||
|
|
|
|||
|
|
@ -324,6 +324,12 @@ port_event(LV2UI_Handle ui,
|
|||
case SFIZZ_STRETCH_TUNING:
|
||||
self->uiReceiveValue(EditId::StretchTuning, v);
|
||||
break;
|
||||
case SFIZZ_SAMPLE_QUALITY:
|
||||
self->uiReceiveValue(EditId::SampleQuality, v);
|
||||
break;
|
||||
case SFIZZ_OSCILLATOR_QUALITY:
|
||||
self->uiReceiveValue(EditId::OscillatorQuality, v);
|
||||
break;
|
||||
case SFIZZ_ACTIVE_VOICES:
|
||||
self->uiReceiveValue(EditId::UINumActiveVoices, v);
|
||||
break;
|
||||
|
|
@ -582,6 +588,12 @@ void sfizz_ui_t::uiSendValue(EditId id, const EditValue& v)
|
|||
case EditId::StretchTuning:
|
||||
sendFloat(SFIZZ_STRETCH_TUNING, v.to_float());
|
||||
break;
|
||||
case EditId::SampleQuality:
|
||||
sendFloat(SFIZZ_SAMPLE_QUALITY, v.to_float());
|
||||
break;
|
||||
case EditId::OscillatorQuality:
|
||||
sendFloat(SFIZZ_OSCILLATOR_QUALITY, v.to_float());
|
||||
break;
|
||||
case EditId::SfzFile:
|
||||
sendPath(sfizz_sfz_file_uri, v.to_string());
|
||||
break;
|
||||
|
|
@ -635,6 +647,12 @@ void sfizz_ui_t::uiTouch(EditId id, bool t)
|
|||
case EditId::StretchTuning:
|
||||
touch->touch(touch->handle, SFIZZ_STRETCH_TUNING, t);
|
||||
break;
|
||||
case EditId::SampleQuality:
|
||||
touch->touch(touch->handle, SFIZZ_SAMPLE_QUALITY, t);
|
||||
break;
|
||||
case EditId::OscillatorQuality:
|
||||
touch->touch(touch->handle, SFIZZ_OSCILLATOR_QUALITY, t);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,14 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
SfizzRange::getForParameter(kPidStretchedTuning).createParameter(
|
||||
Steinberg::String("Stretched tuning"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kCanAutomate, Vst::kRootUnitId));
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidSampleQuality).createParameter(
|
||||
Steinberg::String("Sample quality"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId));
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidOscillatorQuality).createParameter(
|
||||
Steinberg::String("Oscillator quality"), pid++, nullptr,
|
||||
0, Vst::ParameterInfo::kNoFlags, Vst::kRootUnitId));
|
||||
|
||||
// MIDI special controllers
|
||||
parameters.addParameter(Steinberg::String("Aftertouch"), nullptr, 0, 0.5, 0, pid++, Vst::kRootUnitId);
|
||||
|
|
@ -73,7 +81,7 @@ tresult PLUGIN_API SfizzVstControllerNoUi::initialize(FUnknown* context)
|
|||
shortTitle.printf("CC%u", i);
|
||||
|
||||
parameters.addParameter(
|
||||
SfizzRange::getForParameter(kPidStretchedTuning).createParameter(
|
||||
SfizzRange::getForParameter(kPidCC0 + i).createParameter(
|
||||
title, pid++, nullptr, 0, Vst::ParameterInfo::kCanAutomate,
|
||||
Vst::kRootUnitId, shortTitle));
|
||||
}
|
||||
|
|
@ -174,6 +182,8 @@ tresult PLUGIN_API SfizzVstControllerNoUi::setComponentState(IBStream* stream)
|
|||
setParam(kPidScalaRootKey, s.scalaRootKey);
|
||||
setParam(kPidTuningFrequency, s.tuningFrequency);
|
||||
setParam(kPidStretchedTuning, s.stretchedTuning);
|
||||
setParam(kPidSampleQuality, s.sampleQuality);
|
||||
setParam(kPidOscillatorQuality, s.oscillatorQuality);
|
||||
|
||||
uint32 ccLimit = uint32(std::min(s.controllers.size(), size_t(sfz::config::numCCs)));
|
||||
for (uint32 cc = 0; cc < ccLimit; ++cc) {
|
||||
|
|
|
|||
|
|
@ -279,6 +279,12 @@ void PLUGIN_API SfizzVstEditor::update(FUnknown* changedUnknown, int32 message)
|
|||
case kPidStretchedTuning:
|
||||
uiReceiveValue(EditId::StretchTuning, range.denormalize(value));
|
||||
break;
|
||||
case kPidSampleQuality:
|
||||
uiReceiveValue(EditId::SampleQuality, range.denormalize(value));
|
||||
break;
|
||||
case kPidOscillatorQuality:
|
||||
uiReceiveValue(EditId::OscillatorQuality, range.denormalize(value));
|
||||
break;
|
||||
default:
|
||||
if (id >= kPidCC0 && id <= kPidCCLast) {
|
||||
int cc = int(id - kPidCC0);
|
||||
|
|
@ -373,6 +379,12 @@ void SfizzVstEditor::uiSendValue(EditId id, const EditValue& v)
|
|||
case EditId::StretchTuning:
|
||||
normalizeAndSet(kPidStretchedTuning, v.to_float());
|
||||
break;
|
||||
case EditId::SampleQuality:
|
||||
normalizeAndSet(kPidSampleQuality, v.to_float());
|
||||
break;
|
||||
case EditId::OscillatorQuality:
|
||||
normalizeAndSet(kPidOscillatorQuality, v.to_float());
|
||||
break;
|
||||
|
||||
case EditId::UserFilesDir:
|
||||
SfizzPaths::setSfzConfigDefaultPath(fs::u8path(v.to_string()));
|
||||
|
|
@ -479,6 +491,8 @@ Vst::ParamID SfizzVstEditor::parameterOfEditId(EditId id)
|
|||
case EditId::ScalaRootKey: return kPidScalaRootKey;
|
||||
case EditId::TuningFrequency: return kPidTuningFrequency;
|
||||
case EditId::StretchTuning: return kPidStretchedTuning;
|
||||
case EditId::SampleQuality: return kPidSampleQuality;
|
||||
case EditId::OscillatorQuality: return kPidOscillatorQuality;
|
||||
default:
|
||||
if (editIdIsCC(id))
|
||||
return kPidCC0 + ccForEditId(id);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ enum {
|
|||
kPidScalaRootKey,
|
||||
kPidTuningFrequency,
|
||||
kPidStretchedTuning,
|
||||
kPidSampleQuality,
|
||||
kPidOscillatorQuality,
|
||||
kPidAftertouch,
|
||||
kPidPitchBend,
|
||||
kPidCC0,
|
||||
|
|
@ -68,6 +70,10 @@ struct SfizzRange {
|
|||
return {440.0, 300.0, 500.0};
|
||||
case kPidStretchedTuning:
|
||||
return {0.0, 0.0, 1.0};
|
||||
case kPidSampleQuality:
|
||||
return {1.0, 0.0, 10.0};
|
||||
case kPidOscillatorQuality:
|
||||
return {1.0, 0.0, 3.0};
|
||||
case kPidAftertouch:
|
||||
return {0.0, 0.0, 1.0};
|
||||
case kPidPitchBend:
|
||||
|
|
|
|||
|
|
@ -287,6 +287,8 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
|||
synth.loadStretchTuningByRatio(_state.stretchedTuning);
|
||||
_currentStretchedTuning = _state.stretchedTuning;
|
||||
}
|
||||
synth.setSampleQuality(sfz::Sfizz::ProcessLive, _state.sampleQuality);
|
||||
synth.setOscillatorQuality(sfz::Sfizz::ProcessLive, _state.oscillatorQuality);
|
||||
|
||||
synth.renderBlock(outputs, numFrames, numChannels);
|
||||
|
||||
|
|
@ -380,6 +382,12 @@ void SfizzVstProcessor::playOrderedParameter(int32 sampleOffset, Vst::ParamID id
|
|||
case kPidStretchedTuning:
|
||||
_state.stretchedTuning = range.denormalize(value);
|
||||
break;
|
||||
case kPidSampleQuality:
|
||||
_state.sampleQuality = static_cast<int32>(range.denormalize(value));
|
||||
break;
|
||||
case kPidOscillatorQuality:
|
||||
_state.oscillatorQuality = static_cast<int32>(range.denormalize(value));
|
||||
break;
|
||||
case kPidAftertouch:
|
||||
synth.aftertouch(sampleOffset, fastRound(value * 127.0));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ tresult SfizzVstState::load(IBStream* state)
|
|||
if (!s.readInt64u(version))
|
||||
return kResultFalse;
|
||||
|
||||
if (version > currentStateVersion)
|
||||
return kResultFalse;
|
||||
|
||||
if (const char* str = s.readStr8())
|
||||
sfzFile = str;
|
||||
else
|
||||
|
|
@ -58,6 +61,18 @@ tresult SfizzVstState::load(IBStream* state)
|
|||
stretchedTuning = defaults.stretchedTuning;
|
||||
}
|
||||
|
||||
if (version >= 3) {
|
||||
if (!s.readInt32(sampleQuality))
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.readInt32(oscillatorQuality))
|
||||
return kResultFalse;
|
||||
}
|
||||
else {
|
||||
sampleQuality = defaults.sampleQuality;
|
||||
oscillatorQuality = defaults.oscillatorQuality;
|
||||
}
|
||||
|
||||
controllers.clear();
|
||||
if (version >= 2) {
|
||||
uint32 count;
|
||||
|
|
@ -77,9 +92,6 @@ tresult SfizzVstState::load(IBStream* state)
|
|||
controllers.shrink_to_fit();
|
||||
}
|
||||
|
||||
if (version > 2)
|
||||
return kResultFalse;
|
||||
|
||||
return kResultTrue;
|
||||
}
|
||||
|
||||
|
|
@ -117,6 +129,12 @@ tresult SfizzVstState::store(IBStream* state) const
|
|||
if (!s.writeFloat(stretchedTuning))
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.writeInt32(sampleQuality))
|
||||
return kResultFalse;
|
||||
|
||||
if (!s.writeInt32(oscillatorQuality))
|
||||
return kResultFalse;
|
||||
|
||||
{
|
||||
uint32 ccCount = 0;
|
||||
uint32 ccLimit = uint32(std::min(controllers.size(), size_t(0x10000)));
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@ public:
|
|||
int32 scalaRootKey = 60;
|
||||
float tuningFrequency = 440.0;
|
||||
float stretchedTuning = 0.0;
|
||||
int32 sampleQuality = 1;
|
||||
int32 oscillatorQuality = 1;
|
||||
std::vector<absl::optional<float>> controllers;
|
||||
|
||||
static constexpr uint64 currentStateVersion = 2;
|
||||
static constexpr uint64 currentStateVersion = 3;
|
||||
|
||||
tresult load(IBStream* state);
|
||||
tresult store(IBStream* state) const;
|
||||
|
|
|
|||
38
src/sfizz.h
38
src/sfizz.h
|
|
@ -648,7 +648,7 @@ SFIZZ_EXPORTED_API bool sfizz_set_oversampling_factor(sfizz_synth_t* synth, sfiz
|
|||
* @param synth The synth.
|
||||
* @param[in] mode The processing mode.
|
||||
*
|
||||
* @return The sample quality for the given mode, in the range 1 to 10.
|
||||
* @return The sample quality for the given mode, in the range 0 to 10.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode);
|
||||
|
||||
|
|
@ -663,13 +663,47 @@ SFIZZ_EXPORTED_API int sfizz_get_sample_quality(sfizz_synth_t* synth, sfizz_proc
|
|||
*
|
||||
* @param synth The synth.
|
||||
* @param[in] mode The processing mode.
|
||||
* @param[in] quality The desired sample quality, in the range 1 to 10.
|
||||
* @param[in] quality The desired sample quality, in the range 0 to 10.
|
||||
*
|
||||
* @par Thread-safety constraints
|
||||
* - @b RT: the function must be invoked from the Real-time thread
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode, int quality);
|
||||
|
||||
/**
|
||||
* @brief Get the default oscillator quality.
|
||||
*
|
||||
* This is the quality setting which the engine uses when the instrument
|
||||
* does not use the opcode `oscillator_quality`. The engine uses distinct
|
||||
* default quality settings for live mode and freewheeling mode,
|
||||
* which both can be accessed by the means of this function.
|
||||
* @since 0.6.0
|
||||
*
|
||||
* @param synth The synth.
|
||||
* @param[in] mode The processing mode.
|
||||
*
|
||||
* @return The oscillator quality for the given mode, in the range 0 to 10.
|
||||
*/
|
||||
SFIZZ_EXPORTED_API int sfizz_get_oscillator_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Set the default oscillator quality.
|
||||
*
|
||||
* This is the quality setting which the engine uses when the instrument
|
||||
* does not use the opcode `oscillator_quality`. The engine uses distinct
|
||||
* default quality settings for live mode and freewheeling mode,
|
||||
* which both can be accessed by the means of this function.
|
||||
* @since 0.6.0
|
||||
*
|
||||
* @param synth The synth.
|
||||
* @param[in] mode The processing mode.
|
||||
* @param[in] quality The desired oscillator quality, in the range 0 to 10.
|
||||
*
|
||||
* @par Thread-safety constraints
|
||||
* - @b RT: the function must be invoked from the Real-time thread
|
||||
*/
|
||||
SFIZZ_EXPORTED_API void sfizz_set_oscillator_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode, int quality);
|
||||
|
||||
/**
|
||||
* @brief Set the global instrument volume.
|
||||
* @since 0.2.0
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ public:
|
|||
*
|
||||
* @param[in] mode The processing mode.
|
||||
*
|
||||
* @return The sample quality for the given mode, in the range 1 to 10.
|
||||
* @return The sample quality for the given mode, in the range 0 to 10.
|
||||
*/
|
||||
int getSampleQuality(ProcessMode mode);
|
||||
|
||||
|
|
@ -305,13 +305,46 @@ public:
|
|||
* @since 0.4.0
|
||||
*
|
||||
* @param[in] mode The processing mode.
|
||||
* @param[in] quality The desired sample quality, in the range 1 to 10.
|
||||
* @param[in] quality The desired sample quality, in the range 0 to 10.
|
||||
*
|
||||
* @par Thread-safety constraints
|
||||
* - @b RT: the function must be invoked from the Real-time thread
|
||||
*/
|
||||
void setSampleQuality(ProcessMode mode, int quality);
|
||||
|
||||
/**
|
||||
* @brief Get the default oscillator quality.
|
||||
*
|
||||
* This is the quality setting which the engine uses when the instrument
|
||||
* does not use the opcode `oscillator_quality`. The engine uses distinct
|
||||
* default quality settings for live mode and freewheeling mode,
|
||||
* which both can be accessed by the means of this function.
|
||||
* @since 0.6.0
|
||||
*
|
||||
* @param[in] mode The processing mode.
|
||||
*
|
||||
* @return The oscillator quality for the given mode, in the range 0 to 3.
|
||||
*/
|
||||
int getOscillatorQuality(ProcessMode mode);
|
||||
|
||||
/**
|
||||
* @brief Set the default oscillator quality.
|
||||
*
|
||||
* This is the quality setting which the engine uses when the instrument
|
||||
* does not use the opcode `oscillator_quality`. The engine uses distinct
|
||||
* default quality settings for live mode and freewheeling mode,
|
||||
* which both can be accessed by the means of this function.
|
||||
*
|
||||
* @since 0.6.0
|
||||
*
|
||||
* @param[in] mode The processing mode.
|
||||
* @param[in] quality The desired oscillator quality, in the range 0 to 3.
|
||||
*
|
||||
* @par Thread-safety constraints
|
||||
* - @b RT: the function must be invoked from the Real-time thread
|
||||
*/
|
||||
void setOscillatorQuality(ProcessMode mode, int quality);
|
||||
|
||||
/**
|
||||
* @brief Return the current value for the volume, in dB.
|
||||
* @since 0.2.0
|
||||
|
|
|
|||
|
|
@ -314,7 +314,8 @@ namespace Default
|
|||
|
||||
// Various defaut values
|
||||
// e.g. "additional" or multiple defautl values
|
||||
constexpr int freewheelingQuality { 10 };
|
||||
constexpr int freewheelingSampleQuality { 10 };
|
||||
constexpr int freewheelingOscillatorQuality { 3 };
|
||||
constexpr float globalVolume { -7.35f };
|
||||
constexpr float defaultEQFreq [numEQs] { 50.0f, 500.0f, 5000.0f };
|
||||
} // namespace Default
|
||||
|
|
|
|||
|
|
@ -1645,9 +1645,9 @@ int Synth::getSampleQuality(ProcessMode mode)
|
|||
|
||||
void Synth::setSampleQuality(ProcessMode mode, int quality)
|
||||
{
|
||||
SFIZZ_CHECK(quality >= 1 && quality <= 10);
|
||||
SFIZZ_CHECK(quality >= 0 && quality <= 10);
|
||||
Impl& impl = *impl_;
|
||||
quality = clamp(quality, 1, 10);
|
||||
quality = clamp(quality, 0, 10);
|
||||
|
||||
switch (mode) {
|
||||
case ProcessLive:
|
||||
|
|
@ -1662,6 +1662,39 @@ void Synth::setSampleQuality(ProcessMode mode, int quality)
|
|||
}
|
||||
}
|
||||
|
||||
int Synth::getOscillatorQuality(ProcessMode mode)
|
||||
{
|
||||
Impl& impl = *impl_;
|
||||
switch (mode) {
|
||||
case ProcessLive:
|
||||
return impl.resources_.synthConfig.liveOscillatorQuality;
|
||||
case ProcessFreewheeling:
|
||||
return impl.resources_.synthConfig.freeWheelingOscillatorQuality;
|
||||
default:
|
||||
SFIZZ_CHECK(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Synth::setOscillatorQuality(ProcessMode mode, int quality)
|
||||
{
|
||||
SFIZZ_CHECK(quality >= 0 && quality <= 3);
|
||||
Impl& impl = *impl_;
|
||||
quality = clamp(quality, 0, 3);
|
||||
|
||||
switch (mode) {
|
||||
case ProcessLive:
|
||||
impl.resources_.synthConfig.liveOscillatorQuality = quality;
|
||||
break;
|
||||
case ProcessFreewheeling:
|
||||
impl.resources_.synthConfig.freeWheelingOscillatorQuality = quality;
|
||||
break;
|
||||
default:
|
||||
SFIZZ_CHECK(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float Synth::getVolume() const noexcept
|
||||
{
|
||||
Impl& impl = *impl_;
|
||||
|
|
|
|||
|
|
@ -315,6 +315,21 @@ public:
|
|||
* @param quality the quality setting
|
||||
*/
|
||||
void setSampleQuality(ProcessMode mode, int quality);
|
||||
/**
|
||||
* @brief Get the default oscillator quality for the given mode.
|
||||
*
|
||||
* @param mode the processing mode
|
||||
*
|
||||
* @return the quality setting
|
||||
*/
|
||||
int getOscillatorQuality(ProcessMode mode);
|
||||
/**
|
||||
* @brief Set the default oscillator quality for the given mode.
|
||||
*
|
||||
* @param mode the processing mode
|
||||
* @param quality the quality setting
|
||||
*/
|
||||
void setOscillatorQuality(ProcessMode mode, int quality);
|
||||
/**
|
||||
* @brief Get the current value for the volume, in dB.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -14,11 +14,19 @@ struct SynthConfig
|
|||
bool freeWheeling { false };
|
||||
|
||||
int liveSampleQuality { Default::sampleQuality };
|
||||
int freeWheelingSampleQuality { Default::freewheelingQuality };
|
||||
int freeWheelingSampleQuality { Default::freewheelingSampleQuality };
|
||||
|
||||
int liveOscillatorQuality { Default::oscillatorQuality };
|
||||
int freeWheelingOscillatorQuality { Default::freewheelingOscillatorQuality };
|
||||
|
||||
int currentSampleQuality() const noexcept
|
||||
{
|
||||
return freeWheeling ? freeWheelingSampleQuality : liveSampleQuality;
|
||||
}
|
||||
|
||||
int currentOscillatorQuality() const noexcept
|
||||
{
|
||||
return freeWheeling ? freeWheelingOscillatorQuality : liveOscillatorQuality;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,6 +161,12 @@ struct Voice::Impl
|
|||
* @return int
|
||||
*/
|
||||
int getCurrentSampleQuality() const noexcept;
|
||||
/**
|
||||
* @brief Get the oscillator quality determined by the active region.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getCurrentOscillatorQuality() const noexcept;
|
||||
/**
|
||||
* @brief Reset the loop information
|
||||
*
|
||||
|
|
@ -531,6 +537,18 @@ int Voice::getCurrentSampleQuality() const noexcept
|
|||
return impl.getCurrentSampleQuality();
|
||||
}
|
||||
|
||||
int Voice::Impl::getCurrentOscillatorQuality() const noexcept
|
||||
{
|
||||
return (region_ && region_->oscillatorQuality) ?
|
||||
*region_->oscillatorQuality : resources_.synthConfig.currentOscillatorQuality();
|
||||
}
|
||||
|
||||
int Voice::getCurrentOscillatorQuality() const noexcept
|
||||
{
|
||||
Impl& impl = *impl_;
|
||||
return impl.getCurrentOscillatorQuality();
|
||||
}
|
||||
|
||||
bool Voice::isFree() const noexcept
|
||||
{
|
||||
Impl& impl = *impl_;
|
||||
|
|
@ -1459,6 +1477,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
|
|||
|
||||
const int oscillatorMode = region_->oscillatorMode;
|
||||
const int oscillatorMulti = region_->oscillatorMulti;
|
||||
const int quality = getCurrentOscillatorQuality();
|
||||
|
||||
if (oscillatorMode <= 0 && oscillatorMulti < 2) {
|
||||
// single oscillator
|
||||
|
|
@ -1467,6 +1486,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
|
|||
return;
|
||||
|
||||
WavetableOscillator& osc = waveOscillators_[0];
|
||||
osc.setQuality(quality);
|
||||
fill(*detuneSpan, 1.0f);
|
||||
osc.processModulated(frequencies->data(), detuneSpan->data(), tempSpan->data(), buffer.getNumFrames());
|
||||
copy<float>(*tempSpan, leftSpan);
|
||||
|
|
@ -1483,6 +1503,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
|
|||
const float* detuneMod = resources_.modMatrix.getModulation(oscillatorDetuneTarget_);
|
||||
for (unsigned u = 0, uSize = waveUnisonSize_; u < uSize; ++u) {
|
||||
WavetableOscillator& osc = waveOscillators_[u];
|
||||
osc.setQuality(quality);
|
||||
if (!detuneMod)
|
||||
fill(*detuneSpan, waveDetuneRatio_[u]);
|
||||
else {
|
||||
|
|
@ -1512,6 +1533,8 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
|
|||
|
||||
WavetableOscillator& oscCar = waveOscillators_[0];
|
||||
WavetableOscillator& oscMod = waveOscillators_[1];
|
||||
oscCar.setQuality(quality);
|
||||
oscMod.setQuality(quality);
|
||||
|
||||
// compute the modulator
|
||||
auto modulatorSpan = resources_.bufferPool.getBuffer(numFrames);
|
||||
|
|
|
|||
|
|
@ -122,6 +122,13 @@ public:
|
|||
*/
|
||||
int getCurrentSampleQuality() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the oscillator quality determined by the active region.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getCurrentOscillatorQuality() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Register a note-off event; this may trigger a release.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -140,6 +140,16 @@ void sfz::Sfizz::setSampleQuality(ProcessMode mode, int quality)
|
|||
synth->synth.setSampleQuality(static_cast<sfz::Synth::ProcessMode>(mode), quality);
|
||||
}
|
||||
|
||||
int sfz::Sfizz::getOscillatorQuality(ProcessMode mode)
|
||||
{
|
||||
return synth->synth.getOscillatorQuality(static_cast<sfz::Synth::ProcessMode>(mode));
|
||||
}
|
||||
|
||||
void sfz::Sfizz::setOscillatorQuality(ProcessMode mode, int quality)
|
||||
{
|
||||
synth->synth.setOscillatorQuality(static_cast<sfz::Synth::ProcessMode>(mode), quality);
|
||||
}
|
||||
|
||||
float sfz::Sfizz::getVolume() const noexcept
|
||||
{
|
||||
return synth->synth.getVolume();
|
||||
|
|
|
|||
|
|
@ -217,6 +217,16 @@ void sfizz_set_sample_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode, i
|
|||
return synth->synth.setSampleQuality(static_cast<sfz::Synth::ProcessMode>(mode), quality);
|
||||
}
|
||||
|
||||
int sfizz_get_oscillator_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode)
|
||||
{
|
||||
return synth->synth.getOscillatorQuality(static_cast<sfz::Synth::ProcessMode>(mode));
|
||||
}
|
||||
|
||||
void sfizz_set_oscillator_quality(sfizz_synth_t* synth, sfizz_process_mode_t mode, int quality)
|
||||
{
|
||||
return synth->synth.setOscillatorQuality(static_cast<sfz::Synth::ProcessMode>(mode), quality);
|
||||
}
|
||||
|
||||
void sfizz_set_volume(sfizz_synth_t* synth, float volume)
|
||||
{
|
||||
synth->synth.setVolume(volume);
|
||||
|
|
|
|||
|
|
@ -584,7 +584,7 @@ TEST_CASE("[Synth] sample quality")
|
|||
synth.noteOn(0, 60, 100);
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE(synth.getNumActiveVoices() == 1);
|
||||
REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == sfz::Default::freewheelingQuality);
|
||||
REQUIRE(synth.getVoiceView(0)->getCurrentSampleQuality() == sfz::Default::freewheelingSampleQuality);
|
||||
synth.allSoundOff();
|
||||
synth.disableFreeWheeling();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue