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.

752 lines
24KB

  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 <array>
  19. //==============================================================================
  20. PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor (PaulstretchpluginAudioProcessor& p)
  21. : AudioProcessorEditor (&p),
  22. m_wavecomponent(p.m_afm),
  23. processor (p)
  24. {
  25. addAndMakeVisible(&m_import_button);
  26. m_import_button.setButtonText("Import file...");
  27. attachCallback(m_import_button, [this]() { chooseFile(); });
  28. addAndMakeVisible(&m_settings_button);
  29. m_settings_button.setButtonText("Settings...");
  30. attachCallback(m_settings_button, [this]() { showSettingsMenu(); });
  31. addAndMakeVisible(&m_info_label);
  32. addAndMakeVisible(&m_wavecomponent);
  33. const auto& pars = processor.getParameters();
  34. for (int i=0;i<pars.size();++i)
  35. {
  36. AudioProcessorParameterWithID* parid = dynamic_cast<AudioProcessorParameterWithID*>(pars[i]);
  37. jassert(parid);
  38. if (parid)
  39. {
  40. bool notifyonlyonrelease = false;
  41. if (parid->paramID.startsWith("fftsize"))
  42. notifyonlyonrelease = true;
  43. if (parid->paramID.startsWith("numoutchans"))
  44. notifyonlyonrelease = true;
  45. m_parcomps.push_back(std::make_shared<ParameterComponent>(pars[i],notifyonlyonrelease));
  46. addAndMakeVisible(m_parcomps.back().get());
  47. }
  48. }
  49. //addAndMakeVisible(&m_specvis);
  50. setSize (1000, 30+(pars.size()/2)*25+200);
  51. m_wavecomponent.TimeSelectionChangedCallback = [this](Range<double> range, int which)
  52. {
  53. *processor.getFloatParameter(5) = range.getStart();
  54. *processor.getFloatParameter(6) = range.getEnd();
  55. };
  56. m_wavecomponent.CursorPosCallback = [this]()
  57. {
  58. return processor.getStretchSource()->getInfilePositionPercent();
  59. };
  60. m_wavecomponent.ShowFileCacheRange = true;
  61. m_spec_order_ed.setSource(processor.getStretchSource());
  62. addAndMakeVisible(&m_spec_order_ed);
  63. startTimer(1, 100);
  64. startTimer(2, 1000);
  65. startTimer(3, 200);
  66. m_wavecomponent.startTimer(100);
  67. }
  68. PaulstretchpluginAudioProcessorEditor::~PaulstretchpluginAudioProcessorEditor()
  69. {
  70. }
  71. void PaulstretchpluginAudioProcessorEditor::paint (Graphics& g)
  72. {
  73. g.fillAll(Colours::darkgrey);
  74. }
  75. void PaulstretchpluginAudioProcessorEditor::resized()
  76. {
  77. m_import_button.setBounds(1, 1, 60, 24);
  78. m_import_button.changeWidthToFitText();
  79. m_settings_button.setBounds(m_import_button.getRight() + 1, 1, 60, 24);
  80. m_settings_button.changeWidthToFitText();
  81. m_info_label.setBounds(m_settings_button.getRight() + 1, m_settings_button.getY(),
  82. getWidth()-m_settings_button.getRight()-1, 24);
  83. int w = getWidth();
  84. int xoffs = 1;
  85. int yoffs = 30;
  86. int div = w / 4;
  87. m_parcomps[cpi_capture_enabled]->setBounds(xoffs, yoffs, div-1, 24);
  88. xoffs += div;
  89. m_parcomps[cpi_passthrough]->setBounds(xoffs, yoffs, div - 1, 24);
  90. xoffs += div;
  91. m_parcomps[cpi_pause_enabled]->setBounds(xoffs, yoffs, div-1, 24);
  92. xoffs += div;
  93. m_parcomps[cpi_freeze]->setBounds(xoffs, yoffs, div - 1, 24);
  94. xoffs = 1;
  95. yoffs += 25;
  96. div = w / 2;
  97. m_parcomps[cpi_main_volume]->setBounds(xoffs, yoffs, div-1, 24);
  98. xoffs += div;
  99. m_parcomps[cpi_num_outchans]->setBounds(xoffs, yoffs, div-1, 24);
  100. xoffs = 1;
  101. yoffs += 25;
  102. m_parcomps[cpi_fftsize]->setBounds(xoffs, yoffs, div - 1, 24);
  103. xoffs += div;
  104. m_parcomps[cpi_stretchamount]->setBounds(xoffs, yoffs, div - 1, 24);
  105. xoffs = 1;
  106. yoffs += 25;
  107. m_parcomps[cpi_pitchshift]->setBounds(xoffs, yoffs, div - 1, 24);
  108. xoffs += div;
  109. m_parcomps[cpi_frequencyshift]->setBounds(xoffs, yoffs, div - 1, 24);
  110. xoffs = 1;
  111. yoffs += 25;
  112. m_parcomps[cpi_octavesm2]->setBounds(xoffs, yoffs, div - 1, 24);
  113. xoffs += div;
  114. m_parcomps[cpi_octavesm1]->setBounds(xoffs, yoffs, div - 1, 24);
  115. xoffs = 1;
  116. yoffs += 25;
  117. m_parcomps[cpi_octaves0]->setBounds(xoffs, yoffs, div - 1, 24);
  118. xoffs += div;
  119. m_parcomps[cpi_octaves1]->setBounds(xoffs, yoffs, div - 1, 24);
  120. xoffs = 1;
  121. yoffs += 25;
  122. m_parcomps[cpi_octaves15]->setBounds(xoffs, yoffs, div - 1, 24);
  123. xoffs += div;
  124. m_parcomps[cpi_octaves2]->setBounds(xoffs, yoffs, div - 1, 24);
  125. xoffs = 1;
  126. yoffs += 25;
  127. m_parcomps[cpi_numharmonics]->setBounds(xoffs, yoffs, div - 1, 24);
  128. xoffs += div;
  129. m_parcomps[cpi_harmonicsfreq]->setBounds(xoffs, yoffs, div - 1, 24);
  130. xoffs = 1;
  131. yoffs += 25;
  132. m_parcomps[cpi_harmonicsbw]->setBounds(xoffs, yoffs, div - 1, 24);
  133. xoffs += div;
  134. m_parcomps[cpi_harmonicsgauss]->setBounds(xoffs, yoffs, div - 1, 24);
  135. xoffs = 1;
  136. yoffs += 25;
  137. m_parcomps[cpi_spreadamount]->setBounds(xoffs, yoffs, div - 1, 24);
  138. xoffs += div;
  139. m_parcomps[cpi_compress]->setBounds(xoffs, yoffs, div - 1, 24);
  140. xoffs = 1;
  141. yoffs += 25;
  142. m_parcomps[cpi_tonalvsnoisebw]->setBounds(xoffs, yoffs, div - 1, 24);
  143. xoffs += div;
  144. m_parcomps[cpi_tonalvsnoisepreserve]->setBounds(xoffs, yoffs, div - 1, 24);
  145. xoffs = 1;
  146. yoffs += 25;
  147. m_parcomps[cpi_soundstart]->setBounds(xoffs, yoffs, div - 1, 24);
  148. xoffs += div;
  149. m_parcomps[cpi_soundend]->setBounds(xoffs, yoffs, div - 1, 24);
  150. xoffs = 1;
  151. yoffs += 25;
  152. m_parcomps[cpi_filter_low]->setBounds(xoffs, yoffs, div - 1, 24);
  153. xoffs += div;
  154. m_parcomps[cpi_filter_high]->setBounds(xoffs, yoffs, div - 1, 24);
  155. xoffs = 1;
  156. yoffs += 25;
  157. m_parcomps[cpi_loopxfadelen]->setBounds(xoffs, yoffs, div - 1, 24);
  158. xoffs += div;
  159. m_parcomps[cpi_onsetdetection]->setBounds(xoffs, yoffs, div - 1, 24);
  160. yoffs += 25;
  161. int remain_h = getHeight() - 1 - yoffs;
  162. m_spec_order_ed.setBounds(1, yoffs, getWidth() - 2, remain_h / 5 * 1);
  163. m_wavecomponent.setBounds(1, m_spec_order_ed.getBottom()+1, getWidth()-2, remain_h/5*4);
  164. //m_specvis.setBounds(1, yoffs, getWidth() - 2, getHeight() - 1 - yoffs);
  165. }
  166. void PaulstretchpluginAudioProcessorEditor::timerCallback(int id)
  167. {
  168. if (id == 1)
  169. {
  170. for (auto& e : m_parcomps)
  171. e->updateComponent();
  172. if (processor.isRecordingEnabled())
  173. {
  174. m_wavecomponent.setRecordingPosition(processor.getRecordingPositionPercent());
  175. } else
  176. m_wavecomponent.setRecordingPosition(-1.0);
  177. String infotext = String(processor.getPreBufferingPercent(), 1) + "% buffered "
  178. + String(processor.getStretchSource()->m_param_change_count)+" param changes "+m_last_err+" FFT size "+
  179. String(processor.getStretchSource()->getFFTSize());
  180. if (processor.m_abnormal_output_samples > 0)
  181. infotext += " " + String(processor.m_abnormal_output_samples) + " invalid sample values";
  182. if (processor.isNonRealtime())
  183. infotext += " (offline rendering)";
  184. if (processor.m_playposinfo.isPlaying)
  185. infotext += " "+String(processor.m_playposinfo.timeInSeconds,1);
  186. m_info_label.setText(infotext, dontSendNotification);
  187. }
  188. if (id == 2)
  189. {
  190. if (processor.getAudioFile() != File() && processor.getAudioFile() != m_wavecomponent.getAudioFile())
  191. {
  192. m_wavecomponent.setAudioFile(processor.getAudioFile());
  193. }
  194. if (processor.getAudioFile()==File() && processor.isRecordingEnabled()==false && m_wavecomponent.isUsingAudioBuffer()==false)
  195. {
  196. auto bufptr = processor.getStretchSource()->getSourceAudioBuffer();
  197. if (bufptr!=nullptr)
  198. m_wavecomponent.setAudioBuffer(bufptr,
  199. processor.getSampleRateChecked(), bufptr->getNumSamples());
  200. }
  201. m_wavecomponent.setTimeSelection(processor.getTimeSelection());
  202. }
  203. if (id == 3)
  204. {
  205. //m_specvis.setState(processor.getStretchSource()->getProcessParameters(), processor.getStretchSource()->getFFTSize() / 2,
  206. // processor.getSampleRate());
  207. }
  208. }
  209. void PaulstretchpluginAudioProcessorEditor::setAudioFile(File f)
  210. {
  211. m_wavecomponent.setAudioFile(f);
  212. }
  213. void PaulstretchpluginAudioProcessorEditor::setAudioBuffer(AudioBuffer<float>* buf, int samplerate, int len)
  214. {
  215. MessageManager::callAsync([this,buf, samplerate, len]()
  216. {
  217. m_wavecomponent.setAudioBuffer(buf, samplerate, len);
  218. });
  219. }
  220. void PaulstretchpluginAudioProcessorEditor::beginAddingAudioBlocks(int channels, int samplerate, int totalllen)
  221. {
  222. m_wavecomponent.beginAddingAudioBlocks(channels, samplerate, totalllen);
  223. }
  224. void PaulstretchpluginAudioProcessorEditor::addAudioBlock(AudioBuffer<float>& buf, int samplerate, int pos)
  225. {
  226. m_wavecomponent.addAudioBlock(buf, samplerate, pos);
  227. }
  228. void PaulstretchpluginAudioProcessorEditor::chooseFile()
  229. {
  230. String initiallocfn = processor.m_propsfile->m_props_file->getValue("importfilefolder",
  231. File::getSpecialLocation(File::userHomeDirectory).getFullPathName());
  232. File initialloc(initiallocfn);
  233. String filterstring = processor.m_afm->getWildcardForAllFormats();
  234. FileChooser myChooser("Please select audio file...",
  235. initialloc,
  236. filterstring,true);
  237. if (myChooser.browseForFileToOpen())
  238. {
  239. File resu = myChooser.getResult();
  240. String pathname = resu.getFullPathName();
  241. if (pathname.startsWith("/localhost"))
  242. {
  243. pathname = pathname.substring(10);
  244. resu = File(pathname);
  245. }
  246. processor.m_propsfile->m_props_file->setValue("importfilefolder", resu.getParentDirectory().getFullPathName());
  247. m_last_err = processor.setAudioFile(resu);
  248. if (processor.getAudioFile() != File())
  249. {
  250. m_wavecomponent.setAudioFile(processor.getAudioFile());
  251. }
  252. }
  253. }
  254. void PaulstretchpluginAudioProcessorEditor::showSettingsMenu()
  255. {
  256. PopupMenu menu;
  257. menu.addItem(1, "Play when host transport running", true, processor.m_play_when_host_plays);
  258. menu.addItem(2, "Capture when host transport running", true, processor.m_capture_when_host_plays);
  259. menu.addItem(3, "Always pass audio input through", true, processor.m_pass_input_through);
  260. //menu.addItem(3, "Prebuffering", true, processor.m_use_backgroundbuffering);
  261. PopupMenu bufferingmenu;
  262. int curbufamount = processor.getPreBufferAmount();
  263. bufferingmenu.addItem(100,"None",true,curbufamount == -1);
  264. bufferingmenu.addItem(101,"Small",true,curbufamount == 1);
  265. bufferingmenu.addItem(102,"Medium",true,curbufamount == 2);
  266. bufferingmenu.addItem(103,"Large",true,curbufamount == 3);
  267. bufferingmenu.addItem(104,"Very large",true,curbufamount == 4);
  268. bufferingmenu.addItem(105,"Huge",true,curbufamount == 5);
  269. menu.addSubMenu("Prebuffering", bufferingmenu);
  270. int r = menu.show();
  271. if (r == 1)
  272. {
  273. processor.m_play_when_host_plays = !processor.m_play_when_host_plays;
  274. }
  275. if (r == 2)
  276. {
  277. processor.m_capture_when_host_plays = !processor.m_capture_when_host_plays;
  278. }
  279. if (r == 3)
  280. {
  281. processor.m_pass_input_through = !processor.m_pass_input_through;
  282. }
  283. if (r >= 100 && r < 200)
  284. {
  285. if (r == 100)
  286. processor.m_use_backgroundbuffering = false;
  287. if (r > 100)
  288. processor.setPreBufferAmount(r-100);
  289. }
  290. }
  291. WaveformComponent::WaveformComponent(AudioFormatManager* afm)
  292. {
  293. TimeSelectionChangedCallback = [](Range<double>, int) {};
  294. if (m_use_opengl == true)
  295. m_ogl.attachTo(*this);
  296. // The default priority of 2 is a bit too low in some cases, it seems...
  297. m_thumbcache->getTimeSliceThread().setPriority(3);
  298. m_thumb = std::make_unique<AudioThumbnail>(512, *afm, *m_thumbcache);
  299. m_thumb->addChangeListener(this);
  300. setOpaque(true);
  301. }
  302. WaveformComponent::~WaveformComponent()
  303. {
  304. if (m_use_opengl == true)
  305. m_ogl.detach();
  306. }
  307. void WaveformComponent::changeListenerCallback(ChangeBroadcaster * /*cb*/)
  308. {
  309. m_waveimage = Image();
  310. repaint();
  311. }
  312. void WaveformComponent::paint(Graphics & g)
  313. {
  314. //Logger::writeToLog("Waveform component paint");
  315. g.fillAll(Colours::black);
  316. g.setColour(Colours::darkgrey);
  317. g.fillRect(0, 0, getWidth(), m_topmargin);
  318. if (m_thumb == nullptr || m_thumb->getTotalLength() < 0.1)
  319. {
  320. g.setColour(Colours::aqua.darker());
  321. g.drawText("No file loaded", 2, m_topmargin + 2, getWidth(), 20, Justification::topLeft);
  322. return;
  323. }
  324. g.setColour(Colours::lightslategrey);
  325. double thumblen = m_thumb->getTotalLength();
  326. double tick_interval = 1.0;
  327. if (thumblen > 60.0)
  328. tick_interval = 5.0;
  329. for (double secs = 0.0; secs < thumblen; secs += tick_interval)
  330. {
  331. float tickxcor = (float)jmap<double>(secs,
  332. thumblen*m_view_range.getStart(), thumblen*m_view_range.getEnd(), 0.0f, (float)getWidth());
  333. g.drawLine(tickxcor, 0.0, tickxcor, (float)m_topmargin, 1.0f);
  334. }
  335. bool m_use_cached_image = true;
  336. if (m_use_cached_image == true)
  337. {
  338. if (m_waveimage.isValid() == false || m_waveimage.getWidth() != getWidth()
  339. || m_waveimage.getHeight() != getHeight() - m_topmargin)
  340. {
  341. //Logger::writeToLog("updating cached waveform image");
  342. m_waveimage = Image(Image::ARGB, getWidth(), getHeight() - m_topmargin, true);
  343. Graphics tempg(m_waveimage);
  344. tempg.fillAll(Colours::black);
  345. tempg.setColour(Colours::darkgrey);
  346. m_thumb->drawChannels(tempg, { 0,0,getWidth(),getHeight() - m_topmargin },
  347. thumblen*m_view_range.getStart(), thumblen*m_view_range.getEnd(), 1.0f);
  348. }
  349. g.drawImage(m_waveimage, 0, m_topmargin, getWidth(), getHeight() - m_topmargin, 0, 0, getWidth(), getHeight() - m_topmargin);
  350. }
  351. else
  352. {
  353. //g.fillAll(Colours::black);
  354. g.setColour(Colours::darkgrey);
  355. m_thumb->drawChannels(g, { 0,m_topmargin,getWidth(),getHeight() - m_topmargin },
  356. thumblen*m_view_range.getStart(), thumblen*m_view_range.getEnd(), 1.0f);
  357. }
  358. //g.setColour(Colours::darkgrey);
  359. //m_thumb->drawChannels(g, { 0,m_topmargin,getWidth(),getHeight()-m_topmargin },
  360. // 0.0, thumblen, 1.0f);
  361. g.setColour(Colours::white.withAlpha(0.5f));
  362. int xcorleft = (int)jmap<double>(m_time_sel_start, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  363. int xcorright = (int)jmap<double>(m_time_sel_end, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  364. g.fillRect(xcorleft, m_topmargin, xcorright - xcorleft, getHeight() - m_topmargin);
  365. if (m_file_cached.first.getLength() > 0.0 &&
  366. (bool)ShowFileCacheRange.getValue())
  367. {
  368. g.setColour(Colours::red.withAlpha(0.2f));
  369. xcorleft = (int)jmap<double>(m_file_cached.first.getStart(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  370. xcorright = (int)jmap<double>(m_file_cached.first.getEnd(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  371. g.fillRect(xcorleft, 0, xcorright - xcorleft, m_topmargin / 2);
  372. xcorleft = (int)jmap<double>(m_file_cached.second.getStart(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  373. xcorright = (int)jmap<double>(m_file_cached.second.getEnd(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  374. if (xcorright - xcorleft>0)
  375. {
  376. g.setColour(Colours::blue.withAlpha(0.2f));
  377. g.fillRect(xcorleft, m_topmargin / 2, xcorright - xcorleft, m_topmargin / 2);
  378. }
  379. }
  380. g.setColour(Colours::white);
  381. if (CursorPosCallback)
  382. {
  383. double pos = jmap<double>(CursorPosCallback(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  384. g.fillRect((int)pos, m_topmargin, 1, getHeight() - m_topmargin);
  385. }
  386. if (m_rec_pos >= 0.0)
  387. {
  388. g.setColour(Colours::lightpink);
  389. double pos = jmap<double>(m_rec_pos, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  390. g.fillRect((int)pos, m_topmargin, 1, getHeight() - m_topmargin);
  391. }
  392. g.setColour(Colours::aqua.darker());
  393. g.drawText(m_curfile.getFullPathName(), 2, m_topmargin + 2, getWidth(), 20, Justification::topLeft);
  394. }
  395. void WaveformComponent::setAudioFile(File f)
  396. {
  397. if (f.existsAsFile())
  398. {
  399. m_waveimage = Image();
  400. if (m_thumb != nullptr && f == m_curfile) // reloading same file, might happen that the overview needs to be redone...
  401. m_thumbcache->removeThumb(m_thumb->getHashCode());
  402. if (m_thumb != nullptr)
  403. m_thumb->reset(0, 0.0);
  404. m_thumb->setSource(new FileInputSource(f));
  405. m_curfile = f;
  406. m_using_audio_buffer = false;
  407. }
  408. else
  409. {
  410. m_thumb->setSource(nullptr);
  411. m_curfile = File();
  412. }
  413. repaint();
  414. }
  415. void WaveformComponent::setAudioBuffer(AudioBuffer<float>* buf, int samplerate, int len)
  416. {
  417. jassert(buf!=nullptr);
  418. m_using_audio_buffer = true;
  419. m_waveimage = Image();
  420. m_curfile = File();
  421. m_thumb->reset(buf->getNumChannels(), samplerate, len);
  422. m_thumb->addBlock(0, *buf, 0, len);
  423. }
  424. void WaveformComponent::beginAddingAudioBlocks(int channels, int samplerate, int totalllen)
  425. {
  426. m_waveimage = Image();
  427. m_curfile = File();
  428. m_thumb->reset(channels, samplerate, totalllen);
  429. }
  430. void WaveformComponent::addAudioBlock(AudioBuffer<float>& buf, int samplerate, int pos)
  431. {
  432. m_thumb->addBlock(pos, buf, 0, buf.getNumSamples());
  433. }
  434. void WaveformComponent::timerCallback()
  435. {
  436. repaint();
  437. }
  438. void WaveformComponent::setFileCachedRange(std::pair<Range<double>, Range<double>> rng)
  439. {
  440. m_file_cached = rng;
  441. //repaint();
  442. }
  443. void WaveformComponent::setTimerEnabled(bool b)
  444. {
  445. if (b == true)
  446. startTimer(100);
  447. else
  448. stopTimer();
  449. }
  450. void WaveformComponent::setViewRange(Range<double> rng)
  451. {
  452. m_view_range = rng;
  453. m_waveimage = Image();
  454. repaint();
  455. }
  456. void WaveformComponent::mouseDown(const MouseEvent & e)
  457. {
  458. m_mousedown = true;
  459. m_lock_timesel_set = true;
  460. double pos = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  461. if (e.y < m_topmargin)
  462. {
  463. if (SeekCallback)
  464. SeekCallback(pos);
  465. m_didseek = true;
  466. }
  467. else
  468. {
  469. m_time_sel_drag_target = getTimeSelectionEdge(e.x, e.y);
  470. m_drag_time_start = pos;
  471. if (m_time_sel_drag_target == 0)
  472. {
  473. m_time_sel_start = -1.0;
  474. m_time_sel_end = -1.0;
  475. }
  476. }
  477. repaint();
  478. }
  479. void WaveformComponent::mouseUp(const MouseEvent & /*e*/)
  480. {
  481. m_lock_timesel_set = false;
  482. m_mousedown = false;
  483. m_didseek = false;
  484. if (m_didchangetimeselection)
  485. {
  486. TimeSelectionChangedCallback(Range<double>(m_time_sel_start, m_time_sel_end), 1);
  487. m_didchangetimeselection = false;
  488. }
  489. }
  490. void WaveformComponent::mouseDrag(const MouseEvent & e)
  491. {
  492. if (m_didseek == true)
  493. return;
  494. if (m_time_sel_drag_target == 0)
  495. {
  496. m_time_sel_start = m_drag_time_start;
  497. m_time_sel_end = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  498. }
  499. if (m_time_sel_drag_target == 1)
  500. {
  501. m_time_sel_start = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  502. }
  503. if (m_time_sel_drag_target == 2)
  504. {
  505. m_time_sel_end = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  506. }
  507. if (m_time_sel_start > m_time_sel_end)
  508. {
  509. std::swap(m_time_sel_start, m_time_sel_end);
  510. if (m_time_sel_drag_target == 1)
  511. m_time_sel_drag_target = 2;
  512. else if (m_time_sel_drag_target == 2)
  513. m_time_sel_drag_target = 1;
  514. }
  515. m_time_sel_start = jlimit(0.0, 1.0, m_time_sel_start);
  516. m_time_sel_end = jlimit(0.0, 1.0, m_time_sel_end);
  517. if (TimeSelectionChangedCallback)
  518. {
  519. if (m_time_sel_end>m_time_sel_start)
  520. TimeSelectionChangedCallback(Range<double>(m_time_sel_start, m_time_sel_end), 0);
  521. else
  522. TimeSelectionChangedCallback(Range<double>(0.0, 1.0), 0);
  523. }
  524. m_didchangetimeselection = true;
  525. repaint();
  526. }
  527. void WaveformComponent::mouseMove(const MouseEvent & e)
  528. {
  529. m_time_sel_drag_target = getTimeSelectionEdge(e.x, e.y);
  530. if (m_time_sel_drag_target == 0)
  531. setMouseCursor(MouseCursor::NormalCursor);
  532. if (m_time_sel_drag_target == 1)
  533. setMouseCursor(MouseCursor::LeftRightResizeCursor);
  534. if (m_time_sel_drag_target == 2)
  535. setMouseCursor(MouseCursor::LeftRightResizeCursor);
  536. }
  537. int WaveformComponent::getTimeSelectionEdge(int x, int y)
  538. {
  539. int xcorleft = (int)jmap<double>(m_time_sel_start, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  540. int xcorright = (int)jmap<double>(m_time_sel_end, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  541. if (juce::Rectangle<int>(xcorleft - 5, m_topmargin, 10, getHeight() - m_topmargin).contains(x, y))
  542. return 1;
  543. if (juce::Rectangle<int>(xcorright - 5, m_topmargin, 10, getHeight() - m_topmargin).contains(x, y))
  544. return 2;
  545. return 0;
  546. }
  547. SpectralVisualizer::SpectralVisualizer()
  548. {
  549. m_img = Image(Image::RGB, 500, 200, true);
  550. }
  551. void SpectralVisualizer::setState(const ProcessParameters & pars, int nfreqs, double samplerate)
  552. {
  553. double t0 = Time::getMillisecondCounterHiRes();
  554. double hz = 440.0;
  555. int numharmonics = 40;
  556. double scaler = 1.0 / numharmonics;
  557. if (m_img.getWidth()!=getWidth() || m_img.getHeight()!=getHeight())
  558. m_img = Image(Image::RGB, getWidth(), getHeight(), true);
  559. if (m_nfreqs == 0 || nfreqs != m_nfreqs)
  560. {
  561. m_nfreqs = nfreqs;
  562. m_insamples = std::vector<REALTYPE>(nfreqs * 2);
  563. m_freqs1 = std::vector<REALTYPE>(nfreqs);
  564. m_freqs2 = std::vector<REALTYPE>(nfreqs);
  565. m_freqs3 = std::vector<REALTYPE>(nfreqs);
  566. m_fft = std::make_unique<FFT>(nfreqs*2);
  567. std::fill(m_insamples.begin(), m_insamples.end(), 0.0f);
  568. for (int i = 0; i < nfreqs; ++i)
  569. {
  570. for (int j = 0; j < numharmonics; ++j)
  571. {
  572. double oscgain = 1.0 - (1.0 / numharmonics)*j;
  573. m_insamples[i] += scaler * oscgain * sin(2 * 3.141592653 / samplerate * i* (hz + hz * j));
  574. }
  575. }
  576. }
  577. //std::fill(m_freqs1.begin(), m_freqs1.end(), 0.0f);
  578. //std::fill(m_freqs2.begin(), m_freqs2.end(), 0.0f);
  579. //std::fill(m_freqs3.begin(), m_freqs3.end(), 0.0f);
  580. //std::fill(m_fft->freq.begin(), m_fft->freq.end(), 0.0f);
  581. for (int i = 0; i < nfreqs; ++i)
  582. {
  583. m_fft->smp[i] = m_insamples[i];
  584. }
  585. m_fft->applywindow(W_HAMMING);
  586. m_fft->smp2freq();
  587. double ratio = pow(2.0f, pars.pitch_shift.cents / 1200.0f);
  588. spectrum_do_pitch_shift(pars, nfreqs, m_fft->freq.data(), m_freqs2.data(), ratio);
  589. spectrum_do_freq_shift(pars, nfreqs, samplerate, m_freqs2.data(), m_freqs1.data());
  590. spectrum_do_compressor(pars, nfreqs, m_freqs1.data(), m_freqs2.data());
  591. spectrum_spread(nfreqs, samplerate, m_freqs3, m_freqs2.data(), m_freqs1.data(), pars.spread.bandwidth);
  592. if (pars.harmonics.enabled)
  593. spectrum_do_harmonics(pars, m_freqs3, nfreqs, samplerate, m_freqs1.data(), m_freqs2.data());
  594. else spectrum_copy(nfreqs, m_freqs1.data(), m_freqs2.data());
  595. Graphics g(m_img);
  596. g.fillAll(Colours::black);
  597. g.setColour(Colours::white);
  598. for (int i = 0; i < nfreqs; ++i)
  599. {
  600. double binfreq = (samplerate / 2 / nfreqs)*i;
  601. double xcor = jmap<double>(binfreq, 0.0, samplerate / 2.0, 0.0, getWidth());
  602. double ycor = getHeight()- jmap<double>(m_freqs2[i], 0.0, nfreqs/128, 0.0, getHeight());
  603. ycor = jlimit<double>(0.0, getHeight(), ycor);
  604. g.drawLine(xcor, getHeight(), xcor, ycor, 1.0);
  605. }
  606. double t1 = Time::getMillisecondCounterHiRes();
  607. m_elapsed = t1 - t0;
  608. repaint();
  609. }
  610. void SpectralVisualizer::paint(Graphics & g)
  611. {
  612. g.drawImage(m_img, 0, 0, getWidth(), getHeight(), 0, 0, m_img.getWidth(), m_img.getHeight());
  613. g.setColour(Colours::yellow);
  614. g.drawText(String(m_elapsed, 1)+" ms", 1, 1, getWidth(), 30, Justification::topLeft);
  615. }
  616. void SpectralChainEditor::paint(Graphics & g)
  617. {
  618. g.fillAll(Colours::black);
  619. if (m_src == nullptr)
  620. return;
  621. int box_w = getWidth() / m_order.size();
  622. int box_h = getHeight();
  623. for (int i = 0; i < m_order.size(); ++i)
  624. {
  625. //if (i!=m_cur_index)
  626. drawBox(g, i, i*box_w, 0, box_w - 30, box_h);
  627. if (i<m_order.size() - 1)
  628. g.drawArrow(juce::Line<float>(i*box_w + (box_w - 30), box_h / 2, i*box_w + box_w, box_h / 2), 2.0f, 15.0f, 15.0f);
  629. }
  630. if (m_drag_x>=0 && m_drag_x<getWidth() && m_cur_index>=0)
  631. drawBox(g, m_cur_index, m_drag_x, 0, box_w - 30, box_h);
  632. }
  633. void SpectralChainEditor::mouseDown(const MouseEvent & ev)
  634. {
  635. m_did_drag = false;
  636. int box_w = getWidth() / m_order.size();
  637. int box_h = getHeight();
  638. m_cur_index = ev.x / box_w;
  639. m_drag_x = -1;
  640. repaint();
  641. }
  642. void SpectralChainEditor::mouseDrag(const MouseEvent & ev)
  643. {
  644. if (m_cur_index >= 0 && m_cur_index < m_order.size())
  645. {
  646. int box_w = getWidth() / m_order.size();
  647. int box_h = getHeight();
  648. int new_index = ev.x / box_w;
  649. if (new_index >= 0 && new_index < m_order.size() && new_index != m_cur_index)
  650. {
  651. std::swap(m_order[m_cur_index], m_order[new_index]);
  652. m_cur_index = new_index;
  653. m_did_drag = true;
  654. m_src->setSpectrumProcessOrder(m_order);
  655. }
  656. m_drag_x = ev.x;
  657. repaint();
  658. }
  659. }
  660. void SpectralChainEditor::mouseUp(const MouseEvent & ev)
  661. {
  662. m_drag_x = -1;
  663. //m_cur_index = -1;
  664. repaint();
  665. }
  666. void SpectralChainEditor::drawBox(Graphics & g, int index, int x, int y, int w, int h)
  667. {
  668. String txt;
  669. if (m_order[index] == 0)
  670. txt = "Harmonics";
  671. if (m_order[index] == 1)
  672. txt = "Tonal vs Noise";
  673. if (m_order[index] == 2)
  674. txt = "Frequency shift";
  675. if (m_order[index] == 3)
  676. txt = "Pitch shift";
  677. if (m_order[index] == 4)
  678. txt = "Octaves";
  679. if (m_order[index] == 5)
  680. txt = "Spread";
  681. if (m_order[index] == 6)
  682. txt = "Filter";
  683. if (m_order[index] == 7)
  684. txt = "Compressor";
  685. if (index == m_cur_index)
  686. {
  687. g.setColour(Colours::darkgrey);
  688. //g.fillRect(i*box_w, 0, box_w - 30, box_h - 1);
  689. g.fillRect(x, y, w, h);
  690. }
  691. g.setColour(Colours::white);
  692. g.drawRect(x, y, w, h);
  693. g.drawFittedText(txt, x,y,w,h, Justification::centred, 3);
  694. }