1 /*
2 * TripleOscillator.cpp - powerful instrument with three oscillators
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
25
26 #include <QDomDocument>
27 #include <QBitmap>
28 #include <QPainter>
29
30 #include "TripleOscillator.h"
31 #include "AutomatableButton.h"
32 #include "debug.h"
33 #include "Engine.h"
34 #include "InstrumentTrack.h"
35 #include "Knob.h"
36 #include "Mixer.h"
37 #include "NotePlayHandle.h"
38 #include "PixmapButton.h"
39 #include "SampleBuffer.h"
40 #include "ToolTip.h"
41
42 #include "embed.cpp"
43
44
45 extern "C"
46 {
47
48 Plugin::Descriptor PLUGIN_EXPORT tripleoscillator_plugin_descriptor =
49 {
50 STRINGIFY( PLUGIN_NAME ),
51 "TripleOscillator",
52 QT_TRANSLATE_NOOP( "pluginBrowser",
53 "Three powerful oscillators you can modulate "
54 "in several ways" ),
55 "Tobias Doerffel <tobydox/at/users.sf.net>",
56 0x0110,
57 Plugin::Instrument,
58 new PluginPixmapLoader( "logo" ),
59 NULL,
60 NULL
61 } ;
62
63 }
64
65
66
OscillatorObject(Model * _parent,int _idx)67 OscillatorObject::OscillatorObject( Model * _parent, int _idx ) :
68 Model( _parent ),
69 m_volumeModel( DefaultVolume / NUM_OF_OSCILLATORS, MinVolume,
70 MaxVolume, 1.0f, this, tr( "Osc %1 volume" ).arg( _idx+1 ) ),
71 m_panModel( DefaultPanning, PanningLeft, PanningRight, 1.0f, this,
72 tr( "Osc %1 panning" ).arg( _idx+1 ) ),
73 m_coarseModel( -_idx*KeysPerOctave,
74 -2 * KeysPerOctave, 2 * KeysPerOctave, 1.0f, this,
75 tr( "Osc %1 coarse detuning" ).arg( _idx+1 ) ),
76 m_fineLeftModel( 0.0f, -100.0f, 100.0f, 1.0f, this,
77 tr( "Osc %1 fine detuning left" ).arg( _idx+1 ) ),
78 m_fineRightModel( 0.0f, -100.0f, 100.0f, 1.0f, this,
79 tr( "Osc %1 fine detuning right" ).arg( _idx + 1 ) ),
80 m_phaseOffsetModel( 0.0f, 0.0f, 360.0f, 1.0f, this,
81 tr( "Osc %1 phase-offset" ).arg( _idx+1 ) ),
82 m_stereoPhaseDetuningModel( 0.0f, 0.0f, 360.0f, 1.0f, this,
83 tr( "Osc %1 stereo phase-detuning" ).arg( _idx+1 ) ),
84 m_waveShapeModel( Oscillator::SineWave, 0,
85 Oscillator::NumWaveShapes-1, this,
86 tr( "Osc %1 wave shape" ).arg( _idx+1 ) ),
87 m_modulationAlgoModel( Oscillator::SignalMix, 0,
88 Oscillator::NumModulationAlgos-1, this,
89 tr( "Modulation type %1" ).arg( _idx+1 ) ),
90
91 m_sampleBuffer( new SampleBuffer ),
92 m_volumeLeft( 0.0f ),
93 m_volumeRight( 0.0f ),
94 m_detuningLeft( 0.0f ),
95 m_detuningRight( 0.0f ),
96 m_phaseOffsetLeft( 0.0f ),
97 m_phaseOffsetRight( 0.0f )
98 {
99 // Connect knobs with Oscillators' inputs
100 connect( &m_volumeModel, SIGNAL( dataChanged() ),
101 this, SLOT( updateVolume() ) );
102 connect( &m_panModel, SIGNAL( dataChanged() ),
103 this, SLOT( updateVolume() ) );
104 updateVolume();
105
106 connect( &m_coarseModel, SIGNAL( dataChanged() ),
107 this, SLOT( updateDetuningLeft() ) );
108 connect( &m_coarseModel, SIGNAL( dataChanged() ),
109 this, SLOT( updateDetuningRight() ) );
110 connect( &m_fineLeftModel, SIGNAL( dataChanged() ),
111 this, SLOT( updateDetuningLeft() ) );
112 connect( &m_fineRightModel, SIGNAL( dataChanged() ),
113 this, SLOT( updateDetuningRight() ) );
114 updateDetuningLeft();
115 updateDetuningRight();
116
117 connect( &m_phaseOffsetModel, SIGNAL( dataChanged() ),
118 this, SLOT( updatePhaseOffsetLeft() ) );
119 connect( &m_phaseOffsetModel, SIGNAL( dataChanged() ),
120 this, SLOT( updatePhaseOffsetRight() ) );
121 connect( &m_stereoPhaseDetuningModel, SIGNAL( dataChanged() ),
122 this, SLOT( updatePhaseOffsetLeft() ) );
123 updatePhaseOffsetLeft();
124 updatePhaseOffsetRight();
125
126 }
127
128
129
130
~OscillatorObject()131 OscillatorObject::~OscillatorObject()
132 {
133 sharedObject::unref( m_sampleBuffer );
134 }
135
136
137
138
oscUserDefWaveDblClick()139 void OscillatorObject::oscUserDefWaveDblClick()
140 {
141 QString af = m_sampleBuffer->openAndSetWaveformFile();
142 if( af != "" )
143 {
144 // TODO:
145 //ToolTip::add( m_usrWaveBtn, m_sampleBuffer->audioFile() );
146 }
147 }
148
149
150
151
updateVolume()152 void OscillatorObject::updateVolume()
153 {
154 if( m_panModel.value() >= 0.0f )
155 {
156 const float panningFactorLeft = 1.0f - m_panModel.value()
157 / (float)PanningRight;
158 m_volumeLeft = panningFactorLeft * m_volumeModel.value() /
159 100.0f;
160 m_volumeRight = m_volumeModel.value() / 100.0f;
161 }
162 else
163 {
164 m_volumeLeft = m_volumeModel.value() / 100.0f;
165 const float panningFactorRight = 1.0f + m_panModel.value()
166 / (float)PanningRight;
167 m_volumeRight = panningFactorRight * m_volumeModel.value() /
168 100.0f;
169 }
170 }
171
172
173
174
updateDetuningLeft()175 void OscillatorObject::updateDetuningLeft()
176 {
177 m_detuningLeft = powf( 2.0f, ( (float)m_coarseModel.value() * 100.0f
178 + (float)m_fineLeftModel.value() ) / 1200.0f )
179 / Engine::mixer()->processingSampleRate();
180 }
181
182
183
184
updateDetuningRight()185 void OscillatorObject::updateDetuningRight()
186 {
187 m_detuningRight = powf( 2.0f, ( (float)m_coarseModel.value() * 100.0f
188 + (float)m_fineRightModel.value() ) / 1200.0f )
189 / Engine::mixer()->processingSampleRate();
190 }
191
192
193
194
updatePhaseOffsetLeft()195 void OscillatorObject::updatePhaseOffsetLeft()
196 {
197 m_phaseOffsetLeft = ( m_phaseOffsetModel.value() +
198 m_stereoPhaseDetuningModel.value() ) / 360.0f;
199 }
200
201
202
203
updatePhaseOffsetRight()204 void OscillatorObject::updatePhaseOffsetRight()
205 {
206 m_phaseOffsetRight = m_phaseOffsetModel.value() / 360.0f;
207 }
208
209
210
211
TripleOscillator(InstrumentTrack * _instrument_track)212 TripleOscillator::TripleOscillator( InstrumentTrack * _instrument_track ) :
213 Instrument( _instrument_track, &tripleoscillator_plugin_descriptor )
214 {
215 for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
216 {
217 m_osc[i] = new OscillatorObject( this, i );
218
219 }
220
221 connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
222 this, SLOT( updateAllDetuning() ) );
223 }
224
225
226
227
~TripleOscillator()228 TripleOscillator::~TripleOscillator()
229 {
230 }
231
232
233
234
saveSettings(QDomDocument & _doc,QDomElement & _this)235 void TripleOscillator::saveSettings( QDomDocument & _doc, QDomElement & _this )
236 {
237 for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
238 {
239 QString is = QString::number( i );
240 m_osc[i]->m_volumeModel.saveSettings( _doc, _this, "vol" + is );
241 m_osc[i]->m_panModel.saveSettings( _doc, _this, "pan" + is );
242 m_osc[i]->m_coarseModel.saveSettings( _doc, _this, "coarse"
243 + is );
244 m_osc[i]->m_fineLeftModel.saveSettings( _doc, _this, "finel" +
245 is );
246 m_osc[i]->m_fineRightModel.saveSettings( _doc, _this, "finer" +
247 is );
248 m_osc[i]->m_phaseOffsetModel.saveSettings( _doc, _this,
249 "phoffset" + is );
250 m_osc[i]->m_stereoPhaseDetuningModel.saveSettings( _doc, _this,
251 "stphdetun" + is );
252 m_osc[i]->m_waveShapeModel.saveSettings( _doc, _this,
253 "wavetype" + is );
254 m_osc[i]->m_modulationAlgoModel.saveSettings( _doc, _this,
255 "modalgo" + QString::number( i+1 ) );
256 _this.setAttribute( "userwavefile" + is,
257 m_osc[i]->m_sampleBuffer->audioFile() );
258 }
259 }
260
261
262
263
loadSettings(const QDomElement & _this)264 void TripleOscillator::loadSettings( const QDomElement & _this )
265 {
266 for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
267 {
268 const QString is = QString::number( i );
269 m_osc[i]->m_volumeModel.loadSettings( _this, "vol" + is );
270 m_osc[i]->m_panModel.loadSettings( _this, "pan" + is );
271 m_osc[i]->m_coarseModel.loadSettings( _this, "coarse" + is );
272 m_osc[i]->m_fineLeftModel.loadSettings( _this, "finel" + is );
273 m_osc[i]->m_fineRightModel.loadSettings( _this, "finer" + is );
274 m_osc[i]->m_phaseOffsetModel.loadSettings( _this,
275 "phoffset" + is );
276 m_osc[i]->m_stereoPhaseDetuningModel.loadSettings( _this,
277 "stphdetun" + is );
278 m_osc[i]->m_waveShapeModel.loadSettings( _this, "wavetype" +
279 is );
280 m_osc[i]->m_modulationAlgoModel.loadSettings( _this,
281 "modalgo" + QString::number( i+1 ) );
282 m_osc[i]->m_sampleBuffer->setAudioFile( _this.attribute(
283 "userwavefile" + is ) );
284 }
285 }
286
287
288
289
nodeName() const290 QString TripleOscillator::nodeName() const
291 {
292 return( tripleoscillator_plugin_descriptor.name );
293 }
294
295
296
297
playNote(NotePlayHandle * _n,sampleFrame * _working_buffer)298 void TripleOscillator::playNote( NotePlayHandle * _n,
299 sampleFrame * _working_buffer )
300 {
301 if( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
302 {
303 Oscillator * oscs_l[NUM_OF_OSCILLATORS];
304 Oscillator * oscs_r[NUM_OF_OSCILLATORS];
305
306 for( int i = NUM_OF_OSCILLATORS - 1; i >= 0; --i )
307 {
308
309 // the last oscs needs no sub-oscs...
310 if( i == NUM_OF_OSCILLATORS - 1 )
311 {
312 oscs_l[i] = new Oscillator(
313 &m_osc[i]->m_waveShapeModel,
314 &m_osc[i]->m_modulationAlgoModel,
315 _n->frequency(),
316 m_osc[i]->m_detuningLeft,
317 m_osc[i]->m_phaseOffsetLeft,
318 m_osc[i]->m_volumeLeft );
319 oscs_r[i] = new Oscillator(
320 &m_osc[i]->m_waveShapeModel,
321 &m_osc[i]->m_modulationAlgoModel,
322 _n->frequency(),
323 m_osc[i]->m_detuningRight,
324 m_osc[i]->m_phaseOffsetRight,
325 m_osc[i]->m_volumeRight );
326 }
327 else
328 {
329 oscs_l[i] = new Oscillator(
330 &m_osc[i]->m_waveShapeModel,
331 &m_osc[i]->m_modulationAlgoModel,
332 _n->frequency(),
333 m_osc[i]->m_detuningLeft,
334 m_osc[i]->m_phaseOffsetLeft,
335 m_osc[i]->m_volumeLeft,
336 oscs_l[i + 1] );
337 oscs_r[i] = new Oscillator(
338 &m_osc[i]->m_waveShapeModel,
339 &m_osc[i]->m_modulationAlgoModel,
340 _n->frequency(),
341 m_osc[i]->m_detuningRight,
342 m_osc[i]->m_phaseOffsetRight,
343 m_osc[i]->m_volumeRight,
344 oscs_r[i + 1] );
345 }
346
347 oscs_l[i]->setUserWave( m_osc[i]->m_sampleBuffer );
348 oscs_r[i]->setUserWave( m_osc[i]->m_sampleBuffer );
349
350 }
351
352 _n->m_pluginData = new oscPtr;
353 static_cast<oscPtr *>( _n->m_pluginData )->oscLeft = oscs_l[0];
354 static_cast< oscPtr *>( _n->m_pluginData )->oscRight =
355 oscs_r[0];
356 }
357
358 Oscillator * osc_l = static_cast<oscPtr *>( _n->m_pluginData )->oscLeft;
359 Oscillator * osc_r = static_cast<oscPtr *>( _n->m_pluginData )->oscRight;
360
361 const fpp_t frames = _n->framesLeftForCurrentPeriod();
362 const f_cnt_t offset = _n->noteOffset();
363
364 osc_l->update( _working_buffer + offset, frames, 0 );
365 osc_r->update( _working_buffer + offset, frames, 1 );
366
367 applyRelease( _working_buffer, _n );
368
369 instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
370 }
371
372
373
374
deleteNotePluginData(NotePlayHandle * _n)375 void TripleOscillator::deleteNotePluginData( NotePlayHandle * _n )
376 {
377 delete static_cast<Oscillator *>( static_cast<oscPtr *>(
378 _n->m_pluginData )->oscLeft );
379 delete static_cast<Oscillator *>( static_cast<oscPtr *>(
380 _n->m_pluginData )->oscRight );
381 delete static_cast<oscPtr *>( _n->m_pluginData );
382 }
383
384
385
386
instantiateView(QWidget * _parent)387 PluginView * TripleOscillator::instantiateView( QWidget * _parent )
388 {
389 return new TripleOscillatorView( this, _parent );
390 }
391
392
393
394
updateAllDetuning()395 void TripleOscillator::updateAllDetuning()
396 {
397 for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
398 {
399 m_osc[i]->updateDetuningLeft();
400 m_osc[i]->updateDetuningRight();
401 }
402 }
403
404
405
406
407 class TripleOscKnob : public Knob
408 {
409 public:
TripleOscKnob(QWidget * _parent)410 TripleOscKnob( QWidget * _parent ) :
411 Knob( knobStyled, _parent )
412 {
413 setFixedSize( 28, 35 );
414 }
415 };
416
417 // 82, 109
418
419
TripleOscillatorView(Instrument * _instrument,QWidget * _parent)420 TripleOscillatorView::TripleOscillatorView( Instrument * _instrument,
421 QWidget * _parent ) :
422 InstrumentView( _instrument, _parent )
423 {
424 setAutoFillBackground( true );
425 QPalette pal;
426 pal.setBrush( backgroundRole(),
427 PLUGIN_NAME::getIconPixmap( "artwork" ) );
428 setPalette( pal );
429
430 const int mod_x = 66;
431 const int mod1_y = 58;
432 const int mod2_y = 75;
433 const int osc_y = 109;
434 const int osc_h = 52;
435
436 // TODO: clean rewrite using layouts and all that...
437 PixmapButton * pm_osc1_btn = new PixmapButton( this, NULL );
438 pm_osc1_btn->move( mod_x, mod1_y );
439 pm_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
440 "pm_active" ) );
441 pm_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
442 "pm_inactive" ) );
443 ToolTip::add( pm_osc1_btn, tr( "Use phase modulation for "
444 "modulating oscillator 1 with "
445 "oscillator 2" ) );
446
447 PixmapButton * am_osc1_btn = new PixmapButton( this, NULL );
448 am_osc1_btn->move( mod_x + 35, mod1_y );
449 am_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
450 "am_active" ) );
451 am_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
452 "am_inactive" ) );
453 ToolTip::add( am_osc1_btn, tr( "Use amplitude modulation for "
454 "modulating oscillator 1 with "
455 "oscillator 2" ) );
456
457 PixmapButton * mix_osc1_btn = new PixmapButton( this, NULL );
458 mix_osc1_btn->move( mod_x + 70, mod1_y );
459 mix_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
460 "mix_active" ) );
461 mix_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
462 "mix_inactive" ) );
463 ToolTip::add( mix_osc1_btn, tr( "Mix output of oscillator 1 & 2" ) );
464
465 PixmapButton * sync_osc1_btn = new PixmapButton( this, NULL );
466 sync_osc1_btn->move( mod_x + 105, mod1_y );
467 sync_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
468 "sync_active" ) );
469 sync_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
470 "sync_inactive" ) );
471 ToolTip::add( sync_osc1_btn, tr( "Synchronize oscillator 1 with "
472 "oscillator 2" ) );
473
474 PixmapButton * fm_osc1_btn = new PixmapButton( this, NULL );
475 fm_osc1_btn->move( mod_x + 140, mod1_y );
476 fm_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
477 "fm_active" ) );
478 fm_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
479 "fm_inactive" ) );
480 ToolTip::add( fm_osc1_btn, tr( "Use frequency modulation for "
481 "modulating oscillator 1 with "
482 "oscillator 2" ) );
483
484 m_mod1BtnGrp = new automatableButtonGroup( this );
485 m_mod1BtnGrp->addButton( pm_osc1_btn );
486 m_mod1BtnGrp->addButton( am_osc1_btn );
487 m_mod1BtnGrp->addButton( mix_osc1_btn );
488 m_mod1BtnGrp->addButton( sync_osc1_btn );
489 m_mod1BtnGrp->addButton( fm_osc1_btn );
490
491
492
493 PixmapButton * pm_osc2_btn = new PixmapButton( this, NULL );
494 pm_osc2_btn->move( mod_x, mod2_y );
495 pm_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
496 "pm_active" ) );
497 pm_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
498 "pm_inactive" ) );
499 ToolTip::add( pm_osc2_btn, tr( "Use phase modulation for "
500 "modulating oscillator 2 with "
501 "oscillator 3" ) );
502
503 PixmapButton * am_osc2_btn = new PixmapButton( this, NULL );
504 am_osc2_btn->move( mod_x + 35, mod2_y );
505 am_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
506 "am_active" ) );
507 am_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
508 "am_inactive" ) );
509 ToolTip::add( am_osc2_btn, tr( "Use amplitude modulation for "
510 "modulating oscillator 2 with "
511 "oscillator 3" ) );
512
513 PixmapButton * mix_osc2_btn = new PixmapButton( this, NULL );
514 mix_osc2_btn->move( mod_x + 70, mod2_y );
515 mix_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
516 "mix_active" ) );
517 mix_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
518 "mix_inactive" ) );
519 ToolTip::add( mix_osc2_btn, tr("Mix output of oscillator 2 & 3" ) );
520
521 PixmapButton * sync_osc2_btn = new PixmapButton( this, NULL );
522 sync_osc2_btn->move( mod_x + 105, mod2_y );
523 sync_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
524 "sync_active" ) );
525 sync_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
526 "sync_inactive" ) );
527 ToolTip::add( sync_osc2_btn, tr( "Synchronize oscillator 2 with "
528 "oscillator 3" ) );
529
530 PixmapButton * fm_osc2_btn = new PixmapButton( this, NULL );
531 fm_osc2_btn->move( mod_x + 140, mod2_y );
532 fm_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
533 "fm_active" ) );
534 fm_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
535 "fm_inactive" ) );
536 ToolTip::add( fm_osc2_btn, tr( "Use frequency modulation for "
537 "modulating oscillator 2 with "
538 "oscillator 3" ) );
539
540 m_mod2BtnGrp = new automatableButtonGroup( this );
541
542 m_mod2BtnGrp->addButton( pm_osc2_btn );
543 m_mod2BtnGrp->addButton( am_osc2_btn );
544 m_mod2BtnGrp->addButton( mix_osc2_btn );
545 m_mod2BtnGrp->addButton( sync_osc2_btn );
546 m_mod2BtnGrp->addButton( fm_osc2_btn );
547
548
549 for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
550 {
551 int knob_y = osc_y + i * osc_h;
552
553 // setup volume-knob
554 Knob * vk = new Knob( knobStyled, this );
555 vk->setVolumeKnob( true );
556 vk->setFixedSize( 28, 35 );
557 vk->move( 6, knob_y );
558 vk->setHintText( tr( "Osc %1 volume:" ).arg(
559 i+1 ), "%" );
560 vk->setWhatsThis(
561 tr( "With this knob you can set the volume of "
562 "oscillator %1. When setting a value of 0 the "
563 "oscillator is turned off. Otherwise you can "
564 "hear the oscillator as loud as you set it "
565 "here.").arg( i+1 ) );
566
567 // setup panning-knob
568 Knob * pk = new TripleOscKnob( this );
569 pk->move( 35, knob_y );
570 pk->setHintText( tr("Osc %1 panning:").arg( i + 1 ), "" );
571 pk->setWhatsThis(
572 tr( "With this knob you can set the panning of the "
573 "oscillator %1. A value of -100 means 100% "
574 "left and a value of 100 moves oscillator-"
575 "output right.").arg( i+1 ) );
576
577 // setup coarse-knob
578 Knob * ck = new TripleOscKnob( this );
579 ck->move( 82, knob_y );
580 ck->setHintText( tr( "Osc %1 coarse detuning:" ).arg( i + 1 )
581 , " " + tr( "semitones" ) );
582 ck->setWhatsThis(
583 tr( "With this knob you can set the coarse detuning of "
584 "oscillator %1. You can detune the oscillator "
585 "24 semitones (2 octaves) up and down. This is "
586 "useful for creating sounds with a chord." ).
587 arg( i + 1 ) );
588
589
590 // setup knob for left fine-detuning
591 Knob * flk = new TripleOscKnob( this );
592 flk->move( 111, knob_y );
593 flk->setHintText( tr( "Osc %1 fine detuning left:" ).
594 arg( i + 1 ),
595 " " + tr( "cents" ) );
596 flk->setWhatsThis(
597 tr( "With this knob you can set the fine detuning of "
598 "oscillator %1 for the left channel. The fine-"
599 "detuning is ranged between -100 cents and "
600 "+100 cents. This is useful for creating "
601 "\"fat\" sounds." ).arg( i + 1 ) );
602
603 // setup knob for right fine-detuning
604 Knob * frk = new TripleOscKnob( this );
605 frk->move( 140, knob_y );
606 frk->setHintText( tr( "Osc %1 fine detuning right:" ).
607 arg( i + 1 ),
608 " " + tr( "cents" ) );
609 frk->setWhatsThis(
610 tr( "With this knob you can set the fine detuning of "
611 "oscillator %1 for the right channel. The "
612 "fine-detuning is ranged between -100 cents "
613 "and +100 cents. This is useful for creating "
614 "\"fat\" sounds." ).arg( i+1 ) );
615
616
617 // setup phase-offset-knob
618 Knob * pok = new TripleOscKnob( this );
619 pok->move( 188, knob_y );
620 pok->setHintText( tr( "Osc %1 phase-offset:" ).
621 arg( i + 1 ),
622 " " + tr( "degrees" ) );
623 pok->setWhatsThis(
624 tr( "With this knob you can set the phase-offset of "
625 "oscillator %1. That means you can move the "
626 "point within an oscillation where the "
627 "oscillator begins to oscillate. For example "
628 "if you have a sine-wave and have a phase-"
629 "offset of 180 degrees the wave will first go "
630 "down. It's the same with a square-wave."
631 ).arg( i+1 ) );
632
633 // setup stereo-phase-detuning-knob
634 Knob * spdk = new TripleOscKnob( this );
635 spdk->move( 217, knob_y );
636 spdk->setHintText( tr("Osc %1 stereo phase-detuning:" ).
637 arg( i + 1 ),
638 " " + tr( "degrees" ) );
639 spdk->setWhatsThis(
640 tr( "With this knob you can set the stereo phase-"
641 "detuning of oscillator %1. The stereo phase-"
642 "detuning specifies the size of the difference "
643 "between the phase-offset of left and right "
644 "channel. This is very good for creating wide "
645 "stereo sounds." ).arg( i+1 ) );
646
647 int btn_y = 96 + i * osc_h;
648
649 PixmapButton * sin_wave_btn = new PixmapButton( this, NULL );
650 sin_wave_btn->move( 128, btn_y );
651 sin_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
652 "sin_shape_active" ) );
653 sin_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
654 "sin_shape_inactive" ) );
655 ToolTip::add( sin_wave_btn,
656 tr( "Use a sine-wave for "
657 "current oscillator." ) );
658
659 PixmapButton * triangle_wave_btn =
660 new PixmapButton( this, NULL );
661 triangle_wave_btn->move( 143, btn_y );
662 triangle_wave_btn->setActiveGraphic(
663 PLUGIN_NAME::getIconPixmap( "triangle_shape_active" ) );
664 triangle_wave_btn->setInactiveGraphic(
665 PLUGIN_NAME::getIconPixmap( "triangle_shape_inactive" ) );
666 ToolTip::add( triangle_wave_btn,
667 tr( "Use a triangle-wave "
668 "for current oscillator." ) );
669
670 PixmapButton * saw_wave_btn = new PixmapButton( this, NULL );
671 saw_wave_btn->move( 158, btn_y );
672 saw_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
673 "saw_shape_active" ) );
674 saw_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
675 "saw_shape_inactive" ) );
676 ToolTip::add( saw_wave_btn,
677 tr( "Use a saw-wave for "
678 "current oscillator." ) );
679
680 PixmapButton * sqr_wave_btn = new PixmapButton( this, NULL );
681 sqr_wave_btn->move( 173, btn_y );
682 sqr_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
683 "square_shape_active" ) );
684 sqr_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
685 "square_shape_inactive" ) );
686 ToolTip::add( sqr_wave_btn,
687 tr( "Use a square-wave for "
688 "current oscillator." ) );
689
690 PixmapButton * moog_saw_wave_btn =
691 new PixmapButton( this, NULL );
692 moog_saw_wave_btn->move( 188, btn_y );
693 moog_saw_wave_btn->setActiveGraphic(
694 PLUGIN_NAME::getIconPixmap( "moog_saw_shape_active" ) );
695 moog_saw_wave_btn->setInactiveGraphic(
696 PLUGIN_NAME::getIconPixmap( "moog_saw_shape_inactive" ) );
697 ToolTip::add( moog_saw_wave_btn,
698 tr( "Use a moog-like saw-wave "
699 "for current oscillator." ) );
700
701 PixmapButton * exp_wave_btn = new PixmapButton( this, NULL );
702 exp_wave_btn->move( 203, btn_y );
703 exp_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
704 "exp_shape_active" ) );
705 exp_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
706 "exp_shape_inactive" ) );
707 ToolTip::add( exp_wave_btn,
708 tr( "Use an exponential "
709 "wave for current oscillator." ) );
710
711 PixmapButton * white_noise_btn = new PixmapButton( this, NULL );
712 white_noise_btn->move( 218, btn_y );
713 white_noise_btn->setActiveGraphic(
714 PLUGIN_NAME::getIconPixmap( "white_noise_shape_active" ) );
715 white_noise_btn->setInactiveGraphic(
716 PLUGIN_NAME::getIconPixmap( "white_noise_shape_inactive" ) );
717 ToolTip::add( white_noise_btn,
718 tr( "Use white-noise for "
719 "current oscillator." ) );
720
721 PixmapButton * uwb = new PixmapButton( this, NULL );
722 uwb->move( 233, btn_y );
723 uwb->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
724 "usr_shape_active" ) );
725 uwb->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
726 "usr_shape_inactive" ) );
727 ToolTip::add( uwb, tr( "Use a user-defined "
728 "waveform for current oscillator." ) );
729
730 automatableButtonGroup * wsbg =
731 new automatableButtonGroup( this );
732
733 wsbg->addButton( sin_wave_btn );
734 wsbg->addButton( triangle_wave_btn );
735 wsbg->addButton( saw_wave_btn );
736 wsbg->addButton( sqr_wave_btn );
737 wsbg->addButton( moog_saw_wave_btn );
738 wsbg->addButton( exp_wave_btn );
739 wsbg->addButton( white_noise_btn );
740 wsbg->addButton( uwb );
741
742 m_oscKnobs[i] = OscillatorKnobs( vk, pk, ck, flk, frk, pok,
743 spdk, uwb, wsbg );
744 }
745 }
746
747
748
749
~TripleOscillatorView()750 TripleOscillatorView::~TripleOscillatorView()
751 {
752 }
753
754
755
756
modelChanged()757 void TripleOscillatorView::modelChanged()
758 {
759 TripleOscillator * t = castModel<TripleOscillator>();
760 m_mod1BtnGrp->setModel( &t->m_osc[0]->m_modulationAlgoModel );
761 m_mod2BtnGrp->setModel( &t->m_osc[1]->m_modulationAlgoModel );
762
763 for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
764 {
765 m_oscKnobs[i].m_volKnob->setModel(
766 &t->m_osc[i]->m_volumeModel );
767 m_oscKnobs[i].m_panKnob->setModel(
768 &t->m_osc[i]->m_panModel );
769 m_oscKnobs[i].m_coarseKnob->setModel(
770 &t->m_osc[i]->m_coarseModel );
771 m_oscKnobs[i].m_fineLeftKnob->setModel(
772 &t->m_osc[i]->m_fineLeftModel );
773 m_oscKnobs[i].m_fineRightKnob->setModel(
774 &t->m_osc[i]->m_fineRightModel );
775 m_oscKnobs[i].m_phaseOffsetKnob->setModel(
776 &t->m_osc[i]->m_phaseOffsetModel );
777 m_oscKnobs[i].m_stereoPhaseDetuningKnob->setModel(
778 &t->m_osc[i]->m_stereoPhaseDetuningModel );
779 m_oscKnobs[i].m_waveShapeBtnGrp->setModel(
780 &t->m_osc[i]->m_waveShapeModel );
781 connect( m_oscKnobs[i].m_userWaveButton,
782 SIGNAL( doubleClicked() ),
783 t->m_osc[i], SLOT( oscUserDefWaveDblClick() ) );
784 }
785 }
786
787
788
789
790 extern "C"
791 {
792
793 // necessary for getting instance out of shared lib
lmms_plugin_main(Model *,void * _data)794 Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
795 {
796 return new TripleOscillator( static_cast<InstrumentTrack *>( _data ) );
797 }
798
799 }
800
801
802
803
804