/* * 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 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