209 lines
5.7 KiB
C
209 lines
5.7 KiB
C
|
/*
|
||
|
* SampleGenerator.h
|
||
|
* -----------------
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "openmpt/all/BuildSettings.hpp"
|
||
|
|
||
|
#ifdef MPT_DISABLED_CODE
|
||
|
|
||
|
#include "Mptrack.h"
|
||
|
#include "Mainfrm.h"
|
||
|
#include "Sndfile.h"
|
||
|
#include "../muParser/include/muParser.h"
|
||
|
|
||
|
// sample length
|
||
|
#define SMPGEN_MINLENGTH 1
|
||
|
#define SMPGEN_MAXLENGTH MAX_SAMPLE_LENGTH
|
||
|
// sample frequency
|
||
|
#define SMPGEN_MINFREQ 1
|
||
|
#define SMPGEN_MAXFREQ 96000 // MAX_SAMPLE_RATE
|
||
|
// 16-bit sample quality - when changing this, also change CSampleGenerator::sampling_type and 16-bit flags in SampleGenerator.cpp!
|
||
|
#define SMPGEN_MIXBYTES 2
|
||
|
|
||
|
enum smpgen_clip_methods
|
||
|
{
|
||
|
smpgen_clip,
|
||
|
smpgen_overflow,
|
||
|
smpgen_normalize,
|
||
|
};
|
||
|
|
||
|
class CSampleGenerator
|
||
|
{
|
||
|
protected:
|
||
|
|
||
|
// sample parameters
|
||
|
static int sample_frequency;
|
||
|
static int sample_length;
|
||
|
static mu::string_type expression;
|
||
|
static smpgen_clip_methods sample_clipping;
|
||
|
|
||
|
// rendering helper variables (they're here for the callback functions)
|
||
|
static mu::value_type *sample_buffer;
|
||
|
static size_t samples_written;
|
||
|
|
||
|
typedef int16 sampling_type; // has to match SMPGEN_MIXBYTES!
|
||
|
static constexpr sampling_type sample_maxvalue = (1 << ((SMPGEN_MIXBYTES << 3) - 1)) - 1;
|
||
|
|
||
|
// muParser object for parsing the expression
|
||
|
mu::Parser muParser;
|
||
|
|
||
|
// Rendering callback functions
|
||
|
// functions
|
||
|
static mu::value_type ClipCallback(mu::value_type val, mu::value_type min, mu::value_type max) { return Clamp(val, min, max); };
|
||
|
static mu::value_type PWMCallback(mu::value_type pos, mu::value_type duty, mu::value_type width) { if(width == 0) return 0; else return (fmod(pos, width) < ((duty / 100) * width)) ? 1 : -1; };
|
||
|
static mu::value_type RndCallback(mu::value_type v) { return v * std::rand() / (mu::value_type)(RAND_MAX + 1.0); };
|
||
|
static mu::value_type SampleDataCallback(mu::value_type v);
|
||
|
static mu::value_type TriangleCallback(mu::value_type pos, mu::value_type width) { if((int)width == 0) return 0; else return std::abs(((int)pos % (int)(width)) - width / 2) / (width / 4) - 1; };
|
||
|
|
||
|
// binary operators
|
||
|
static mu::value_type ModuloCallback(mu::value_type x, mu::value_type y) { if(y == 0) return 0; else return fmod(x , y); };
|
||
|
|
||
|
void ShowError(mu::Parser::exception_type *e);
|
||
|
|
||
|
public:
|
||
|
|
||
|
bool ShowDialog();
|
||
|
bool TestExpression();
|
||
|
bool CanRenderSample() const;
|
||
|
bool RenderSample(CSoundFile *pSndFile, SAMPLEINDEX nSample);
|
||
|
|
||
|
CSampleGenerator();
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Sample Generator Formula Preset implementation
|
||
|
|
||
|
|
||
|
struct samplegen_expression
|
||
|
{
|
||
|
std::string description; // e.g. "Pulse"
|
||
|
mu::string_type expression; // e.g. "pwm(x,y,z)" - empty if this is a sub menu
|
||
|
};
|
||
|
#define MAX_SAMPLEGEN_PRESETS 100
|
||
|
|
||
|
|
||
|
class CSmpGenPresets
|
||
|
{
|
||
|
protected:
|
||
|
vector<samplegen_expression> presets;
|
||
|
|
||
|
public:
|
||
|
bool AddPreset(samplegen_expression new_preset) { if(GetNumPresets() >= MAX_SAMPLEGEN_PRESETS) return false; presets.push_back(new_preset); return true;};
|
||
|
bool RemovePreset(size_t which) { if(which < GetNumPresets()) { presets.erase(presets.begin() + which); return true; } else return false; };
|
||
|
samplegen_expression *GetPreset(size_t which) { if(which < GetNumPresets()) return &presets[which]; else return nullptr; };
|
||
|
size_t GetNumPresets() { return presets.size(); };
|
||
|
void Clear() { presets.clear(); };
|
||
|
|
||
|
CSmpGenPresets() { Clear(); }
|
||
|
~CSmpGenPresets() { Clear(); }
|
||
|
};
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Sample Generator Dialog implementation
|
||
|
|
||
|
|
||
|
class CSmpGenDialog: public CDialog
|
||
|
{
|
||
|
protected:
|
||
|
|
||
|
// sample parameters
|
||
|
int sample_frequency;
|
||
|
int sample_length;
|
||
|
double sample_seconds;
|
||
|
mu::string_type expression;
|
||
|
smpgen_clip_methods sample_clipping;
|
||
|
// pressed "OK"?
|
||
|
bool apply;
|
||
|
// preset slots
|
||
|
CSmpGenPresets presets;
|
||
|
|
||
|
HFONT hButtonFont; // "Marlett" font for "dropdown" button
|
||
|
|
||
|
void RecalcParameters(bool secondsChanged, bool forceRefresh = false);
|
||
|
|
||
|
// function presets
|
||
|
void CreateDefaultPresets();
|
||
|
|
||
|
public:
|
||
|
|
||
|
const int GetFrequency() { return sample_frequency; };
|
||
|
const int GetLength() { return sample_length; };
|
||
|
const smpgen_clip_methods GetClipping() { return sample_clipping; }
|
||
|
const mu::string_type GetExpression() { return expression; };
|
||
|
bool CanApply() { return apply; };
|
||
|
|
||
|
CSmpGenDialog(int freq, int len, smpgen_clip_methods clipping, mu::string_type expr):CDialog(IDD_SAMPLE_GENERATOR, CMainFrame::GetMainFrame())
|
||
|
{
|
||
|
sample_frequency = freq;
|
||
|
sample_length = len;
|
||
|
sample_clipping = clipping;
|
||
|
expression = expr;
|
||
|
apply = false;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
virtual BOOL OnInitDialog();
|
||
|
virtual void OnOK();
|
||
|
virtual void OnCancel();
|
||
|
|
||
|
afx_msg void OnSampleLengthChanged();
|
||
|
afx_msg void OnSampleSecondsChanged();
|
||
|
afx_msg void OnSampleFreqChanged();
|
||
|
afx_msg void OnExpressionChanged();
|
||
|
afx_msg void OnShowExpressions();
|
||
|
afx_msg void OnShowPresets();
|
||
|
afx_msg void OnInsertExpression(UINT nId);
|
||
|
afx_msg void OnSelectPreset(UINT nId);
|
||
|
|
||
|
//}}AFX_MSG
|
||
|
DECLARE_MESSAGE_MAP()
|
||
|
};
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
// Sample Generator Preset Dialog implementation
|
||
|
|
||
|
|
||
|
class CSmpGenPresetDlg: public CDialog
|
||
|
{
|
||
|
protected:
|
||
|
CSmpGenPresets *presets;
|
||
|
size_t currentItem; // first item is actually 1!
|
||
|
|
||
|
void RefreshList();
|
||
|
|
||
|
public:
|
||
|
CSmpGenPresetDlg(CSmpGenPresets *pPresets):CDialog(IDD_SAMPLE_GENERATOR_PRESETS, CMainFrame::GetMainFrame())
|
||
|
{
|
||
|
presets = pPresets;
|
||
|
currentItem = 0;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
virtual BOOL OnInitDialog();
|
||
|
virtual void OnOK();
|
||
|
|
||
|
afx_msg void OnListSelChange();
|
||
|
|
||
|
afx_msg void OnTextChanged();
|
||
|
afx_msg void OnExpressionChanged();
|
||
|
|
||
|
afx_msg void OnAddPreset();
|
||
|
afx_msg void OnRemovePreset();
|
||
|
|
||
|
DECLARE_MESSAGE_MAP()
|
||
|
};
|
||
|
|
||
|
#endif // MPT_DISABLED_CODE
|