OpulenZ.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. /*
  2. * OpulenZ.cpp - AdLib OPL2 FM synth based instrument
  3. *
  4. * Copyright (c) 2014 Raine M. Ekman <raine/at/iki/fi>
  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. // TODO:
  25. // - Better voice allocation: long releases get cut short :(
  26. // - RT safety = get rid of mutex = make emulator code thread-safe
  27. // - Extras:
  28. // - double release: first release is in effect until noteoff (heard if percussive sound),
  29. // second is switched in just before key bit cleared (is this useful???)
  30. // - Unison: 2,3,4, or 9 voices with configurable spread?
  31. // - Portamento (needs mono mode?)
  32. // - Pre-bend/post-bend in poly mode could use portamento speed?
  33. #include "OpulenZ.h"
  34. #include "Instrument.h"
  35. #include "Engine.h"
  36. #include "InstrumentPlayHandle.h"
  37. #include "InstrumentTrack.h"
  38. #include "Mixer.h"
  39. #include <QDomDocument>
  40. #include <QFile>
  41. #include <QFileInfo>
  42. #include <QByteArray>
  43. #include <assert.h>
  44. #include <math.h>
  45. #include "opl.h"
  46. #include "temuopl.h"
  47. #include "mididata.h"
  48. #include "embed.h"
  49. #include "debug.h"
  50. #include "Knob.h"
  51. #include "LcdSpinBox.h"
  52. #include "PixmapButton.h"
  53. #include "ToolTip.h"
  54. #include "plugin_export.h"
  55. extern "C"
  56. {
  57. Plugin::Descriptor PLUGIN_EXPORT opulenz_plugin_descriptor =
  58. {
  59. STRINGIFY( PLUGIN_NAME ),
  60. "OpulenZ",
  61. QT_TRANSLATE_NOOP( "pluginBrowser",
  62. "2-operator FM Synth" ),
  63. "Raine M. Ekman <raine/at/iki/fi>",
  64. 0x0100,
  65. Plugin::Instrument,
  66. new PluginPixmapLoader( "logo" ),
  67. "sbi",
  68. NULL
  69. };
  70. // necessary for getting instance out of shared lib
  71. PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * )
  72. {
  73. return( new OpulenzInstrument( static_cast<InstrumentTrack *>( m ) ) );
  74. }
  75. }
  76. // I'd much rather do without a mutex, but it looks like
  77. // the emulator code isn't really ready for threads
  78. QMutex OpulenzInstrument::emulatorMutex;
  79. // Weird ordering of voice parameters
  80. const unsigned int adlib_opadd[OPL2_VOICES] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};
  81. OpulenzInstrument::OpulenzInstrument( InstrumentTrack * _instrument_track ) :
  82. Instrument( _instrument_track, &opulenz_plugin_descriptor ),
  83. m_patchModel( 0, 0, 127, this, tr( "Patch" ) ),
  84. op1_a_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 attack" ) ),
  85. op1_d_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 decay" ) ),
  86. op1_s_mdl(3.0, 0.0, 15.0, 1.0, this, tr( "Op 1 sustain" ) ),
  87. op1_r_mdl(10.0, 0.0, 15.0, 1.0, this, tr( "Op 1 release" ) ),
  88. op1_lvl_mdl(62.0, 0.0, 63.0, 1.0, this, tr( "Op 1 level" ) ),
  89. op1_scale_mdl(0.0, 0.0, 3.0, 1.0, this, tr( "Op 1 level scaling" ) ),
  90. op1_mul_mdl(0.0, 0.0, 15.0, 1.0, this, tr( "Op 1 frequency multiplier" ) ),
  91. feedback_mdl(0.0, 0.0, 7.0, 1.0, this, tr( "Op 1 feedback" ) ),
  92. op1_ksr_mdl(false, this, tr( "Op 1 key scaling rate" ) ),
  93. op1_perc_mdl(false, this, tr( "Op 1 percussive envelope" ) ),
  94. op1_trem_mdl(true, this, tr( "Op 1 tremolo" ) ),
  95. op1_vib_mdl(false, this, tr( "Op 1 vibrato" ) ),
  96. op1_w0_mdl( ),
  97. op1_w1_mdl( ),
  98. op1_w2_mdl( ),
  99. op1_w3_mdl( ),
  100. op1_waveform_mdl(0,0,3,this, tr( "Op 1 waveform" ) ),
  101. op2_a_mdl(1.0, 0.0, 15.0, 1.0, this, tr( "Op 2 attack" ) ),
  102. op2_d_mdl(3.0, 0.0, 15.0, 1.0, this, tr( "Op 2 decay" ) ),
  103. op2_s_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 2 sustain" ) ),
  104. op2_r_mdl(12.0, 0.0, 15.0, 1.0, this, tr( "Op 2 release" ) ),
  105. op2_lvl_mdl(63.0, 0.0, 63.0, 1.0, this, tr( "Op 2 level" ) ),
  106. op2_scale_mdl(0.0, 0.0, 3.0, 1.0, this, tr( "Op 2 level scaling" ) ),
  107. op2_mul_mdl(1.0, 0.0, 15.0, 1.0, this, tr( "Op 2 frequency multiplier" ) ),
  108. op2_ksr_mdl(false, this, tr( "Op 2 key scaling rate" ) ),
  109. op2_perc_mdl(false, this, tr( "Op 2 percussive envelope" ) ),
  110. op2_trem_mdl(false, this, tr( "Op 2 tremolo" ) ),
  111. op2_vib_mdl(true, this, tr( "Op 2 vibrato" ) ),
  112. op2_w0_mdl( ),
  113. op2_w1_mdl( ),
  114. op2_w2_mdl( ),
  115. op2_w3_mdl( ),
  116. op2_waveform_mdl(0,0,3,this, tr( "Op 2 waveform" ) ),
  117. fm_mdl(true, this, tr( "FM" ) ),
  118. vib_depth_mdl(false, this, tr( "Vibrato depth" ) ),
  119. trem_depth_mdl(false, this, tr( "Tremolo depth" ) )
  120. {
  121. // Create an emulator - samplerate, 16 bit, mono
  122. emulatorMutex.lock();
  123. theEmulator = new CTemuopl(Engine::mixer()->processingSampleRate(), true, false);
  124. theEmulator->init();
  125. // Enable waveform selection
  126. theEmulator->write(0x01,0x20);
  127. emulatorMutex.unlock();
  128. //Initialize voice values
  129. // voiceNote[0] = 0;
  130. // voiceLRU[0] = 0;
  131. for(int i=0; i<OPL2_VOICES; ++i) {
  132. voiceNote[i] = OPL2_VOICE_FREE;
  133. voiceLRU[i] = i;
  134. }
  135. storedname = displayName();
  136. updatePatch();
  137. // Can the buffer size change suddenly? I bet that would break lots of stuff
  138. frameCount = Engine::mixer()->framesPerPeriod();
  139. renderbuffer = new short[frameCount];
  140. // Some kind of sane defaults
  141. pitchbend = 0;
  142. pitchBendRange = 100; // cents
  143. RPNcoarse = RPNfine = 255;
  144. tuneEqual(69, 440);
  145. connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
  146. this, SLOT( reloadEmulator() ) );
  147. // Connect knobs
  148. // This one's for testing...
  149. connect( &m_patchModel, SIGNAL( dataChanged() ), this, SLOT( loadGMPatch() ) );
  150. #define MOD_CON( model ) connect( &model, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) );
  151. MOD_CON( op1_a_mdl );
  152. MOD_CON( op1_d_mdl );
  153. MOD_CON( op1_s_mdl );
  154. MOD_CON( op1_r_mdl );
  155. MOD_CON( op1_lvl_mdl );
  156. MOD_CON( op1_scale_mdl );
  157. MOD_CON( op1_mul_mdl );
  158. MOD_CON( feedback_mdl );
  159. MOD_CON( op1_ksr_mdl );
  160. MOD_CON( op1_perc_mdl );
  161. MOD_CON( op1_trem_mdl );
  162. MOD_CON( op1_vib_mdl );
  163. MOD_CON( op1_w0_mdl );
  164. MOD_CON( op1_w1_mdl );
  165. MOD_CON( op1_w2_mdl );
  166. MOD_CON( op1_w3_mdl );
  167. MOD_CON( op1_waveform_mdl );
  168. MOD_CON( op2_a_mdl );
  169. MOD_CON( op2_d_mdl );
  170. MOD_CON( op2_s_mdl );
  171. MOD_CON( op2_r_mdl );
  172. MOD_CON( op2_lvl_mdl );
  173. MOD_CON( op2_scale_mdl );
  174. MOD_CON( op2_mul_mdl );
  175. MOD_CON( op2_ksr_mdl );
  176. MOD_CON( op2_perc_mdl );
  177. MOD_CON( op2_trem_mdl );
  178. MOD_CON( op2_vib_mdl );
  179. MOD_CON( op2_w0_mdl );
  180. MOD_CON( op2_w1_mdl );
  181. MOD_CON( op2_w2_mdl );
  182. MOD_CON( op2_w3_mdl );
  183. MOD_CON( op2_waveform_mdl );
  184. MOD_CON( fm_mdl );
  185. MOD_CON( vib_depth_mdl );
  186. MOD_CON( trem_depth_mdl );
  187. // Connect the plugin to the mixer...
  188. InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrument_track );
  189. Engine::mixer()->addPlayHandle( iph );
  190. }
  191. OpulenzInstrument::~OpulenzInstrument() {
  192. delete theEmulator;
  193. Engine::mixer()->removePlayHandlesOfTypes( instrumentTrack(),
  194. PlayHandle::TypeNotePlayHandle
  195. | PlayHandle::TypeInstrumentPlayHandle );
  196. delete [] renderbuffer;
  197. }
  198. // Samplerate changes when choosing oversampling, so this is more or less mandatory
  199. void OpulenzInstrument::reloadEmulator() {
  200. delete theEmulator;
  201. emulatorMutex.lock();
  202. theEmulator = new CTemuopl(Engine::mixer()->processingSampleRate(), true, false);
  203. theEmulator->init();
  204. theEmulator->write(0x01,0x20);
  205. emulatorMutex.unlock();
  206. for(int i=0; i<OPL2_VOICES; ++i) {
  207. voiceNote[i] = OPL2_VOICE_FREE;
  208. voiceLRU[i] = i;
  209. }
  210. updatePatch();
  211. }
  212. // This shall only be called from code protected by the holy Mutex!
  213. void OpulenzInstrument::setVoiceVelocity(int voice, int vel) {
  214. int vel_adjusted;
  215. // Velocity calculation, some kind of approximation
  216. // Only calculate for operator 1 if in adding mode, don't want to change timbre
  217. if( fm_mdl.value() == false ) {
  218. vel_adjusted = 63 - ( op1_lvl_mdl.value() * vel/127.0) ;
  219. } else {
  220. vel_adjusted = 63 - op1_lvl_mdl.value();
  221. }
  222. theEmulator->write(0x40+adlib_opadd[voice],
  223. ( (int)op1_scale_mdl.value() & 0x03 << 6) +
  224. ( vel_adjusted & 0x3f ) );
  225. vel_adjusted = 63 - ( op2_lvl_mdl.value() * vel/127.0 );
  226. // vel_adjusted = 63 - op2_lvl_mdl.value();
  227. theEmulator->write(0x43+adlib_opadd[voice],
  228. ( (int)op2_scale_mdl.value() & 0x03 << 6) +
  229. ( vel_adjusted & 0x3f ) );
  230. }
  231. // Pop least recently used voice
  232. int OpulenzInstrument::popVoice() {
  233. int tmp = voiceLRU[0];
  234. for( int i=0; i<OPL2_VOICES-1; ++i) {
  235. voiceLRU[i] = voiceLRU[i+1];
  236. }
  237. voiceLRU[OPL2_VOICES-1] = OPL2_NO_VOICE;
  238. #ifdef false
  239. printf("<-- %d %d %d %d %d %d %d %d %d \n", voiceLRU[0],voiceLRU[1],voiceLRU[2],
  240. voiceLRU[3],voiceLRU[4],voiceLRU[5],voiceLRU[6],voiceLRU[7],voiceLRU[8]);
  241. #endif
  242. return tmp;
  243. }
  244. // Push voice into first free slot
  245. int OpulenzInstrument::pushVoice(int v) {
  246. int i;
  247. assert(voiceLRU[OPL2_VOICES-1]==OPL2_NO_VOICE);
  248. for(i=OPL2_VOICES-1; i>0; --i) {
  249. if( voiceLRU[i-1] != OPL2_NO_VOICE ) {
  250. break;
  251. }
  252. }
  253. voiceLRU[i] = v;
  254. #ifdef false
  255. printf("%d %d %d %d %d %d %d %d %d <-- \n", voiceLRU[0],voiceLRU[1],voiceLRU[2],
  256. voiceLRU[3],voiceLRU[4],voiceLRU[5],voiceLRU[6],voiceLRU[7],voiceLRU[8]);
  257. #endif
  258. return i;
  259. }
  260. bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset )
  261. {
  262. emulatorMutex.lock();
  263. int key, vel, voice, tmp_pb;
  264. switch(event.type()) {
  265. case MidiNoteOn:
  266. // to get us in line with MIDI(?)
  267. key = event.key() +12;
  268. vel = event.velocity();
  269. voice = popVoice();
  270. if( voice != OPL2_NO_VOICE ) {
  271. // Turn voice on, NB! the frequencies are straight by voice number,
  272. // not by the adlib_opadd table!
  273. theEmulator->write(0xA0+voice, fnums[key] & 0xff);
  274. theEmulator->write(0xB0+voice, 32 + ((fnums[key] & 0x1f00) >> 8) );
  275. setVoiceVelocity(voice, vel);
  276. voiceNote[voice] = key;
  277. velocities[key] = vel;
  278. }
  279. break;
  280. case MidiNoteOff:
  281. key = event.key() +12;
  282. for(voice=0; voice<OPL2_VOICES; ++voice) {
  283. if( voiceNote[voice] == key ) {
  284. theEmulator->write(0xA0+voice, fnums[key] & 0xff);
  285. theEmulator->write(0xB0+voice, (fnums[key] & 0x1f00) >> 8 );
  286. voiceNote[voice] |= OPL2_VOICE_FREE;
  287. pushVoice(voice);
  288. }
  289. }
  290. velocities[key] = 0;
  291. break;
  292. case MidiKeyPressure:
  293. key = event.key() +12;
  294. vel = event.velocity();
  295. if( velocities[key] != 0) {
  296. velocities[key] = vel;
  297. }
  298. for(voice=0; voice<OPL2_VOICES; ++voice) {
  299. if(voiceNote[voice] == key) {
  300. setVoiceVelocity(voice, vel);
  301. }
  302. }
  303. break;
  304. case MidiPitchBend:
  305. // Update fnumber table
  306. // Neutral = 8192, full downbend = 0, full upbend = 16383
  307. tmp_pb = ( event.pitchBend()-8192 ) * pitchBendRange / 8192;
  308. if( tmp_pb != pitchbend ) {
  309. pitchbend = tmp_pb;
  310. tuneEqual(69, 440.0);
  311. }
  312. // Update pitch of all voices (also released ones)
  313. for( int v=0; v<OPL2_VOICES; ++v ) {
  314. int vn = (voiceNote[v] & ~OPL2_VOICE_FREE); // remove the flag bit
  315. int playing = (voiceNote[v] & OPL2_VOICE_FREE) == 0; // just the flag bit
  316. theEmulator->write(0xA0+v, fnums[vn] & 0xff);
  317. theEmulator->write(0xB0+v, (playing ? 32 : 0) + ((fnums[vn] & 0x1f00) >> 8) );
  318. }
  319. break;
  320. case MidiControlChange:
  321. switch (event.controllerNumber()) {
  322. case MidiControllerRegisteredParameterNumberLSB:
  323. RPNfine = event.controllerValue();
  324. break;
  325. case MidiControllerRegisteredParameterNumberMSB:
  326. RPNcoarse = event.controllerValue();
  327. break;
  328. case MidiControllerDataEntry:
  329. if( (RPNcoarse << 8) + RPNfine == MidiPitchBendSensitivityRPN) {
  330. pitchBendRange = event.controllerValue() * 100;
  331. }
  332. break;
  333. default:
  334. #ifdef LMMS_DEBUG
  335. printf("Midi CC %02x %02x\n", event.controllerNumber(), event.controllerValue() );
  336. #endif
  337. break;
  338. }
  339. break;
  340. default:
  341. #ifdef LMMS_DEBUG
  342. printf("Midi event type %d\n",event.type());
  343. #endif
  344. break;
  345. }
  346. emulatorMutex.unlock();
  347. return true;
  348. }
  349. QString OpulenzInstrument::nodeName() const
  350. {
  351. return( opulenz_plugin_descriptor.name );
  352. }
  353. PluginView * OpulenzInstrument::instantiateView( QWidget * _parent )
  354. {
  355. return( new OpulenzInstrumentView( this, _parent ) );
  356. }
  357. void OpulenzInstrument::play( sampleFrame * _working_buffer )
  358. {
  359. emulatorMutex.lock();
  360. theEmulator->update(renderbuffer, frameCount);
  361. for( fpp_t frame = 0; frame < frameCount; ++frame )
  362. {
  363. sample_t s = float(renderbuffer[frame]) / 8192.0;
  364. for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
  365. {
  366. _working_buffer[frame][ch] = s;
  367. }
  368. }
  369. emulatorMutex.unlock();
  370. // Throw the data to the track...
  371. instrumentTrack()->processAudioBuffer( _working_buffer, frameCount, NULL );
  372. }
  373. void OpulenzInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
  374. {
  375. op1_a_mdl.saveSettings( _doc, _this, "op1_a" );
  376. op1_d_mdl.saveSettings( _doc, _this, "op1_d" );
  377. op1_s_mdl.saveSettings( _doc, _this, "op1_s" );
  378. op1_r_mdl.saveSettings( _doc, _this, "op1_r" );
  379. op1_lvl_mdl.saveSettings( _doc, _this, "op1_lvl" );
  380. op1_scale_mdl.saveSettings( _doc, _this, "op1_scale" );
  381. op1_mul_mdl.saveSettings( _doc, _this, "op1_mul" );
  382. feedback_mdl.saveSettings( _doc, _this, "feedback" );
  383. op1_ksr_mdl.saveSettings( _doc, _this, "op1_ksr" );
  384. op1_perc_mdl.saveSettings( _doc, _this, "op1_perc" );
  385. op1_trem_mdl.saveSettings( _doc, _this, "op1_trem" );
  386. op1_vib_mdl.saveSettings( _doc, _this, "op1_vib" );
  387. op1_waveform_mdl.saveSettings( _doc, _this, "op1_waveform" );
  388. op2_a_mdl.saveSettings( _doc, _this, "op2_a" );
  389. op2_d_mdl.saveSettings( _doc, _this, "op2_d" );
  390. op2_s_mdl.saveSettings( _doc, _this, "op2_s" );
  391. op2_r_mdl.saveSettings( _doc, _this, "op2_r" );
  392. op2_lvl_mdl.saveSettings( _doc, _this, "op2_lvl" );
  393. op2_scale_mdl.saveSettings( _doc, _this, "op2_scale" );
  394. op2_mul_mdl.saveSettings( _doc, _this, "op2_mul" );
  395. op2_ksr_mdl.saveSettings( _doc, _this, "op2_ksr" );
  396. op2_perc_mdl.saveSettings( _doc, _this, "op2_perc" );
  397. op2_trem_mdl.saveSettings( _doc, _this, "op2_trem" );
  398. op2_vib_mdl.saveSettings( _doc, _this, "op2_vib" );
  399. op2_waveform_mdl.saveSettings( _doc, _this, "op2_waveform" );
  400. fm_mdl.saveSettings( _doc, _this, "fm" );
  401. vib_depth_mdl.saveSettings( _doc, _this, "vib_depth" );
  402. trem_depth_mdl.saveSettings( _doc, _this, "trem_depth" );
  403. }
  404. void OpulenzInstrument::loadSettings( const QDomElement & _this )
  405. {
  406. op1_a_mdl.loadSettings( _this, "op1_a" );
  407. op1_d_mdl.loadSettings( _this, "op1_d" );
  408. op1_s_mdl.loadSettings( _this, "op1_s" );
  409. op1_r_mdl.loadSettings( _this, "op1_r" );
  410. op1_lvl_mdl.loadSettings( _this, "op1_lvl" );
  411. op1_scale_mdl.loadSettings( _this, "op1_scale" );
  412. op1_mul_mdl.loadSettings( _this, "op1_mul" );
  413. feedback_mdl.loadSettings( _this, "feedback" );
  414. op1_ksr_mdl.loadSettings( _this, "op1_ksr" );
  415. op1_perc_mdl.loadSettings( _this, "op1_perc" );
  416. op1_trem_mdl.loadSettings( _this, "op1_trem" );
  417. op1_vib_mdl.loadSettings( _this, "op1_vib" );
  418. op1_waveform_mdl.loadSettings( _this, "op1_waveform" );
  419. op2_a_mdl.loadSettings( _this, "op2_a" );
  420. op2_d_mdl.loadSettings( _this, "op2_d" );
  421. op2_s_mdl.loadSettings( _this, "op2_s" );
  422. op2_r_mdl.loadSettings( _this, "op2_r" );
  423. op2_lvl_mdl.loadSettings( _this, "op2_lvl" );
  424. op2_scale_mdl.loadSettings( _this, "op2_scale" );
  425. op2_mul_mdl.loadSettings( _this, "op2_mul" );
  426. op2_ksr_mdl.loadSettings( _this, "op2_ksr" );
  427. op2_perc_mdl.loadSettings( _this, "op2_perc" );
  428. op2_trem_mdl.loadSettings( _this, "op2_trem" );
  429. op2_vib_mdl.loadSettings( _this, "op2_vib" );
  430. op2_waveform_mdl.loadSettings( _this, "op2_waveform" );
  431. fm_mdl.loadSettings( _this, "fm" );
  432. vib_depth_mdl.loadSettings( _this, "vib_depth" );
  433. trem_depth_mdl.loadSettings( _this, "trem_depth" );
  434. }
  435. // Load a patch into the emulator
  436. void OpulenzInstrument::loadPatch(const unsigned char inst[14]) {
  437. emulatorMutex.lock();
  438. for(int v=0; v<OPL2_VOICES; ++v) {
  439. theEmulator->write(0x20+adlib_opadd[v],inst[0]); // op1 AM/VIB/EG/KSR/Multiplier
  440. theEmulator->write(0x23+adlib_opadd[v],inst[1]); // op2
  441. // theEmulator->write(0x40+adlib_opadd[v],inst[2]); // op1 KSL/Output Level - these are handled by noteon/aftertouch code
  442. // theEmulator->write(0x43+adlib_opadd[v],inst[3]); // op2
  443. theEmulator->write(0x60+adlib_opadd[v],inst[4]); // op1 A/D
  444. theEmulator->write(0x63+adlib_opadd[v],inst[5]); // op2
  445. theEmulator->write(0x80+adlib_opadd[v],inst[6]); // op1 S/R
  446. theEmulator->write(0x83+adlib_opadd[v],inst[7]); // op2
  447. theEmulator->write(0xe0+adlib_opadd[v],inst[8]); // op1 waveform
  448. theEmulator->write(0xe3+adlib_opadd[v],inst[9]); // op2
  449. theEmulator->write(0xc0+v,inst[10]); // feedback/algorithm
  450. }
  451. emulatorMutex.unlock();
  452. }
  453. void OpulenzInstrument::tuneEqual(int center, float Hz) {
  454. float tmp;
  455. for(int n=0; n<128; ++n) {
  456. tmp = Hz*pow( 2.0, ( n - center ) * ( 1.0 / 12.0 ) + pitchbend * ( 1.0 / 1200.0 ) );
  457. fnums[n] = Hz2fnum( tmp );
  458. }
  459. }
  460. // Find suitable F number in lowest possible block
  461. int OpulenzInstrument::Hz2fnum(float Hz) {
  462. for(int block=0; block<8; ++block) {
  463. unsigned int fnum = Hz * pow( 2.0, 20.0 - (double)block ) * ( 1.0 / 49716.0 );
  464. if(fnum<1023) {
  465. return fnum + (block << 10);
  466. }
  467. }
  468. return 0;
  469. }
  470. // Load one of the default patches
  471. void OpulenzInstrument::loadGMPatch() {
  472. unsigned char *inst = midi_fm_instruments[m_patchModel.value()];
  473. loadPatch(inst);
  474. }
  475. // Update patch from the models to the chip emulation
  476. void OpulenzInstrument::updatePatch() {
  477. unsigned char inst[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  478. inst[0] = ( op1_trem_mdl.value() ? 128 : 0 ) +
  479. ( op1_vib_mdl.value() ? 64 : 0 ) +
  480. ( op1_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus"
  481. ( op1_ksr_mdl.value() ? 16 : 0 ) +
  482. ((int)op1_mul_mdl.value() & 0x0f);
  483. inst[1] = ( op2_trem_mdl.value() ? 128 : 0 ) +
  484. ( op2_vib_mdl.value() ? 64 : 0 ) +
  485. ( op2_perc_mdl.value() ? 0 : 32 ) + // NB. This envelope mode is "perc", not "sus"
  486. ( op2_ksr_mdl.value() ? 16 : 0 ) +
  487. ((int)op2_mul_mdl.value() & 0x0f);
  488. inst[2] = ( ((int)op1_scale_mdl.value() & 0x03) << 6 ) +
  489. (63 - ( (int)op1_lvl_mdl.value() & 0x3f ) );
  490. inst[3] = ( ((int)op2_scale_mdl.value() & 0x03) << 6 ) +
  491. (63 - ( (int)op2_lvl_mdl.value() & 0x3f ) );
  492. inst[4] = ((15 - ((int)op1_a_mdl.value() & 0x0f ) ) << 4 )+
  493. (15 - ( (int)op1_d_mdl.value() & 0x0f ) );
  494. inst[5] = ((15 - ( (int)op2_a_mdl.value() & 0x0f ) ) << 4 )+
  495. (15 - ( (int)op2_d_mdl.value() & 0x0f ) );
  496. inst[6] = ((15 - ( (int)op1_s_mdl.value() & 0x0f ) ) << 4 ) +
  497. (15 - ( (int)op1_r_mdl.value() & 0x0f ) );
  498. inst[7] = ((15 - ( (int)op2_s_mdl.value() & 0x0f ) ) << 4 ) +
  499. (15 - ( (int)op2_r_mdl.value() & 0x0f ) );
  500. inst[8] = (int)op1_waveform_mdl.value() & 0x03;
  501. inst[9] = (int)op2_waveform_mdl.value() & 0x03;
  502. inst[10] = (fm_mdl.value() ? 0 : 1 ) +
  503. (((int)feedback_mdl.value() & 0x07 )<< 1);
  504. // These are always 0 in the list I had?
  505. inst[11] = 0;
  506. inst[12] = 0;
  507. inst[13] = 0;
  508. // Not part of the per-voice patch info
  509. theEmulator->write(0xBD, (trem_depth_mdl.value() ? 128 : 0 ) +
  510. (vib_depth_mdl.value() ? 64 : 0 ));
  511. // have to do this, as the level knobs might've changed
  512. for( int voice = 0; voice < OPL2_VOICES ; ++voice) {
  513. if(voiceNote[voice] && OPL2_VOICE_FREE == 0) {
  514. setVoiceVelocity(voice, velocities[voiceNote[voice]] );
  515. }
  516. }
  517. #ifdef false
  518. printf("UPD: %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x\n",
  519. inst[0], inst[1], inst[2], inst[3], inst[4],
  520. inst[5], inst[6], inst[7], inst[8], inst[9], inst[10]);
  521. #endif
  522. loadPatch(inst);
  523. }
  524. // Load an SBI file into the knob models
  525. void OpulenzInstrument::loadFile( const QString& file ) {
  526. // http://cd.textfiles.com/soundsensations/SYNTH/SBINS/
  527. // http://cd.textfiles.com/soundsensations/SYNTH/SBI1198/1198SBI.ZIP
  528. if( !file.isEmpty() && QFileInfo( file ).exists() )
  529. {
  530. QFile sbifile(file);
  531. if (!sbifile.open(QIODevice::ReadOnly )) {
  532. printf("Can't open file\n");
  533. return;
  534. }
  535. QByteArray sbidata = sbifile.read(52);
  536. if( !sbidata.startsWith("SBI\0x1a") ) {
  537. printf("No SBI signature\n");
  538. return;
  539. }
  540. if( sbidata.size() != 52 ) {
  541. printf("SBI size error: expected 52, got %d\n",sbidata.size() );
  542. }
  543. // Minimum size of SBI if we ignore "reserved" bytes at end
  544. // https://courses.engr.illinois.edu/ece390/resources/sound/cmf.txt.html
  545. if( sbidata.size() < 47 ) {
  546. return;
  547. }
  548. QString sbiname = sbidata.mid(4, 32);
  549. // If user has changed track name... let's hope my logic is valid.
  550. if( sbiname.size() > 0 && instrumentTrack()->displayName() == storedname ) {
  551. instrumentTrack()->setName(sbiname);
  552. storedname = sbiname;
  553. }
  554. #ifdef false
  555. printf("SBI: %02x %02x %02x %02x %02x -- %02x %02x %02x %02x %02x %02x\n",
  556. (unsigned char)sbidata[36], (unsigned char)sbidata[37], (unsigned char)sbidata[38], (unsigned char)sbidata[39], (unsigned char)sbidata[40],
  557. (unsigned char)sbidata[41], (unsigned char)sbidata[42], (unsigned char)sbidata[43], (unsigned char)sbidata[44], (unsigned char)sbidata[45], (unsigned char)sbidata[46]);
  558. #endif
  559. // Modulator Sound Characteristic (Mult, KSR, EG, VIB, AM)
  560. op1_trem_mdl.setValue( (sbidata[36] & 0x80 ) == 0x80 ? true : false );
  561. op1_vib_mdl.setValue( (sbidata[36] & 0x40 ) == 0x40 ? true : false );
  562. op1_perc_mdl.setValue( (sbidata[36] & 0x20 ) == 0x20 ? false : true );
  563. op1_ksr_mdl.setValue( (sbidata[36] & 0x10 ) == 0x10 ? true : false );
  564. op1_mul_mdl.setValue( sbidata[36] & 0x0f );
  565. // Carrier Sound Characteristic
  566. op2_trem_mdl.setValue( (sbidata[37] & 0x80 ) == 0x80 ? true : false );
  567. op2_vib_mdl.setValue( (sbidata[37] & 0x40 ) == 0x40 ? true : false );
  568. op2_perc_mdl.setValue( (sbidata[37] & 0x20 ) == 0x20 ? false : true );
  569. op2_ksr_mdl.setValue( (sbidata[37] & 0x10 ) == 0x10 ? true : false );
  570. op2_mul_mdl.setValue( sbidata[37] & 0x0f );
  571. // Modulator Scaling/Output Level
  572. op1_scale_mdl.setValue( (sbidata[38] & 0xc0 ) >> 6 );
  573. op1_lvl_mdl.setValue( 63 - (sbidata[38] & 0x3f) );
  574. // Carrier Scaling/Output Level
  575. op2_scale_mdl.setValue( (sbidata[39] & 0xc0) >> 6 );
  576. op2_lvl_mdl.setValue( 63 - (sbidata[39] & 0x3f) );
  577. // Modulator Attack/Decay
  578. op1_a_mdl.setValue( 15 - ( ( sbidata[40] & 0xf0 ) >> 4 ) );
  579. op1_d_mdl.setValue( 15 - ( sbidata[40] & 0x0f ) );
  580. // Carrier Attack/Decay
  581. op2_a_mdl.setValue( 15 - ( ( sbidata[41] & 0xf0 ) >> 4 ) );
  582. op2_d_mdl.setValue( 15 - ( sbidata[41] & 0x0f ) );
  583. // Modulator Sustain/Release
  584. op1_s_mdl.setValue( 15 - ( ( sbidata[42] & 0xf0 ) >> 4 ) );
  585. op1_r_mdl.setValue( 15 - ( sbidata[42] & 0x0f ) );
  586. // Carrier Sustain/Release
  587. op2_s_mdl.setValue( 15 - ( ( sbidata[43] & 0xf0 ) >> 4 ) );
  588. op2_r_mdl.setValue( 15 - ( sbidata[43] & 0x0f ) );
  589. // Modulator Wave Select
  590. op1_waveform_mdl.setValue( sbidata[44] & 0x03 );
  591. // Carrier Wave Select
  592. op2_waveform_mdl.setValue( sbidata[45] & 0x03 );
  593. // Feedback/Connection
  594. fm_mdl.setValue( (sbidata[46] & 0x01) == 0x01 ? false : true );
  595. feedback_mdl.setValue( ( (sbidata[46] & 0x0e ) >> 1 ) );
  596. }
  597. }
  598. OpulenzInstrumentView::OpulenzInstrumentView( Instrument * _instrument,
  599. QWidget * _parent ) :
  600. InstrumentViewFixedSize( _instrument, _parent )
  601. {
  602. #define KNOB_GEN(knobname, hinttext, hintunit,xpos,ypos) \
  603. knobname = new Knob( knobStyled, this );\
  604. knobname->setHintText( tr(hinttext), hintunit );\
  605. knobname->setFixedSize(22,22);\
  606. knobname->setCenterPointX(11.0);\
  607. knobname->setCenterPointY(11.0);\
  608. knobname->setTotalAngle(270.0);\
  609. knobname->move(xpos,ypos);
  610. #define BUTTON_GEN(buttname, tooltip, xpos, ypos) \
  611. buttname = new PixmapButton( this, NULL );\
  612. buttname->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "led_on" ) );\
  613. buttname->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "led_off" ) );\
  614. buttname->setCheckable( true );\
  615. ToolTip::add( buttname, tr( tooltip ) );\
  616. buttname->move( xpos, ypos );
  617. #define WAVEBUTTON_GEN(buttname, tooltip, xpos, ypos, icon_on, icon_off, buttgroup) \
  618. buttname = new PixmapButton( this, NULL );\
  619. buttname->setActiveGraphic( PLUGIN_NAME::getIconPixmap( icon_on ) ); \
  620. buttname->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( icon_off ) ); \
  621. ToolTip::add( buttname, tr( tooltip ) );\
  622. buttname->move( xpos, ypos );\
  623. buttgroup->addButton(buttname);
  624. // OP1 knobs & buttons...
  625. KNOB_GEN(op1_a_kn, "Attack", "", 6, 48);
  626. KNOB_GEN(op1_d_kn, "Decay", "", 34, 48);
  627. KNOB_GEN(op1_s_kn, "Sustain", "", 62, 48);
  628. KNOB_GEN(op1_r_kn, "Release", "", 90, 48);
  629. KNOB_GEN(op1_lvl_kn, "Level", "", 166, 48);
  630. KNOB_GEN(op1_scale_kn, "Scale", "", 194, 48);
  631. KNOB_GEN(op1_mul_kn, "Frequency multiplier", "", 222, 48);
  632. BUTTON_GEN(op1_ksr_btn, "Keyboard scaling rate", 9, 87);
  633. BUTTON_GEN(op1_perc_btn, "Percussive envelope", 36, 87);
  634. BUTTON_GEN(op1_trem_btn, "Tremolo", 65, 87);
  635. BUTTON_GEN(op1_vib_btn, "Vibrato", 93, 87);
  636. KNOB_GEN(feedback_kn, "Feedback", "", 128, 48);
  637. op1_waveform = new automatableButtonGroup( this );
  638. WAVEBUTTON_GEN(op1_w0_btn,"Sine", 154, 86, "wave1_on", "wave1_off", op1_waveform);
  639. WAVEBUTTON_GEN(op1_w1_btn,"Half sine", 178, 86, "wave2_on", "wave2_off", op1_waveform);
  640. WAVEBUTTON_GEN(op1_w2_btn,"Absolute sine", 199, 86, "wave3_on", "wave3_off", op1_waveform);
  641. WAVEBUTTON_GEN(op1_w3_btn,"Quarter sine", 220, 86, "wave4_on", "wave4_off", op1_waveform);
  642. // And the same for OP2
  643. KNOB_GEN(op2_a_kn, "Attack", "", 6, 138);
  644. KNOB_GEN(op2_d_kn, "Decay", "", 34, 138);
  645. KNOB_GEN(op2_s_kn, "Sustain", "", 62, 138);
  646. KNOB_GEN(op2_r_kn, "Release", "", 90, 138);
  647. KNOB_GEN(op2_lvl_kn, "Level", "", 166, 138);
  648. KNOB_GEN(op2_scale_kn, "Scale", "", 194, 138);
  649. KNOB_GEN(op2_mul_kn, "Frequency multiplier", "", 222, 138);
  650. BUTTON_GEN(op2_ksr_btn, "Keyboard scaling rate", 9, 177);
  651. BUTTON_GEN(op2_perc_btn, "Percussive envelope", 36, 177);
  652. BUTTON_GEN(op2_trem_btn, "Tremolo", 65, 177);
  653. BUTTON_GEN(op2_vib_btn, "Vibrato", 93, 177);
  654. op2_waveform = new automatableButtonGroup( this );
  655. WAVEBUTTON_GEN(op2_w0_btn,"Sine", 154, 176, "wave1_on", "wave1_off", op2_waveform);
  656. WAVEBUTTON_GEN(op2_w1_btn,"Half sine", 178, 176, "wave2_on", "wave2_off", op2_waveform);
  657. WAVEBUTTON_GEN(op2_w2_btn,"Absolute sine", 199, 176, "wave3_on", "wave3_off", op2_waveform);
  658. WAVEBUTTON_GEN(op2_w3_btn,"Quarter Sine", 220, 176, "wave4_on", "wave4_off", op2_waveform);
  659. BUTTON_GEN(fm_btn, "FM", 9, 220);
  660. BUTTON_GEN(vib_depth_btn, "Vibrato depth", 65, 220);
  661. BUTTON_GEN(trem_depth_btn, "Tremolo depth", 93, 220);
  662. setAutoFillBackground( true );
  663. QPalette pal;
  664. pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap(
  665. "artwork" ) );
  666. setPalette( pal );
  667. }
  668. OpulenzInstrumentView::~OpulenzInstrumentView() {
  669. // Knobs are QWidgets and our children, so they're
  670. // destroyed automagically
  671. }
  672. // Returns text for time knob formatted nicely
  673. inline QString OpulenzInstrumentView::knobHintHelper(float n) {
  674. if(n>1000) {
  675. return QString::number(n/1000, 'f', 0)+ " s";
  676. } else if(n>10) {
  677. return QString::number(n, 'f', 0)+ " ms";
  678. } else {
  679. return QString::number(n, 'f', 1)+ " ms";
  680. }
  681. }
  682. void OpulenzInstrumentView::updateKnobHints()
  683. {
  684. // Envelope times in ms: t[0] = 0, t[n] = ( 1<<n ) * X, X = 0.11597 for A, 0.6311 for D/R
  685. // Here some rounding has been applied.
  686. const float attack_times[16] = {
  687. 0.0, 0.2, 0.4, 0.9, 1.8, 3.7, 7.4,
  688. 15.0, 30.0, 60.0, 120.0, 240.0, 480.0,
  689. 950.0, 1900.0, 3800.0
  690. };
  691. const float dr_times[16] = {
  692. 0.0, 1.2, 2.5, 5.0, 10.0, 20.0, 40.0,
  693. 80.0, 160.0, 320.0, 640.0, 1300.0, 2600.0,
  694. 5200.0, 10000.0, 20000.0
  695. };
  696. const int fmultipliers[16] = {
  697. -12, 0, 12, 19, 24, 28, 31, 34, 36, 38, 40, 40, 43, 43, 47, 47
  698. };
  699. OpulenzInstrument * m = castModel<OpulenzInstrument>();
  700. op1_a_kn->setHintText( tr( "Attack" ),
  701. " (" + knobHintHelper(attack_times[(int)m->op1_a_mdl.value()]) + ")");
  702. op2_a_kn->setHintText( tr( "Attack" ),
  703. " (" + knobHintHelper(attack_times[(int)m->op2_a_mdl.value()]) + ")");
  704. op1_d_kn->setHintText( tr( "Decay" ),
  705. " (" + knobHintHelper(dr_times[(int)m->op1_d_mdl.value()]) + ")");
  706. op2_d_kn->setHintText( tr( "Decay" ),
  707. " (" + knobHintHelper(dr_times[(int)m->op2_d_mdl.value()]) + ")");
  708. op1_r_kn->setHintText( tr( "Release" ),
  709. " (" + knobHintHelper(dr_times[(int)m->op1_r_mdl.value()]) + ")");
  710. op2_r_kn->setHintText( tr( "Release" ),
  711. " (" + knobHintHelper(dr_times[(int)m->op2_r_mdl.value()]) + ")");
  712. op1_mul_kn->setHintText( tr( "Frequency multiplier" ),
  713. " (" + QString::number(fmultipliers[(int)m->op1_mul_mdl.value()]) + " semitones)");
  714. op2_mul_kn->setHintText( tr( "Frequency multiplier" ),
  715. " (" + QString::number(fmultipliers[(int)m->op2_mul_mdl.value()]) + " semitones)");
  716. }
  717. void OpulenzInstrumentView::modelChanged()
  718. {
  719. OpulenzInstrument * m = castModel<OpulenzInstrument>();
  720. // m_patch->setModel( &m->m_patchModel );
  721. op1_a_kn->setModel( &m->op1_a_mdl );
  722. op1_d_kn->setModel( &m->op1_d_mdl );
  723. op1_s_kn->setModel( &m->op1_s_mdl );
  724. op1_r_kn->setModel( &m->op1_r_mdl );
  725. op1_lvl_kn->setModel( &m->op1_lvl_mdl );
  726. op1_scale_kn->setModel( &m->op1_scale_mdl );
  727. op1_mul_kn->setModel( &m->op1_mul_mdl );
  728. feedback_kn->setModel( &m->feedback_mdl );
  729. op1_ksr_btn->setModel( &m->op1_ksr_mdl );
  730. op1_perc_btn->setModel( &m->op1_perc_mdl );
  731. op1_trem_btn->setModel( &m->op1_trem_mdl );
  732. op1_vib_btn->setModel( &m->op1_vib_mdl );
  733. op1_waveform->setModel( &m->op1_waveform_mdl );
  734. op2_a_kn->setModel( &m->op2_a_mdl );
  735. op2_d_kn->setModel( &m->op2_d_mdl );
  736. op2_s_kn->setModel( &m->op2_s_mdl );
  737. op2_r_kn->setModel( &m->op2_r_mdl );
  738. op2_lvl_kn->setModel( &m->op2_lvl_mdl );
  739. op2_scale_kn->setModel( &m->op2_scale_mdl );
  740. op2_mul_kn->setModel( &m->op2_mul_mdl );
  741. op2_ksr_btn->setModel( &m->op2_ksr_mdl );
  742. op2_perc_btn->setModel( &m->op2_perc_mdl );
  743. op2_trem_btn->setModel( &m->op2_trem_mdl );
  744. op2_vib_btn->setModel( &m->op2_vib_mdl );
  745. op2_waveform->setModel( &m->op2_waveform_mdl );
  746. fm_btn->setModel( &m->fm_mdl );
  747. vib_depth_btn->setModel( &m->vib_depth_mdl );
  748. trem_depth_btn->setModel( &m->trem_depth_mdl );
  749. // All knobs needing a user friendly unit
  750. connect( &m->op1_a_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  751. connect( &m->op2_a_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  752. connect( &m->op1_d_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  753. connect( &m->op2_d_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  754. connect( &m->op1_r_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  755. connect( &m->op2_r_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  756. connect( &m->op1_mul_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  757. connect( &m->op2_mul_mdl, SIGNAL( dataChanged() ), this, SLOT( updateKnobHints() ) );
  758. updateKnobHints();
  759. }