audio_file_processor.cpp 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292
  1. /*
  2. * audio_file_processor.cpp - instrument for using audio-files
  3. *
  4. * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
  5. *
  6. * This file is part of LMMS - https://lmms.io
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2 of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public
  19. * License along with this program (see COPYING); if not, write to the
  20. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. * Boston, MA 02110-1301 USA.
  22. *
  23. */
  24. #include "audio_file_processor.h"
  25. #include <QPainter>
  26. #include <QBitmap>
  27. #include <QDomDocument>
  28. #include <QFileInfo>
  29. #include <QDropEvent>
  30. #include <samplerate.h>
  31. #include "ConfigManager.h"
  32. #include "DataFile.h"
  33. #include "Engine.h"
  34. #include "gui_templates.h"
  35. #include "InstrumentTrack.h"
  36. #include "interpolation.h"
  37. #include "Mixer.h"
  38. #include "NotePlayHandle.h"
  39. #include "PathUtil.h"
  40. #include "Song.h"
  41. #include "StringPairDrag.h"
  42. #include "ToolTip.h"
  43. #include "Clipboard.h"
  44. #include "embed.h"
  45. #include "plugin_export.h"
  46. extern "C"
  47. {
  48. Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor =
  49. {
  50. STRINGIFY( PLUGIN_NAME ),
  51. "AudioFileProcessor",
  52. QT_TRANSLATE_NOOP( "PluginBrowser",
  53. "Simple sampler with various settings for "
  54. "using samples (e.g. drums) in an "
  55. "instrument-track" ),
  56. "Tobias Doerffel <tobydox/at/users.sf.net>",
  57. 0x0100,
  58. Plugin::Instrument,
  59. new PluginPixmapLoader( "logo" ),
  60. "wav,ogg,ds,spx,au,voc,aif,aiff,flac,raw",
  61. NULL
  62. } ;
  63. }
  64. audioFileProcessor::audioFileProcessor( InstrumentTrack * _instrument_track ) :
  65. Instrument( _instrument_track, &audiofileprocessor_plugin_descriptor ),
  66. m_sampleBuffer(),
  67. m_ampModel( 100, 0, 500, 1, this, tr( "Amplify" ) ),
  68. m_startPointModel( 0, 0, 1, 0.0000001f, this, tr( "Start of sample" ) ),
  69. m_endPointModel( 1, 0, 1, 0.0000001f, this, tr( "End of sample" ) ),
  70. m_loopPointModel( 0, 0, 1, 0.0000001f, this, tr( "Loopback point" ) ),
  71. m_reverseModel( false, this, tr( "Reverse sample" ) ),
  72. m_loopModel( 0, 0, 2, this, tr( "Loop mode" ) ),
  73. m_stutterModel( false, this, tr( "Stutter" ) ),
  74. m_interpolationModel( this, tr( "Interpolation mode" ) ),
  75. m_nextPlayStartPoint( 0 ),
  76. m_nextPlayBackwards( false )
  77. {
  78. connect( &m_reverseModel, SIGNAL( dataChanged() ),
  79. this, SLOT( reverseModelChanged() ), Qt::DirectConnection );
  80. connect( &m_ampModel, SIGNAL( dataChanged() ),
  81. this, SLOT( ampModelChanged() ), Qt::DirectConnection );
  82. connect( &m_startPointModel, SIGNAL( dataChanged() ),
  83. this, SLOT( startPointChanged() ), Qt::DirectConnection );
  84. connect( &m_endPointModel, SIGNAL( dataChanged() ),
  85. this, SLOT( endPointChanged() ), Qt::DirectConnection );
  86. connect( &m_loopPointModel, SIGNAL( dataChanged() ),
  87. this, SLOT( loopPointChanged() ), Qt::DirectConnection );
  88. connect( &m_stutterModel, SIGNAL( dataChanged() ),
  89. this, SLOT( stutterModelChanged() ), Qt::DirectConnection );
  90. //interpolation modes
  91. m_interpolationModel.addItem( tr( "None" ) );
  92. m_interpolationModel.addItem( tr( "Linear" ) );
  93. m_interpolationModel.addItem( tr( "Sinc" ) );
  94. m_interpolationModel.setValue( 1 );
  95. pointChanged();
  96. }
  97. audioFileProcessor::~audioFileProcessor()
  98. {
  99. }
  100. void audioFileProcessor::playNote( NotePlayHandle * _n,
  101. sampleFrame * _working_buffer )
  102. {
  103. const fpp_t frames = _n->framesLeftForCurrentPeriod();
  104. const f_cnt_t offset = _n->noteOffset();
  105. // Magic key - a frequency < 20 (say, the bottom piano note if using
  106. // a A4 base tuning) restarts the start point. The note is not actually
  107. // played.
  108. if( m_stutterModel.value() == true && _n->frequency() < 20.0 )
  109. {
  110. m_nextPlayStartPoint = m_sampleBuffer.startFrame();
  111. m_nextPlayBackwards = false;
  112. return;
  113. }
  114. if( !_n->m_pluginData )
  115. {
  116. if( m_stutterModel.value() == true && m_nextPlayStartPoint >= m_sampleBuffer.endFrame() )
  117. {
  118. // Restart playing the note if in stutter mode, not in loop mode,
  119. // and we're at the end of the sample.
  120. m_nextPlayStartPoint = m_sampleBuffer.startFrame();
  121. m_nextPlayBackwards = false;
  122. }
  123. // set interpolation mode for libsamplerate
  124. int srcmode = SRC_LINEAR;
  125. switch( m_interpolationModel.value() )
  126. {
  127. case 0:
  128. srcmode = SRC_ZERO_ORDER_HOLD;
  129. break;
  130. case 1:
  131. srcmode = SRC_LINEAR;
  132. break;
  133. case 2:
  134. srcmode = SRC_SINC_MEDIUM_QUALITY;
  135. break;
  136. }
  137. _n->m_pluginData = new handleState( _n->hasDetuningInfo(), srcmode );
  138. ((handleState *)_n->m_pluginData)->setFrameIndex( m_nextPlayStartPoint );
  139. ((handleState *)_n->m_pluginData)->setBackwards( m_nextPlayBackwards );
  140. // debug code
  141. /* qDebug( "frames %d", m_sampleBuffer.frames() );
  142. qDebug( "startframe %d", m_sampleBuffer.startFrame() );
  143. qDebug( "nextPlayStartPoint %d", m_nextPlayStartPoint );*/
  144. }
  145. if( ! _n->isFinished() )
  146. {
  147. if( m_sampleBuffer.play( _working_buffer + offset,
  148. (handleState *)_n->m_pluginData,
  149. frames, _n->frequency(),
  150. static_cast<SampleBuffer::LoopMode>( m_loopModel.value() ) ) )
  151. {
  152. applyRelease( _working_buffer, _n );
  153. instrumentTrack()->processAudioBuffer( _working_buffer,
  154. frames + offset, _n );
  155. emit isPlaying( ((handleState *)_n->m_pluginData)->frameIndex() );
  156. }
  157. else
  158. {
  159. memset( _working_buffer, 0, ( frames + offset ) * sizeof( sampleFrame ) );
  160. emit isPlaying( 0 );
  161. }
  162. }
  163. else
  164. {
  165. emit isPlaying( 0 );
  166. }
  167. if( m_stutterModel.value() == true )
  168. {
  169. m_nextPlayStartPoint = ((handleState *)_n->m_pluginData)->frameIndex();
  170. m_nextPlayBackwards = ((handleState *)_n->m_pluginData)->isBackwards();
  171. }
  172. }
  173. void audioFileProcessor::deleteNotePluginData( NotePlayHandle * _n )
  174. {
  175. delete (handleState *)_n->m_pluginData;
  176. }
  177. void audioFileProcessor::saveSettings( QDomDocument & _doc,
  178. QDomElement & _this )
  179. {
  180. _this.setAttribute( "src", m_sampleBuffer.audioFile() );
  181. if( m_sampleBuffer.audioFile() == "" )
  182. {
  183. QString s;
  184. _this.setAttribute( "sampledata",
  185. m_sampleBuffer.toBase64( s ) );
  186. }
  187. m_reverseModel.saveSettings( _doc, _this, "reversed" );
  188. m_loopModel.saveSettings( _doc, _this, "looped" );
  189. m_ampModel.saveSettings( _doc, _this, "amp" );
  190. m_startPointModel.saveSettings( _doc, _this, "sframe" );
  191. m_endPointModel.saveSettings( _doc, _this, "eframe" );
  192. m_loopPointModel.saveSettings( _doc, _this, "lframe" );
  193. m_stutterModel.saveSettings( _doc, _this, "stutter" );
  194. m_interpolationModel.saveSettings( _doc, _this, "interp" );
  195. }
  196. void audioFileProcessor::loadSettings( const QDomElement & _this )
  197. {
  198. if( _this.attribute( "src" ) != "" )
  199. {
  200. setAudioFile( _this.attribute( "src" ), false );
  201. QString absolutePath = PathUtil::toAbsolute( m_sampleBuffer.audioFile() );
  202. if ( !QFileInfo( absolutePath ).exists() )
  203. {
  204. QString message = tr( "Sample not found: %1" ).arg( m_sampleBuffer.audioFile() );
  205. Engine::getSong()->collectError( message );
  206. }
  207. }
  208. else if( _this.attribute( "sampledata" ) != "" )
  209. {
  210. m_sampleBuffer.loadFromBase64( _this.attribute( "srcdata" ) );
  211. }
  212. m_loopModel.loadSettings( _this, "looped" );
  213. m_ampModel.loadSettings( _this, "amp" );
  214. m_endPointModel.loadSettings( _this, "eframe" );
  215. m_startPointModel.loadSettings( _this, "sframe" );
  216. // compat code for not having a separate loopback point
  217. if (_this.hasAttribute("lframe") || !(_this.firstChildElement("lframe").isNull()))
  218. {
  219. m_loopPointModel.loadSettings( _this, "lframe" );
  220. }
  221. else
  222. {
  223. m_loopPointModel.loadSettings( _this, "sframe" );
  224. }
  225. m_reverseModel.loadSettings( _this, "reversed" );
  226. m_stutterModel.loadSettings( _this, "stutter" );
  227. if( _this.hasAttribute( "interp" ) )
  228. {
  229. m_interpolationModel.loadSettings( _this, "interp" );
  230. }
  231. else
  232. {
  233. m_interpolationModel.setValue( 1 ); //linear by default
  234. }
  235. pointChanged();
  236. }
  237. void audioFileProcessor::loadFile( const QString & _file )
  238. {
  239. setAudioFile( _file );
  240. }
  241. QString audioFileProcessor::nodeName( void ) const
  242. {
  243. return audiofileprocessor_plugin_descriptor.name;
  244. }
  245. int audioFileProcessor::getBeatLen( NotePlayHandle * _n ) const
  246. {
  247. const float freq_factor = BaseFreq / _n->frequency() *
  248. Engine::mixer()->processingSampleRate() / Engine::mixer()->baseSampleRate();
  249. return static_cast<int>( floorf( ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * freq_factor ) );
  250. }
  251. PluginView * audioFileProcessor::instantiateView( QWidget * _parent )
  252. {
  253. return new AudioFileProcessorView( this, _parent );
  254. }
  255. void audioFileProcessor::setAudioFile( const QString & _audio_file,
  256. bool _rename )
  257. {
  258. // is current channel-name equal to previous-filename??
  259. if( _rename &&
  260. ( instrumentTrack()->name() ==
  261. QFileInfo( m_sampleBuffer.audioFile() ).fileName() ||
  262. m_sampleBuffer.audioFile().isEmpty() ) )
  263. {
  264. // then set it to new one
  265. instrumentTrack()->setName( PathUtil::cleanName( _audio_file ) );
  266. }
  267. // else we don't touch the track-name, because the user named it self
  268. m_sampleBuffer.setAudioFile( _audio_file );
  269. loopPointChanged();
  270. }
  271. void audioFileProcessor::reverseModelChanged( void )
  272. {
  273. m_sampleBuffer.setReversed( m_reverseModel.value() );
  274. m_nextPlayStartPoint = m_sampleBuffer.startFrame();
  275. m_nextPlayBackwards = false;
  276. }
  277. void audioFileProcessor::ampModelChanged( void )
  278. {
  279. m_sampleBuffer.setAmplification( m_ampModel.value() / 100.0f );
  280. }
  281. void audioFileProcessor::stutterModelChanged()
  282. {
  283. m_nextPlayStartPoint = m_sampleBuffer.startFrame();
  284. m_nextPlayBackwards = false;
  285. }
  286. void audioFileProcessor::startPointChanged( void )
  287. {
  288. // check if start is over end and swap values if so
  289. if( m_startPointModel.value() > m_endPointModel.value() )
  290. {
  291. float tmp = m_endPointModel.value();
  292. m_endPointModel.setValue( m_startPointModel.value() );
  293. m_startPointModel.setValue( tmp );
  294. }
  295. // nudge loop point with end
  296. if( m_loopPointModel.value() >= m_endPointModel.value() )
  297. {
  298. m_loopPointModel.setValue( qMax( m_endPointModel.value() - 0.001f, 0.0f ) );
  299. }
  300. // nudge loop point with start
  301. if( m_loopPointModel.value() < m_startPointModel.value() )
  302. {
  303. m_loopPointModel.setValue( m_startPointModel.value() );
  304. }
  305. // check if start & end overlap and nudge end up if so
  306. if( m_startPointModel.value() == m_endPointModel.value() )
  307. {
  308. m_endPointModel.setValue( qMin( m_endPointModel.value() + 0.001f, 1.0f ) );
  309. }
  310. pointChanged();
  311. }
  312. void audioFileProcessor::endPointChanged( void )
  313. {
  314. // same as start, for now
  315. startPointChanged();
  316. }
  317. void audioFileProcessor::loopPointChanged( void )
  318. {
  319. // check that loop point is between start-end points and not overlapping with endpoint
  320. // ...and move start/end points ahead if loop point is moved over them
  321. if( m_loopPointModel.value() >= m_endPointModel.value() )
  322. {
  323. m_endPointModel.setValue( m_loopPointModel.value() + 0.001f );
  324. if( m_endPointModel.value() == 1.0f )
  325. {
  326. m_loopPointModel.setValue( 1.0f - 0.001f );
  327. }
  328. }
  329. // nudge start point with loop
  330. if( m_loopPointModel.value() < m_startPointModel.value() )
  331. {
  332. m_startPointModel.setValue( m_loopPointModel.value() );
  333. }
  334. pointChanged();
  335. }
  336. void audioFileProcessor::pointChanged( void )
  337. {
  338. const f_cnt_t f_start = static_cast<f_cnt_t>( m_startPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
  339. const f_cnt_t f_end = static_cast<f_cnt_t>( m_endPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
  340. const f_cnt_t f_loop = static_cast<f_cnt_t>( m_loopPointModel.value() * ( m_sampleBuffer.frames()-1 ) );
  341. m_nextPlayStartPoint = f_start;
  342. m_nextPlayBackwards = false;
  343. m_sampleBuffer.setAllPointFrames( f_start, f_end, f_loop, f_end );
  344. emit dataChanged();
  345. }
  346. QPixmap * AudioFileProcessorView::s_artwork = NULL;
  347. AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument,
  348. QWidget * _parent ) :
  349. InstrumentViewFixedSize( _instrument, _parent )
  350. {
  351. if( s_artwork == NULL )
  352. {
  353. s_artwork = new QPixmap( PLUGIN_NAME::getIconPixmap(
  354. "artwork" ) );
  355. }
  356. m_openAudioFileButton = new PixmapButton( this );
  357. m_openAudioFileButton->setCursor( QCursor( Qt::PointingHandCursor ) );
  358. m_openAudioFileButton->move( 227, 72 );
  359. m_openAudioFileButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  360. "select_file" ) );
  361. m_openAudioFileButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  362. "select_file" ) );
  363. connect( m_openAudioFileButton, SIGNAL( clicked() ),
  364. this, SLOT( openAudioFile() ) );
  365. ToolTip::add( m_openAudioFileButton, tr( "Open sample" ) );
  366. m_reverseButton = new PixmapButton( this );
  367. m_reverseButton->setCheckable( true );
  368. m_reverseButton->move( 164, 105 );
  369. m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  370. "reverse_on" ) );
  371. m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  372. "reverse_off" ) );
  373. ToolTip::add( m_reverseButton, tr( "Reverse sample" ) );
  374. // loop button group
  375. PixmapButton * m_loopOffButton = new PixmapButton( this );
  376. m_loopOffButton->setCheckable( true );
  377. m_loopOffButton->move( 190, 105 );
  378. m_loopOffButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  379. "loop_off_on" ) );
  380. m_loopOffButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  381. "loop_off_off" ) );
  382. ToolTip::add( m_loopOffButton, tr( "Disable loop" ) );
  383. PixmapButton * m_loopOnButton = new PixmapButton( this );
  384. m_loopOnButton->setCheckable( true );
  385. m_loopOnButton->move( 190, 124 );
  386. m_loopOnButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  387. "loop_on_on" ) );
  388. m_loopOnButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  389. "loop_on_off" ) );
  390. ToolTip::add( m_loopOnButton, tr( "Enable loop" ) );
  391. PixmapButton * m_loopPingPongButton = new PixmapButton( this );
  392. m_loopPingPongButton->setCheckable( true );
  393. m_loopPingPongButton->move( 216, 124 );
  394. m_loopPingPongButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  395. "loop_pingpong_on" ) );
  396. m_loopPingPongButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  397. "loop_pingpong_off" ) );
  398. ToolTip::add( m_loopPingPongButton, tr( "Enable ping-pong loop" ) );
  399. m_loopGroup = new automatableButtonGroup( this );
  400. m_loopGroup->addButton( m_loopOffButton );
  401. m_loopGroup->addButton( m_loopOnButton );
  402. m_loopGroup->addButton( m_loopPingPongButton );
  403. m_stutterButton = new PixmapButton( this );
  404. m_stutterButton->setCheckable( true );
  405. m_stutterButton->move( 164, 124 );
  406. m_stutterButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  407. "stutter_on" ) );
  408. m_stutterButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  409. "stutter_off" ) );
  410. ToolTip::add( m_stutterButton,
  411. tr( "Continue sample playback across notes" ) );
  412. m_ampKnob = new Knob( knobBright_26, this );
  413. m_ampKnob->setVolumeKnob( true );
  414. m_ampKnob->move( 5, 108 );
  415. m_ampKnob->setHintText( tr( "Amplify:" ), "%" );
  416. m_startKnob = new AudioFileProcessorWaveView::knob( this );
  417. m_startKnob->move( 45, 108 );
  418. m_startKnob->setHintText( tr( "Start point:" ), "" );
  419. m_endKnob = new AudioFileProcessorWaveView::knob( this );
  420. m_endKnob->move( 125, 108 );
  421. m_endKnob->setHintText( tr( "End point:" ), "" );
  422. m_loopKnob = new AudioFileProcessorWaveView::knob( this );
  423. m_loopKnob->move( 85, 108 );
  424. m_loopKnob->setHintText( tr( "Loopback point:" ), "" );
  425. // interpolation selector
  426. m_interpBox = new ComboBox( this );
  427. m_interpBox->setGeometry( 142, 62, 82, ComboBox::DEFAULT_HEIGHT );
  428. m_interpBox->setFont( pointSize<8>( m_interpBox->font() ) );
  429. // wavegraph
  430. m_waveView = 0;
  431. newWaveView();
  432. connect( castModel<audioFileProcessor>(), SIGNAL( isPlaying( f_cnt_t ) ),
  433. m_waveView, SLOT( isPlaying( f_cnt_t ) ) );
  434. qRegisterMetaType<f_cnt_t>( "f_cnt_t" );
  435. setAcceptDrops( true );
  436. }
  437. AudioFileProcessorView::~AudioFileProcessorView()
  438. {
  439. }
  440. void AudioFileProcessorView::dragEnterEvent( QDragEnterEvent * _dee )
  441. {
  442. // For mimeType() and MimeType enum class
  443. using namespace Clipboard;
  444. if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) )
  445. {
  446. QString txt = _dee->mimeData()->data(
  447. mimeType( MimeType::StringPair ) );
  448. if( txt.section( ':', 0, 0 ) == QString( "tco_%1" ).arg(
  449. Track::SampleTrack ) )
  450. {
  451. _dee->acceptProposedAction();
  452. }
  453. else if( txt.section( ':', 0, 0 ) == "samplefile" )
  454. {
  455. _dee->acceptProposedAction();
  456. }
  457. else
  458. {
  459. _dee->ignore();
  460. }
  461. }
  462. else
  463. {
  464. _dee->ignore();
  465. }
  466. }
  467. void AudioFileProcessorView::newWaveView()
  468. {
  469. if ( m_waveView )
  470. {
  471. delete m_waveView;
  472. m_waveView = 0;
  473. }
  474. m_waveView = new AudioFileProcessorWaveView( this, 245, 75, castModel<audioFileProcessor>()->m_sampleBuffer );
  475. m_waveView->move( 2, 172 );
  476. m_waveView->setKnobs(
  477. dynamic_cast<AudioFileProcessorWaveView::knob *>( m_startKnob ),
  478. dynamic_cast<AudioFileProcessorWaveView::knob *>( m_endKnob ),
  479. dynamic_cast<AudioFileProcessorWaveView::knob *>( m_loopKnob ) );
  480. m_waveView->show();
  481. }
  482. void AudioFileProcessorView::dropEvent( QDropEvent * _de )
  483. {
  484. QString type = StringPairDrag::decodeKey( _de );
  485. QString value = StringPairDrag::decodeValue( _de );
  486. if( type == "samplefile" )
  487. {
  488. castModel<audioFileProcessor>()->setAudioFile( value );
  489. _de->accept();
  490. newWaveView();
  491. return;
  492. }
  493. else if( type == QString( "tco_%1" ).arg( Track::SampleTrack ) )
  494. {
  495. DataFile dataFile( value.toUtf8() );
  496. castModel<audioFileProcessor>()->setAudioFile( dataFile.content().firstChild().toElement().attribute( "src" ) );
  497. _de->accept();
  498. return;
  499. }
  500. _de->ignore();
  501. }
  502. void AudioFileProcessorView::paintEvent( QPaintEvent * )
  503. {
  504. QPainter p( this );
  505. p.drawPixmap( 0, 0, *s_artwork );
  506. audioFileProcessor * a = castModel<audioFileProcessor>();
  507. QString file_name = "";
  508. int idx = a->m_sampleBuffer.audioFile().length();
  509. p.setFont( pointSize<8>( font() ) );
  510. QFontMetrics fm( p.font() );
  511. // simple algorithm for creating a text from the filename that
  512. // matches in the white rectangle
  513. while( idx > 0 &&
  514. fm.size( Qt::TextSingleLine, file_name + "..." ).width() < 210 )
  515. {
  516. file_name = a->m_sampleBuffer.audioFile()[--idx] + file_name;
  517. }
  518. if( idx > 0 )
  519. {
  520. file_name = "..." + file_name;
  521. }
  522. p.setPen( QColor( 255, 255, 255 ) );
  523. p.drawText( 8, 99, file_name );
  524. }
  525. void AudioFileProcessorView::sampleUpdated( void )
  526. {
  527. m_waveView->updateSampleRange();
  528. m_waveView->update();
  529. update();
  530. }
  531. void AudioFileProcessorView::openAudioFile( void )
  532. {
  533. QString af = castModel<audioFileProcessor>()->m_sampleBuffer.
  534. openAudioFile();
  535. if( af != "" )
  536. {
  537. castModel<audioFileProcessor>()->setAudioFile( af );
  538. Engine::getSong()->setModified();
  539. m_waveView->updateSampleRange();
  540. }
  541. }
  542. void AudioFileProcessorView::modelChanged( void )
  543. {
  544. audioFileProcessor * a = castModel<audioFileProcessor>();
  545. connect( &a->m_sampleBuffer, SIGNAL( sampleUpdated() ),
  546. this, SLOT( sampleUpdated() ) );
  547. m_ampKnob->setModel( &a->m_ampModel );
  548. m_startKnob->setModel( &a->m_startPointModel );
  549. m_endKnob->setModel( &a->m_endPointModel );
  550. m_loopKnob->setModel( &a->m_loopPointModel );
  551. m_reverseButton->setModel( &a->m_reverseModel );
  552. m_loopGroup->setModel( &a->m_loopModel );
  553. m_stutterButton->setModel( &a->m_stutterModel );
  554. m_interpBox->setModel( &a->m_interpolationModel );
  555. sampleUpdated();
  556. }
  557. void AudioFileProcessorWaveView::updateSampleRange()
  558. {
  559. if( m_sampleBuffer.frames() > 1 )
  560. {
  561. const f_cnt_t marging = ( m_sampleBuffer.endFrame() - m_sampleBuffer.startFrame() ) * 0.1;
  562. m_from = qMax( 0, m_sampleBuffer.startFrame() - marging );
  563. m_to = qMin( m_sampleBuffer.endFrame() + marging, m_sampleBuffer.frames() );
  564. }
  565. }
  566. AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _w, int _h, SampleBuffer& buf ) :
  567. QWidget( _parent ),
  568. m_sampleBuffer( buf ),
  569. m_graph( QPixmap( _w - 2 * s_padding, _h - 2 * s_padding ) ),
  570. m_from( 0 ),
  571. m_to( m_sampleBuffer.frames() ),
  572. m_last_from( 0 ),
  573. m_last_to( 0 ),
  574. m_last_amp( 0 ),
  575. m_startKnob( 0 ),
  576. m_endKnob( 0 ),
  577. m_loopKnob( 0 ),
  578. m_isDragging( false ),
  579. m_reversed( false ),
  580. m_framesPlayed( 0 ),
  581. m_animation(ConfigManager::inst()->value("ui", "animateafp").toInt())
  582. {
  583. setFixedSize( _w, _h );
  584. setMouseTracking( true );
  585. updateSampleRange();
  586. m_graph.fill( Qt::transparent );
  587. update();
  588. updateCursor();
  589. }
  590. void AudioFileProcessorWaveView::isPlaying( f_cnt_t _current_frame )
  591. {
  592. m_framesPlayed = _current_frame;
  593. update();
  594. }
  595. void AudioFileProcessorWaveView::enterEvent( QEvent * _e )
  596. {
  597. updateCursor();
  598. }
  599. void AudioFileProcessorWaveView::leaveEvent( QEvent * _e )
  600. {
  601. updateCursor();
  602. }
  603. void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me )
  604. {
  605. m_isDragging = true;
  606. m_draggingLastPoint = _me->pos();
  607. const int x = _me->x();
  608. const int start_dist = qAbs( m_startFrameX - x );
  609. const int end_dist = qAbs( m_endFrameX - x );
  610. const int loop_dist = qAbs( m_loopFrameX - x );
  611. draggingType dt = sample_loop; int md = loop_dist;
  612. if( start_dist < loop_dist ) { dt = sample_start; md = start_dist; }
  613. else if( end_dist < loop_dist ) { dt = sample_end; md = end_dist; }
  614. if( md < 4 )
  615. {
  616. m_draggingType = dt;
  617. }
  618. else
  619. {
  620. m_draggingType = wave;
  621. updateCursor(_me);
  622. }
  623. }
  624. void AudioFileProcessorWaveView::mouseReleaseEvent( QMouseEvent * _me )
  625. {
  626. m_isDragging = false;
  627. if( m_draggingType == wave )
  628. {
  629. updateCursor(_me);
  630. }
  631. }
  632. void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me )
  633. {
  634. if( ! m_isDragging )
  635. {
  636. updateCursor(_me);
  637. return;
  638. }
  639. const int step = _me->x() - m_draggingLastPoint.x();
  640. switch( m_draggingType )
  641. {
  642. case sample_start:
  643. slideSamplePointByPx( start, step );
  644. break;
  645. case sample_end:
  646. slideSamplePointByPx( end, step );
  647. break;
  648. case sample_loop:
  649. slideSamplePointByPx( loop, step );
  650. break;
  651. case wave:
  652. default:
  653. if( qAbs( _me->y() - m_draggingLastPoint.y() )
  654. < 2 * qAbs( _me->x() - m_draggingLastPoint.x() ) )
  655. {
  656. slide( step );
  657. }
  658. else
  659. {
  660. zoom( _me->y() < m_draggingLastPoint.y() );
  661. }
  662. }
  663. m_draggingLastPoint = _me->pos();
  664. update();
  665. }
  666. void AudioFileProcessorWaveView::wheelEvent( QWheelEvent * _we )
  667. {
  668. zoom( _we->angleDelta().y() > 0 );
  669. update();
  670. }
  671. void AudioFileProcessorWaveView::paintEvent( QPaintEvent * _pe )
  672. {
  673. QPainter p( this );
  674. p.drawPixmap( s_padding, s_padding, m_graph );
  675. const QRect graph_rect( s_padding, s_padding, width() - 2 * s_padding, height() - 2 * s_padding );
  676. const f_cnt_t frames = m_to - m_from;
  677. m_startFrameX = graph_rect.x() + ( m_sampleBuffer.startFrame() - m_from ) *
  678. double( graph_rect.width() ) / frames;
  679. m_endFrameX = graph_rect.x() + ( m_sampleBuffer.endFrame() - m_from ) *
  680. double( graph_rect.width() ) / frames;
  681. m_loopFrameX = graph_rect.x() + ( m_sampleBuffer.loopStartFrame() - m_from ) *
  682. double( graph_rect.width() ) / frames;
  683. const int played_width_px = ( m_framesPlayed - m_from ) *
  684. double( graph_rect.width() ) / frames;
  685. // loop point line
  686. p.setPen( QColor( 0x7F, 0xFF, 0xFF ) ); //TODO: put into a qproperty
  687. p.drawLine( m_loopFrameX, graph_rect.y(),
  688. m_loopFrameX,
  689. graph_rect.height() + graph_rect.y() );
  690. // start/end lines
  691. p.setPen( QColor( 0xFF, 0xFF, 0xFF ) ); //TODO: put into a qproperty
  692. p.drawLine( m_startFrameX, graph_rect.y(),
  693. m_startFrameX,
  694. graph_rect.height() + graph_rect.y() );
  695. p.drawLine( m_endFrameX, graph_rect.y(),
  696. m_endFrameX,
  697. graph_rect.height() + graph_rect.y() );
  698. if( m_endFrameX - m_startFrameX > 2 )
  699. {
  700. p.fillRect(
  701. m_startFrameX + 1,
  702. graph_rect.y(),
  703. m_endFrameX - m_startFrameX - 1,
  704. graph_rect.height() + graph_rect.y(),
  705. QColor( 95, 175, 255, 50 ) //TODO: put into a qproperty
  706. );
  707. if( m_endFrameX - m_loopFrameX > 2 )
  708. p.fillRect(
  709. m_loopFrameX + 1,
  710. graph_rect.y(),
  711. m_endFrameX - m_loopFrameX - 1,
  712. graph_rect.height() + graph_rect.y(),
  713. QColor( 95, 205, 255, 65 ) //TODO: put into a qproperty
  714. );
  715. if( m_framesPlayed && m_animation)
  716. {
  717. QLinearGradient g( m_startFrameX, 0, played_width_px, 0 );
  718. const QColor c( 0, 120, 255, 180 ); //TODO: put into a qproperty
  719. g.setColorAt( 0, Qt::transparent );
  720. g.setColorAt( 0.8, c );
  721. g.setColorAt( 1, c );
  722. p.fillRect(
  723. m_startFrameX + 1,
  724. graph_rect.y(),
  725. played_width_px - ( m_startFrameX + 1 ),
  726. graph_rect.height() + graph_rect.y(),
  727. g
  728. );
  729. p.setPen( QColor( 255, 255, 255 ) ); //TODO: put into a qproperty
  730. p.drawLine(
  731. played_width_px,
  732. graph_rect.y(),
  733. played_width_px,
  734. graph_rect.height() + graph_rect.y()
  735. );
  736. m_framesPlayed = 0;
  737. }
  738. }
  739. QLinearGradient g( 0, 0, width() * 0.7, 0 );
  740. const QColor c( 16, 111, 170, 180 );
  741. g.setColorAt( 0, c );
  742. g.setColorAt( 0.4, c );
  743. g.setColorAt( 1, Qt::transparent );
  744. p.fillRect( s_padding, s_padding, m_graph.width(), 14, g );
  745. p.setPen( QColor( 255, 255, 255 ) );
  746. p.setFont( pointSize<8>( font() ) );
  747. QString length_text;
  748. const int length = m_sampleBuffer.sampleLength();
  749. if( length > 20000 )
  750. {
  751. length_text = QString::number( length / 1000 ) + "s";
  752. }
  753. else if( length > 2000 )
  754. {
  755. length_text = QString::number( ( length / 100 ) / 10.0 ) + "s";
  756. }
  757. else
  758. {
  759. length_text = QString::number( length ) + "ms";
  760. }
  761. p.drawText(
  762. s_padding + 2,
  763. s_padding + 10,
  764. tr( "Sample length:" ) + " " + length_text
  765. );
  766. }
  767. void AudioFileProcessorWaveView::updateGraph()
  768. {
  769. if( m_to == 1 )
  770. {
  771. m_to = m_sampleBuffer.frames() * 0.7;
  772. slideSamplePointToFrames( end, m_to * 0.7 );
  773. }
  774. if( m_from > m_sampleBuffer.startFrame() )
  775. {
  776. m_from = m_sampleBuffer.startFrame();
  777. }
  778. if( m_to < m_sampleBuffer.endFrame() )
  779. {
  780. m_to = m_sampleBuffer.endFrame();
  781. }
  782. if( m_sampleBuffer.reversed() != m_reversed )
  783. {
  784. reverse();
  785. }
  786. else if( m_last_from == m_from && m_last_to == m_to && m_sampleBuffer.amplification() == m_last_amp )
  787. {
  788. return;
  789. }
  790. m_last_from = m_from;
  791. m_last_to = m_to;
  792. m_last_amp = m_sampleBuffer.amplification();
  793. m_graph.fill( Qt::transparent );
  794. QPainter p( &m_graph );
  795. p.setPen( QColor( 255, 255, 255 ) );
  796. m_sampleBuffer.visualize(
  797. p,
  798. QRect( 0, 0, m_graph.width(), m_graph.height() ),
  799. m_from, m_to
  800. );
  801. }
  802. void AudioFileProcessorWaveView::zoom( const bool _out )
  803. {
  804. const f_cnt_t start = m_sampleBuffer.startFrame();
  805. const f_cnt_t end = m_sampleBuffer.endFrame();
  806. const f_cnt_t frames = m_sampleBuffer.frames();
  807. const f_cnt_t d_from = start - m_from;
  808. const f_cnt_t d_to = m_to - end;
  809. const f_cnt_t step = qMax( 1, qMax( d_from, d_to ) / 10 );
  810. const f_cnt_t step_from = ( _out ? - step : step );
  811. const f_cnt_t step_to = ( _out ? step : - step );
  812. const double comp_ratio = double( qMin( d_from, d_to ) )
  813. / qMax( 1, qMax( d_from, d_to ) );
  814. f_cnt_t new_from;
  815. f_cnt_t new_to;
  816. if( ( _out && d_from < d_to ) || ( ! _out && d_to < d_from ) )
  817. {
  818. new_from = qBound( 0, m_from + step_from, start );
  819. new_to = qBound(
  820. end,
  821. m_to + f_cnt_t( step_to * ( new_from == m_from ? 1 : comp_ratio ) ),
  822. frames
  823. );
  824. }
  825. else
  826. {
  827. new_to = qBound( end, m_to + step_to, frames );
  828. new_from = qBound(
  829. 0,
  830. m_from + f_cnt_t( step_from * ( new_to == m_to ? 1 : comp_ratio ) ),
  831. start
  832. );
  833. }
  834. if( double( new_to - new_from ) / m_sampleBuffer.sampleRate() > 0.05 )
  835. {
  836. m_from = new_from;
  837. m_to = new_to;
  838. }
  839. }
  840. void AudioFileProcessorWaveView::slide( int _px )
  841. {
  842. const double fact = qAbs( double( _px ) / width() );
  843. f_cnt_t step = ( m_to - m_from ) * fact;
  844. if( _px > 0 )
  845. {
  846. step = -step;
  847. }
  848. f_cnt_t step_from = qBound( 0, m_from + step, m_sampleBuffer.frames() ) - m_from;
  849. f_cnt_t step_to = qBound( m_from + 1, m_to + step, m_sampleBuffer.frames() ) - m_to;
  850. step = qAbs( step_from ) < qAbs( step_to ) ? step_from : step_to;
  851. m_from += step;
  852. m_to += step;
  853. slideSampleByFrames( step );
  854. }
  855. void AudioFileProcessorWaveView::setKnobs( knob * _start, knob * _end, knob * _loop )
  856. {
  857. m_startKnob = _start;
  858. m_endKnob = _end;
  859. m_loopKnob = _loop;
  860. m_startKnob->setWaveView( this );
  861. m_startKnob->setRelatedKnob( m_endKnob );
  862. m_endKnob->setWaveView( this );
  863. m_endKnob->setRelatedKnob( m_startKnob );
  864. m_loopKnob->setWaveView( this );
  865. }
  866. void AudioFileProcessorWaveView::slideSamplePointByPx( knobType _point, int _px )
  867. {
  868. slideSamplePointByFrames(
  869. _point,
  870. f_cnt_t( ( double( _px ) / width() ) * ( m_to - m_from ) )
  871. );
  872. }
  873. void AudioFileProcessorWaveView::slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to )
  874. {
  875. knob * a_knob = m_startKnob;
  876. switch( _point )
  877. {
  878. case end:
  879. a_knob = m_endKnob;
  880. break;
  881. case loop:
  882. a_knob = m_loopKnob;
  883. break;
  884. case start:
  885. break;
  886. }
  887. if( a_knob == NULL )
  888. {
  889. return;
  890. }
  891. else
  892. {
  893. const double v = static_cast<double>( _frames ) / m_sampleBuffer.frames();
  894. if( _slide_to )
  895. {
  896. a_knob->slideTo( v );
  897. }
  898. else
  899. {
  900. a_knob->slideBy( v );
  901. }
  902. }
  903. }
  904. void AudioFileProcessorWaveView::slideSampleByFrames( f_cnt_t _frames )
  905. {
  906. if( m_sampleBuffer.frames() <= 1 )
  907. {
  908. return;
  909. }
  910. const double v = static_cast<double>( _frames ) / m_sampleBuffer.frames();
  911. if( m_startKnob ) {
  912. m_startKnob->slideBy( v, false );
  913. }
  914. if( m_endKnob ) {
  915. m_endKnob->slideBy( v, false );
  916. }
  917. if( m_loopKnob ) {
  918. m_loopKnob->slideBy( v, false );
  919. }
  920. }
  921. void AudioFileProcessorWaveView::reverse()
  922. {
  923. slideSampleByFrames(
  924. m_sampleBuffer.frames()
  925. - m_sampleBuffer.endFrame()
  926. - m_sampleBuffer.startFrame()
  927. );
  928. const f_cnt_t from = m_from;
  929. m_from = m_sampleBuffer.frames() - m_to;
  930. m_to = m_sampleBuffer.frames() - from;
  931. m_reversed = ! m_reversed;
  932. }
  933. void AudioFileProcessorWaveView::updateCursor( QMouseEvent * _me )
  934. {
  935. bool const waveIsDragged = m_isDragging && (m_draggingType == wave);
  936. bool const pointerCloseToStartEndOrLoop = (_me != nullptr ) &&
  937. ( isCloseTo( _me->x(), m_startFrameX ) ||
  938. isCloseTo( _me->x(), m_endFrameX ) ||
  939. isCloseTo( _me->x(), m_loopFrameX ) );
  940. if( !m_isDragging && pointerCloseToStartEndOrLoop)
  941. setCursor(Qt::SizeHorCursor);
  942. else if( waveIsDragged )
  943. setCursor(Qt::ClosedHandCursor);
  944. else
  945. setCursor(Qt::OpenHandCursor);
  946. }
  947. void AudioFileProcessorWaveView::knob::slideTo( double _v, bool _check_bound )
  948. {
  949. if( _check_bound && ! checkBound( _v ) )
  950. {
  951. return;
  952. }
  953. model()->setValue( _v );
  954. emit sliderMoved( model()->value() );
  955. }
  956. float AudioFileProcessorWaveView::knob::getValue( const QPoint & _p )
  957. {
  958. const double dec_fact = ! m_waveView ? 1 :
  959. double( m_waveView->m_to - m_waveView->m_from )
  960. / m_waveView->m_sampleBuffer.frames();
  961. const float inc = ::Knob::getValue( _p ) * dec_fact;
  962. return inc;
  963. }
  964. bool AudioFileProcessorWaveView::knob::checkBound( double _v ) const
  965. {
  966. if( ! m_relatedKnob || ! m_waveView )
  967. {
  968. return true;
  969. }
  970. if( ( m_relatedKnob->model()->value() - _v > 0 ) !=
  971. ( m_relatedKnob->model()->value() - model()->value() >= 0 ) )
  972. return false;
  973. const double d1 = qAbs( m_relatedKnob->model()->value() - model()->value() )
  974. * ( m_waveView->m_sampleBuffer.frames() )
  975. / m_waveView->m_sampleBuffer.sampleRate();
  976. const double d2 = qAbs( m_relatedKnob->model()->value() - _v )
  977. * ( m_waveView->m_sampleBuffer.frames() )
  978. / m_waveView->m_sampleBuffer.sampleRate();
  979. return d1 < d2 || d2 > 0.005;
  980. }
  981. extern "C"
  982. {
  983. // necessary for getting instance out of shared lib
  984. PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * model, void *)
  985. {
  986. return new audioFileProcessor(static_cast<InstrumentTrack *>(model));
  987. }
  988. }