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.

1178 lines
37KB

  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. extern String g_plugintitle;
  20. //==============================================================================
  21. PaulstretchpluginAudioProcessorEditor::PaulstretchpluginAudioProcessorEditor(PaulstretchpluginAudioProcessor& p)
  22. : AudioProcessorEditor(&p),
  23. m_wavecomponent(p.m_afm,p.m_thumb.get()),
  24. processor(p), m_perfmeter(&p)
  25. {
  26. addAndMakeVisible(&m_perfmeter);
  27. addAndMakeVisible(&m_import_button);
  28. m_import_button.setButtonText("Import file...");
  29. m_import_button.onClick = [this]() { chooseFile(); };
  30. addAndMakeVisible(&m_settings_button);
  31. m_settings_button.setButtonText("Settings...");
  32. m_settings_button.onClick = [this]() { showSettingsMenu(); };
  33. addAndMakeVisible(&m_info_label);
  34. m_info_label.setJustificationType(Justification::centredRight);
  35. m_wavecomponent.GetFileCallback = [this]() { return processor.getAudioFile(); };
  36. addAndMakeVisible(&m_wavecomponent);
  37. const auto& pars = processor.getParameters();
  38. for (int i=0;i<pars.size();++i)
  39. {
  40. AudioProcessorParameterWithID* parid = dynamic_cast<AudioProcessorParameterWithID*>(pars[i]);
  41. jassert(parid);
  42. bool notifyonlyonrelease = false;
  43. if (parid->paramID.startsWith("fftsize") || parid->paramID.startsWith("numoutchans")
  44. || parid->paramID.startsWith("numinchans"))
  45. notifyonlyonrelease = true;
  46. m_parcomps.push_back(std::make_shared<ParameterComponent>(pars[i],notifyonlyonrelease));
  47. int group_id = -1;
  48. if (i == cpi_harmonicsbw || i == cpi_harmonicsfreq || i == cpi_harmonicsgauss || i == cpi_numharmonics)
  49. group_id = 0;
  50. if (i == cpi_octavesm2 || i == cpi_octavesm1 || i == cpi_octaves0 || i == cpi_octaves1 || i == cpi_octaves15 ||
  51. i == cpi_octaves2)
  52. group_id = 4;
  53. if (i == cpi_tonalvsnoisebw || i == cpi_tonalvsnoisepreserve)
  54. group_id = 1;
  55. if (i == cpi_filter_low || i == cpi_filter_high)
  56. group_id = 6;
  57. if (i == cpi_compress)
  58. group_id = 7;
  59. if (i == cpi_spreadamount)
  60. group_id = 5;
  61. if (i == cpi_frequencyshift)
  62. group_id = 2;
  63. if (i == cpi_pitchshift)
  64. group_id = 3;
  65. m_parcomps.back()->m_group_id = group_id;
  66. addAndMakeVisible(m_parcomps.back().get());
  67. }
  68. //addAndMakeVisible(&m_specvis);
  69. addAndMakeVisible(&m_zs);
  70. m_zs.RangeChanged = [this](Range<double> r)
  71. {
  72. m_wavecomponent.setViewRange(r);
  73. processor.m_wave_view_range = r;
  74. };
  75. m_zs.setRange(processor.m_wave_view_range, true);
  76. setSize (1000, 30+(pars.size()/2)*25+200+15);
  77. m_wavecomponent.TimeSelectionChangedCallback = [this](Range<double> range, int which)
  78. {
  79. *processor.getFloatParameter(cpi_soundstart) = range.getStart();
  80. *processor.getFloatParameter(cpi_soundend) = range.getEnd();
  81. };
  82. m_wavecomponent.CursorPosCallback = [this]()
  83. {
  84. return processor.getStretchSource()->getInfilePositionPercent();
  85. };
  86. m_wavecomponent.SeekCallback = [this](double pos)
  87. {
  88. if (processor.getStretchSource()->getPlayRange().contains(pos))
  89. processor.getStretchSource()->seekPercent(pos);
  90. };
  91. m_wavecomponent.ShowFileCacheRange = true;
  92. m_spec_order_ed.setSource(processor.getStretchSource());
  93. addAndMakeVisible(&m_spec_order_ed);
  94. m_spec_order_ed.ModuleSelectedCallback = [this](int id)
  95. {
  96. for (int i = 0; i < m_parcomps.size(); ++i)
  97. {
  98. if (m_parcomps[i]->m_group_id == id)
  99. m_parcomps[i]->setHighLighted(true);
  100. else
  101. m_parcomps[i]->setHighLighted(false);
  102. }
  103. };
  104. m_spec_order_ed.ModuleOrderOrEnabledChangedCallback = [this]()
  105. {
  106. processor.setDirty();
  107. };
  108. startTimer(1, 100);
  109. startTimer(2, 1000);
  110. startTimer(3, 200);
  111. m_wavecomponent.startTimer(100);
  112. }
  113. PaulstretchpluginAudioProcessorEditor::~PaulstretchpluginAudioProcessorEditor()
  114. {
  115. }
  116. void PaulstretchpluginAudioProcessorEditor::paint (Graphics& g)
  117. {
  118. g.fillAll(Colours::darkgrey);
  119. }
  120. void PaulstretchpluginAudioProcessorEditor::resized()
  121. {
  122. m_import_button.setBounds(1, 1, 60, 24);
  123. m_import_button.changeWidthToFitText();
  124. m_settings_button.setBounds(m_import_button.getRight() + 1, 1, 60, 24);
  125. m_settings_button.changeWidthToFitText();
  126. m_perfmeter.setBounds(m_settings_button.getRight() + 1, 1, 150, 24);
  127. m_info_label.setBounds(m_perfmeter.getRight() + 1, m_settings_button.getY(),
  128. getWidth()- m_perfmeter.getRight()-1, 24);
  129. int w = getWidth();
  130. int xoffs = 1;
  131. int yoffs = 30;
  132. int div = w / 4;
  133. //std::vector<std::vector<int>> layout;
  134. //layout.emplace_back(cpi_capture_enabled, cpi_passthrough, cpi_pause_enabled, cpi_freeze);
  135. //layout.emplace_back(cpi_main_volume, cpi_num_inchans, cpi_num_outchans);
  136. m_parcomps[cpi_capture_enabled]->setBounds(xoffs, yoffs, div-1, 24);
  137. //xoffs += div;
  138. //m_parcomps[cpi_max_capture_len]->setBounds(xoffs, yoffs, div - 1, 24);
  139. xoffs += div;
  140. m_parcomps[cpi_passthrough]->setBounds(xoffs, yoffs, div - 1, 24);
  141. xoffs += div;
  142. m_parcomps[cpi_pause_enabled]->setBounds(xoffs, yoffs, div-1, 24);
  143. xoffs += div;
  144. m_parcomps[cpi_freeze]->setBounds(xoffs, yoffs, div - 1, 24);
  145. xoffs = 1;
  146. yoffs += 25;
  147. div = w / 3;
  148. m_parcomps[cpi_main_volume]->setBounds(xoffs, yoffs, div-1, 24);
  149. xoffs += div;
  150. m_parcomps[cpi_num_inchans]->setBounds(xoffs, yoffs, div - 1, 24);
  151. xoffs += div;
  152. m_parcomps[cpi_num_outchans]->setBounds(xoffs, yoffs, div-1, 24);
  153. div = w / 2;
  154. xoffs = 1;
  155. yoffs += 25;
  156. m_parcomps[cpi_fftsize]->setBounds(xoffs, yoffs, div - 1, 24);
  157. xoffs += div;
  158. m_parcomps[cpi_stretchamount]->setBounds(xoffs, yoffs, div - 1, 24);
  159. xoffs = 1;
  160. yoffs += 25;
  161. m_parcomps[cpi_pitchshift]->setBounds(xoffs, yoffs, div - 1, 24);
  162. xoffs += div;
  163. m_parcomps[cpi_frequencyshift]->setBounds(xoffs, yoffs, div - 1, 24);
  164. xoffs = 1;
  165. yoffs += 25;
  166. m_parcomps[cpi_octavesm2]->setBounds(xoffs, yoffs, div - 1, 24);
  167. xoffs += div;
  168. m_parcomps[cpi_octavesm1]->setBounds(xoffs, yoffs, div - 1, 24);
  169. xoffs = 1;
  170. yoffs += 25;
  171. m_parcomps[cpi_octaves0]->setBounds(xoffs, yoffs, div - 1, 24);
  172. xoffs += div;
  173. m_parcomps[cpi_octaves1]->setBounds(xoffs, yoffs, div - 1, 24);
  174. xoffs = 1;
  175. yoffs += 25;
  176. m_parcomps[cpi_octaves15]->setBounds(xoffs, yoffs, div - 1, 24);
  177. xoffs += div;
  178. m_parcomps[cpi_octaves2]->setBounds(xoffs, yoffs, div - 1, 24);
  179. xoffs = 1;
  180. yoffs += 25;
  181. m_parcomps[cpi_numharmonics]->setBounds(xoffs, yoffs, div - 1, 24);
  182. xoffs += div;
  183. m_parcomps[cpi_harmonicsfreq]->setBounds(xoffs, yoffs, div - 1, 24);
  184. xoffs = 1;
  185. yoffs += 25;
  186. m_parcomps[cpi_harmonicsbw]->setBounds(xoffs, yoffs, div - 1, 24);
  187. xoffs += div;
  188. m_parcomps[cpi_harmonicsgauss]->setBounds(xoffs, yoffs, div - 1, 24);
  189. xoffs = 1;
  190. yoffs += 25;
  191. m_parcomps[cpi_spreadamount]->setBounds(xoffs, yoffs, div - 1, 24);
  192. xoffs += div;
  193. m_parcomps[cpi_compress]->setBounds(xoffs, yoffs, div - 1, 24);
  194. xoffs = 1;
  195. yoffs += 25;
  196. m_parcomps[cpi_tonalvsnoisebw]->setBounds(xoffs, yoffs, div - 1, 24);
  197. xoffs += div;
  198. m_parcomps[cpi_tonalvsnoisepreserve]->setBounds(xoffs, yoffs, div - 1, 24);
  199. xoffs = 1;
  200. yoffs += 25;
  201. // filter here
  202. m_parcomps[cpi_filter_low]->setBounds(xoffs, yoffs, div - 1, 24);
  203. xoffs += div;
  204. m_parcomps[cpi_filter_high]->setBounds(xoffs, yoffs, div - 1, 24);
  205. xoffs = 1;
  206. yoffs += 25;
  207. m_parcomps[cpi_loopxfadelen]->setBounds(xoffs, yoffs, div - 1, 24);
  208. xoffs += div;
  209. m_parcomps[cpi_onsetdetection]->setBounds(xoffs, yoffs, div - 1, 24);
  210. xoffs = 1;
  211. yoffs += 25;
  212. m_parcomps[cpi_soundstart]->setBounds(xoffs, yoffs, div - 1, 24);
  213. xoffs += div;
  214. m_parcomps[cpi_soundend]->setBounds(xoffs, yoffs, div - 1, 24);
  215. #ifdef SOUNDRANGE_OFFSET_ENABLED
  216. yoffs += 25;
  217. xoffs = 1;
  218. m_parcomps[cpi_playrangeoffset]->setBounds(xoffs, yoffs, getWidth() - 2, 24);
  219. #endif
  220. yoffs += 25;
  221. int remain_h = getHeight() - 1 - yoffs -15;
  222. m_spec_order_ed.setBounds(1, yoffs, getWidth() - 2, remain_h / 5 * 1);
  223. m_wavecomponent.setBounds(1, m_spec_order_ed.getBottom()+1, getWidth()-2, remain_h/5*4);
  224. m_zs.setBounds(1, m_wavecomponent.getBottom(), getWidth() - 2, 16);
  225. //m_specvis.setBounds(1, yoffs, getWidth() - 2, getHeight() - 1 - yoffs);
  226. }
  227. void PaulstretchpluginAudioProcessorEditor::timerCallback(int id)
  228. {
  229. if (id == 1)
  230. {
  231. for (auto& e : m_parcomps)
  232. e->updateComponent();
  233. if (processor.isRecordingEnabled())
  234. {
  235. m_wavecomponent.setRecordingPosition(processor.getRecordingPositionPercent());
  236. } else
  237. m_wavecomponent.setRecordingPosition(-1.0);
  238. String infotext;
  239. if (processor.m_show_technical_info)
  240. {
  241. infotext += String(processor.getStretchSource()->m_param_change_count);
  242. infotext += " param changes ";
  243. }
  244. infotext += m_last_err + " FFT size " +
  245. String(processor.getStretchSource()->getFFTSize());
  246. if (processor.m_abnormal_output_samples > 0)
  247. infotext += " " + String(processor.m_abnormal_output_samples) + " invalid sample values";
  248. if (processor.isNonRealtime())
  249. infotext += " (offline rendering)";
  250. if (processor.m_playposinfo.isPlaying)
  251. infotext += " "+String(processor.m_playposinfo.timeInSeconds,1);
  252. if (processor.m_show_technical_info)
  253. infotext += " " + String(m_wavecomponent.m_image_init_count) + " " + String(m_wavecomponent.m_image_update_count);
  254. m_info_label.setText(infotext, dontSendNotification);
  255. m_perfmeter.repaint();
  256. }
  257. if (id == 2)
  258. {
  259. m_wavecomponent.setTimeSelection(processor.getTimeSelection());
  260. if (processor.m_state_dirty)
  261. {
  262. m_spec_order_ed.setSource(processor.getStretchSource());
  263. processor.m_state_dirty = false;
  264. }
  265. }
  266. if (id == 3)
  267. {
  268. //m_specvis.setState(processor.getStretchSource()->getProcessParameters(), processor.getStretchSource()->getFFTSize() / 2,
  269. // processor.getSampleRate());
  270. }
  271. }
  272. bool PaulstretchpluginAudioProcessorEditor::isInterestedInFileDrag(const StringArray & files)
  273. {
  274. if (files.size() == 0)
  275. return false;
  276. File f(files[0]);
  277. String extension = f.getFileExtension().toLowerCase();
  278. if (processor.m_afm->getWildcardForAllFormats().containsIgnoreCase(extension))
  279. return true;
  280. return false;
  281. }
  282. void PaulstretchpluginAudioProcessorEditor::filesDropped(const StringArray & files, int x, int y)
  283. {
  284. if (files.size() > 0)
  285. {
  286. File f(files[0]);
  287. processor.setAudioFile(f);
  288. toFront(true);
  289. }
  290. }
  291. void PaulstretchpluginAudioProcessorEditor::chooseFile()
  292. {
  293. String initiallocfn = processor.m_propsfile->m_props_file->getValue("importfilefolder",
  294. File::getSpecialLocation(File::userHomeDirectory).getFullPathName());
  295. File initialloc(initiallocfn);
  296. String filterstring = processor.m_afm->getWildcardForAllFormats();
  297. FileChooser myChooser("Please select audio file...",
  298. initialloc,
  299. filterstring,true);
  300. if (myChooser.browseForFileToOpen())
  301. {
  302. File resu = myChooser.getResult();
  303. String pathname = resu.getFullPathName();
  304. if (pathname.startsWith("/localhost"))
  305. {
  306. pathname = pathname.substring(10);
  307. resu = File(pathname);
  308. }
  309. processor.m_propsfile->m_props_file->setValue("importfilefolder", resu.getParentDirectory().getFullPathName());
  310. m_last_err = processor.setAudioFile(resu);
  311. }
  312. }
  313. void PaulstretchpluginAudioProcessorEditor::showSettingsMenu()
  314. {
  315. PopupMenu menu;
  316. menu.addItem(4, "Reset parameters", true, false);
  317. menu.addItem(5, "Load file with plugin state", true, processor.m_load_file_with_state);
  318. menu.addItem(1, "Play when host transport running", true, processor.m_play_when_host_plays);
  319. menu.addItem(2, "Capture when host transport running", true, processor.m_capture_when_host_plays);
  320. int capturelen = *processor.getFloatParameter(cpi_max_capture_len);
  321. PopupMenu capturelenmenu;
  322. std::vector<int> capturelens{ 2,5,10,30,60,120 };
  323. for (int i=0;i<capturelens.size();++i)
  324. capturelenmenu.addItem(200+i, String(capturelens[i])+" seconds", true, capturelen == capturelens[i]);
  325. menu.addSubMenu("Capture buffer length", capturelenmenu);
  326. menu.addItem(3, "About...", true, false);
  327. #ifdef JUCE_DEBUG
  328. menu.addItem(6, "Dump preset to clipboard", true, false);
  329. #endif
  330. menu.addItem(7, "Show technical info", true, processor.m_show_technical_info);
  331. int r = menu.show();
  332. if (r >= 200 && r < 210)
  333. {
  334. int caplen = capturelens[r - 200];
  335. *processor.getFloatParameter(cpi_max_capture_len) = (float)caplen;
  336. }
  337. if (r == 1)
  338. {
  339. toggleBool(processor.m_play_when_host_plays);
  340. }
  341. if (r == 2)
  342. {
  343. toggleBool(processor.m_capture_when_host_plays);
  344. }
  345. if (r == 4)
  346. {
  347. processor.resetParameters();
  348. }
  349. if (r == 5)
  350. {
  351. toggleBool(processor.m_load_file_with_state);
  352. }
  353. if (r == 3)
  354. {
  355. String fftlib = fftwf_version;
  356. String juceversiontxt = String("JUCE ") + String(JUCE_MAJOR_VERSION) + "." + String(JUCE_MINOR_VERSION);
  357. String title = g_plugintitle;
  358. #ifdef JUCE_DEBUG
  359. title += " (DEBUG)";
  360. #endif
  361. AlertWindow::showMessageBoxAsync(AlertWindow::InfoIcon,
  362. title,
  363. "Plugin for extreme time stretching and other sound processing\nBuilt on " + String(__DATE__) + " " + String(__TIME__) + "\n"
  364. "Copyright (C) 2006-2011 Nasca Octavian Paul, Tg. Mures, Romania\n"
  365. "(C) 2017-2018 Xenakios\n\n"
  366. "Using " + fftlib + " for FFT\n\n"
  367. + juceversiontxt + " (c) Roli. Used under the GPL license.\n\n"
  368. "GPL licensed source code for this plugin at : https://bitbucket.org/xenakios/paulstretchplugin/overview\n"
  369. , "OK",
  370. this);
  371. }
  372. if (r == 6)
  373. {
  374. ValueTree tree = processor.getStateTree(true,true);
  375. MemoryBlock destData;
  376. MemoryOutputStream stream(destData, true);
  377. tree.writeToStream(stream);
  378. String txt = Base64::toBase64(destData.getData(), destData.getSize());
  379. SystemClipboard::copyTextToClipboard(txt);
  380. }
  381. if (r == 7)
  382. {
  383. toggleBool(processor.m_show_technical_info);
  384. processor.m_propsfile->m_props_file->setValue("showtechnicalinfo", processor.m_show_technical_info);
  385. }
  386. }
  387. WaveformComponent::WaveformComponent(AudioFormatManager* afm, AudioThumbnail* thumb)
  388. {
  389. TimeSelectionChangedCallback = [](Range<double>, int) {};
  390. if (m_use_opengl == true)
  391. m_ogl.attachTo(*this);
  392. m_thumbnail = thumb;
  393. m_thumbnail->addChangeListener(this);
  394. setOpaque(true);
  395. }
  396. WaveformComponent::~WaveformComponent()
  397. {
  398. if (m_use_opengl == true)
  399. m_ogl.detach();
  400. m_thumbnail->removeChangeListener(this);
  401. }
  402. void WaveformComponent::changeListenerCallback(ChangeBroadcaster * /*cb*/)
  403. {
  404. jassert(MessageManager::getInstance()->isThisTheMessageThread());
  405. m_image_dirty = true;
  406. //repaint();
  407. }
  408. void WaveformComponent::updateCachedImage()
  409. {
  410. Graphics tempg(m_waveimage);
  411. tempg.fillAll(Colours::black);
  412. tempg.setColour(Colours::darkgrey);
  413. double thumblen = m_thumbnail->getTotalLength();
  414. m_thumbnail->drawChannels(tempg, { 0,0,getWidth(),getHeight() - m_topmargin },
  415. thumblen*m_view_range.getStart(), thumblen*m_view_range.getEnd(), 1.0f);
  416. m_image_dirty = false;
  417. ++m_image_update_count;
  418. }
  419. void WaveformComponent::paint(Graphics & g)
  420. {
  421. jassert(GetFileCallback);
  422. //Logger::writeToLog("Waveform component paint");
  423. g.fillAll(Colours::black);
  424. g.setColour(Colours::darkgrey);
  425. g.fillRect(0, 0, getWidth(), m_topmargin);
  426. if (m_thumbnail == nullptr || m_thumbnail->getTotalLength() < 0.01)
  427. {
  428. g.setColour(Colours::aqua.darker());
  429. g.drawText("No file loaded", 2, m_topmargin + 2, getWidth(), 20, Justification::topLeft);
  430. return;
  431. }
  432. g.setColour(Colours::lightslategrey);
  433. double thumblen = m_thumbnail->getTotalLength();
  434. double tick_interval = 1.0;
  435. if (thumblen > 60.0)
  436. tick_interval = 5.0;
  437. for (double secs = 0.0; secs < thumblen; secs += tick_interval)
  438. {
  439. float tickxcor = (float)jmap<double>(secs,
  440. thumblen*m_view_range.getStart(), thumblen*m_view_range.getEnd(), 0.0f, (float)getWidth());
  441. g.drawLine(tickxcor, 0.0, tickxcor, (float)m_topmargin, 1.0f);
  442. }
  443. bool m_use_cached_image = true;
  444. if (m_use_cached_image == true)
  445. {
  446. if (m_image_dirty == true || m_waveimage.getWidth() != getWidth()
  447. || m_waveimage.getHeight() != getHeight() - m_topmargin)
  448. {
  449. if (m_waveimage.getWidth() != getWidth()
  450. || m_waveimage.getHeight() != getHeight() - m_topmargin)
  451. {
  452. m_waveimage = Image(Image::ARGB, getWidth(), getHeight() - m_topmargin, true);
  453. ++m_image_init_count;
  454. }
  455. updateCachedImage();
  456. }
  457. g.drawImage(m_waveimage, 0, m_topmargin, getWidth(), getHeight() - m_topmargin, 0, 0, getWidth(), getHeight() - m_topmargin);
  458. }
  459. else
  460. {
  461. g.setColour(Colours::darkgrey);
  462. m_thumbnail->drawChannels(g, { 0,m_topmargin,getWidth(),getHeight() - m_topmargin },
  463. thumblen*m_view_range.getStart(), thumblen*m_view_range.getEnd(), 1.0f);
  464. }
  465. g.setColour(Colours::white.withAlpha(0.5f));
  466. double sel_len = m_time_sel_end - m_time_sel_start;
  467. //if (sel_len > 0.0 && sel_len < 1.0)
  468. {
  469. int xcorleft = (int)jmap<double>(m_time_sel_start, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  470. int xcorright = (int)jmap<double>(m_time_sel_end, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  471. g.fillRect(xcorleft, m_topmargin, xcorright - xcorleft, getHeight() - m_topmargin);
  472. }
  473. if (m_file_cached.first.getLength() > 0.0 &&
  474. (bool)ShowFileCacheRange.getValue())
  475. {
  476. g.setColour(Colours::red.withAlpha(0.2f));
  477. int xcorleft = (int)jmap<double>(m_file_cached.first.getStart(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  478. int xcorright = (int)jmap<double>(m_file_cached.first.getEnd(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  479. g.fillRect(xcorleft, 0, xcorright - xcorleft, m_topmargin / 2);
  480. xcorleft = (int)jmap<double>(m_file_cached.second.getStart(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  481. xcorright = (int)jmap<double>(m_file_cached.second.getEnd(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  482. if (xcorright - xcorleft>0)
  483. {
  484. g.setColour(Colours::blue.withAlpha(0.2f));
  485. g.fillRect(xcorleft, m_topmargin / 2, xcorright - xcorleft, m_topmargin / 2);
  486. }
  487. }
  488. g.setColour(Colours::white);
  489. if (CursorPosCallback)
  490. {
  491. double pos = jmap<double>(CursorPosCallback(), m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  492. g.fillRect((int)pos, m_topmargin, 1, getHeight() - m_topmargin);
  493. }
  494. if (m_rec_pos >= 0.0)
  495. {
  496. g.setColour(Colours::lightpink);
  497. double pos = jmap<double>(m_rec_pos, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  498. g.fillRect((int)pos, m_topmargin, 1, getHeight() - m_topmargin);
  499. }
  500. g.setColour(Colours::aqua);
  501. g.drawText(GetFileCallback().getFileName(), 2, m_topmargin + 2, getWidth(), 20, Justification::topLeft);
  502. g.drawText(secondsToString2(thumblen), getWidth() - 200, m_topmargin + 2, 200, 20, Justification::topRight);
  503. }
  504. void WaveformComponent::timerCallback()
  505. {
  506. repaint();
  507. }
  508. void WaveformComponent::setFileCachedRange(std::pair<Range<double>, Range<double>> rng)
  509. {
  510. m_file_cached = rng;
  511. }
  512. void WaveformComponent::setTimerEnabled(bool b)
  513. {
  514. if (b == true)
  515. startTimer(100);
  516. else
  517. stopTimer();
  518. }
  519. void WaveformComponent::setViewRange(Range<double> rng)
  520. {
  521. m_view_range = rng;
  522. m_waveimage = Image();
  523. repaint();
  524. }
  525. void WaveformComponent::mouseDown(const MouseEvent & e)
  526. {
  527. m_mousedown = true;
  528. m_lock_timesel_set = true;
  529. double pos = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  530. if (e.y < m_topmargin || e.mods.isCommandDown())
  531. {
  532. if (SeekCallback)
  533. SeekCallback(pos);
  534. m_didseek = true;
  535. }
  536. else
  537. {
  538. m_time_sel_drag_target = getTimeSelectionEdge(e.x, e.y);
  539. m_drag_time_start = pos;
  540. if (m_time_sel_drag_target == 0)
  541. {
  542. //m_time_sel_start = 0.0;
  543. //m_time_sel_end = 1.0;
  544. }
  545. }
  546. repaint();
  547. }
  548. void WaveformComponent::mouseUp(const MouseEvent & /*e*/)
  549. {
  550. m_lock_timesel_set = false;
  551. m_mousedown = false;
  552. m_didseek = false;
  553. if (m_didchangetimeselection)
  554. {
  555. TimeSelectionChangedCallback(Range<double>(m_time_sel_start, m_time_sel_end), 1);
  556. m_didchangetimeselection = false;
  557. }
  558. }
  559. void WaveformComponent::mouseDrag(const MouseEvent & e)
  560. {
  561. if (m_didseek == true)
  562. return;
  563. if (m_time_sel_drag_target == 0)
  564. {
  565. m_time_sel_start = m_drag_time_start;
  566. m_time_sel_end = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  567. }
  568. if (m_time_sel_drag_target == 1)
  569. {
  570. m_time_sel_start = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  571. }
  572. if (m_time_sel_drag_target == 2)
  573. {
  574. m_time_sel_end = jmap<double>(e.x, 0, getWidth(), m_view_range.getStart(), m_view_range.getEnd());
  575. }
  576. if (m_time_sel_start > m_time_sel_end)
  577. {
  578. std::swap(m_time_sel_start, m_time_sel_end);
  579. if (m_time_sel_drag_target == 1)
  580. m_time_sel_drag_target = 2;
  581. else if (m_time_sel_drag_target == 2)
  582. m_time_sel_drag_target = 1;
  583. }
  584. m_time_sel_start = jlimit(0.0, 1.0, m_time_sel_start);
  585. m_time_sel_end = jlimit(0.0, 1.0, m_time_sel_end);
  586. if (TimeSelectionChangedCallback)
  587. {
  588. if (m_time_sel_end>m_time_sel_start)
  589. TimeSelectionChangedCallback(Range<double>(m_time_sel_start, m_time_sel_end), 0);
  590. else
  591. TimeSelectionChangedCallback(Range<double>(0.0, 1.0), 0);
  592. }
  593. m_didchangetimeselection = true;
  594. repaint();
  595. }
  596. void WaveformComponent::mouseMove(const MouseEvent & e)
  597. {
  598. m_time_sel_drag_target = getTimeSelectionEdge(e.x, e.y);
  599. if (m_time_sel_drag_target == 0)
  600. setMouseCursor(MouseCursor::NormalCursor);
  601. if (m_time_sel_drag_target == 1)
  602. setMouseCursor(MouseCursor::LeftRightResizeCursor);
  603. if (m_time_sel_drag_target == 2)
  604. setMouseCursor(MouseCursor::LeftRightResizeCursor);
  605. }
  606. void WaveformComponent::mouseDoubleClick(const MouseEvent & e)
  607. {
  608. m_time_sel_start = 0.0;
  609. m_time_sel_end = 1.0;
  610. TimeSelectionChangedCallback({ 0.0,1.0 }, 0);
  611. repaint();
  612. }
  613. Range<double> WaveformComponent::getTimeSelection()
  614. {
  615. if (m_time_sel_start >= 0.0 && m_time_sel_end>m_time_sel_start + 0.001)
  616. return { m_time_sel_start, m_time_sel_end };
  617. return { 0.0, 1.0 };
  618. }
  619. void WaveformComponent::setTimeSelection(Range<double> rng)
  620. {
  621. if (m_lock_timesel_set == true)
  622. return;
  623. if (rng.isEmpty())
  624. rng = { -1.0,1.0 };
  625. m_time_sel_start = rng.getStart();
  626. m_time_sel_end = rng.getEnd();
  627. repaint();
  628. }
  629. int WaveformComponent::getTimeSelectionEdge(int x, int y)
  630. {
  631. int xcorleft = (int)jmap<double>(m_time_sel_start, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  632. int xcorright = (int)jmap<double>(m_time_sel_end, m_view_range.getStart(), m_view_range.getEnd(), 0, getWidth());
  633. if (juce::Rectangle<int>(xcorleft - 5, m_topmargin, 10, getHeight() - m_topmargin).contains(x, y))
  634. return 1;
  635. if (juce::Rectangle<int>(xcorright - 5, m_topmargin, 10, getHeight() - m_topmargin).contains(x, y))
  636. return 2;
  637. return 0;
  638. }
  639. SpectralVisualizer::SpectralVisualizer()
  640. {
  641. m_img = Image(Image::RGB, 500, 200, true);
  642. }
  643. void SpectralVisualizer::setState(const ProcessParameters & pars, int nfreqs, double samplerate)
  644. {
  645. double t0 = Time::getMillisecondCounterHiRes();
  646. double hz = 440.0;
  647. int numharmonics = 40;
  648. double scaler = 1.0 / numharmonics;
  649. if (m_img.getWidth()!=getWidth() || m_img.getHeight()!=getHeight())
  650. m_img = Image(Image::RGB, getWidth(), getHeight(), true);
  651. if (m_nfreqs == 0 || nfreqs != m_nfreqs)
  652. {
  653. m_nfreqs = nfreqs;
  654. m_insamples = std::vector<REALTYPE>(nfreqs * 2);
  655. m_freqs1 = std::vector<REALTYPE>(nfreqs);
  656. m_freqs2 = std::vector<REALTYPE>(nfreqs);
  657. m_freqs3 = std::vector<REALTYPE>(nfreqs);
  658. m_fft = std::make_unique<FFT>(nfreqs*2);
  659. std::fill(m_insamples.begin(), m_insamples.end(), 0.0f);
  660. for (int i = 0; i < nfreqs; ++i)
  661. {
  662. for (int j = 0; j < numharmonics; ++j)
  663. {
  664. double oscgain = 1.0 - (1.0 / numharmonics)*j;
  665. m_insamples[i] += scaler * oscgain * sin(2 * 3.141592653 / samplerate * i* (hz + hz * j));
  666. }
  667. }
  668. }
  669. //std::fill(m_freqs1.begin(), m_freqs1.end(), 0.0f);
  670. //std::fill(m_freqs2.begin(), m_freqs2.end(), 0.0f);
  671. //std::fill(m_freqs3.begin(), m_freqs3.end(), 0.0f);
  672. //std::fill(m_fft->freq.begin(), m_fft->freq.end(), 0.0f);
  673. for (int i = 0; i < nfreqs; ++i)
  674. {
  675. m_fft->smp[i] = m_insamples[i];
  676. }
  677. m_fft->applywindow(W_HAMMING);
  678. m_fft->smp2freq();
  679. double ratio = pow(2.0f, pars.pitch_shift.cents / 1200.0f);
  680. spectrum_do_pitch_shift(pars, nfreqs, m_fft->freq.data(), m_freqs2.data(), ratio);
  681. spectrum_do_freq_shift(pars, nfreqs, samplerate, m_freqs2.data(), m_freqs1.data());
  682. spectrum_do_compressor(pars, nfreqs, m_freqs1.data(), m_freqs2.data());
  683. spectrum_spread(nfreqs, samplerate, m_freqs3, m_freqs2.data(), m_freqs1.data(), pars.spread.bandwidth);
  684. //if (pars.harmonics.enabled)
  685. // spectrum_do_harmonics(pars, m_freqs3, nfreqs, samplerate, m_freqs1.data(), m_freqs2.data());
  686. //else spectrum_copy(nfreqs, m_freqs1.data(), m_freqs2.data());
  687. Graphics g(m_img);
  688. g.fillAll(Colours::black);
  689. g.setColour(Colours::white);
  690. for (int i = 0; i < nfreqs; ++i)
  691. {
  692. double binfreq = (samplerate / 2 / nfreqs)*i;
  693. double xcor = jmap<double>(binfreq, 0.0, samplerate / 2.0, 0.0, getWidth());
  694. double ycor = getHeight()- jmap<double>(m_freqs2[i], 0.0, nfreqs/128, 0.0, getHeight());
  695. ycor = jlimit<double>(0.0, getHeight(), ycor);
  696. g.drawLine(xcor, getHeight(), xcor, ycor, 1.0);
  697. }
  698. double t1 = Time::getMillisecondCounterHiRes();
  699. m_elapsed = t1 - t0;
  700. repaint();
  701. }
  702. void SpectralVisualizer::paint(Graphics & g)
  703. {
  704. g.drawImage(m_img, 0, 0, getWidth(), getHeight(), 0, 0, m_img.getWidth(), m_img.getHeight());
  705. g.setColour(Colours::yellow);
  706. g.drawText(String(m_elapsed, 1)+" ms", 1, 1, getWidth(), 30, Justification::topLeft);
  707. }
  708. void SpectralChainEditor::paint(Graphics & g)
  709. {
  710. g.fillAll(Colours::black);
  711. if (m_src == nullptr)
  712. return;
  713. int box_w = getWidth() / m_order.size();
  714. int box_h = getHeight();
  715. for (int i = 0; i < m_order.size(); ++i)
  716. {
  717. //if (i!=m_cur_index)
  718. drawBox(g, i, i*box_w, 0, box_w - 30, box_h);
  719. if (i<m_order.size() - 1)
  720. 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);
  721. }
  722. if (m_drag_x>=0 && m_drag_x<getWidth() && m_cur_index>=0)
  723. drawBox(g, m_cur_index, m_drag_x, 0, box_w - 30, box_h);
  724. }
  725. void SpectralChainEditor::setSource(StretchAudioSource * src)
  726. {
  727. m_src = src;
  728. m_order = m_src->getSpectrumProcessOrder();
  729. repaint();
  730. }
  731. void SpectralChainEditor::mouseDown(const MouseEvent & ev)
  732. {
  733. m_did_drag = false;
  734. int box_w = getWidth() / m_order.size();
  735. int box_h = getHeight();
  736. m_cur_index = ev.x / box_w;
  737. if (m_cur_index >= 0)
  738. {
  739. if (ModuleSelectedCallback)
  740. ModuleSelectedCallback(m_order[m_cur_index].m_index);
  741. juce::Rectangle<int> r(box_w*m_cur_index, 1, 12, 12);
  742. if (r.contains(ev.x, ev.y))
  743. {
  744. m_order[m_cur_index].m_enabled = !m_order[m_cur_index].m_enabled;
  745. m_src->setSpectrumProcessOrder(m_order);
  746. if (ModuleOrderOrEnabledChangedCallback)
  747. ModuleOrderOrEnabledChangedCallback();
  748. repaint();
  749. return;
  750. }
  751. }
  752. m_drag_x = -1;
  753. repaint();
  754. }
  755. void SpectralChainEditor::mouseDrag(const MouseEvent & ev)
  756. {
  757. int box_w = getWidth() / m_order.size();
  758. juce::Rectangle<int> r(box_w*m_cur_index, 1, 12, 12);
  759. if (r.contains(ev.x, ev.y))
  760. return;
  761. if (m_cur_index >= 0 && m_cur_index < m_order.size())
  762. {
  763. int box_h = getHeight();
  764. int new_index = ev.x / box_w;
  765. if (new_index >= 0 && new_index < m_order.size() && new_index != m_cur_index)
  766. {
  767. std::swap(m_order[m_cur_index], m_order[new_index]);
  768. m_cur_index = new_index;
  769. m_did_drag = true;
  770. m_src->setSpectrumProcessOrder(m_order);
  771. if (ModuleOrderOrEnabledChangedCallback)
  772. ModuleOrderOrEnabledChangedCallback();
  773. }
  774. m_drag_x = ev.x;
  775. repaint();
  776. }
  777. }
  778. void SpectralChainEditor::mouseUp(const MouseEvent & ev)
  779. {
  780. m_drag_x = -1;
  781. //m_cur_index = -1;
  782. repaint();
  783. }
  784. void SpectralChainEditor::drawBox(Graphics & g, int index, int x, int y, int w, int h)
  785. {
  786. String txt;
  787. if (m_order[index].m_index == 0)
  788. txt = "Harmonics";
  789. if (m_order[index].m_index == 1)
  790. txt = "Tonal vs Noise";
  791. if (m_order[index].m_index == 2)
  792. txt = "Frequency shift";
  793. if (m_order[index].m_index == 3)
  794. txt = "Pitch shift";
  795. if (m_order[index].m_index == 4)
  796. txt = "Octaves";
  797. if (m_order[index].m_index == 5)
  798. txt = "Spread";
  799. if (m_order[index].m_index == 6)
  800. txt = "Filter";
  801. if (m_order[index].m_index == 7)
  802. txt = "Compressor";
  803. if (index == m_cur_index)
  804. {
  805. g.setColour(Colours::darkgrey);
  806. //g.fillRect(i*box_w, 0, box_w - 30, box_h - 1);
  807. g.fillRect(x, y, w, h);
  808. }
  809. g.setColour(Colours::white);
  810. g.drawRect(x, y, w, h);
  811. g.drawFittedText(txt, x,y,w,h, Justification::centred, 3);
  812. g.setColour(Colours::gold);
  813. g.drawRect(x + 2, y + 2, 12, 12);
  814. if (m_order[index].m_enabled == true)
  815. {
  816. g.drawLine(x+2, y+2, x+14, y+14);
  817. g.drawLine(x+2, y+14, x+14, y+2);
  818. }
  819. g.setColour(Colours::white);
  820. }
  821. ParameterComponent::ParameterComponent(AudioProcessorParameter * par, bool notifyOnlyOnRelease) : m_par(par)
  822. {
  823. addAndMakeVisible(&m_label);
  824. m_labeldefcolor = m_label.findColour(Label::textColourId);
  825. m_label.setText(par->getName(50), dontSendNotification);
  826. AudioParameterFloat* floatpar = dynamic_cast<AudioParameterFloat*>(par);
  827. if (floatpar)
  828. {
  829. m_slider = std::make_unique<MySlider>(&floatpar->range);
  830. m_notify_only_on_release = notifyOnlyOnRelease;
  831. m_slider->setRange(floatpar->range.start, floatpar->range.end, floatpar->range.interval);
  832. m_slider->setValue(*floatpar, dontSendNotification);
  833. m_slider->addListener(this);
  834. m_slider->setDoubleClickReturnValue(true, floatpar->range.convertFrom0to1(par->getDefaultValue()));
  835. addAndMakeVisible(m_slider.get());
  836. }
  837. AudioParameterInt* intpar = dynamic_cast<AudioParameterInt*>(par);
  838. if (intpar)
  839. {
  840. m_slider = std::make_unique<MySlider>();
  841. m_notify_only_on_release = notifyOnlyOnRelease;
  842. m_slider->setRange(intpar->getRange().getStart(), intpar->getRange().getEnd(), 1.0);
  843. m_slider->setValue(*intpar, dontSendNotification);
  844. m_slider->addListener(this);
  845. addAndMakeVisible(m_slider.get());
  846. }
  847. AudioParameterChoice* choicepar = dynamic_cast<AudioParameterChoice*>(par);
  848. if (choicepar)
  849. {
  850. }
  851. AudioParameterBool* boolpar = dynamic_cast<AudioParameterBool*>(par);
  852. if (boolpar)
  853. {
  854. m_togglebut = std::make_unique<ToggleButton>();
  855. m_togglebut->setToggleState(*boolpar, dontSendNotification);
  856. m_togglebut->addListener(this);
  857. m_togglebut->setButtonText(par->getName(50));
  858. addAndMakeVisible(m_togglebut.get());
  859. }
  860. }
  861. void ParameterComponent::resized()
  862. {
  863. if (m_slider)
  864. {
  865. int labw = 200;
  866. if (getWidth() < 400)
  867. labw = 100;
  868. m_label.setBounds(0, 0, labw, 24);
  869. m_slider->setBounds(m_label.getRight() + 1, 0, getWidth() - 2 - m_label.getWidth(), 24);
  870. }
  871. if (m_togglebut)
  872. m_togglebut->setBounds(1, 0, getWidth() - 1, 24);
  873. }
  874. void ParameterComponent::sliderValueChanged(Slider * slid)
  875. {
  876. if (m_notify_only_on_release == true)
  877. return;
  878. AudioParameterFloat* floatpar = dynamic_cast<AudioParameterFloat*>(m_par);
  879. if (floatpar != nullptr)
  880. *floatpar = slid->getValue();
  881. AudioParameterInt* intpar = dynamic_cast<AudioParameterInt*>(m_par);
  882. if (intpar != nullptr)
  883. *intpar = slid->getValue();
  884. }
  885. void ParameterComponent::sliderDragStarted(Slider * slid)
  886. {
  887. m_dragging = true;
  888. }
  889. void ParameterComponent::sliderDragEnded(Slider * slid)
  890. {
  891. m_dragging = false;
  892. if (m_notify_only_on_release == false)
  893. return;
  894. AudioParameterFloat* floatpar = dynamic_cast<AudioParameterFloat*>(m_par);
  895. if (floatpar != nullptr)
  896. *floatpar = slid->getValue();
  897. AudioParameterInt* intpar = dynamic_cast<AudioParameterInt*>(m_par);
  898. if (intpar != nullptr)
  899. *intpar = slid->getValue();
  900. }
  901. void ParameterComponent::buttonClicked(Button * but)
  902. {
  903. AudioParameterBool* boolpar = dynamic_cast<AudioParameterBool*>(m_par);
  904. if (m_togglebut != nullptr && m_togglebut->getToggleState() != *boolpar)
  905. {
  906. *boolpar = m_togglebut->getToggleState();
  907. }
  908. }
  909. void ParameterComponent::updateComponent()
  910. {
  911. AudioParameterFloat* floatpar = dynamic_cast<AudioParameterFloat*>(m_par);
  912. if (floatpar != nullptr && m_slider != nullptr && m_dragging == false && (float)m_slider->getValue() != *floatpar)
  913. {
  914. m_slider->setValue(*floatpar, dontSendNotification);
  915. }
  916. AudioParameterInt* intpar = dynamic_cast<AudioParameterInt*>(m_par);
  917. if (intpar != nullptr && m_slider != nullptr && m_dragging == false && (int)m_slider->getValue() != *intpar)
  918. {
  919. m_slider->setValue(*intpar, dontSendNotification);
  920. }
  921. AudioParameterBool* boolpar = dynamic_cast<AudioParameterBool*>(m_par);
  922. if (m_togglebut != nullptr && m_togglebut->getToggleState() != *boolpar)
  923. {
  924. m_togglebut->setToggleState(*boolpar, dontSendNotification);
  925. }
  926. }
  927. void ParameterComponent::setHighLighted(bool b)
  928. {
  929. if (b == false)
  930. {
  931. m_label.setColour(Label::textColourId, m_labeldefcolor);
  932. if (m_togglebut)
  933. m_togglebut->setColour(ToggleButton::textColourId, m_labeldefcolor);
  934. }
  935. else
  936. {
  937. m_label.setColour(Label::textColourId, Colours::yellow);
  938. if (m_togglebut)
  939. m_togglebut->setColour(ToggleButton::textColourId, Colours::yellow);
  940. }
  941. }
  942. MySlider::MySlider(NormalisableRange<float>* range) : m_range(range)
  943. {
  944. }
  945. double MySlider::proportionOfLengthToValue(double x)
  946. {
  947. if (m_range)
  948. return m_range->convertFrom0to1(x);
  949. return Slider::proportionOfLengthToValue(x);
  950. }
  951. double MySlider::valueToProportionOfLength(double x)
  952. {
  953. if (m_range)
  954. return m_range->convertTo0to1(x);
  955. return Slider::valueToProportionOfLength(x);
  956. }
  957. PerfMeterComponent::PerfMeterComponent(PaulstretchpluginAudioProcessor * p)
  958. : m_proc(p)
  959. {
  960. m_gradient.isRadial = false;
  961. m_gradient.addColour(0.0, Colours::red);
  962. m_gradient.addColour(0.25, Colours::yellow);
  963. m_gradient.addColour(1.0, Colours::green);
  964. }
  965. void PerfMeterComponent::paint(Graphics & g)
  966. {
  967. m_gradient.point1 = {0.0f,0.0f};
  968. m_gradient.point2 = {(float)getWidth(),0.0f};
  969. g.fillAll(Colours::grey);
  970. double amt = m_proc->getPreBufferingPercent();
  971. g.setColour(Colours::green);
  972. int w = amt * getWidth();
  973. //g.setGradientFill(m_gradient);
  974. g.fillRect(0, 0, w, getHeight());
  975. g.setColour(Colours::white);
  976. g.drawRect(0, 0, getWidth(), getHeight());
  977. g.setFont(10.0f);
  978. if (m_proc->getPreBufferAmount()>0)
  979. g.drawText("PREBUFFER", 0, 0, getWidth(), getHeight(), Justification::centred);
  980. else
  981. g.drawText("NO PREBUFFER", 0, 0, getWidth(), getHeight(), Justification::centred);
  982. }
  983. void PerfMeterComponent::mouseDown(const MouseEvent & ev)
  984. {
  985. PopupMenu bufferingmenu;
  986. int curbufamount = m_proc->getPreBufferAmount();
  987. bufferingmenu.addItem(100, "None", true, curbufamount == -1);
  988. bufferingmenu.addItem(101, "Small", true, curbufamount == 1);
  989. bufferingmenu.addItem(102, "Medium", true, curbufamount == 2);
  990. bufferingmenu.addItem(103, "Large", true, curbufamount == 3);
  991. bufferingmenu.addItem(104, "Very large", true, curbufamount == 4);
  992. bufferingmenu.addItem(105, "Huge", true, curbufamount == 5);
  993. int r = bufferingmenu.show();
  994. if (r >= 100 && r < 200)
  995. {
  996. if (r == 100)
  997. m_proc->m_use_backgroundbuffering = false;
  998. if (r > 100)
  999. m_proc->setPreBufferAmount(r - 100);
  1000. }
  1001. }
  1002. void zoom_scrollbar::mouseDown(const MouseEvent &e)
  1003. {
  1004. m_drag_start_x = e.x;
  1005. }
  1006. void zoom_scrollbar::mouseMove(const MouseEvent &e)
  1007. {
  1008. auto ha = get_hot_area(e.x, e.y);
  1009. if (ha == ha_left_edge || m_hot_area == ha_right_edge)
  1010. setMouseCursor(MouseCursor::LeftRightResizeCursor);
  1011. else
  1012. setMouseCursor(MouseCursor::NormalCursor);
  1013. if (ha != m_hot_area)
  1014. {
  1015. m_hot_area = ha;
  1016. repaint();
  1017. }
  1018. }
  1019. void zoom_scrollbar::mouseDrag(const MouseEvent &e)
  1020. {
  1021. if (m_hot_area == ha_none)
  1022. return;
  1023. if (m_hot_area == ha_left_edge)
  1024. {
  1025. double new_left_edge = 1.0 / getWidth()*e.x;
  1026. m_therange.setStart(jlimit(0.0, m_therange.getEnd() - 0.01, new_left_edge));
  1027. repaint();
  1028. }
  1029. if (m_hot_area == ha_right_edge)
  1030. {
  1031. double new_right_edge = 1.0 / getWidth()*e.x;
  1032. m_therange.setEnd(jlimit(m_therange.getStart() + 0.01, 1.0, new_right_edge));
  1033. repaint();
  1034. }
  1035. if (m_hot_area == ha_handle)
  1036. {
  1037. double delta = 1.0 / getWidth()*(e.x - m_drag_start_x);
  1038. //double old_start = m_start;
  1039. //double old_end = m_end;
  1040. double old_len = m_therange.getLength();
  1041. m_therange.setStart(jlimit(0.0, 1.0 - old_len, m_therange.getStart() + delta));
  1042. m_therange.setEnd(jlimit(old_len, m_therange.getStart() + old_len, m_therange.getEnd() + delta));
  1043. m_drag_start_x = e.x;
  1044. repaint();
  1045. }
  1046. if (RangeChanged)
  1047. RangeChanged(m_therange);
  1048. }
  1049. void zoom_scrollbar::mouseEnter(const MouseEvent & event)
  1050. {
  1051. m_hot_area = get_hot_area(event.x, event.y);
  1052. repaint();
  1053. }
  1054. void zoom_scrollbar::mouseExit(const MouseEvent &)
  1055. {
  1056. m_hot_area = ha_none;
  1057. repaint();
  1058. }
  1059. void zoom_scrollbar::paint(Graphics &g)
  1060. {
  1061. g.setColour(Colours::darkgrey);
  1062. g.fillRect(0, 0, getWidth(), getHeight());
  1063. int x0 = (int)(getWidth()*m_therange.getStart());
  1064. int x1 = (int)(getWidth()*m_therange.getEnd());
  1065. if (m_hot_area != ha_none)
  1066. g.setColour(Colours::white);
  1067. else g.setColour(Colours::lightgrey);
  1068. g.fillRect(x0, 0, x1 - x0, getHeight());
  1069. }
  1070. void zoom_scrollbar::setRange(Range<double> rng, bool docallback)
  1071. {
  1072. if (rng.isEmpty())
  1073. return;
  1074. m_therange = rng.constrainRange({ 0.0,1.0 });
  1075. if (RangeChanged && docallback)
  1076. RangeChanged(m_therange);
  1077. repaint();
  1078. }
  1079. zoom_scrollbar::hot_area zoom_scrollbar::get_hot_area(int x, int)
  1080. {
  1081. int x0 = (int)(getWidth()*m_therange.getStart());
  1082. int x1 = (int)(getWidth()*m_therange.getEnd());
  1083. if (is_in_range(x, x0 - 5, x0 + 5))
  1084. return ha_left_edge;
  1085. if (is_in_range(x, x1 - 5, x1 + 5))
  1086. return ha_right_edge;
  1087. if (is_in_range(x, x0 + 5, x1 - 5))
  1088. return ha_handle;
  1089. return ha_none;
  1090. }