LFOPlugin.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /*
  2. * LFOPlugin.cpp
  3. * -------------
  4. * Purpose: Plugin for automating other plugins' parameters
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #ifndef NO_PLUGINS
  11. #include "LFOPlugin.h"
  12. #include "../Sndfile.h"
  13. #include "../../common/FileReader.h"
  14. #ifdef MODPLUG_TRACKER
  15. #include "../../mptrack/plugins/LFOPluginEditor.h"
  16. #endif // MODPLUG_TRACKER
  17. #include "mpt/base/numbers.hpp"
  18. OPENMPT_NAMESPACE_BEGIN
  19. IMixPlugin* LFOPlugin::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  20. {
  21. return new (std::nothrow) LFOPlugin(factory, sndFile, mixStruct);
  22. }
  23. LFOPlugin::LFOPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
  24. : IMixPlugin(factory, sndFile, mixStruct)
  25. , m_PRNG(mpt::make_prng<mpt::fast_prng>(mpt::global_prng()))
  26. {
  27. RecalculateFrequency();
  28. RecalculateIncrement();
  29. m_mixBuffer.Initialize(2, 2);
  30. InsertIntoFactoryList();
  31. }
  32. // Processing (we do not process audio, just send out parameters)
  33. void LFOPlugin::Process(float *pOutL, float *pOutR, uint32 numFrames)
  34. {
  35. if(!m_bypassed)
  36. {
  37. ResetSilence();
  38. if(m_tempoSync)
  39. {
  40. double tempo = m_SndFile.GetCurrentBPM();
  41. if(tempo != m_tempo)
  42. {
  43. m_tempo = tempo;
  44. RecalculateIncrement();
  45. }
  46. }
  47. if(m_oneshot)
  48. {
  49. LimitMax(m_phase, 1.0);
  50. } else
  51. {
  52. int intPhase = static_cast<int>(m_phase);
  53. if(intPhase > 0 && (m_waveForm == kSHNoise || m_waveForm == kSmoothNoise))
  54. {
  55. // Phase wrap-around happened
  56. NextRandom();
  57. }
  58. m_phase -= intPhase;
  59. }
  60. double value = 0;
  61. switch(m_waveForm)
  62. {
  63. case kSine:
  64. value = std::sin(m_phase * (2.0 * mpt::numbers::pi));
  65. break;
  66. case kTriangle:
  67. value = 1.0 - 4.0 * std::abs(m_phase - 0.5);
  68. break;
  69. case kSaw:
  70. value = 2.0 * m_phase - 1.0;
  71. break;
  72. case kSquare:
  73. value = m_phase < 0.5 ? -1.0 : 1.0;
  74. break;
  75. case kSHNoise:
  76. value = m_random;
  77. break;
  78. case kSmoothNoise:
  79. value = m_phase * m_phase * m_phase * (m_phase * (m_phase * 6 - 15) + 10); // Smootherstep
  80. value = m_nextRandom * value + m_random * (1.0 - value);
  81. break;
  82. default:
  83. break;
  84. }
  85. if(m_polarity)
  86. value = -value;
  87. // Transform value from -1...+1 to 0...1 range and apply offset/amplitude
  88. value = value * m_amplitude + m_offset;
  89. Limit(value, 0.0, 1.0);
  90. IMixPlugin *plugin = GetOutputPlugin();
  91. if(plugin != nullptr)
  92. {
  93. if(m_outputToCC)
  94. {
  95. plugin->MidiSend(MIDIEvents::CC(static_cast<MIDIEvents::MidiCC>(m_outputParam & 0x7F), static_cast<uint8>((m_outputParam >> 8) & 0x0F), mpt::saturate_round<uint8>(value * 127.0f)));
  96. } else
  97. {
  98. plugin->SetParameter(m_outputParam, static_cast<PlugParamValue>(value));
  99. }
  100. }
  101. m_phase += m_increment * numFrames;
  102. }
  103. ProcessMixOps(pOutL, pOutR, m_mixBuffer.GetInputBuffer(0), m_mixBuffer.GetInputBuffer(1), numFrames);
  104. }
  105. PlugParamValue LFOPlugin::GetParameter(PlugParamIndex index)
  106. {
  107. switch(index)
  108. {
  109. case kAmplitude: return m_amplitude;
  110. case kOffset: return m_offset;
  111. case kFrequency: return m_frequency;
  112. case kTempoSync: return m_tempoSync ? 1.0f : 0.0f;
  113. case kWaveform: return WaveformToParam(m_waveForm);
  114. case kPolarity: return m_polarity ? 1.0f : 0.0f;
  115. case kBypassed: return m_bypassed ? 1.0f : 0.0f;
  116. case kLoopMode: return m_oneshot ? 1.0f : 0.0f;
  117. default: return 0;
  118. }
  119. }
  120. void LFOPlugin::SetParameter(PlugParamIndex index, PlugParamValue value)
  121. {
  122. ResetSilence();
  123. value = mpt::safe_clamp(value, 0.0f, 1.0f);
  124. switch(index)
  125. {
  126. case kAmplitude: m_amplitude = value; break;
  127. case kOffset: m_offset = value; break;
  128. case kFrequency:
  129. m_frequency = value;
  130. RecalculateFrequency();
  131. break;
  132. case kTempoSync:
  133. m_tempoSync = (value >= 0.5f);
  134. RecalculateFrequency();
  135. break;
  136. case kWaveform:
  137. m_waveForm = ParamToWaveform(value);
  138. break;
  139. case kPolarity: m_polarity = (value >= 0.5f); break;
  140. case kBypassed: m_bypassed = (value >= 0.5f); break;
  141. case kLoopMode: m_oneshot = (value >= 0.5f); break;
  142. case kCurrentPhase:
  143. if(value == 0)
  144. {
  145. // Enforce next random value for random LFOs
  146. NextRandom();
  147. }
  148. m_phase = value;
  149. return;
  150. default: return;
  151. }
  152. #ifdef MODPLUG_TRACKER
  153. if(GetEditor() != nullptr)
  154. {
  155. GetEditor()->PostMessage(WM_PARAM_UDPATE, GetSlot(), index);
  156. }
  157. #endif
  158. }
  159. void LFOPlugin::Resume()
  160. {
  161. m_isResumed = true;
  162. RecalculateIncrement();
  163. NextRandom();
  164. PositionChanged();
  165. }
  166. void LFOPlugin::PositionChanged()
  167. {
  168. // TODO Changing tempo (with tempo sync enabled), parameter automation over time and setting the LFO phase manually is not considered here.
  169. m_phase = m_increment * m_SndFile.GetTotalSampleCount();
  170. m_phase -= static_cast<int64>(m_phase);
  171. }
  172. bool LFOPlugin::MidiSend(uint32 midiCode)
  173. {
  174. if(IMixPlugin *plugin = GetOutputPlugin())
  175. return plugin->MidiSend(midiCode);
  176. else
  177. return true;
  178. }
  179. bool LFOPlugin::MidiSysexSend(mpt::const_byte_span sysex)
  180. {
  181. if(IMixPlugin *plugin = GetOutputPlugin())
  182. return plugin->MidiSysexSend(sysex);
  183. else
  184. return true;
  185. }
  186. void LFOPlugin::MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel)
  187. {
  188. if(IMixPlugin *plugin = GetOutputPlugin())
  189. {
  190. plugin->MidiCC(nController, nParam, trackChannel);
  191. }
  192. }
  193. void LFOPlugin::MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackChannel)
  194. {
  195. if(IMixPlugin *plugin = GetOutputPlugin())
  196. {
  197. plugin->MidiPitchBend(increment, pwd, trackChannel);
  198. }
  199. }
  200. void LFOPlugin::MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackChannel)
  201. {
  202. if(IMixPlugin *plugin = GetOutputPlugin())
  203. {
  204. plugin->MidiVibrato(depth, pwd, trackChannel);
  205. }
  206. }
  207. void LFOPlugin::MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel)
  208. {
  209. if(ModCommand::IsNote(static_cast<ModCommand::NOTE>(note)) && vol > 0)
  210. {
  211. SetParameter(kCurrentPhase, 0);
  212. }
  213. if(IMixPlugin *plugin = GetOutputPlugin())
  214. {
  215. plugin->MidiCommand(instr, note, vol, trackChannel);
  216. }
  217. }
  218. void LFOPlugin::HardAllNotesOff()
  219. {
  220. if(IMixPlugin *plugin = GetOutputPlugin())
  221. {
  222. plugin->HardAllNotesOff();
  223. }
  224. }
  225. bool LFOPlugin::IsNotePlaying(uint8 note, CHANNELINDEX trackerChn)
  226. {
  227. if(IMixPlugin *plugin = GetOutputPlugin())
  228. return plugin->IsNotePlaying(note, trackerChn);
  229. else
  230. return false;
  231. }
  232. void LFOPlugin::SaveAllParameters()
  233. {
  234. auto chunk = GetChunk(false);
  235. if(chunk.empty())
  236. return;
  237. m_pMixStruct->defaultProgram = -1;
  238. m_pMixStruct->pluginData.assign(chunk.begin(), chunk.end());
  239. }
  240. void LFOPlugin::RestoreAllParameters(int32 /*program*/)
  241. {
  242. SetChunk(mpt::as_span(m_pMixStruct->pluginData), false);
  243. }
  244. struct PluginData
  245. {
  246. char magic[4];
  247. uint32le version;
  248. uint32le amplitude; // float
  249. uint32le offset; // float
  250. uint32le frequency; // float
  251. uint32le waveForm;
  252. uint32le outputParam;
  253. uint8le tempoSync;
  254. uint8le polarity;
  255. uint8le bypassed;
  256. uint8le outputToCC;
  257. uint8le loopMode;
  258. };
  259. MPT_BINARY_STRUCT(PluginData, 33)
  260. IMixPlugin::ChunkData LFOPlugin::GetChunk(bool)
  261. {
  262. PluginData chunk;
  263. memcpy(chunk.magic, "LFO ", 4);
  264. chunk.version = 0;
  265. chunk.amplitude = IEEE754binary32LE(m_amplitude).GetInt32();
  266. chunk.offset = IEEE754binary32LE(m_offset).GetInt32();
  267. chunk.frequency = IEEE754binary32LE(m_frequency).GetInt32();
  268. chunk.waveForm = m_waveForm;
  269. chunk.outputParam = m_outputParam;
  270. chunk.tempoSync = m_tempoSync ? 1 : 0;
  271. chunk.polarity = m_polarity ? 1 : 0;
  272. chunk.bypassed = m_bypassed ? 1 : 0;
  273. chunk.outputToCC = m_outputToCC ? 1 : 0;
  274. chunk.loopMode = m_oneshot ? 1 : 0;
  275. m_chunkData.resize(sizeof(chunk));
  276. memcpy(m_chunkData.data(), &chunk, sizeof(chunk));
  277. return mpt::as_span(m_chunkData);
  278. }
  279. void LFOPlugin::SetChunk(const ChunkData &chunk, bool)
  280. {
  281. FileReader file(chunk);
  282. PluginData data;
  283. if(file.ReadStructPartial(data, file.BytesLeft())
  284. && !memcmp(data.magic, "LFO ", 4)
  285. && data.version == 0)
  286. {
  287. const float amplitude = IEEE754binary32LE().SetInt32(data.amplitude);
  288. m_amplitude = mpt::safe_clamp(amplitude, 0.0f, 1.0f);
  289. const float offset = IEEE754binary32LE().SetInt32(data.offset);
  290. m_offset = mpt::safe_clamp(offset, 0.0f, 1.0f);
  291. const float frequency = IEEE754binary32LE().SetInt32(data.frequency);
  292. m_frequency = mpt::safe_clamp(frequency, 0.0f, 1.0f);
  293. if(data.waveForm < kNumWaveforms)
  294. m_waveForm = static_cast<LFOWaveform>(data.waveForm.get());
  295. m_outputParam = data.outputParam;
  296. m_tempoSync = data.tempoSync != 0;
  297. m_polarity = data.polarity != 0;
  298. m_bypassed = data.bypassed != 0;
  299. m_outputToCC = data.outputToCC != 0;
  300. m_oneshot = data.loopMode != 0;
  301. RecalculateFrequency();
  302. }
  303. }
  304. #ifdef MODPLUG_TRACKER
  305. std::pair<PlugParamValue, PlugParamValue> LFOPlugin::GetParamUIRange(PlugParamIndex param)
  306. {
  307. if(param == kWaveform)
  308. return {0.0f, WaveformToParam(static_cast<LFOWaveform>(kNumWaveforms - 1))};
  309. else
  310. return {0.0f, 1.0f};
  311. }
  312. CString LFOPlugin::GetParamName(PlugParamIndex param)
  313. {
  314. switch(param)
  315. {
  316. case kAmplitude: return _T("Amplitude");
  317. case kOffset: return _T("Offset");
  318. case kFrequency: return _T("Frequency");
  319. case kTempoSync: return _T("Tempo Sync");
  320. case kWaveform: return _T("Waveform");
  321. case kPolarity: return _T("Polarity");
  322. case kBypassed: return _T("Bypassed");
  323. case kLoopMode: return _T("Loop Mode");
  324. case kCurrentPhase: return _T("Set LFO Phase");
  325. }
  326. return CString();
  327. }
  328. CString LFOPlugin::GetParamLabel(PlugParamIndex param)
  329. {
  330. if(param == kFrequency)
  331. {
  332. if(m_tempoSync && m_computedFrequency > 0.0 && m_computedFrequency < 1.0)
  333. return _T("Beats Per Cycle");
  334. else if(m_tempoSync)
  335. return _T("Cycles Per Beat");
  336. else
  337. return _T("Hz");
  338. }
  339. return CString();
  340. }
  341. CString LFOPlugin::GetParamDisplay(PlugParamIndex param)
  342. {
  343. CString s;
  344. if(param == kPolarity)
  345. {
  346. return m_polarity ? _T("Inverted") : _T("Normal");
  347. } else if(param == kTempoSync)
  348. {
  349. return m_tempoSync ? _T("Yes") : _T("No");
  350. } else if(param == kBypassed)
  351. {
  352. return m_bypassed ? _T("Yes") : _T("No");
  353. } else if(param == kWaveform)
  354. {
  355. static constexpr const TCHAR * const waveforms[] = { _T("Sine"), _T("Triangle"), _T("Saw"), _T("Square"), _T("Noise"), _T("Smoothed Noise") };
  356. if(m_waveForm < static_cast<int>(std::size(waveforms)))
  357. return waveforms[m_waveForm];
  358. } else if(param == kLoopMode)
  359. {
  360. return m_oneshot ? _T("One-Shot") : _T("Looped");
  361. } else if(param == kCurrentPhase)
  362. {
  363. return _T("Write-Only");
  364. } else if(param < kLFONumParameters)
  365. {
  366. auto val = GetParameter(param);
  367. if(param == kOffset)
  368. val = 2.0f * val - 1.0f;
  369. if(param == kFrequency)
  370. {
  371. val = static_cast<PlugParamValue>(m_computedFrequency);
  372. if(m_tempoSync && val > 0.0f && val < 1.0f)
  373. val = static_cast<PlugParamValue>(1.0 / m_computedFrequency);
  374. }
  375. s.Format(_T("%.3f"), val);
  376. }
  377. return s;
  378. }
  379. CAbstractVstEditor *LFOPlugin::OpenEditor()
  380. {
  381. try
  382. {
  383. return new LFOPluginEditor(*this);
  384. } catch(mpt::out_of_memory e)
  385. {
  386. mpt::delete_out_of_memory(e);
  387. return nullptr;
  388. }
  389. }
  390. #endif // MODPLUG_TRACKER
  391. void LFOPlugin::NextRandom()
  392. {
  393. m_random = m_nextRandom;
  394. m_nextRandom = mpt::random<int32>(m_PRNG) / static_cast<float>(int32_min);
  395. }
  396. void LFOPlugin::RecalculateFrequency()
  397. {
  398. m_computedFrequency = 0.25 * std::pow(2.0, m_frequency * 8.0) - 0.25;
  399. if(m_tempoSync)
  400. {
  401. if(m_computedFrequency > 0.00045)
  402. {
  403. double freqLog = std::log(m_computedFrequency) / mpt::numbers::ln2;
  404. double freqFrac = freqLog - std::floor(freqLog);
  405. freqLog -= freqFrac;
  406. // Lock to powers of two and 1.5 times or 1.333333... times the powers of two
  407. if(freqFrac < 0.20751874963942190927313052802609)
  408. freqFrac = 0.0;
  409. else if(freqFrac < 0.5)
  410. freqFrac = 0.41503749927884381854626105605218;
  411. else if(freqFrac < 0.79248125036057809072686947197391)
  412. freqFrac = 0.58496250072115618145373894394782;
  413. else
  414. freqFrac = 1.0;
  415. m_computedFrequency = std::pow(2.0, freqLog + freqFrac) * 0.5;
  416. } else
  417. {
  418. m_computedFrequency = 0;
  419. }
  420. }
  421. RecalculateIncrement();
  422. }
  423. void LFOPlugin::RecalculateIncrement()
  424. {
  425. m_increment = m_computedFrequency / m_SndFile.GetSampleRate();
  426. if(m_tempoSync)
  427. {
  428. m_increment *= m_tempo / 60.0;
  429. }
  430. }
  431. IMixPlugin *LFOPlugin::GetOutputPlugin() const
  432. {
  433. PLUGINDEX outPlug = m_pMixStruct->GetOutputPlugin();
  434. if(outPlug > m_nSlot && outPlug < MAX_MIXPLUGINS)
  435. return m_SndFile.m_MixPlugins[outPlug].pMixPlugin;
  436. else
  437. return nullptr;
  438. }
  439. OPENMPT_NAMESPACE_END
  440. #else
  441. MPT_MSVC_WORKAROUND_LNK4221(LFOPlugin)
  442. #endif // !NO_PLUGINS