audio_file_processor.cpp 32 KB

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