winamp/Src/external_dependencies/openmpt-trunk/mptrack/SampleGenerator.cpp
2024-09-24 14:54:57 +02:00

738 lines
21 KiB
C++

/*
* SampleGenerator.cpp
* -------------------
* Purpose: Generate samples from math formulas using muParser
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#if MPT_DISABLED_CODE
#include "SampleGenerator.h"
#include "modsmp_ctrl.h"
int CSampleGenerator::sample_frequency = 44100;
int CSampleGenerator::sample_length = CSampleGenerator::sample_frequency;
mu::string_type CSampleGenerator::expression = _T("sin(xp * _pi)");
smpgen_clip_methods CSampleGenerator::sample_clipping = smpgen_normalize;
mu::value_type *CSampleGenerator::sample_buffer = nullptr;
size_t CSampleGenerator::samples_written = 0;
CSampleGenerator::CSampleGenerator()
{
// Setup function callbacks
muParser.DefineFun(_T("clip"), &ClipCallback, false);
muParser.DefineFun(_T("pwm"), &PWMCallback, false);
muParser.DefineFun(_T("rnd"), &RndCallback, false);
muParser.DefineFun(_T("smp"), &SampleDataCallback, false);
muParser.DefineFun(_T("tri"), &TriangleCallback, false);
// Setup binary operator callbacks
muParser.DefineOprt(_T("mod"), &ModuloCallback, 0);
//muParser.DefineConst("pi", (mu::value_type)PARSER_CONST_PI);
}
// Open the smpgen dialog
bool CSampleGenerator::ShowDialog()
{
bool isDone = false, result = false;
while(!isDone)
{
CSmpGenDialog dlg(sample_frequency, sample_length, sample_clipping, expression);
dlg.DoModal();
// pressed "OK" button?
if(dlg.CanApply())
{
sample_frequency = dlg.GetFrequency();
sample_length = dlg.GetLength();
sample_clipping = dlg.GetClipping();
expression = dlg.GetExpression();
isDone = CanRenderSample();
if(isDone) isDone = TestExpression(); // show dialog again if the formula can't be parsed.
result = true;
} else
{
isDone = true; // just quit.
result = false;
}
}
return result;
}
// Check if the currently select expression can be parsed by muParser.
bool CSampleGenerator::TestExpression()
{
// reset helper variables
samples_written = 0;
sample_buffer = nullptr;
muParser.SetExpr(expression);
mu::value_type x = 0;
muParser.DefineVar(_T("x"), &x);
muParser.DefineVar(_T("xp"), &x);
muParser.DefineVar(_T("len"), &x);
muParser.DefineVar(_T("lens"), &x);
muParser.DefineVar(_T("freq"), &x);
try
{
muParser.Eval();
}
catch (mu::Parser::exception_type &e)
{
ShowError(&e);
return false;
}
return true;
}
// Check if sample parameters are valid.
bool CSampleGenerator::CanRenderSample() const
{
if(sample_frequency < SMPGEN_MINFREQ || sample_frequency > SMPGEN_MAXFREQ || sample_length < SMPGEN_MINLENGTH || sample_length > SMPGEN_MAXLENGTH) return false;
return true;
}
// Actual render loop.
bool CSampleGenerator::RenderSample(CSoundFile *pSndFile, SAMPLEINDEX nSample)
{
if(!CanRenderSample() || !TestExpression() || (pSndFile == nullptr) || (nSample < 1) || (nSample > pSndFile->m_nSamples)) return false;
// allocate a new buffer
sample_buffer = (mu::value_type *)malloc(sample_length * sizeof(mu::value_type));
if(sample_buffer == nullptr) return false;
memset(sample_buffer, 0, sample_length * sizeof(mu::value_type));
mu::value_type x = 0, xp = 0;
mu::value_type v_len = sample_length, v_freq = sample_frequency, v_lens = v_len / v_freq;
muParser.DefineVar(_T("x"), &x);
muParser.DefineVar(_T("xp"), &xp);
muParser.DefineVar(_T("len"), &v_len);
muParser.DefineVar(_T("lens"), &v_lens);
muParser.DefineVar(_T("freq"), &v_freq);
bool success = true;
mu::value_type minmax = 0;
for(size_t i = 0; i < (size_t)sample_length; i++)
{
samples_written = i;
x = (mu::value_type)i;
xp = x * 100 / sample_length;
try
{
sample_buffer[i] = muParser.Eval();
}
catch (mu::Parser::exception_type &e)
{
// let's just ignore div by zero errors (note: this error code is currently unused (muParser 1.30))
if(e.GetCode() != mu::ecDIV_BY_ZERO)
{
ShowError(&e);
success = false;
break;
}
sample_buffer[i] = 0;
}
// new maximum value?
if(std::abs(sample_buffer[i]) > minmax) minmax = std::abs(sample_buffer[i]);
}
if(success)
{
MODSAMPLE *pModSample = &pSndFile->Samples[nSample];
BEGIN_CRITICAL();
// first, save some memory... (leads to crashes)
//CSoundFile::FreeSample(pModSample->pSample);
//pModSample->pSample = nullptr;
if(minmax == 0) minmax = 1; // avoid division by 0
// convert sample to 16-bit (or whateve rhas been specified)
int16 *pSample = (sampling_type *)CSoundFile::AllocateSample((sample_length + 4) * SMPGEN_MIXBYTES);
for(size_t i = 0; i < (size_t)sample_length; i++)
{
switch(sample_clipping)
{
case smpgen_clip: sample_buffer[i] = CLAMP(sample_buffer[i], -1, 1); break; // option 1: clip
case smpgen_normalize: sample_buffer[i] /= minmax; break; // option 3: normalize
}
pSample[i] = (sampling_type)(sample_buffer[i] * sample_maxvalue);
}
// set new sample proprerties
pModSample->nC5Speed = sample_frequency;
CSoundFile::FrequencyToTranspose(pModSample);
pModSample->uFlags |= CHN_16BIT; // has to be adjusted if SMPGEN_MIXBYTES changes!
pModSample->uFlags &= ~(CHN_STEREO|CHN_SUSTAINLOOP|CHN_PINGPONGSUSTAIN);
pModSample->nLoopStart = 0;
pModSample->nLoopEnd = sample_length;
pModSample->nSustainStart = pModSample->nSustainEnd = 0;
if(sample_length / sample_frequency < 5) // arbitrary limit for automatic sample loop (5 seconds)
pModSample->uFlags |= CHN_LOOP;
else
pModSample->uFlags &= ~(CHN_LOOP|CHN_PINGPONGLOOP);
ctrlSmp::ReplaceSample(*pModSample, (LPSTR)pSample, sample_length, pSndFile);
END_CRITICAL();
}
free(sample_buffer);
sample_buffer = nullptr;
return success;
}
// Callback function to access sample data
mu::value_type CSampleGenerator::SampleDataCallback(mu::value_type v)
{
if(sample_buffer == nullptr) return 0;
v = CLAMP(v, 0, samples_written);
size_t pos = static_cast<size_t>(v);
return sample_buffer[pos];
}
void CSampleGenerator::ShowError(mu::Parser::exception_type *e)
{
std::string errmsg;
errmsg = "The expression\n " + e->GetExpr() + "\ncontains an error ";
if(!e->GetToken().empty())
errmsg += "in the token\n " + e->GetToken() + "\n";
errmsg += "at position " + Stringify(e->GetPos()) + ".\nThe error message was: " + e->GetMsg();
::MessageBox(0, errmsg.c_str(), _T("muParser Sample Generator"), 0);
}
//////////////////////////////////////////////////////////////////////////
// Sample Generator Dialog implementation
#define MAX_SAMPLEGEN_EXPRESSIONS 61
BEGIN_MESSAGE_MAP(CSmpGenDialog, CDialog)
ON_EN_CHANGE(IDC_EDIT_SAMPLE_LENGTH, &CSmpGenDialog::OnSampleLengthChanged)
ON_EN_CHANGE(IDC_EDIT_SAMPLE_LENGTH_SEC, &CSmpGenDialog::OnSampleSecondsChanged)
ON_EN_CHANGE(IDC_EDIT_SAMPLE_FREQ, &CSmpGenDialog::OnSampleFreqChanged)
ON_EN_CHANGE(IDC_EDIT_FORMULA, &CSmpGenDialog::OnExpressionChanged)
ON_COMMAND(IDC_BUTTON_SHOW_EXPRESSIONS, &CSmpGenDialog::OnShowExpressions)
ON_COMMAND(IDC_BUTTON_SAMPLEGEN_PRESETS, &CSmpGenDialog::OnShowPresets)
ON_COMMAND_RANGE(ID_SAMPLE_GENERATOR_MENU, ID_SAMPLE_GENERATOR_MENU + MAX_SAMPLEGEN_EXPRESSIONS - 1, &CSmpGenDialog::OnInsertExpression)
ON_COMMAND_RANGE(ID_SAMPLE_GENERATOR_PRESET_MENU, ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS + 1, &CSmpGenDialog::OnSelectPreset)
END_MESSAGE_MAP()
// List of all possible expression for expression menu
const samplegen_expression menu_descriptions[MAX_SAMPLEGEN_EXPRESSIONS] =
{
{"Variables", ""},
{"Current position (sampling point)", "x"},
{"Current position (percentage)", "xp"},
{"Sample length", "len"},
{"Sample length (seconds)", "lens"},
{"Sampling frequency", "freq"},
{"Constants", ""},
{"Pi", "_pi"},
{"e", "_e"},
{"Trigonometric functions", ""},
{"Sine", "sin(x)"},
{"Cosine", "cos(x)"},
{"Tangens", "tan(x)"},
{"Arcus Sine", "asin(x)"},
{"Arcus Cosine", "acos(x)"},
{"Arcus Tangens", "atan(x)"},
{"Hyperbolic Sine", "sinh(x)"},
{"Hyperbolic Cosine", "cosh(x)"},
{"Hyperbolic Tangens", "tanh(x)"},
{"Hyperbolic Arcus Sine", "asinh(x)"},
{"Hyperbolic Arcus Cosine", "acosh(x)"},
{"Hyperbolic Arcus Tangens", "atanh(x)"},
{"Log, Exp, Root", ""},
{"Logarithm (base 2)", "log2(x)"},
{"Logarithm (base 10)", "log(x)"},
{"Natural Logarithm (base e)", "ln(x)"},
{"e^x", "exp(x)"},
{"Square Root", "sqrt(x)"},
{"Sign and rounding", ""},
{"Sign", "sign(x)"},
{"Absolute value", "abs(x)"},
{"Round to nearest integer", "rint(x)"},
{"Sets", ""},
{"Minimum", "min(x, y, ...)"},
{"Maximum", "max(x, y, ...)"},
{"Sum", "sum(x, y, ...)"},
{"Mean value", "avg(x, y, ...)"},
{"Misc functions", ""},
{"Pulse generator", "pwm(position, duty%, width)"},
{"Triangle", "tri(position, width)"},
{"Random value between 0 and x", "rnd(x)"},
{"Access previous sampling point", "smp(position)"},
{"Clip between values", "clip(value, minclip, maxclip)"},
{"If...Then...Else", "if(condition, statement1, statement2)"},
{"Operators", ""},
{"Assignment", "x = y"},
{"Logical And", "x abd y"},
{"Logical Or", "x or y"},
{"Logical Xor", "x xor y"},
{"Less or equal", "x <= y"},
{"Greater or equal", "x >= y"},
{"Not equal", "x != y"},
{"Equal", "x == y"},
{"Greater than", "x > y"},
{"Less than", "x < y"},
{"Addition", "x + y"},
{"Subtraction", "x - y"},
{"Multiplication", "x * y"},
{"Division", "x / y"},
{"x^y", "x ^ y"},
{"Modulo", "x mod y"},
};
BOOL CSmpGenDialog::OnInitDialog()
{
CDialog::OnInitDialog();
RecalcParameters(false, true);
SetDlgItemText(IDC_EDIT_FORMULA, expression.c_str());
int check = IDC_RADIO_SMPCLIP1;
switch(sample_clipping)
{
case smpgen_clip: check = IDC_RADIO_SMPCLIP1; break;
case smpgen_overflow: check = IDC_RADIO_SMPCLIP2; break;
case smpgen_normalize: check = IDC_RADIO_SMPCLIP3; break;
}
CheckRadioButton(IDC_RADIO_SMPCLIP1, IDC_RADIO_SMPCLIP3, check);
if(presets.GetNumPresets() == 0)
{
CreateDefaultPresets();
}
// Create font for "dropdown" button (Marlett system font)
hButtonFont = CreateFont(14, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, TEXT("Marlett"));
::SendMessage(GetDlgItem(IDC_BUTTON_SHOW_EXPRESSIONS)->m_hWnd, WM_SETFONT, (WPARAM)hButtonFont, MAKELPARAM(TRUE, 0));
return TRUE;
}
void CSmpGenDialog::OnOK()
{
CDialog::OnOK();
apply = true;
int check = GetCheckedRadioButton(IDC_RADIO_SMPCLIP1, IDC_RADIO_SMPCLIP3);
switch(check)
{
case IDC_RADIO_SMPCLIP1: sample_clipping = smpgen_clip; break;
case IDC_RADIO_SMPCLIP2: sample_clipping = smpgen_overflow; break;
case IDC_RADIO_SMPCLIP3: sample_clipping = smpgen_normalize; break;
}
DeleteObject(hButtonFont);
}
void CSmpGenDialog::OnCancel()
{
CDialog::OnCancel();
apply = false;
}
// User changed formula
void CSmpGenDialog::OnExpressionChanged()
{
CString result;
GetDlgItemText(IDC_EDIT_FORMULA, result);
expression = result;
}
// User changed sample length field
void CSmpGenDialog::OnSampleLengthChanged()
{
int temp_length = GetDlgItemInt(IDC_EDIT_SAMPLE_LENGTH);
if(temp_length >= SMPGEN_MINLENGTH && temp_length <= SMPGEN_MAXLENGTH)
{
sample_length = temp_length;
RecalcParameters(false);
}
}
// User changed sample length (seconds) field
void CSmpGenDialog::OnSampleSecondsChanged()
{
CString str;
GetDlgItemText(IDC_EDIT_SAMPLE_LENGTH_SEC, str);
double temp_seconds = atof(str);
if(temp_seconds > 0)
{
sample_seconds = temp_seconds;
RecalcParameters(true);
}
}
// User changed sample frequency field
void CSmpGenDialog::OnSampleFreqChanged()
{
int temp_freq = GetDlgItemInt(IDC_EDIT_SAMPLE_FREQ);
if(temp_freq >= SMPGEN_MINFREQ && temp_freq <= SMPGEN_MAXFREQ)
{
sample_frequency = temp_freq;
RecalcParameters(false);
}
}
// Show all expressions that can be input
void CSmpGenDialog::OnShowExpressions()
{
HMENU hMenu = ::CreatePopupMenu(), hSubMenu = NULL;
if(!hMenu) return;
for(int i = 0; i < MAX_SAMPLEGEN_EXPRESSIONS; i++)
{
if(menu_descriptions[i].expression == "")
{
// add sub menu
if(hSubMenu != NULL) ::DestroyMenu(hSubMenu);
hSubMenu = ::CreatePopupMenu();
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hSubMenu, menu_descriptions[i].description.c_str());
} else
{
// add sub menu entry (formula)
AppendMenu(hSubMenu, MF_STRING, ID_SAMPLE_GENERATOR_MENU + i, menu_descriptions[i].description.c_str());
}
}
// place popup menu below button
RECT button;
GetDlgItem(IDC_BUTTON_SHOW_EXPRESSIONS)->GetWindowRect(&button);
::TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, button.left, button.bottom, 0, m_hWnd, NULL);
::DestroyMenu(hMenu);
::DestroyMenu(hSubMenu);
}
// Show all expression presets
void CSmpGenDialog::OnShowPresets()
{
HMENU hMenu = ::CreatePopupMenu();
if(!hMenu) return;
bool prestsExist = false;
for(size_t i = 0; i < presets.GetNumPresets(); i++)
{
if(presets.GetPreset(i)->expression != "")
{
AppendMenu(hMenu, MF_STRING, ID_SAMPLE_GENERATOR_PRESET_MENU + i, presets.GetPreset(i)->description.c_str());
prestsExist = true;
}
}
if(prestsExist) AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hMenu, MF_STRING, ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS, _TEXT("Manage..."));
CString result;
GetDlgItemText(IDC_EDIT_FORMULA, result);
if((!result.IsEmpty()) && (presets.GetNumPresets() < MAX_SAMPLEGEN_PRESETS))
{
AppendMenu(hMenu, MF_STRING, ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS + 1, _TEXT("Add current..."));
}
// place popup menu below button
RECT button;
GetDlgItem(IDC_BUTTON_SAMPLEGEN_PRESETS)->GetWindowRect(&button);
::TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, button.left, button.bottom, 0, m_hWnd, NULL);
::DestroyMenu(hMenu);
}
// Insert expression from context menu
void CSmpGenDialog::OnInsertExpression(UINT nId)
{
if((nId < ID_SAMPLE_GENERATOR_MENU) || (nId >= ID_SAMPLE_GENERATOR_MENU + MAX_SAMPLEGEN_EXPRESSIONS)) return;
expression += " " + menu_descriptions[nId - ID_SAMPLE_GENERATOR_MENU].expression;
SetDlgItemText(IDC_EDIT_FORMULA, expression.c_str());
}
// Select a preset (or manage them, or add one)
void CSmpGenDialog::OnSelectPreset(UINT nId)
{
if((nId < ID_SAMPLE_GENERATOR_PRESET_MENU) || (nId >= ID_SAMPLE_GENERATOR_PRESET_MENU + MAX_SAMPLEGEN_PRESETS + 2)) return;
if(nId - ID_SAMPLE_GENERATOR_PRESET_MENU >= MAX_SAMPLEGEN_PRESETS)
{
// add...
if((nId - ID_SAMPLE_GENERATOR_PRESET_MENU == MAX_SAMPLEGEN_PRESETS + 1))
{
samplegen_expression newPreset;
newPreset.description = newPreset.expression = expression;
presets.AddPreset(newPreset);
// call preset manager now.
}
// manage...
CSmpGenPresetDlg dlg(&presets);
dlg.DoModal();
} else
{
expression = presets.GetPreset(nId - ID_SAMPLE_GENERATOR_PRESET_MENU)->expression;
SetDlgItemText(IDC_EDIT_FORMULA, expression.c_str());
}
}
// Update input fields, depending on what has been chagned
void CSmpGenDialog::RecalcParameters(bool secondsChanged, bool forceRefresh)
{
static bool isLocked = false;
if(isLocked) return;
isLocked = true; // avoid deadlock
if(secondsChanged)
{
// seconds changed => recalc length
sample_length = (int)(sample_seconds * sample_frequency);
if(sample_length < SMPGEN_MINLENGTH || sample_length > SMPGEN_MAXLENGTH) sample_length = SMPGEN_MAXLENGTH;
} else
{
// length/freq changed => recalc seconds
sample_seconds = ((double)sample_length) / ((double)sample_frequency);
}
if(secondsChanged || forceRefresh) SetDlgItemInt(IDC_EDIT_SAMPLE_LENGTH, sample_length);
if(secondsChanged || forceRefresh) SetDlgItemInt(IDC_EDIT_SAMPLE_FREQ, sample_frequency);
CString str;
str.Format("%.4f", sample_seconds);
if(!secondsChanged || forceRefresh) SetDlgItemText(IDC_EDIT_SAMPLE_LENGTH_SEC, str);
int smpsize = sample_length * SMPGEN_MIXBYTES;
if(smpsize < 1024)
{
str.Format("Sample Size: %d Bytes", smpsize);
} else if((smpsize >> 10) < 1024)
{
str.Format("Sample Size: %d KB", smpsize >> 10);
} else
{
str.Format("Sample Size: %d MB", smpsize >> 20);
}
SetDlgItemText(IDC_STATIC_SMPSIZE_KB, str);
isLocked = false;
}
// Create a set of default formla presets
void CSmpGenDialog::CreateDefaultPresets()
{
samplegen_expression preset;
preset.description = "A440";
preset.expression = "sin(xp * _pi / 50 * 440 * len / freq)";
presets.AddPreset(preset);
preset.description = "Brown Noise (kind of)";
preset.expression = "rnd(1) * 0.1 + smp(x - 1) * 0.9";
presets.AddPreset(preset);
preset.description = "Noisy Saw";
preset.expression = "(x mod 800) / 800 - 0.5 + rnd(0.1)";
presets.AddPreset(preset);
preset.description = "PWM Filter";
preset.expression = "pwm(x, 50 + sin(xp * _pi / 100) * 40, 100) + tri(x, 50)";
presets.AddPreset(preset);
preset.description = "Fat PWM Pad";
preset.expression = "pwm(x, xp, 500) + pwm(x, abs(50 - xp), 1000)";
presets.AddPreset(preset);
preset.description = "Dual Square";
preset.expression = "if((x mod 100) < 50, (x mod 200), -x mod 200)";
presets.AddPreset(preset);
preset.description = "Noise Hit";
preset.expression = "exp(-xp) * (rnd(x) - x / 2)";
presets.AddPreset(preset);
preset.description = "Laser";
preset.expression = "sin(xp * _pi * 100 /(xp ^ 2)) * 100 / sqrt(xp)";
presets.AddPreset(preset);
preset.description = "Noisy Laser Hit";
preset.expression = "(sin(sqrt(xp) * 100) + rnd(1) - 0.5) * exp(-xp / 10)";
presets.AddPreset(preset);
preset.description = "Twinkle, Twinkle...";
preset.expression = "sin(xp * _pi * 100 / xp) * 100 / sqrt(xp)";
presets.AddPreset(preset);
preset.description = "FM Tom";
preset.expression = "sin(xp * _pi * 2 + (xp / 5 - 50) ^ 2) * exp(-xp / 10)";
presets.AddPreset(preset);
preset.description = "FM Warp";
preset.expression = "sin(_pi * xp / 2 * (1 + (1 + sin(_pi * xp / 4 * 50)) / 4)) * exp(-(xp / 8) * .6)";
presets.AddPreset(preset);
preset.description = "Weird Noise";
preset.expression = "rnd(1) * 0.1 + smp(x - rnd(xp)) * 0.9";
presets.AddPreset(preset);
}
//////////////////////////////////////////////////////////////////////////
// Sample Generator Preset Dialog implementation
BEGIN_MESSAGE_MAP(CSmpGenPresetDlg, CDialog)
ON_COMMAND(IDC_BUTTON_ADD, &CSmpGenPresetDlg::OnAddPreset)
ON_COMMAND(IDC_BUTTON_REMOVE, &CSmpGenPresetDlg::OnRemovePreset)
ON_EN_CHANGE(IDC_EDIT_PRESET_NAME, &CSmpGenPresetDlg::OnTextChanged)
ON_EN_CHANGE(IDC_EDIT_PRESET_EXPR, &CSmpGenPresetDlg::OnExpressionChanged)
ON_LBN_SELCHANGE(IDC_LIST_SAMPLEGEN_PRESETS, &CSmpGenPresetDlg::OnListSelChange)
END_MESSAGE_MAP()
BOOL CSmpGenPresetDlg::OnInitDialog()
{
CDialog::OnInitDialog();
RefreshList();
return TRUE;
}
void CSmpGenPresetDlg::OnOK()
{
// remove empty presets
for(size_t i = 0; i < presets->GetNumPresets(); i++)
{
if(presets->GetPreset(i)->expression.empty())
{
presets->RemovePreset(i);
}
}
CDialog::OnOK();
}
void CSmpGenPresetDlg::OnListSelChange()
{
currentItem = ((CListBox *)GetDlgItem(IDC_LIST_SAMPLEGEN_PRESETS))->GetCurSel() + 1;
if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
samplegen_expression *preset = presets->GetPreset(currentItem - 1);
if(preset == nullptr) return;
SetDlgItemText(IDC_EDIT_PRESET_NAME, preset->description.c_str());
SetDlgItemText(IDC_EDIT_PRESET_EXPR, preset->expression.c_str());
}
void CSmpGenPresetDlg::OnTextChanged()
{
if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
CString result;
GetDlgItemText(IDC_EDIT_PRESET_NAME, result);
samplegen_expression *preset = presets->GetPreset(currentItem - 1);
if(preset == nullptr) return;
preset->description = result;
CListBox *clist = (CListBox *)GetDlgItem(IDC_LIST_SAMPLEGEN_PRESETS);
clist->DeleteString(currentItem - 1);
clist->InsertString(currentItem - 1, (preset->description).c_str());
clist->SetCurSel(currentItem - 1);
}
void CSmpGenPresetDlg::OnExpressionChanged()
{
if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
CString result;
GetDlgItemText(IDC_EDIT_PRESET_EXPR, result);
samplegen_expression *preset = presets->GetPreset(currentItem - 1);
if(preset != nullptr) preset->expression = result;
}
void CSmpGenPresetDlg::OnAddPreset()
{
samplegen_expression newPreset;
newPreset.description = "New Preset";
newPreset.expression = "";
if(presets->AddPreset(newPreset))
{
currentItem = presets->GetNumPresets();
RefreshList();
}
}
void CSmpGenPresetDlg::OnRemovePreset()
{
if(currentItem == 0 || currentItem > presets->GetNumPresets()) return;
if(presets->RemovePreset(currentItem - 1))
RefreshList();
}
void CSmpGenPresetDlg::RefreshList()
{
CListBox *clist = (CListBox *)GetDlgItem(IDC_LIST_SAMPLEGEN_PRESETS);
clist->SetRedraw(FALSE); //disable lisbox refreshes during fill to avoid flicker
clist->ResetContent();
for(size_t i = 0; i < presets->GetNumPresets(); i++)
{
samplegen_expression *preset = presets->GetPreset(i);
if(preset != nullptr)
clist->AddString((preset->description).c_str());
}
clist->SetRedraw(TRUE); //re-enable lisbox refreshes
if(currentItem == 0 || currentItem > presets->GetNumPresets())
{
currentItem = presets->GetNumPresets();
}
if(currentItem != 0) clist->SetCurSel(currentItem - 1);
OnListSelChange();
}
#endif // MPT_DISABLED_CODE