You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

851 lines
31KB

  1. /*
  2. Copyright (C) 2006-2011 Nasca Octavian Paul
  3. Author: Nasca Octavian Paul
  4. Copyright (C) 2017 Xenakios
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of version 2 of the GNU General Public License
  7. as published by the Free Software Foundation.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License (version 2) for more details.
  12. You should have received a copy of the GNU General Public License (version 2)
  13. along with this program; if not, write to the Free Software Foundation,
  14. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  15. */
  16. #include "PluginProcessor.h"
  17. #include "PluginEditor.h"
  18. #include <set>
  19. #include <thread>
  20. #ifdef WIN32
  21. #undef min
  22. #undef max
  23. #endif
  24. String g_plugintitle{ "PaulXStretch 1.1.0" };
  25. std::set<PaulstretchpluginAudioProcessor*> g_activeprocessors;
  26. int get_optimized_updown(int n, bool up) {
  27. int orig_n = n;
  28. while (true) {
  29. n = orig_n;
  30. while (!(n % 11)) n /= 11;
  31. while (!(n % 7)) n /= 7;
  32. while (!(n % 5)) n /= 5;
  33. while (!(n % 3)) n /= 3;
  34. while (!(n % 2)) n /= 2;
  35. if (n<2) break;
  36. if (up) orig_n++;
  37. else orig_n--;
  38. if (orig_n<4) return 4;
  39. };
  40. return orig_n;
  41. };
  42. int optimizebufsize(int n) {
  43. int n1 = get_optimized_updown(n, false);
  44. int n2 = get_optimized_updown(n, true);
  45. if ((n - n1)<(n2 - n)) return n1;
  46. else return n2;
  47. };
  48. inline AudioParameterFloat* make_floatpar(String id, String name, float minv, float maxv, float defv, float step, float skew)
  49. {
  50. return new AudioParameterFloat(id, name, NormalisableRange<float>(minv, maxv, step, skew), defv);
  51. }
  52. //==============================================================================
  53. PaulstretchpluginAudioProcessor::PaulstretchpluginAudioProcessor()
  54. : m_bufferingthread("pspluginprebufferthread")
  55. #ifndef JucePlugin_PreferredChannelConfigurations
  56. : AudioProcessor (BusesProperties()
  57. #if ! JucePlugin_IsMidiEffect
  58. #if ! JucePlugin_IsSynth
  59. .withInput ("Input", AudioChannelSet::stereo(), true)
  60. #endif
  61. .withOutput ("Output", AudioChannelSet::stereo(), true)
  62. #endif
  63. )
  64. #endif
  65. {
  66. g_activeprocessors.insert(this);
  67. m_playposinfo.timeInSeconds = 0.0;
  68. m_free_filter_envelope = std::make_shared<breakpoint_envelope>();
  69. m_free_filter_envelope->SetName("Free filter");
  70. m_free_filter_envelope->AddNode({ 0.0,0.75 });
  71. m_free_filter_envelope->AddNode({ 1.0,0.75 });
  72. m_free_filter_envelope->set_reset_nodes(m_free_filter_envelope->get_all_nodes());
  73. m_recbuffer.setSize(2, 44100);
  74. m_recbuffer.clear();
  75. if (m_afm->getNumKnownFormats()==0)
  76. m_afm->registerBasicFormats();
  77. m_thumb = std::make_unique<AudioThumbnail>(512, *m_afm, *m_thumbcache);
  78. // The default priority of 2 is a bit too low in some cases, it seems...
  79. m_thumbcache->getTimeSliceThread().setPriority(3);
  80. for (int i = 0; i < 9; ++i) // 41-49
  81. {
  82. m_sm_enab_pars[i] = new AudioParameterBool("enab_specmodule" + String(i), "Enable spectral module " + String(i + 1), false);
  83. }
  84. m_stretch_source = std::make_unique<StretchAudioSource>(2, m_afm,m_sm_enab_pars);
  85. m_stretch_source->setOnsetDetection(0.0);
  86. m_stretch_source->setLoopingEnabled(true);
  87. m_stretch_source->setFFTWindowingType(1);
  88. addParameter(make_floatpar("mainvolume0", "Main volume", -24.0, 12.0, -3.0, 0.1, 1.0));
  89. addParameter(make_floatpar("stretchamount0", "Stretch amount", 0.1, 1024.0, 2.0, 0.1, 0.25));
  90. addParameter(make_floatpar("fftsize0", "FFT size", 0.0, 1.0, 0.7, 0.01, 1.0));
  91. addParameter(make_floatpar("pitchshift0", "Pitch shift", -24.0f, 24.0f, 0.0f, 0.1,1.0)); // 3
  92. addParameter(make_floatpar("freqshift0", "Frequency shift", -1000.0f, 1000.0f, 0.0f, 1.0, 1.0)); // 4
  93. addParameter(make_floatpar("playrange_start0", "Sound start", 0.0f, 1.0f, 0.0f, 0.0001,1.0)); // 5
  94. addParameter(make_floatpar("playrange_end0", "Sound end", 0.0f, 1.0f, 1.0f, 0.0001,1.0)); // 6
  95. addParameter(new AudioParameterBool("freeze0", "Freeze", false)); // 7
  96. addParameter(make_floatpar("spread0", "Frequency spread", 0.0f, 1.0f, 0.0f, 0.001,1.0)); // 8
  97. addParameter(make_floatpar("compress0", "Compress", 0.0f, 1.0f, 0.0f, 0.001,1.0)); // 9
  98. addParameter(make_floatpar("loopxfadelen0", "Loop xfade length", 0.0f, 1.0f, 0.01f, 0.001, 1.0)); // 10
  99. addParameter(new AudioParameterInt("numharmonics0", "Num harmonics", 1, 100, 10)); // 11
  100. addParameter(make_floatpar("harmonicsfreq0", "Harmonics base freq", 1.0, 5000.0, 128.0, 0.1, 0.5));
  101. addParameter(make_floatpar("harmonicsbw0", "Harmonics bandwidth", 0.1f, 200.0f, 25.0f, 0.01, 1.0)); // 13
  102. addParameter(new AudioParameterBool("harmonicsgauss0", "Gaussian harmonics", false)); // 14
  103. addParameter(make_floatpar("octavemixm2_0", "2 octaves down level", 0.0f, 1.0f, 0.0f, 0.001, 1.0)); // 15
  104. addParameter(make_floatpar("octavemixm1_0", "Octave down level", 0.0f, 1.0f, 0.0f, 0.001, 1.0)); // 16
  105. addParameter(make_floatpar("octavemix0_0", "Normal pitch level", 0.0f, 1.0f, 1.0f, 0.001, 1.0)); // 17
  106. addParameter(make_floatpar("octavemix1_0", "1 octave up level", 0.0f, 1.0f, 0.0f, 0.001, 1.0)); // 18
  107. addParameter(make_floatpar("octavemix15_0", "1 octave and fifth up level", 0.0f, 1.0f, 0.0f, 0.001, 1.0)); // 19
  108. addParameter(make_floatpar("octavemix2_0", "2 octaves up level", 0.0f, 1.0f, 0.0f, 0.001, 1.0)); // 20
  109. addParameter(make_floatpar("tonalvsnoisebw_0", "Tonal vs Noise BW", 0.74f, 1.0f, 0.74f, 0.001, 1.0)); // 21
  110. addParameter(make_floatpar("tonalvsnoisepreserve_0", "Tonal vs Noise preserve", -1.0f, 1.0f, 0.5f, 0.001, 1.0)); // 22
  111. auto filt_convertFrom0To1Func = [](float rangemin, float rangemax, float value)
  112. {
  113. if (value < 0.5f)
  114. return jmap<float>(value, 0.0f, 0.5f, 20.0f, 1000.0f);
  115. return jmap<float>(value, 0.5f, 1.0f, 1000.0f, 20000.0f);
  116. };
  117. auto filt_convertTo0To1Func = [](float rangemin, float rangemax, float value)
  118. {
  119. if (value < 1000.0f)
  120. return jmap<float>(value, 20.0f, 1000.0f, 0.0f, 0.5f);
  121. return jmap<float>(value, 1000.0f, 20000.0f, 0.5f, 1.0f);
  122. };
  123. addParameter(new AudioParameterFloat("filter_low_0", "Filter low",
  124. NormalisableRange<float>(20.0f, 20000.0f,
  125. filt_convertFrom0To1Func, filt_convertTo0To1Func), 20.0f)); // 23
  126. addParameter(new AudioParameterFloat("filter_high_0", "Filter high",
  127. NormalisableRange<float>(20.0f, 20000.0f,
  128. filt_convertFrom0To1Func,filt_convertTo0To1Func), 20000.0f));; // 24
  129. addParameter(make_floatpar("onsetdetect_0", "Onset detection", 0.0f, 1.0f, 0.0f, 0.01, 1.0)); // 25
  130. addParameter(new AudioParameterBool("capture_enabled0", "Capture", false)); // 26
  131. m_outchansparam = new AudioParameterInt("numoutchans0", "Num outs", 2, 8, 2); // 27
  132. addParameter(m_outchansparam); // 27
  133. addParameter(new AudioParameterBool("pause_enabled0", "Pause", false)); // 28
  134. addParameter(new AudioParameterFloat("maxcapturelen_0", "Max capture length", 1.0f, 120.0f, 10.0f)); // 29
  135. addParameter(new AudioParameterBool("passthrough0", "Pass input through", false)); // 30
  136. addParameter(new AudioParameterBool("markdirty0", "Internal (don't use)", false)); // 31
  137. m_inchansparam = new AudioParameterInt("numinchans0", "Num ins", 2, 8, 2); // 32
  138. addParameter(m_inchansparam); // 32
  139. addParameter(new AudioParameterBool("bypass_stretch0", "Bypass stretch", false)); // 33
  140. addParameter(new AudioParameterFloat("freefilter_shiftx_0", "Free filter shift X", -1.0f, 1.0f, 0.0f)); // 34
  141. addParameter(new AudioParameterFloat("freefilter_shifty_0", "Free filter shift Y", -1.0f, 1.0f, 0.0f)); // 35
  142. addParameter(new AudioParameterFloat("freefilter_scaley_0", "Free filter scale Y", -1.0f, 1.0f, 1.0f)); // 36
  143. addParameter(new AudioParameterFloat("freefilter_tilty_0", "Free filter tilt Y", -1.0f, 1.0f, 0.0f)); // 37
  144. addParameter(new AudioParameterInt("freefilter_randomybands0", "Random bands", 2, 128, 16)); // 38
  145. addParameter(new AudioParameterInt("freefilter_randomyrate0", "Random rate", 1, 32, 2)); // 39
  146. addParameter(new AudioParameterFloat("freefilter_randomyamount0", "Random amount", 0.0, 1.0, 0.0)); // 40
  147. for (int i = 0; i < 9; ++i) // 41-49
  148. {
  149. addParameter(m_sm_enab_pars[i]);
  150. }
  151. auto& pars = getParameters();
  152. for (const auto& p : pars)
  153. m_reset_pars.push_back(p->getValue());
  154. setPreBufferAmount(2);
  155. startTimer(1, 50);
  156. m_show_technical_info = m_propsfile->m_props_file->getBoolValue("showtechnicalinfo", false);
  157. }
  158. PaulstretchpluginAudioProcessor::~PaulstretchpluginAudioProcessor()
  159. {
  160. g_activeprocessors.erase(this);
  161. m_thumb->removeAllChangeListeners();
  162. m_thumb = nullptr;
  163. m_bufferingthread.stopThread(1000);
  164. }
  165. void PaulstretchpluginAudioProcessor::resetParameters()
  166. {
  167. ScopedLock locker(m_cs);
  168. for (int i = 0; i < m_reset_pars.size(); ++i)
  169. {
  170. if (i!=cpi_main_volume && i!=cpi_passthrough)
  171. setParameter(i, m_reset_pars[i]);
  172. }
  173. }
  174. void PaulstretchpluginAudioProcessor::setPreBufferAmount(int x)
  175. {
  176. int temp = jlimit(0, 5, x);
  177. if (temp != m_prebuffer_amount || m_use_backgroundbuffering == false)
  178. {
  179. m_use_backgroundbuffering = true;
  180. m_prebuffer_amount = temp;
  181. m_recreate_buffering_source = true;
  182. ScopedLock locker(m_cs);
  183. m_prebuffering_inited = false;
  184. m_cur_num_out_chans = *m_outchansparam;
  185. //Logger::writeToLog("Switching to use " + String(m_cur_num_out_chans) + " out channels");
  186. String err;
  187. startplay({ *getFloatParameter(cpi_soundstart),*getFloatParameter(cpi_soundend) },
  188. m_cur_num_out_chans, m_curmaxblocksize, err);
  189. m_prebuffering_inited = true;
  190. }
  191. }
  192. int PaulstretchpluginAudioProcessor::getPreBufferAmount()
  193. {
  194. if (m_use_backgroundbuffering == false)
  195. return -1;
  196. return m_prebuffer_amount;
  197. }
  198. ValueTree PaulstretchpluginAudioProcessor::getStateTree(bool ignoreoptions, bool ignorefile)
  199. {
  200. ValueTree paramtree("paulstretch3pluginstate");
  201. for (int i = 0; i<getNumParameters(); ++i)
  202. {
  203. storeToTreeProperties(paramtree, nullptr, getFloatParameter(i));
  204. }
  205. storeToTreeProperties(paramtree, nullptr, getIntParameter(cpi_numharmonics));
  206. storeToTreeProperties(paramtree, nullptr, m_outchansparam);
  207. storeToTreeProperties(paramtree, nullptr, m_inchansparam);
  208. storeToTreeProperties(paramtree, nullptr, getIntParameter(cpi_freefilter_randomy_numbands));
  209. storeToTreeProperties(paramtree, nullptr, getIntParameter(cpi_freefilter_randomy_rate));
  210. storeToTreeProperties(paramtree, nullptr, getBoolParameter(cpi_bypass_stretch));
  211. if (m_current_file != File() && ignorefile == false)
  212. {
  213. paramtree.setProperty("importedfile", m_current_file.getFullPathName(), nullptr);
  214. }
  215. auto specorder = m_stretch_source->getSpectrumProcessOrder();
  216. paramtree.setProperty("numspectralstages", (int)specorder.size(), nullptr);
  217. for (int i = 0; i < specorder.size(); ++i)
  218. {
  219. paramtree.setProperty("specorder" + String(i), specorder[i].m_index, nullptr);
  220. //paramtree.setProperty("specstepenabled" + String(i), specorder[i].m_enabled, nullptr);
  221. }
  222. if (ignoreoptions == false)
  223. {
  224. if (m_use_backgroundbuffering)
  225. paramtree.setProperty("prebufamount", m_prebuffer_amount, nullptr);
  226. else
  227. paramtree.setProperty("prebufamount", -1, nullptr);
  228. paramtree.setProperty("loadfilewithstate", m_load_file_with_state, nullptr);
  229. }
  230. storeToTreeProperties(paramtree, nullptr, "waveviewrange", m_wave_view_range);
  231. ValueTree freefilterstate = m_free_filter_envelope->saveState(Identifier("freefilter_envelope"));
  232. paramtree.addChild(freefilterstate, -1, nullptr);
  233. return paramtree;
  234. }
  235. void PaulstretchpluginAudioProcessor::setStateFromTree(ValueTree tree)
  236. {
  237. if (tree.isValid())
  238. {
  239. {
  240. ScopedLock locker(m_cs);
  241. ValueTree freefilterstate = tree.getChildWithName("freefilter_envelope");
  242. m_free_filter_envelope->restoreState(freefilterstate);
  243. m_load_file_with_state = tree.getProperty("loadfilewithstate", true);
  244. if (tree.hasProperty("numspectralstages"))
  245. {
  246. /*
  247. std::vector<SpectrumProcess> order;
  248. int ordersize = tree.getProperty("numspectralstages");
  249. for (int i = 0; i < ordersize; ++i)
  250. {
  251. bool step_enabled = tree.getProperty("specstepenabled" + String(i));
  252. //order.push_back({ (int)tree.getProperty("specorder" + String(i)), step_enabled });
  253. }
  254. if (ordersize<m_stretch_source->getSpectrumProcessOrder().size())
  255. order.emplace_back(8,m_sm_enab_pars[8]);
  256. m_stretch_source->setSpectrumProcessOrder(order);
  257. */
  258. }
  259. getFromTreeProperties(tree, "waveviewrange", m_wave_view_range);
  260. for (int i = 0; i < getNumParameters(); ++i)
  261. {
  262. getFromTreeProperties(tree,getFloatParameter(i));
  263. }
  264. getFromTreeProperties(tree, getIntParameter(cpi_numharmonics));
  265. getFromTreeProperties(tree, m_outchansparam);
  266. getFromTreeProperties(tree, m_inchansparam);
  267. getFromTreeProperties(tree, getBoolParameter(cpi_bypass_stretch));
  268. getFromTreeProperties(tree, getIntParameter(cpi_freefilter_randomy_numbands));
  269. getFromTreeProperties(tree, getIntParameter(cpi_freefilter_randomy_rate));
  270. }
  271. int prebufamt = tree.getProperty("prebufamount", 2);
  272. if (prebufamt == -1)
  273. m_use_backgroundbuffering = false;
  274. else
  275. setPreBufferAmount(prebufamt);
  276. if (m_load_file_with_state == true)
  277. {
  278. String fn = tree.getProperty("importedfile");
  279. if (fn.isEmpty() == false)
  280. {
  281. File f(fn);
  282. setAudioFile(f);
  283. }
  284. }
  285. m_state_dirty = true;
  286. }
  287. }
  288. //==============================================================================
  289. const String PaulstretchpluginAudioProcessor::getName() const
  290. {
  291. return JucePlugin_Name;
  292. }
  293. bool PaulstretchpluginAudioProcessor::acceptsMidi() const
  294. {
  295. #if JucePlugin_WantsMidiInput
  296. return true;
  297. #else
  298. return false;
  299. #endif
  300. }
  301. bool PaulstretchpluginAudioProcessor::producesMidi() const
  302. {
  303. #if JucePlugin_ProducesMidiOutput
  304. return true;
  305. #else
  306. return false;
  307. #endif
  308. }
  309. bool PaulstretchpluginAudioProcessor::isMidiEffect() const
  310. {
  311. #if JucePlugin_IsMidiEffect
  312. return true;
  313. #else
  314. return false;
  315. #endif
  316. }
  317. double PaulstretchpluginAudioProcessor::getTailLengthSeconds() const
  318. {
  319. return 0.0;
  320. //return (double)m_bufamounts[m_prebuffer_amount]/getSampleRate();
  321. }
  322. int PaulstretchpluginAudioProcessor::getNumPrograms()
  323. {
  324. return 1;
  325. }
  326. int PaulstretchpluginAudioProcessor::getCurrentProgram()
  327. {
  328. return 0;
  329. }
  330. void PaulstretchpluginAudioProcessor::setCurrentProgram (int index)
  331. {
  332. }
  333. const String PaulstretchpluginAudioProcessor::getProgramName (int index)
  334. {
  335. return String();
  336. }
  337. void PaulstretchpluginAudioProcessor::changeProgramName (int index, const String& newName)
  338. {
  339. }
  340. void PaulstretchpluginAudioProcessor::setFFTSize(double size)
  341. {
  342. if (m_prebuffer_amount == 5)
  343. m_fft_size_to_use = pow(2, 7.0 + size * 14.5);
  344. else m_fft_size_to_use = pow(2, 7.0 + size * 10.0); // chicken out from allowing huge FFT sizes if not enough prebuffering
  345. int optim = optimizebufsize(m_fft_size_to_use);
  346. m_fft_size_to_use = optim;
  347. m_stretch_source->setFFTSize(optim);
  348. //Logger::writeToLog(String(m_fft_size_to_use));
  349. }
  350. void PaulstretchpluginAudioProcessor::startplay(Range<double> playrange, int numoutchans, int maxBlockSize, String& err)
  351. {
  352. m_stretch_source->setPlayRange(playrange, true);
  353. m_stretch_source->setFreeFilterEnvelope(m_free_filter_envelope);
  354. int bufamt = m_bufamounts[m_prebuffer_amount];
  355. if (m_buffering_source != nullptr && numoutchans != m_buffering_source->getNumberOfChannels())
  356. m_recreate_buffering_source = true;
  357. if (m_recreate_buffering_source == true)
  358. {
  359. m_buffering_source = std::make_unique<MyBufferingAudioSource>(m_stretch_source.get(),
  360. m_bufferingthread, false, bufamt, numoutchans, false);
  361. m_recreate_buffering_source = false;
  362. }
  363. if (m_bufferingthread.isThreadRunning() == false)
  364. m_bufferingthread.startThread();
  365. m_stretch_source->setNumOutChannels(numoutchans);
  366. m_stretch_source->setFFTSize(m_fft_size_to_use);
  367. m_stretch_source->setProcessParameters(&m_ppar);
  368. m_last_outpos_pos = 0.0;
  369. m_last_in_pos = playrange.getStart()*m_stretch_source->getInfileLengthSeconds();
  370. m_buffering_source->prepareToPlay(maxBlockSize, getSampleRateChecked());
  371. }
  372. void PaulstretchpluginAudioProcessor::setParameters(const std::vector<double>& pars)
  373. {
  374. ScopedLock locker(m_cs);
  375. for (int i = 0; i < getNumParameters(); ++i)
  376. {
  377. if (i<pars.size())
  378. setParameter(i, pars[i]);
  379. }
  380. }
  381. void PaulstretchpluginAudioProcessor::updateStretchParametersFromPluginParameters(ProcessParameters & pars)
  382. {
  383. pars.pitch_shift.cents = *getFloatParameter(cpi_pitchshift) * 100.0;
  384. pars.freq_shift.Hz = *getFloatParameter(cpi_frequencyshift);
  385. pars.spread.bandwidth = *getFloatParameter(cpi_spreadamount);
  386. pars.compressor.power = *getFloatParameter(cpi_compress);
  387. pars.harmonics.nharmonics = *getIntParameter(cpi_numharmonics);
  388. pars.harmonics.freq = *getFloatParameter(cpi_harmonicsfreq);
  389. pars.harmonics.bandwidth = *getFloatParameter(cpi_harmonicsbw);
  390. pars.harmonics.gauss = getParameter(cpi_harmonicsgauss);
  391. pars.octave.om2 = *getFloatParameter(cpi_octavesm2);
  392. pars.octave.om1 = *getFloatParameter(cpi_octavesm1);
  393. pars.octave.o0 = *getFloatParameter(cpi_octaves0);
  394. pars.octave.o1 = *getFloatParameter(cpi_octaves1);
  395. pars.octave.o15 = *getFloatParameter(cpi_octaves15);
  396. pars.octave.o2 = *getFloatParameter(cpi_octaves2);
  397. pars.filter.low = *getFloatParameter(cpi_filter_low);
  398. pars.filter.high = *getFloatParameter(cpi_filter_high);
  399. pars.tonal_vs_noise.bandwidth = *getFloatParameter(cpi_tonalvsnoisebw);
  400. pars.tonal_vs_noise.preserve = *getFloatParameter(cpi_tonalvsnoisepreserve);
  401. }
  402. String PaulstretchpluginAudioProcessor::offlineRender(File outputfile)
  403. {
  404. File outputfiletouse = outputfile.getNonexistentSibling();
  405. int numoutchans = *getIntParameter(cpi_num_outchans);
  406. auto ss = std::make_shared<StretchAudioSource>(numoutchans,m_afm,m_sm_enab_pars);
  407. int blocksize = 2048;
  408. ss->setAudioFile(m_current_file);
  409. ProcessParameters renderpars;
  410. updateStretchParametersFromPluginParameters(renderpars);
  411. ss->setProcessParameters(&renderpars);
  412. double t0 = *getFloatParameter(cpi_soundstart);
  413. double t1 = *getFloatParameter(cpi_soundend);
  414. sanitizeTimeRange(t0, t1);
  415. ss->setRate(*getFloatParameter(cpi_stretchamount));
  416. ss->setPlayRange({ t0,t1 }, true);
  417. ss->setLoopingEnabled(true);
  418. ss->setNumOutChannels(numoutchans);
  419. ss->setFFTWindowingType(1);
  420. //ss->setPreviewDry(true);
  421. ss->setOnsetDetection(*getFloatParameter(cpi_onsetdetection));
  422. ss->setLoopXFadeLength(*getFloatParameter(cpi_loopxfadelen));
  423. ss->setFreezing(getParameter(cpi_freeze));
  424. ss->setPaused(getParameter(cpi_pause_enabled));
  425. ss->setSpectrumProcessOrder(m_stretch_source->getSpectrumProcessOrder());
  426. ss->setFFTSize(m_fft_size_to_use);
  427. ss->setMainVolume(*getFloatParameter(cpi_main_volume));
  428. double outsr = getSampleRateChecked();
  429. ss->prepareToPlay(blocksize, outsr);
  430. WavAudioFormat wavformat;
  431. FileOutputStream* outstream = outputfiletouse.createOutputStream();
  432. if (outstream == nullptr)
  433. return "Could not create output file";
  434. auto writer = wavformat.createWriterFor(outstream, getSampleRateChecked(), numoutchans, 32, StringPairArray(), 0);
  435. if (writer == nullptr)
  436. {
  437. delete outstream;
  438. return "Could not create WAV writer";
  439. }
  440. auto rendertask = [ss,writer,blocksize,numoutchans, outsr, this]()
  441. {
  442. AudioBuffer<float> renderbuffer(numoutchans, blocksize);
  443. int64_t outlen = 250 * outsr;
  444. int64_t outcounter = 0;
  445. AudioSourceChannelInfo asci(renderbuffer);
  446. m_offline_render_state = 0;
  447. m_offline_render_cancel_requested = false;
  448. while (outcounter < outlen)
  449. {
  450. if (m_offline_render_cancel_requested == true)
  451. break;
  452. ss->getNextAudioBlock(asci);
  453. writer->writeFromAudioSampleBuffer(renderbuffer, 0, blocksize);
  454. outcounter += blocksize;
  455. m_offline_render_state = 100.0 / outlen * outcounter;
  456. }
  457. m_offline_render_state = 200;
  458. delete writer;
  459. };
  460. std::thread th(rendertask);
  461. th.detach();
  462. return "Rendered OK";
  463. }
  464. double PaulstretchpluginAudioProcessor::getSampleRateChecked()
  465. {
  466. if (m_cur_sr < 1.0 || m_cur_sr>1000000.0)
  467. return 44100.0;
  468. return m_cur_sr;
  469. }
  470. void PaulstretchpluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
  471. {
  472. ++m_prepare_count;
  473. ScopedLock locker(m_cs);
  474. m_cur_sr = sampleRate;
  475. m_curmaxblocksize = samplesPerBlock;
  476. m_input_buffer.setSize(getMainBusNumInputChannels(), samplesPerBlock);
  477. int numoutchans = *m_outchansparam;
  478. if (numoutchans != m_cur_num_out_chans)
  479. m_prebuffering_inited = false;
  480. if (m_using_memory_buffer == true)
  481. {
  482. int len = jlimit(100,m_recbuffer.getNumSamples(),
  483. int(getSampleRateChecked()*(*getFloatParameter(cpi_max_capture_len))));
  484. m_stretch_source->setAudioBufferAsInputSource(&m_recbuffer,
  485. getSampleRateChecked(),
  486. len);
  487. //m_thumb->reset(m_recbuffer.getNumChannels(), sampleRate, len);
  488. }
  489. if (m_prebuffering_inited == false)
  490. {
  491. setFFTSize(*getFloatParameter(cpi_fftsize));
  492. m_stretch_source->setProcessParameters(&m_ppar);
  493. m_stretch_source->setFFTWindowingType(1);
  494. String err;
  495. startplay({ *getFloatParameter(cpi_soundstart),*getFloatParameter(cpi_soundend) },
  496. numoutchans, samplesPerBlock, err);
  497. m_cur_num_out_chans = numoutchans;
  498. m_prebuffering_inited = true;
  499. }
  500. else
  501. {
  502. m_buffering_source->prepareToPlay(samplesPerBlock, getSampleRateChecked());
  503. }
  504. }
  505. void PaulstretchpluginAudioProcessor::releaseResources()
  506. {
  507. //m_control->stopplay();
  508. //m_ready_to_play = false;
  509. }
  510. #ifndef JucePlugin_PreferredChannelConfigurations
  511. bool PaulstretchpluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
  512. {
  513. #if JucePlugin_IsMidiEffect
  514. ignoreUnused (layouts);
  515. return true;
  516. #else
  517. // This is the place where you check if the layout is supported.
  518. // In this template code we only support mono or stereo.
  519. if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono()
  520. && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
  521. return false;
  522. // This checks if the input layout matches the output layout
  523. #if ! JucePlugin_IsSynth
  524. if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
  525. return false;
  526. #endif
  527. return true;
  528. #endif
  529. }
  530. #endif
  531. void copyAudioBufferWrappingPosition(const AudioBuffer<float>& src, AudioBuffer<float>& dest, int destbufpos, int maxdestpos)
  532. {
  533. for (int i = 0; i < dest.getNumChannels(); ++i)
  534. {
  535. int channel_to_copy = i % src.getNumChannels();
  536. if (destbufpos + src.getNumSamples() > maxdestpos)
  537. {
  538. int wrappos = (destbufpos + src.getNumSamples()) % maxdestpos;
  539. int partial_len = src.getNumSamples() - wrappos;
  540. dest.copyFrom(channel_to_copy, destbufpos, src, channel_to_copy, 0, partial_len);
  541. dest.copyFrom(channel_to_copy, partial_len, src, channel_to_copy, 0, wrappos);
  542. }
  543. else
  544. {
  545. dest.copyFrom(channel_to_copy, destbufpos, src, channel_to_copy, 0, src.getNumSamples());
  546. }
  547. }
  548. }
  549. void PaulstretchpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
  550. {
  551. ScopedLock locker(m_cs);
  552. AudioPlayHead* phead = getPlayHead();
  553. if (phead != nullptr)
  554. {
  555. phead->getCurrentPosition(m_playposinfo);
  556. }
  557. else
  558. m_playposinfo.isPlaying = false;
  559. ScopedNoDenormals noDenormals;
  560. double srtemp = getSampleRate();
  561. if (srtemp != m_cur_sr)
  562. m_cur_sr = srtemp;
  563. const int totalNumInputChannels = getTotalNumInputChannels();
  564. const int totalNumOutputChannels = getTotalNumOutputChannels();
  565. for (int i = 0; i < totalNumInputChannels; ++i)
  566. m_input_buffer.copyFrom(i, 0, buffer, i, 0, buffer.getNumSamples());
  567. for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
  568. buffer.clear (i, 0, buffer.getNumSamples());
  569. if (m_prebuffering_inited == false)
  570. return;
  571. if (m_is_recording == true)
  572. {
  573. if (m_playposinfo.isPlaying == false && m_capture_when_host_plays == true)
  574. return;
  575. int recbuflenframes = m_max_reclen * getSampleRate();
  576. copyAudioBufferWrappingPosition(buffer, m_recbuffer, m_rec_pos, recbuflenframes);
  577. m_thumb->addBlock(m_rec_pos, buffer, 0, buffer.getNumSamples());
  578. m_rec_pos = (m_rec_pos + buffer.getNumSamples()) % recbuflenframes;
  579. return;
  580. }
  581. jassert(m_buffering_source != nullptr);
  582. jassert(m_bufferingthread.isThreadRunning());
  583. if (m_last_host_playing == false && m_playposinfo.isPlaying)
  584. {
  585. m_stretch_source->seekPercent(*getFloatParameter(cpi_soundstart));
  586. m_last_host_playing = true;
  587. }
  588. else if (m_last_host_playing == true && m_playposinfo.isPlaying == false)
  589. {
  590. m_last_host_playing = false;
  591. }
  592. if (m_play_when_host_plays == true && m_playposinfo.isPlaying == false)
  593. return;
  594. m_free_filter_envelope->m_transform_x_shift = *getFloatParameter(cpi_freefilter_shiftx);
  595. m_free_filter_envelope->m_transform_y_shift = *getFloatParameter(cpi_freefilter_shifty);
  596. m_free_filter_envelope->m_transform_y_scale = *getFloatParameter(cpi_freefilter_scaley);
  597. m_free_filter_envelope->m_transform_y_tilt = *getFloatParameter(cpi_freefilter_tilty);
  598. m_free_filter_envelope->m_transform_y_random_bands = *getIntParameter(cpi_freefilter_randomy_numbands);
  599. m_free_filter_envelope->m_transform_y_random_rate = *getIntParameter(cpi_freefilter_randomy_rate);
  600. m_free_filter_envelope->m_transform_y_random_amount = *getFloatParameter(cpi_freefilter_randomy_amount);
  601. m_stretch_source->setSpectralModulesEnabled(m_sm_enab_pars);
  602. m_stretch_source->setMainVolume(*getFloatParameter(cpi_main_volume));
  603. m_stretch_source->setRate(*getFloatParameter(cpi_stretchamount));
  604. m_stretch_source->setPreviewDry(*getBoolParameter(cpi_bypass_stretch));
  605. setFFTSize(*getFloatParameter(cpi_fftsize));
  606. updateStretchParametersFromPluginParameters(m_ppar);
  607. m_stretch_source->setOnsetDetection(*getFloatParameter(cpi_onsetdetection));
  608. m_stretch_source->setLoopXFadeLength(*getFloatParameter(cpi_loopxfadelen));
  609. double t0 = *getFloatParameter(cpi_soundstart);
  610. double t1 = *getFloatParameter(cpi_soundend);
  611. sanitizeTimeRange(t0, t1);
  612. m_stretch_source->setPlayRange({ t0,t1 }, true);
  613. m_stretch_source->setFreezing(getParameter(cpi_freeze));
  614. m_stretch_source->setPaused(getParameter(cpi_pause_enabled));
  615. m_stretch_source->setProcessParameters(&m_ppar);
  616. AudioSourceChannelInfo aif(buffer);
  617. if (isNonRealtime() || m_use_backgroundbuffering == false)
  618. {
  619. m_stretch_source->getNextAudioBlock(aif);
  620. }
  621. else
  622. {
  623. m_buffering_source->getNextAudioBlock(aif);
  624. }
  625. if (getParameter(cpi_passthrough) > 0.5f)
  626. {
  627. for (int i = 0; i < totalNumInputChannels; ++i)
  628. {
  629. buffer.addFrom(i, 0, m_input_buffer, i, 0, buffer.getNumSamples());
  630. }
  631. }
  632. for (int i = 0; i < buffer.getNumChannels(); ++i)
  633. {
  634. for (int j = 0; j < buffer.getNumSamples(); ++j)
  635. {
  636. float sample = buffer.getSample(i,j);
  637. if (std::isnan(sample) || std::isinf(sample))
  638. ++m_abnormal_output_samples;
  639. }
  640. }
  641. }
  642. //==============================================================================
  643. bool PaulstretchpluginAudioProcessor::hasEditor() const
  644. {
  645. return true; // (change this to false if you choose to not supply an editor)
  646. }
  647. AudioProcessorEditor* PaulstretchpluginAudioProcessor::createEditor()
  648. {
  649. return new PaulstretchpluginAudioProcessorEditor (*this);
  650. }
  651. //==============================================================================
  652. void PaulstretchpluginAudioProcessor::getStateInformation (MemoryBlock& destData)
  653. {
  654. ValueTree paramtree = getStateTree(false,false);
  655. MemoryOutputStream stream(destData,true);
  656. paramtree.writeToStream(stream);
  657. }
  658. void PaulstretchpluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  659. {
  660. ValueTree tree = ValueTree::readFromData(data, sizeInBytes);
  661. setStateFromTree(tree);
  662. }
  663. void PaulstretchpluginAudioProcessor::setDirty()
  664. {
  665. toggleBool(getBoolParameter(cpi_markdirty));
  666. }
  667. void PaulstretchpluginAudioProcessor::setRecordingEnabled(bool b)
  668. {
  669. ScopedLock locker(m_cs);
  670. int lenbufframes = getSampleRateChecked()*m_max_reclen;
  671. if (b == true)
  672. {
  673. m_using_memory_buffer = true;
  674. m_current_file = File();
  675. int numchans = *m_inchansparam;
  676. m_recbuffer.setSize(numchans, m_max_reclen*getSampleRateChecked()+4096,false,false,true);
  677. m_recbuffer.clear();
  678. m_rec_pos = 0;
  679. m_thumb->reset(m_recbuffer.getNumChannels(), getSampleRateChecked(), lenbufframes);
  680. m_is_recording = true;
  681. }
  682. else
  683. {
  684. if (m_is_recording == true)
  685. {
  686. finishRecording(lenbufframes);
  687. }
  688. }
  689. }
  690. double PaulstretchpluginAudioProcessor::getRecordingPositionPercent()
  691. {
  692. if (m_is_recording==false)
  693. return 0.0;
  694. return 1.0 / m_recbuffer.getNumSamples()*m_rec_pos;
  695. }
  696. String PaulstretchpluginAudioProcessor::setAudioFile(File f)
  697. {
  698. auto ai = unique_from_raw(m_afm->createReaderFor(f));
  699. if (ai != nullptr)
  700. {
  701. if (ai->numChannels > 8)
  702. {
  703. return "Too many channels in file "+f.getFullPathName();
  704. }
  705. if (ai->bitsPerSample>32)
  706. {
  707. return "Too high bit depth in file " + f.getFullPathName();
  708. }
  709. m_thumb->setSource(new FileInputSource(f));
  710. ScopedLock locker(m_cs);
  711. m_stretch_source->setAudioFile(f);
  712. //Range<double> currange{ *getFloatParameter(cpi_soundstart),*getFloatParameter(cpi_soundend) };
  713. //if (currange.contains(m_stretch_source->getInfilePositionPercent())==false)
  714. m_stretch_source->seekPercent(*getFloatParameter(cpi_soundstart));
  715. m_current_file = f;
  716. m_current_file_date = m_current_file.getLastModificationTime();
  717. m_using_memory_buffer = false;
  718. setDirty();
  719. return String();
  720. }
  721. return "Could not open file " + f.getFullPathName();
  722. }
  723. Range<double> PaulstretchpluginAudioProcessor::getTimeSelection()
  724. {
  725. return { *getFloatParameter(cpi_soundstart),*getFloatParameter(cpi_soundend) };
  726. }
  727. double PaulstretchpluginAudioProcessor::getPreBufferingPercent()
  728. {
  729. if (m_buffering_source==nullptr)
  730. return 0.0;
  731. return m_buffering_source->getPercentReady();
  732. }
  733. void PaulstretchpluginAudioProcessor::timerCallback(int id)
  734. {
  735. if (id == 1)
  736. {
  737. bool capture = getParameter(cpi_capture_enabled);
  738. if (capture == false && m_max_reclen != *getFloatParameter(cpi_max_capture_len))
  739. {
  740. m_max_reclen = *getFloatParameter(cpi_max_capture_len);
  741. //Logger::writeToLog("Changing max capture len to " + String(m_max_reclen));
  742. }
  743. if (capture == true && m_is_recording == false)
  744. {
  745. setRecordingEnabled(true);
  746. return;
  747. }
  748. if (capture == false && m_is_recording == true)
  749. {
  750. setRecordingEnabled(false);
  751. return;
  752. }
  753. if (m_cur_num_out_chans != *m_outchansparam)
  754. {
  755. jassert(m_curmaxblocksize > 0);
  756. ScopedLock locker(m_cs);
  757. m_prebuffering_inited = false;
  758. m_cur_num_out_chans = *m_outchansparam;
  759. //Logger::writeToLog("Switching to use " + String(m_cur_num_out_chans) + " out channels");
  760. String err;
  761. startplay({ *getFloatParameter(cpi_soundstart),*getFloatParameter(cpi_soundend) },
  762. m_cur_num_out_chans, m_curmaxblocksize, err);
  763. m_prebuffering_inited = true;
  764. }
  765. }
  766. }
  767. void PaulstretchpluginAudioProcessor::finishRecording(int lenrecording)
  768. {
  769. m_is_recording = false;
  770. m_stretch_source->setAudioBufferAsInputSource(&m_recbuffer, getSampleRateChecked(), lenrecording);
  771. m_stretch_source->setPlayRange({ *getFloatParameter(cpi_soundstart),*getFloatParameter(cpi_soundend) }, true);
  772. }
  773. AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  774. {
  775. return new PaulstretchpluginAudioProcessor();
  776. }