patman.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. /*
  2. * patman.cpp - a GUS-compatible patch instrument plugin
  3. *
  4. * Copyright (c) 2007-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
  5. * Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
  6. *
  7. * This file is part of LMMS - https://lmms.io
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public
  11. * License as published by the Free Software Foundation; either
  12. * version 2 of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public
  20. * License along with this program (see COPYING); if not, write to the
  21. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  22. * Boston, MA 02110-1301 USA.
  23. *
  24. */
  25. #include "patman.h"
  26. #include <QDragEnterEvent>
  27. #include <QPainter>
  28. #include <QDomElement>
  29. #include "ConfigManager.h"
  30. #include "endian_handling.h"
  31. #include "Engine.h"
  32. #include "FileDialog.h"
  33. #include "gui_templates.h"
  34. #include "InstrumentTrack.h"
  35. #include "NotePlayHandle.h"
  36. #include "PathUtil.h"
  37. #include "PixmapButton.h"
  38. #include "Song.h"
  39. #include "StringPairDrag.h"
  40. #include "ToolTip.h"
  41. #include "Clipboard.h"
  42. #include "embed.h"
  43. #include "plugin_export.h"
  44. extern "C"
  45. {
  46. Plugin::Descriptor PLUGIN_EXPORT patman_plugin_descriptor =
  47. {
  48. STRINGIFY( PLUGIN_NAME ),
  49. "PatMan",
  50. QT_TRANSLATE_NOOP( "PluginBrowser",
  51. "GUS-compatible patch instrument" ),
  52. "Javier Serrano Polo <jasp00/at/users.sourceforge.net>",
  53. 0x0100,
  54. Plugin::Instrument,
  55. new PluginPixmapLoader( "logo" ),
  56. "pat",
  57. nullptr,
  58. } ;
  59. // necessary for getting instance out of shared lib
  60. PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * )
  61. {
  62. return new patmanInstrument( static_cast<InstrumentTrack *>( m ) );
  63. }
  64. }
  65. patmanInstrument::patmanInstrument( InstrumentTrack * _instrument_track ) :
  66. Instrument( _instrument_track, &patman_plugin_descriptor ),
  67. m_loopedModel( true, this ),
  68. m_tunedModel( true, this )
  69. {
  70. }
  71. patmanInstrument::~patmanInstrument()
  72. {
  73. unloadCurrentPatch();
  74. }
  75. void patmanInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
  76. {
  77. _this.setAttribute( "src", m_patchFile );
  78. m_loopedModel.saveSettings( _doc, _this, "looped" );
  79. m_tunedModel.saveSettings( _doc, _this, "tuned" );
  80. }
  81. void patmanInstrument::loadSettings( const QDomElement & _this )
  82. {
  83. setFile( _this.attribute( "src" ), false );
  84. m_loopedModel.loadSettings( _this, "looped" );
  85. m_tunedModel.loadSettings( _this, "tuned" );
  86. }
  87. void patmanInstrument::loadFile( const QString & _file )
  88. {
  89. setFile( _file );
  90. }
  91. QString patmanInstrument::nodeName( void ) const
  92. {
  93. return( patman_plugin_descriptor.name );
  94. }
  95. void patmanInstrument::playNote( NotePlayHandle * _n,
  96. sampleFrame * _working_buffer )
  97. {
  98. if( m_patchFile == "" )
  99. {
  100. return;
  101. }
  102. const fpp_t frames = _n->framesLeftForCurrentPeriod();
  103. const f_cnt_t offset = _n->noteOffset();
  104. if( !_n->m_pluginData )
  105. {
  106. selectSample( _n );
  107. }
  108. handle_data * hdata = (handle_data *)_n->m_pluginData;
  109. float play_freq = hdata->tuned ? _n->frequency() :
  110. hdata->sample->frequency();
  111. if( hdata->sample->play( _working_buffer + offset, hdata->state, frames,
  112. play_freq, m_loopedModel.value() ? SampleBuffer::LoopOn : SampleBuffer::LoopOff ) )
  113. {
  114. applyRelease( _working_buffer, _n );
  115. instrumentTrack()->processAudioBuffer( _working_buffer,
  116. frames + offset, _n );
  117. }
  118. else
  119. {
  120. memset( _working_buffer, 0, ( frames + offset ) * sizeof( sampleFrame ) );
  121. }
  122. }
  123. void patmanInstrument::deleteNotePluginData( NotePlayHandle * _n )
  124. {
  125. handle_data * hdata = (handle_data *)_n->m_pluginData;
  126. sharedObject::unref( hdata->sample );
  127. delete hdata->state;
  128. delete hdata;
  129. }
  130. void patmanInstrument::setFile( const QString & _patch_file, bool _rename )
  131. {
  132. if( _patch_file.size() <= 0 )
  133. {
  134. m_patchFile = QString();
  135. return;
  136. }
  137. // is current instrument-track-name equal to previous-filename??
  138. if( _rename &&
  139. ( instrumentTrack()->name() ==
  140. QFileInfo( m_patchFile ).fileName() ||
  141. m_patchFile == "" ) )
  142. {
  143. // then set it to new one
  144. instrumentTrack()->setName( PathUtil::cleanName( _patch_file ) );
  145. }
  146. // else we don't touch the instrument-track-name, because the user
  147. // named it self
  148. m_patchFile = PathUtil::toShortestRelative( _patch_file );
  149. LoadErrors error = loadPatch( PathUtil::toAbsolute( _patch_file ) );
  150. if( error )
  151. {
  152. printf("Load error\n");
  153. }
  154. emit fileChanged();
  155. }
  156. patmanInstrument::LoadErrors patmanInstrument::loadPatch(
  157. const QString & _filename )
  158. {
  159. unloadCurrentPatch();
  160. FILE * fd = fopen( _filename.toUtf8().constData() , "rb" );
  161. if( !fd )
  162. {
  163. perror( "fopen" );
  164. return( LoadOpen );
  165. }
  166. unsigned char header[239];
  167. if( fread( header, 1, 239, fd ) != 239 ||
  168. ( memcmp( header, "GF1PATCH110\0ID#000002", 22 )
  169. && memcmp( header, "GF1PATCH100\0ID#000002", 22 ) ) )
  170. {
  171. fclose( fd );
  172. return( LoadNotGUS );
  173. }
  174. if( header[82] != 1 && header[82] != 0 )
  175. {
  176. fclose( fd );
  177. return( LoadInstruments );
  178. }
  179. if( header[151] != 1 && header[151] != 0 )
  180. {
  181. fclose( fd );
  182. return( LoadLayers );
  183. }
  184. int sample_count = header[198];
  185. for( int i = 0; i < sample_count; ++i )
  186. {
  187. unsigned short tmpshort;
  188. #define SKIP_BYTES( x ) \
  189. if ( fseek( fd, x, SEEK_CUR ) == -1 ) \
  190. { \
  191. fclose( fd ); \
  192. return( LoadIO ); \
  193. }
  194. #define READ_SHORT( x ) \
  195. if ( fread( &tmpshort, 2, 1, fd ) != 1 ) \
  196. { \
  197. fclose( fd ); \
  198. return( LoadIO ); \
  199. } \
  200. x = (unsigned short)swap16IfBE( tmpshort );
  201. #define READ_LONG( x ) \
  202. if ( fread( &x, 4, 1, fd ) != 1 ) \
  203. { \
  204. fclose( fd ); \
  205. return( LoadIO ); \
  206. } \
  207. x = (unsigned)swap32IfBE( x );
  208. // skip wave name, fractions
  209. SKIP_BYTES( 7 + 1 );
  210. unsigned data_length;
  211. READ_LONG( data_length );
  212. unsigned loop_start;
  213. READ_LONG( loop_start );
  214. unsigned loop_end;
  215. READ_LONG( loop_end );
  216. unsigned sample_rate;
  217. READ_SHORT( sample_rate );
  218. // skip low_freq, high_freq
  219. SKIP_BYTES( 4 + 4 );
  220. unsigned root_freq;
  221. READ_LONG( root_freq );
  222. // skip tuning, panning, envelope, tremolo, vibrato
  223. SKIP_BYTES( 2 + 1 + 12 + 3 + 3 );
  224. unsigned char modes;
  225. if ( fread( &modes, 1, 1, fd ) != 1 )
  226. {
  227. fclose( fd );
  228. return( LoadIO );
  229. }
  230. // skip scale frequency, scale factor, reserved space
  231. SKIP_BYTES( 2 + 2 + 36 );
  232. f_cnt_t frames;
  233. sample_t * wave_samples;
  234. if( modes & MODES_16BIT )
  235. {
  236. frames = data_length >> 1;
  237. wave_samples = new sample_t[frames];
  238. for( f_cnt_t frame = 0; frame < frames; ++frame )
  239. {
  240. short sample;
  241. if ( fread( &sample, 2, 1, fd ) != 1 )
  242. {
  243. delete[] wave_samples;
  244. fclose( fd );
  245. return( LoadIO );
  246. }
  247. sample = swap16IfBE( sample );
  248. if( modes & MODES_UNSIGNED )
  249. {
  250. sample ^= 0x8000;
  251. }
  252. wave_samples[frame] = sample / 32767.0f;
  253. }
  254. loop_start >>= 1;
  255. loop_end >>= 1;
  256. }
  257. else
  258. {
  259. frames = data_length;
  260. wave_samples = new sample_t[frames];
  261. for( f_cnt_t frame = 0; frame < frames; ++frame )
  262. {
  263. char sample;
  264. if ( fread( &sample, 1, 1, fd ) != 1 )
  265. {
  266. delete[] wave_samples;
  267. fclose( fd );
  268. return( LoadIO );
  269. }
  270. if( modes & MODES_UNSIGNED )
  271. {
  272. sample ^= 0x80;
  273. }
  274. wave_samples[frame] = sample / 127.0f;
  275. }
  276. }
  277. sampleFrame * data = new sampleFrame[frames];
  278. for( f_cnt_t frame = 0; frame < frames; ++frame )
  279. {
  280. for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS;
  281. ++chnl )
  282. {
  283. data[frame][chnl] = wave_samples[frame];
  284. }
  285. }
  286. SampleBuffer* psample = new SampleBuffer( data, frames );
  287. psample->setFrequency( root_freq / 1000.0f );
  288. psample->setSampleRate( sample_rate );
  289. if( modes & MODES_LOOPING )
  290. {
  291. psample->setLoopStartFrame( loop_start );
  292. psample->setLoopEndFrame( loop_end );
  293. }
  294. m_patchSamples.push_back( psample );
  295. delete[] wave_samples;
  296. delete[] data;
  297. }
  298. fclose( fd );
  299. return( LoadOK );
  300. }
  301. void patmanInstrument::unloadCurrentPatch( void )
  302. {
  303. while( !m_patchSamples.empty() )
  304. {
  305. sharedObject::unref( m_patchSamples.back() );
  306. m_patchSamples.pop_back();
  307. }
  308. }
  309. void patmanInstrument::selectSample( NotePlayHandle * _n )
  310. {
  311. const float freq = _n->frequency();
  312. float min_dist = HUGE_VALF;
  313. SampleBuffer* sample = nullptr;
  314. for( QVector<SampleBuffer *>::iterator it = m_patchSamples.begin(); it != m_patchSamples.end(); ++it )
  315. {
  316. float patch_freq = ( *it )->frequency();
  317. float dist = freq >= patch_freq ? freq / patch_freq :
  318. patch_freq / freq;
  319. if( dist < min_dist )
  320. {
  321. min_dist = dist;
  322. sample = *it;
  323. }
  324. }
  325. handle_data * hdata = new handle_data;
  326. hdata->tuned = m_tunedModel.value();
  327. if( sample )
  328. {
  329. hdata->sample = sharedObject::ref( sample );
  330. }
  331. else
  332. {
  333. hdata->sample = new SampleBuffer( nullptr, 0 );
  334. }
  335. hdata->state = new SampleBuffer::handleState( _n->hasDetuningInfo() );
  336. _n->m_pluginData = hdata;
  337. }
  338. PluginView * patmanInstrument::instantiateView( QWidget * _parent )
  339. {
  340. return( new PatmanView( this, _parent ) );
  341. }
  342. PatmanView::PatmanView( Instrument * _instrument, QWidget * _parent ) :
  343. InstrumentViewFixedSize( _instrument, _parent ),
  344. m_pi( nullptr )
  345. {
  346. setAutoFillBackground( true );
  347. QPalette pal;
  348. pal.setBrush( backgroundRole(),
  349. PLUGIN_NAME::getIconPixmap( "artwork" ) );
  350. setPalette( pal );
  351. m_openFileButton = new PixmapButton( this, nullptr );
  352. m_openFileButton->setObjectName( "openFileButton" );
  353. m_openFileButton->setCursor( QCursor( Qt::PointingHandCursor ) );
  354. m_openFileButton->move( 227, 86 );
  355. m_openFileButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  356. "select_file_on" ) );
  357. m_openFileButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  358. "select_file" ) );
  359. connect( m_openFileButton, SIGNAL( clicked() ),
  360. this, SLOT( openFile() ) );
  361. ToolTip::add( m_openFileButton, tr( "Open patch" ) );
  362. m_loopButton = new PixmapButton( this, tr( "Loop" ) );
  363. m_loopButton->setObjectName("loopButton");
  364. m_loopButton->setCheckable( true );
  365. m_loopButton->move( 195, 138 );
  366. m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  367. "loop_on" ) );
  368. m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  369. "loop_off" ) );
  370. ToolTip::add( m_loopButton, tr( "Loop mode" ) );
  371. m_tuneButton = new PixmapButton( this, tr( "Tune" ) );
  372. m_tuneButton->setObjectName("tuneButton");
  373. m_tuneButton->setCheckable( true );
  374. m_tuneButton->move( 223, 138 );
  375. m_tuneButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
  376. "tune_on" ) );
  377. m_tuneButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
  378. "tune_off" ) );
  379. ToolTip::add( m_tuneButton, tr( "Tune mode" ) );
  380. m_displayFilename = tr( "No file selected" );
  381. setAcceptDrops( true );
  382. }
  383. PatmanView::~PatmanView()
  384. {
  385. }
  386. void PatmanView::openFile( void )
  387. {
  388. FileDialog ofd( nullptr, tr( "Open patch file" ) );
  389. ofd.setFileMode( FileDialog::ExistingFiles );
  390. QStringList types;
  391. types << tr( "Patch-Files (*.pat)" );
  392. ofd.setNameFilters( types );
  393. if( m_pi->m_patchFile == "" )
  394. {
  395. if( QDir( "/usr/share/midi/freepats" ).exists() )
  396. {
  397. ofd.setDirectory( "/usr/share/midi/freepats" );
  398. }
  399. else
  400. {
  401. ofd.setDirectory(
  402. ConfigManager::inst()->userSamplesDir() );
  403. }
  404. }
  405. else if( QFileInfo( m_pi->m_patchFile ).isRelative() )
  406. {
  407. QString f = ConfigManager::inst()->userSamplesDir()
  408. + m_pi->m_patchFile;
  409. if( QFileInfo( f ).exists() == false )
  410. {
  411. f = ConfigManager::inst()->factorySamplesDir()
  412. + m_pi->m_patchFile;
  413. }
  414. ofd.selectFile( f );
  415. }
  416. else
  417. {
  418. ofd.selectFile( m_pi->m_patchFile );
  419. }
  420. if( ofd.exec() == QDialog::Accepted && !ofd.selectedFiles().isEmpty() )
  421. {
  422. QString f = ofd.selectedFiles()[0];
  423. if( f != "" )
  424. {
  425. m_pi->setFile( f );
  426. Engine::getSong()->setModified();
  427. }
  428. }
  429. }
  430. void PatmanView::updateFilename( void )
  431. {
  432. m_displayFilename = "";
  433. int idx = m_pi->m_patchFile.length();
  434. QFontMetrics fm( pointSize<8>( font() ) );
  435. // simple algorithm for creating a text from the filename that
  436. // matches in the white rectangle
  437. while( idx > 0 && fm.size( Qt::TextSingleLine,
  438. m_displayFilename + "..." ).width() < 225 )
  439. {
  440. m_displayFilename = m_pi->m_patchFile[--idx] +
  441. m_displayFilename;
  442. }
  443. if( idx > 0 )
  444. {
  445. m_displayFilename = "..." + m_displayFilename;
  446. }
  447. update();
  448. }
  449. void PatmanView::dragEnterEvent( QDragEnterEvent * _dee )
  450. {
  451. // For mimeType() and MimeType enum class
  452. using namespace Clipboard;
  453. if( _dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) )
  454. {
  455. QString txt = _dee->mimeData()->data(
  456. mimeType( MimeType::StringPair ) );
  457. if( txt.section( ':', 0, 0 ) == "samplefile" )
  458. {
  459. _dee->acceptProposedAction();
  460. }
  461. else
  462. {
  463. _dee->ignore();
  464. }
  465. }
  466. else
  467. {
  468. _dee->ignore();
  469. }
  470. }
  471. void PatmanView::dropEvent( QDropEvent * _de )
  472. {
  473. QString type = StringPairDrag::decodeKey( _de );
  474. QString value = StringPairDrag::decodeValue( _de );
  475. if( type == "samplefile" )
  476. {
  477. m_pi->setFile( value );
  478. _de->accept();
  479. return;
  480. }
  481. _de->ignore();
  482. }
  483. void PatmanView::paintEvent( QPaintEvent * )
  484. {
  485. QPainter p( this );
  486. p.setFont( pointSize<8>( font() ) );
  487. p.drawText( 8, 116, 235, 16,
  488. Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter,
  489. m_displayFilename );
  490. }
  491. void PatmanView::modelChanged( void )
  492. {
  493. m_pi = castModel<patmanInstrument>();
  494. m_loopButton->setModel( &m_pi->m_loopedModel );
  495. m_tuneButton->setModel( &m_pi->m_tunedModel );
  496. connect( m_pi, SIGNAL( fileChanged() ),
  497. this, SLOT( updateFilename() ) );
  498. }