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