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.

710 lines
21KB

  1. /*
  2. Copyright (C) 2006-2011 Nasca Octavian Paul
  3. Author: Nasca Octavian Paul
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of version 2 of the GNU General Public License
  6. as published by the Free Software Foundation.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License (version 2) for more details.
  11. You should have received a copy of the GNU General Public License (version 2)
  12. along with this program; if not, write to the Free Software Foundation,
  13. Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  14. */
  15. #include <math.h>
  16. #include <stdlib.h>
  17. #include "globals.h"
  18. #include "PaulStretchControl.h"
  19. #include "../WDL/resample.h"
  20. #include <thread>
  21. #ifdef WIN32
  22. #undef min
  23. #undef max
  24. #endif
  25. using namespace std;
  26. extern std::unique_ptr<PropertiesFile> g_propsfile;
  27. Control::Control(AudioFormatManager* afm) : m_afm(afm), m_bufferingthread("stretchbufferingthread")
  28. {
  29. m_stretch_source = std::make_unique<MultiStretchAudioSource>(2,m_afm);
  30. wavinfo.samplerate=44100;
  31. wavinfo.nsamples=0;
  32. process.bufsize=16384;
  33. process.stretch=4.0;
  34. process.onset_detection_sensitivity=0.0;
  35. seek_pos=0.0;
  36. window_type=W_HANN;
  37. volume=1.0;
  38. gui_sliders.fftsize_s=0.5;
  39. gui_sliders.stretch_s=0.5;
  40. gui_sliders.mode_s=0;
  41. ///#warning test
  42. /// process.transient.enable=true;
  43. };
  44. Control::~Control()
  45. {
  46. m_bufferingthread.stopThread(1000);
  47. }
  48. void Control::processAudio(AudioBuffer<float>& buf)
  49. {
  50. if (m_buffering_source != nullptr)
  51. {
  52. AudioSourceChannelInfo aif(buf);
  53. m_buffering_source->getNextAudioBlock(aif);
  54. }
  55. }
  56. void Control::setStretchAmount(double rate)
  57. {
  58. m_stretch_source->setRate(rate);
  59. }
  60. void Control::setFFTSize(double size)
  61. {
  62. if (m_prebuffer_amount == 5)
  63. m_fft_size_to_use = pow(2, 7.0 + size * 14.5);
  64. else m_fft_size_to_use = pow(2, 7.0 + size * 10.0); // chicken out from allowing huge FFT sizes if not enough prebuffering
  65. int optim = optimizebufsize(m_fft_size_to_use);
  66. m_fft_size_to_use = optim;
  67. m_stretch_source->setFFTSize(optim);
  68. //Logger::writeToLog(String(m_fft_size_to_use));
  69. }
  70. void Control::setOnsetDetection(double x)
  71. {
  72. m_stretch_source->setOnsetDetection(x);
  73. }
  74. void Control::set_input_file(File file, std::function<void(String)> cb)
  75. {
  76. auto task = [this,file,cb]()
  77. {
  78. auto ai = unique_from_raw(m_afm->createReaderFor(file));
  79. if (ai!=nullptr)
  80. {
  81. if (ai->numChannels > 32)
  82. {
  83. MessageManager::callAsync([cb,file](){ cb("Too many channels in file "+file.getFullPathName()); });
  84. return;
  85. }
  86. if (ai->bitsPerSample>32)
  87. {
  88. MessageManager::callAsync([cb,file](){ cb("Too high bit depth in file "+file.getFullPathName()); });
  89. return;
  90. }
  91. wavinfo.filename = file.getFullPathName();
  92. wavinfo.samplerate = ai->sampleRate;
  93. wavinfo.nsamples = ai->lengthInSamples;
  94. m_stretch_source->setAudioFile(file);
  95. MessageManager::callAsync([cb,file](){ cb(String()); });
  96. /// if (process.transient.enable) {
  97. /// pre_analyse_whole_audio(ai);
  98. /// };
  99. } else
  100. {
  101. wavinfo.filename="";
  102. wavinfo.samplerate=0;
  103. wavinfo.nsamples=0;
  104. MessageManager::callAsync([cb,file](){ cb("Could not open file "+file.getFullPathName()); });
  105. };
  106. };
  107. //std::thread th(task);
  108. //th.detach();
  109. task();
  110. };
  111. String Control::get_input_filename(){
  112. return wavinfo.filename;
  113. };
  114. String Control::get_stretch_info(Range<double> playrange)
  115. {
  116. double prate = m_stretch_source->getRate();
  117. double realduration = m_stretch_source->getOutputDurationSecondsForRange(playrange, m_fft_size_to_use);
  118. int64_t durintseconds = realduration;
  119. int64_t durintminutes = realduration / 60.0;
  120. int64_t durinthours = realduration / 3600.0;
  121. int64_t durintdays = realduration / (3600 * 24.0);
  122. String timestring;
  123. if (durintminutes < 1)
  124. timestring = String(realduration,3) +" seconds";
  125. if (durintminutes >= 1 && durinthours < 1)
  126. timestring = String(durintminutes) + " mins " + String(durintseconds % 60) + " secs";
  127. if (durinthours >= 1 && durintdays < 1)
  128. timestring = String(durinthours) + " hours " + String(durintminutes % 60) + " mins " + String(durintseconds % 60) + " secs";
  129. if (durintdays >= 1)
  130. timestring = String(durintdays) + " days " + String(durinthours % 24) + " hours " +
  131. String(durintminutes % 60) + " mins ";
  132. double fftwindowlenseconds = (double)m_fft_size_to_use / wavinfo.samplerate;
  133. double fftwindowfreqresol = (double)wavinfo.samplerate / 2 / m_fft_size_to_use;
  134. return "[Stretch " + String(prate, 1) + "x " + timestring + "] [FFT window "+String(fftwindowlenseconds,3)+" secs, frequency resolution "+
  135. String(fftwindowfreqresol,3)+" Hz]";
  136. #ifdef OLDSTRETCHINFO
  137. const int size=200;
  138. char tmp[size];tmp[size-1]=0;
  139. if (wavinfo.nsamples==0) return "Stretch: ";
  140. double realduration = m_stretch_source->getOutputDuration();
  141. if (realduration>(365.25*86400.0*1.0e12)){//more than 1 trillion years
  142. double duration=(realduration/(365.25*86400.0*1.0e12));//my
  143. snprintf(tmp,size,"Stretch: %.7gx (%g trillion years)",process.stretch,duration);
  144. return tmp;
  145. };
  146. if (realduration>(365.25*86400.0*1.0e9)){//more than 1 billion years
  147. double duration=(realduration/(365.25*86400.0*1.0e9));//my
  148. snprintf(tmp,size,"Stretch: %.7gx (%g billion years)",process.stretch,duration);
  149. return tmp;
  150. };
  151. if (realduration>(365.25*86400.0*1.0e6)){//more than 1 million years
  152. double duration=(realduration/(365.25*86400.0*1.0e6));//my
  153. snprintf(tmp,size,"Stretch: %.7gx (%g million years)",process.stretch,duration);
  154. return tmp;
  155. };
  156. if (realduration>(365.25*86400.0*2000.0)){//more than two millenniums
  157. int duration=(int)(realduration/(365.25*86400.0));//years
  158. int years=duration%1000;
  159. int milleniums=duration/1000;
  160. char stryears[size];stryears[0]=0;
  161. if (years!=0){
  162. if (years==1) snprintf(stryears,size," 1 year");
  163. else snprintf(stryears,size," %d years",years);
  164. };
  165. snprintf(tmp,size,"Stretch: %.7gx (%d milleniums%s)",process.stretch,milleniums,stryears);
  166. return tmp;
  167. };
  168. if (realduration>(365.25*86400.0)){//more than 1 year
  169. int duration=(int) (realduration/3600.0);//hours
  170. int hours=duration%24;
  171. int days=(duration/24)%365;
  172. int years=duration/(365*24);
  173. char stryears[size];stryears[0]=0;
  174. if (years==1) snprintf(stryears,size,"1 year ");
  175. else snprintf(stryears,size,"%d years ",years);
  176. char strdays[size];strdays[0]=0;
  177. if (days>0){
  178. if (days==1) snprintf(strdays,size,"1 day");
  179. else snprintf(strdays,size,"%d days",days);
  180. };
  181. if (years>=10) hours=0;
  182. char strhours[size];strhours[0]=0;
  183. if (hours>0){
  184. snprintf(strhours,size," %d h",hours);
  185. };
  186. snprintf(tmp,size,"Stretch: %.7gx (%s%s%s)",process.stretch,stryears,strdays,strhours);
  187. return tmp;
  188. }else{//less than 1 year
  189. int duration=(int)(realduration);//seconds
  190. char strdays[size];strdays[0]=0;
  191. int days=duration/86400;
  192. if (days>0){
  193. if (days==1) snprintf(strdays,size,"1 day ");
  194. else snprintf(strdays,size,"%d days ",duration/86400);
  195. };
  196. REALTYPE stretch=process.stretch;
  197. if (stretch>=1.0){
  198. stretch=((int) (stretch*100.0))*0.01;
  199. };
  200. snprintf(tmp,size,"Stretch: %.7gx (%s%.2d:%.2d:%.2d)",
  201. stretch,strdays,(duration/3600)%24,(duration/60)%60,duration%60);
  202. return tmp;
  203. };
  204. return "";
  205. #endif
  206. };
  207. /*
  208. string Control::get_fftresolution_info(){
  209. string resolution="Resolution: ";
  210. if (wavinfo.nsamples==0) return resolution;
  211. //todo: unctime and uncfreq are correct computed? Need to check later.
  212. REALTYPE unctime=process.bufsize/(REALTYPE)wavinfo.samplerate*sqrt(2.0);
  213. REALTYPE uncfreq=1.0/unctime*sqrt(2.0);
  214. char tmp[100];
  215. snprintf(tmp,100,"%.5g seconds",unctime);resolution+=tmp;
  216. snprintf(tmp,100," (%.5g Hz)",uncfreq);resolution+=tmp;
  217. return resolution;
  218. }
  219. */
  220. double Control::getPreBufferingPercent()
  221. {
  222. if (m_buffering_source == nullptr)
  223. return 0.0;
  224. return m_buffering_source->getPercentReady();
  225. }
  226. void Control::startplay(bool /*bypass*/, bool /*realtime*/, Range<double> playrange, int numoutchans, String& err)
  227. {
  228. m_stretch_source->setPlayRange(playrange, m_stretch_source->isLoopingEnabled());
  229. int bufamt = m_bufamounts[m_prebuffer_amount];
  230. if (m_buffering_source != nullptr && numoutchans != m_buffering_source->getNumberOfChannels())
  231. m_recreate_buffering_source = true;
  232. if (m_recreate_buffering_source == true)
  233. {
  234. m_buffering_source = std::make_unique<MyBufferingAudioSource>(m_stretch_source.get(),
  235. m_bufferingthread, false, bufamt, numoutchans, false);
  236. m_recreate_buffering_source = false;
  237. }
  238. if (m_bufferingthread.isThreadRunning() == false)
  239. m_bufferingthread.startThread();
  240. m_stretch_source->setNumOutChannels(numoutchans);
  241. m_stretch_source->setFFTSize(m_fft_size_to_use);
  242. update_process_parameters();
  243. m_last_outpos_pos = 0.0;
  244. m_last_in_pos = playrange.getStart()*m_stretch_source->getInfileLengthSeconds();
  245. m_buffering_source->prepareToPlay(1024, 44100.0);
  246. // sleep(100);
  247. // update_process_parameters();
  248. };
  249. void Control::stopplay()
  250. {
  251. //m_adm->removeAudioCallback(&m_test_callback);
  252. m_bufferingthread.stopThread(1000);
  253. };
  254. void Control::set_seek_pos(REALTYPE x)
  255. {
  256. seek_pos=x;
  257. m_stretch_source->seekPercent(x);
  258. };
  259. REALTYPE Control::get_seek_pos()
  260. {
  261. return 0.0;
  262. }
  263. double Control::getLivePlayPosition()
  264. {
  265. #ifndef USEOLDPLAYCURSOR
  266. double outpos = m_audiocallback.m_outpos;
  267. double rate = 1.0 / m_stretch_source->getRate();
  268. double outputdiff = (outpos - m_last_outpos_pos);
  269. double fftlenseconds = (double)m_stretch_source->getFFTSize() / 44100.0;
  270. //Logger::writeToLog("output diff " + String(outputdiff)+" fft len "+String(fftlenseconds));
  271. //jassert(outputdiff >= 0.0 && outputdiff<0.5);
  272. jassert(rate > 0.0);
  273. double inlenseconds = m_stretch_source->getInfileLengthSeconds();
  274. if (inlenseconds < 0.0001)
  275. return 0.0;
  276. double inposseconds = m_stretch_source->getInfilePositionSeconds();
  277. double playposseconds = m_last_in_pos + (outputdiff*rate) - fftlenseconds;
  278. if (outputdiff*rate >= fftlenseconds || outputdiff < 0.0001)
  279. {
  280. //Logger::writeToLog("juuh "+String(inposseconds));
  281. m_last_in_pos = inposseconds;
  282. m_last_outpos_pos = outpos;
  283. return 1.0 / inlenseconds*(m_last_in_pos-fftlenseconds-getPreBufferAmountSeconds()*rate);
  284. }
  285. //Logger::writeToLog("jaah " + String(inposseconds));
  286. return 1.0 / inlenseconds*(playposseconds-getPreBufferAmountSeconds()*rate);
  287. #else
  288. return m_stretch_source->getInfilePositionPercent();
  289. #endif
  290. }
  291. bool Control::playing()
  292. {
  293. return false;
  294. }
  295. void Control::set_stretch_controls(double stretch_s,int mode,double fftsize_s,double onset_detection_sensitivity)
  296. {
  297. gui_sliders.stretch_s=stretch_s;
  298. gui_sliders.mode_s=mode;
  299. gui_sliders.fftsize_s=fftsize_s;
  300. double stretch=1.0;
  301. switch(mode){
  302. case 0:
  303. stretch_s=pow(stretch_s,1.2);
  304. stretch=pow(10.0,stretch_s*4.0);
  305. break;
  306. case 1:
  307. stretch_s=pow(stretch_s,1.5);
  308. stretch=pow(10.0,stretch_s*18.0);
  309. break;
  310. case 2:
  311. stretch=1.0/pow(10.0,stretch_s*2.0);
  312. break;
  313. };
  314. fftsize_s=pow(fftsize_s,1.5);
  315. int bufsize=(int)(pow(2.0,fftsize_s*12.0)*512.0);
  316. bufsize=optimizebufsize(bufsize);
  317. process.stretch=stretch;
  318. process.bufsize=bufsize;
  319. process.onset_detection_sensitivity=onset_detection_sensitivity;
  320. };
  321. double Control::get_stretch_control(double stretch,int mode)
  322. {
  323. double result=1.0;
  324. switch(mode){
  325. case 0:
  326. if (stretch<1.0) return -1;
  327. stretch=(log(stretch)/log(10))*0.25;
  328. result=pow(stretch,1.0/1.2);
  329. break;
  330. case 1:
  331. if (stretch<1.0) return -1;
  332. stretch=(log(stretch)/log(10))/18.0;
  333. result=pow(stretch,1.0/1.5);
  334. break;
  335. case 2:
  336. if (stretch>1.0) return -1;
  337. result=2.0/(log(stretch)/log(10));
  338. break;
  339. };
  340. return result;
  341. };
  342. void Control::update_player_stretch()
  343. {
  344. return;
  345. //player->setrap(process.stretch);
  346. //player->set_onset_detection_sensitivity(process.onset_detection_sensitivity);
  347. };
  348. int abs_val(int x){
  349. if (x<0) return -x;
  350. else return x;
  351. };
  352. void Control::setPreBufferAmount(int x)
  353. {
  354. int temp = jlimit(0, 5, x);
  355. if (temp != m_prebuffer_amount)
  356. {
  357. m_prebuffer_amount = temp;
  358. m_recreate_buffering_source = true;
  359. }
  360. }
  361. double Control::getPreBufferAmountSeconds()
  362. {
  363. return 1.0;
  364. }
  365. void Control::setAudioVisualizer(AudioVisualiserComponent * comp)
  366. {
  367. //m_audiocallback.m_aviscomponent = comp;
  368. }
  369. void Control::setOutputAudioFileToRecord(File f)
  370. {
  371. m_audio_out_file = f;
  372. }
  373. int Control::get_optimized_updown(int n,bool up){
  374. int orig_n=n;
  375. while(true){
  376. n=orig_n;
  377. while (!(n%11)) n/=11;
  378. while (!(n%7)) n/=7;
  379. while (!(n%5)) n/=5;
  380. while (!(n%3)) n/=3;
  381. while (!(n%2)) n/=2;
  382. if (n<2) break;
  383. if (up) orig_n++;
  384. else orig_n--;
  385. if (orig_n<4) return 4;
  386. };
  387. return orig_n;
  388. };
  389. int Control::optimizebufsize(int n){
  390. int n1=get_optimized_updown(n,false);
  391. int n2=get_optimized_updown(n,true);
  392. if ((n-n1)<(n2-n)) return n1;
  393. else return n2;
  394. };
  395. void Control::set_window_type(FFTWindow window){
  396. window_type=window;
  397. //if (player) player->set_window_type(window);
  398. };
  399. RenderInfoRef Control::Render2(RenderParameters renpars)
  400. {
  401. RenderInfoRef rinfo = std::make_shared<RenderInfo>();
  402. auto bbparcopy = bbpar;
  403. auto processcopy = process;
  404. auto windowtypecopy = window_type;
  405. auto pparcopy = ppar;
  406. //auto volcopy = volume;
  407. auto ratecopy = m_stretch_source->getRate();
  408. auto fftsize = m_fft_size_to_use;
  409. AudioFormatManager* afm = m_afm;
  410. auto render_task = [rinfo,renpars,
  411. bbparcopy, processcopy, windowtypecopy, pparcopy, fftsize,ratecopy,afm]()mutable->void
  412. {
  413. double t0 = Time::getMillisecondCounterHiRes();
  414. if (renpars.pos2 < renpars.pos1)
  415. std::swap(renpars.pos1, renpars.pos2);
  416. if (renpars.pos2-renpars.pos1<0.0001)
  417. renpars.pos2+=0.0001;
  418. auto ai = std::make_unique<AInputS>(afm);
  419. if (!ai->openAudioFile(renpars.inaudio))
  420. {
  421. rinfo->m_txt = "Error: Could not open audio file (or file format not recognized) :" + renpars.inaudio.getFileName();
  422. return;
  423. }
  424. if (renpars.sampleRate == 0)
  425. renpars.sampleRate = ai->info.samplerate;
  426. WavAudioFormat audioformat;
  427. renpars.outaudio = renpars.outaudio.getNonexistentSibling();
  428. auto outstream = renpars.outaudio.createOutputStream();
  429. int wavbits = 16;
  430. if (renpars.wavformat == 1)
  431. wavbits = 24;
  432. if (renpars.wavformat == 2)
  433. wavbits = 32;
  434. renpars.numoutchans = jlimit(2, g_maxnumoutchans, renpars.numoutchans);
  435. StringPairArray metadata;
  436. metadata.set(WavAudioFormat::bwavOriginator,"PaulStretch3");
  437. /*
  438. metadata.set("NumCuePoints", "2");
  439. metadata.set("Cue0Offset", "44100");
  440. metadata.set("Cue0Identifier", "0");
  441. metadata.set("Cue1Offset", "88200");
  442. metadata.set("Cue1Identifier", "1");
  443. */
  444. AudioFormatWriter* writer = audioformat.createWriterFor(outstream, renpars.sampleRate, renpars.numoutchans, wavbits,
  445. metadata, 0);
  446. if (writer == nullptr)
  447. {
  448. delete outstream;
  449. rinfo->m_txt = "Could not create output file";
  450. return;
  451. }
  452. auto stretchsource = std::make_unique<StretchAudioSource>(renpars.numoutchans,afm);
  453. if (wavbits == 2)
  454. stretchsource->setClippingEnabled(renpars.clipFloatOutput);
  455. else stretchsource->setClippingEnabled(true);
  456. stretchsource->setAudioFile(renpars.inaudio);
  457. stretchsource->setPlayRange({renpars.pos1,renpars.pos2}, renpars.numLoops>0);
  458. stretchsource->setRate(ratecopy);
  459. stretchsource->val_MainVolume.setValue(renpars.voldb);
  460. stretchsource->setNumOutChannels(renpars.numoutchans);
  461. stretchsource->setProcessParameters(&pparcopy);
  462. stretchsource->setFFTSize(fftsize);
  463. int bufsize = 4096;
  464. AudioBuffer<float> procbuf(renpars.numoutchans,bufsize);
  465. AudioSourceChannelInfo asinfo(procbuf);
  466. stretchsource->prepareToPlay(bufsize, renpars.sampleRate);
  467. double render_time_limit = renpars.maxrenderlen;
  468. int64_t outputsamplecount = 0;
  469. int64_t outputlen = ai->info.samplerate*stretchsource->getOutputDurationSecondsForRange({ renpars.pos1,renpars.pos2 }, fftsize);
  470. rinfo->m_progress_percent = 0.01;
  471. stretchsource->setMaxLoops(renpars.numLoops);
  472. while(outputsamplecount<render_time_limit*renpars.sampleRate)
  473. {
  474. if (rinfo->m_cancel == true)
  475. {
  476. rinfo->m_txt = "Cancelled";
  477. break;
  478. }
  479. stretchsource->getNextAudioBlock(asinfo);
  480. writer->writeFromAudioSampleBuffer(procbuf, 0, bufsize);
  481. outputsamplecount +=bufsize;
  482. rinfo->m_progress_percent = 1.0 / outputlen*outputsamplecount;
  483. if (stretchsource->hasReachedEnd())
  484. {
  485. Logger::writeToLog("StretchSource has reached end");
  486. break;
  487. }
  488. if (outputsamplecount>=render_time_limit*ai->info.samplerate)
  489. {
  490. rinfo->m_txt = "Render stopped at time limit";
  491. break;
  492. }
  493. }
  494. delete writer;
  495. double t1 = Time::getMillisecondCounterHiRes();
  496. if (rinfo->m_cancel == false)
  497. {
  498. rinfo->m_elapsed_time = t1 - t0;
  499. if (rinfo->m_txt.isEmpty())
  500. rinfo->m_txt = "Done in "+String((t1-t0)/1000.0,1)+" seconds";
  501. }
  502. else rinfo->m_txt = "Cancelled";
  503. };
  504. std::thread th([rinfo,render_task, renpars]()mutable
  505. {
  506. render_task();
  507. MessageManager::callAsync([rinfo, renpars]()
  508. {
  509. renpars.completion_callback(rinfo);
  510. });
  511. });
  512. th.detach();
  513. return rinfo;
  514. }
  515. void Control::setPrebufferThreadPriority(int v)
  516. {
  517. m_prebufthreadprior = jlimit(4,6,v);
  518. }
  519. string Control::getfftsizestr(int fftsize){
  520. const int size=100;
  521. char tmp[size];tmp[size-1]=0;
  522. if (fftsize<1024.0) snprintf(tmp,size-1,"%d",fftsize);
  523. else if (fftsize<(1024.0*1024.0)) snprintf(tmp,size-1,"%.4gK",fftsize/1024.0);
  524. else if (fftsize<(1024.0*1024.0*1024.0)) snprintf(tmp,size-1,"%.4gM",fftsize/(1024.0*1024.0));
  525. else snprintf(tmp,size-1,"%.7gG",fftsize/(1024.0*1024.0*1024.0));
  526. return tmp;
  527. };
  528. void Control::update_process_parameters()
  529. {
  530. m_stretch_source->setProcessParameters(&ppar);
  531. //if (player)
  532. // player->set_process_parameters(&ppar,&bbpar);
  533. };
  534. AudioCallback::AudioCallback() : AudioIODeviceCallback(),
  535. m_writethread("audio_out_record_thread")
  536. {
  537. }
  538. void AudioCallback::audioDeviceAboutToStart(AudioIODevice * device)
  539. {
  540. m_debugcount = 0;
  541. m_outpos = 0.0;
  542. m_outsr = device->getCurrentSampleRate();
  543. if (m_aviscomponent)
  544. m_aviscomponent->setNumChannels(m_numoutchans);
  545. if (m_bufferingsource)
  546. {
  547. if (m_prebufferthread->isThreadRunning()==false)
  548. m_prebufferthread->startThread(g_propsfile->getIntValue("prebufthreadpriority",5));
  549. int bufsize = std::max(512, device->getCurrentBufferSizeSamples());
  550. //Logger::writeToLog("Using buffer size " + String(bufsize));
  551. m_bufferingsource->prepareToPlay(bufsize, m_outsr);
  552. }
  553. m_playing = true;
  554. //Logger::writeToLog("hw samplerate " + String(m_outsr));
  555. }
  556. void AudioCallback::audioDeviceStopped()
  557. {
  558. m_writer = nullptr;
  559. if (m_writethread.isThreadRunning() == true)
  560. {
  561. if (m_writethread.stopThread(1000) == false)
  562. {
  563. Logger::writeToLog("OUCH, live output recording thread didn't stop cleanly!");
  564. }
  565. }
  566. if (m_bufferingsource)
  567. {
  568. m_bufferingsource->releaseResources();
  569. if (m_prebufferthread->isThreadRunning() == true)
  570. {
  571. if (m_prebufferthread->stopThread(1000) == false)
  572. Logger::writeToLog("OUCH, prebuffering thread did not stop cleanly!");
  573. }
  574. }
  575. m_playing = false;
  576. }
  577. void AudioCallback::audioDeviceIOCallback(const float ** /*inputChannelData*/, int, float ** outputChannelData, int numOutputChannels, int numSamples)
  578. {
  579. if (m_bufferingsource == nullptr)
  580. return;
  581. AudioBuffer<float> buf(outputChannelData, numOutputChannels, numSamples);
  582. AudioSourceChannelInfo ainfo(buf);
  583. m_bufferingsource->getNextAudioBlock(ainfo);
  584. if (m_aviscomponent && m_aviscomponent->isVisible())
  585. {
  586. m_aviscomponent->pushBuffer((const float**)outputChannelData, m_numoutchans, numSamples);
  587. }
  588. if (m_writer && m_is_recording == true)
  589. {
  590. m_writer->write((const float**)outputChannelData, numSamples);
  591. }
  592. m_outpos += (double)numSamples / m_outsr;
  593. }
  594. String AudioCallback::startRecording(File outfile)
  595. {
  596. WavAudioFormat wavformat;
  597. auto outstream = outfile.createOutputStream();
  598. if (outstream == nullptr)
  599. return "Could not create output stream";
  600. auto writer = wavformat.createWriterFor(outstream, m_outsr, m_numoutchans, 32, StringPairArray(), 0);
  601. if (writer != nullptr)
  602. {
  603. if (m_writethread.isThreadRunning()==false)
  604. m_writethread.startThread();
  605. m_writer = std::make_unique<AudioFormatWriter::ThreadedWriter>(writer, m_writethread, 65536);
  606. m_is_recording = true;
  607. return String();
  608. }
  609. return "Could not create audio writer";
  610. }
  611. void AudioCallback::setNumOutchans(int numchans)
  612. {
  613. m_numoutchans = jlimit(2, g_maxnumoutchans, numchans);
  614. }