1 /*
2  * sid_instrument.cpp - ResID based software-synthesizer
3  *
4  * Copyright (c) 2008 Csaba Hruska <csaba.hruska/at/gmail.com>
5  *                    Attila Herman <attila589/at/gmail.com>
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 
26 
27 #include <QPainter>
28 #include <QDomElement>
29 
30 #include <cmath>
31 #include <cstdio>
32 
33 #include "sid.h"
34 
35 #include "sid_instrument.h"
36 #include "Engine.h"
37 #include "InstrumentTrack.h"
38 #include "Knob.h"
39 #include "Mixer.h"
40 #include "NotePlayHandle.h"
41 #include "PixmapButton.h"
42 #include "ToolTip.h"
43 
44 #include "embed.cpp"
45 
46 #define C64_PAL_CYCLES_PER_SEC  985248
47 
48 #define NUMSIDREGS 0x19
49 #define SIDWRITEDELAY 9 // lda $xxxx,x 4 cycles, sta $d400,x 5 cycles
50 #define SIDWAVEDELAY 4 // and $xxxx,x 4 cycles extra
51 
52 unsigned char sidorder[] =
53   {0x15,0x16,0x18,0x17,
54    0x05,0x06,0x02,0x03,0x00,0x01,0x04,
55    0x0c,0x0d,0x09,0x0a,0x07,0x08,0x0b,
56    0x13,0x14,0x10,0x11,0x0e,0x0f,0x12};
57 
58 static const char *attackTime[16] = { "2 mS", "8 mS", "16 mS", "24 mS",
59 									"38 mS", "56 mS", "68 mS", "80 mS",
60 									"100 mS", "250 mS", "500 mS", "800 mS",
61 									"1 S", "3 S", "5 S", "8 S" };
62 static const char *decRelTime[16] = { "6 mS", "24 mS", "48 mS", "72 mS",
63 									"114 mS", "168 mS", "204 mS", "240 mS",
64 									"300 mS", "750 mS", "1.5 S", "2.4 S",
65 									"3 S", "9 S", "15 S", "24 S" };
66 // release time time in ms
67 static const int relTime[16] = { 6, 24, 48, 72, 114, 168, 204, 240, 300, 750,
68 								1500, 2400, 3000, 9000, 15000, 24000 };
69 
70 
71 extern "C"
72 {
73 Plugin::Descriptor PLUGIN_EXPORT sid_plugin_descriptor =
74 {
75 	STRINGIFY( PLUGIN_NAME ),
76 	"SID",
77 	QT_TRANSLATE_NOOP( "pluginBrowser", "Emulation of the MOS6581 and MOS8580 "
78 					"SID.\nThis chip was used in the Commodore 64 computer." ),
79 
80 	"Csaba Hruska <csaba.hruska/at/gmail.com>"
81 	"Attila Herman <attila589/at/gmail.com>",
82 	0x0100,
83 	Plugin::Instrument,
84 	new PluginPixmapLoader( "logo" ),
85 	NULL,
86 	NULL
87 } ;
88 
89 }
90 
voiceObject(Model * _parent,int _idx)91 voiceObject::voiceObject( Model * _parent, int _idx ) :
92 	Model( _parent ),
93 	m_pulseWidthModel( 2048.0f, 0.0f, 4095.0f, 1.0f, this,
94 					tr( "Voice %1 pulse width" ).arg( _idx+1 ) ),
95 	m_attackModel( 8.0f, 0.0f, 15.0f, 1.0f, this,
96 					tr( "Voice %1 attack" ).arg( _idx+1 ) ),
97 	m_decayModel( 8.0f, 0.0f, 15.0f, 1.0f, this,
98 					tr( "Voice %1 decay" ).arg( _idx+1 ) ),
99 	m_sustainModel( 15.0f, 0.0f, 15.0f, 1.0f, this,
100 					tr( "Voice %1 sustain" ).arg( _idx+1 ) ),
101 	m_releaseModel( 8.0f, 0.0f, 15.0f, 1.0f, this,
102 					tr( "Voice %1 release" ).arg( _idx+1 ) ),
103 	m_coarseModel( 0.0f, -24.0, 24.0, 1.0f, this,
104 					tr( "Voice %1 coarse detuning" ).arg( _idx+1 ) ),
105 	m_waveFormModel( TriangleWave, 0, NumWaveShapes-1, this,
106 					tr( "Voice %1 wave shape" ).arg( _idx+1 ) ),
107 
108 	m_syncModel( false, this, tr( "Voice %1 sync" ).arg( _idx+1 ) ),
109 	m_ringModModel( false, this, tr( "Voice %1 ring modulate" ).arg( _idx+1 ) ),
110 	m_filteredModel( false, this, tr( "Voice %1 filtered" ).arg( _idx+1 ) ),
111 	m_testModel( false, this, tr( "Voice %1 test" ).arg( _idx+1 ) )
112 {
113 }
114 
115 
~voiceObject()116 voiceObject::~voiceObject()
117 {
118 }
119 
120 
sidInstrument(InstrumentTrack * _instrument_track)121 sidInstrument::sidInstrument( InstrumentTrack * _instrument_track ) :
122 	Instrument( _instrument_track, &sid_plugin_descriptor ),
123 	// filter
124 	m_filterFCModel( 1024.0f, 0.0f, 2047.0f, 1.0f, this, tr( "Cutoff" ) ),
125 	m_filterResonanceModel( 8.0f, 0.0f, 15.0f, 1.0f, this, tr( "Resonance" ) ),
126 	m_filterModeModel( LowPass, 0, NumFilterTypes-1, this, tr( "Filter type" )),
127 
128 	// misc
129 	m_voice3OffModel( false, this, tr( "Voice 3 off" ) ),
130 	m_volumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this, tr( "Volume" ) ),
131 	m_chipModel( sidMOS8580, 0, NumChipModels-1, this, tr( "Chip model" ) )
132 {
133 	for( int i = 0; i < 3; ++i )
134 	{
135 		m_voice[i] = new voiceObject( this, i );
136 	}
137 }
138 
139 
~sidInstrument()140 sidInstrument::~sidInstrument()
141 {
142 }
143 
144 
saveSettings(QDomDocument & _doc,QDomElement & _this)145 void sidInstrument::saveSettings( QDomDocument & _doc,
146 							QDomElement & _this )
147 {
148 	// voices
149 	for( int i = 0; i < 3; ++i )
150 	{
151 		const QString is = QString::number( i );
152 
153 		m_voice[i]->m_pulseWidthModel.saveSettings(
154 										_doc, _this, "pulsewidth" + is );
155 		m_voice[i]->m_attackModel.saveSettings(
156 										_doc, _this, "attack" + is );
157 		m_voice[i]->m_decayModel.saveSettings(
158 										_doc, _this, "decay" + is );
159 		m_voice[i]->m_sustainModel.saveSettings(
160 										_doc, _this, "sustain" + is );
161 		m_voice[i]->m_releaseModel.saveSettings(
162 										_doc, _this, "release" + is );
163 		m_voice[i]->m_coarseModel.saveSettings(
164 										_doc, _this, "coarse" + is );
165 		m_voice[i]->m_waveFormModel.saveSettings(
166 										_doc, _this,"waveform" + is );
167 		m_voice[i]->m_syncModel.saveSettings(
168 										_doc, _this, "sync" + is );
169 		m_voice[i]->m_ringModModel.saveSettings(
170 										_doc, _this, "ringmod" + is );
171 		m_voice[i]->m_filteredModel.saveSettings(
172 										_doc, _this,"filtered" + is );
173 		m_voice[i]->m_testModel.saveSettings(
174 										_doc, _this, "test" + is );
175 	}
176 
177 	// filter
178 	m_filterFCModel.saveSettings( _doc, _this, "filterFC" );
179 	m_filterResonanceModel.saveSettings( _doc, _this, "filterResonance" );
180 	m_filterModeModel.saveSettings( _doc, _this, "filterMode" );
181 
182 	// misc
183 	m_voice3OffModel.saveSettings( _doc, _this, "voice3Off" );
184 	m_volumeModel.saveSettings( _doc, _this, "volume" );
185 	m_chipModel.saveSettings( _doc, _this, "chipModel" );
186 }
187 
188 
189 
190 
loadSettings(const QDomElement & _this)191 void sidInstrument::loadSettings( const QDomElement & _this )
192 {
193 	// voices
194 	for( int i = 0; i < 3; ++i )
195 	{
196 		const QString is = QString::number( i );
197 
198 		m_voice[i]->m_pulseWidthModel.loadSettings( _this, "pulsewidth" + is );
199 		m_voice[i]->m_attackModel.loadSettings( _this, "attack" + is );
200 		m_voice[i]->m_decayModel.loadSettings( _this, "decay" + is );
201 		m_voice[i]->m_sustainModel.loadSettings( _this, "sustain" + is );
202 		m_voice[i]->m_releaseModel.loadSettings( _this, "release" + is );
203 		m_voice[i]->m_coarseModel.loadSettings( _this, "coarse" + is );
204 		m_voice[i]->m_waveFormModel.loadSettings( _this, "waveform" + is );
205 		m_voice[i]->m_syncModel.loadSettings( _this, "sync" + is );
206 		m_voice[i]->m_ringModModel.loadSettings( _this, "ringmod" + is );
207 		m_voice[i]->m_filteredModel.loadSettings( _this, "filtered" + is );
208 		m_voice[i]->m_testModel.loadSettings( _this, "test" + is );
209 	}
210 
211 	// filter
212 	m_filterFCModel.loadSettings( _this, "filterFC" );
213 	m_filterResonanceModel.loadSettings( _this, "filterResonance" );
214 	m_filterModeModel.loadSettings( _this, "filterMode" );
215 
216 	// misc
217 	m_voice3OffModel.loadSettings( _this, "voice3Off" );
218 	m_volumeModel.loadSettings( _this, "volume" );
219 	m_chipModel.loadSettings( _this, "chipModel" );
220 }
221 
222 
223 
224 
nodeName() const225 QString sidInstrument::nodeName() const
226 {
227 	return( sid_plugin_descriptor.name );
228 }
229 
230 
231 
232 
desiredReleaseFrames() const233 f_cnt_t sidInstrument::desiredReleaseFrames() const
234 {
235 	const float samplerate = Engine::mixer()->processingSampleRate();
236 	int maxrel = 0;
237 	for( int i = 0 ; i < 3 ; ++i )
238 	{
239 		if( maxrel < m_voice[i]->m_releaseModel.value() )
240 			maxrel = (int)m_voice[i]->m_releaseModel.value();
241 	}
242 
243 	return f_cnt_t( float(relTime[maxrel])*samplerate/1000.0 );
244 }
245 
246 
247 
248 
sid_fillbuffer(unsigned char * sidreg,cSID * sid,int tdelta,short * ptr,int samples)249 static int sid_fillbuffer(unsigned char* sidreg, cSID *sid, int tdelta, short *ptr, int samples)
250 {
251   int tdelta2;
252   int result;
253   int total = 0;
254   int c;
255 //  customly added
256   int residdelay = 0;
257 
258   int badline = rand() % NUMSIDREGS;
259 
260   for (c = 0; c < NUMSIDREGS; c++)
261   {
262     unsigned char o = sidorder[c];
263 
264   	// Extra delay for loading the waveform (and mt_chngate,x)
265   	if ((o == 4) || (o == 11) || (o == 18))
266   	{
267   	  tdelta2 = SIDWAVEDELAY;
268       result = sid->clock(tdelta2, ptr, samples);
269       total += result;
270       ptr += result;
271       samples -= result;
272       tdelta -= SIDWAVEDELAY;
273     }
274 
275     // Possible random badline delay once per writing
276     if ((badline == c) && (residdelay))
277   	{
278       tdelta2 = residdelay;
279       result = sid->clock(tdelta2, ptr, samples);
280       total += result;
281       ptr += result;
282       samples -= result;
283       tdelta -= residdelay;
284     }
285 
286     sid->write(o, sidreg[o]);
287 
288     tdelta2 = SIDWRITEDELAY;
289     result = sid->clock(tdelta2, ptr, samples);
290     total += result;
291     ptr += result;
292     samples -= result;
293     tdelta -= SIDWRITEDELAY;
294   }
295   result = sid->clock(tdelta, ptr, samples);
296   total += result;
297 
298   return total;
299 }
300 
301 
302 
303 
playNote(NotePlayHandle * _n,sampleFrame * _working_buffer)304 void sidInstrument::playNote( NotePlayHandle * _n,
305 						sampleFrame * _working_buffer )
306 {
307 	const f_cnt_t tfp = _n->totalFramesPlayed();
308 
309 	const int clockrate = C64_PAL_CYCLES_PER_SEC;
310 	const int samplerate = Engine::mixer()->processingSampleRate();
311 
312 	if ( tfp == 0 )
313 	{
314 		cSID *sid = new cSID();
315 		sid->set_sampling_parameters( clockrate, SAMPLE_FAST, samplerate );
316 		sid->set_chip_model( MOS8580 );
317 		sid->enable_filter( true );
318 		sid->reset();
319 		_n->m_pluginData = sid;
320 	}
321 	const fpp_t frames = _n->framesLeftForCurrentPeriod();
322 	const f_cnt_t offset = _n->noteOffset();
323 
324 	cSID *sid = static_cast<cSID *>( _n->m_pluginData );
325 	int delta_t = clockrate * frames / samplerate + 4;
326 	short buf[frames];
327 	unsigned char sidreg[NUMSIDREGS];
328 
329 	for (int c = 0; c < NUMSIDREGS; c++)
330 	{
331 		sidreg[c] = 0x00;
332 	}
333 
334 	if( (ChipModel)m_chipModel.value() == sidMOS6581 )
335 	{
336 		sid->set_chip_model( MOS6581 );
337 	}
338 	else
339 	{
340 		sid->set_chip_model( MOS8580 );
341 	}
342 
343 	// voices
344 	reg8 data8 = 0;
345 	reg8 data16 = 0;
346 	reg8 base = 0;
347 	float freq = 0.0;
348 	float note = 0.0;
349 	for( reg8 i = 0 ; i < 3 ; ++i )
350 	{
351 		base = i*7;
352 		// freq ( Fn = Fout / Fclk * 16777216 ) + coarse detuning
353 		freq = _n->frequency();
354 		note = 69.0 + 12.0 * log( freq / 440.0 ) / log( 2 );
355 		note += m_voice[i]->m_coarseModel.value();
356 		freq = 440.0 * pow( 2.0, (note-69.0)/12.0 );
357 		data16 = int( freq / float(clockrate) * 16777216.0 );
358 
359 		sidreg[base+0] = data16&0x00FF;
360 		sidreg[base+1] = (data16>>8)&0x00FF;
361 		// pw
362 		data16 = (int)m_voice[i]->m_pulseWidthModel.value();
363 
364 		sidreg[base+2] = data16&0x00FF;
365 		sidreg[base+3] = (data16>>8)&0x000F;
366 		// control: wave form, (test), ringmod, sync, gate
367 		data8 = _n->isReleased()?0:1;
368 		data8 += m_voice[i]->m_syncModel.value()?2:0;
369 		data8 += m_voice[i]->m_ringModModel.value()?4:0;
370 		data8 += m_voice[i]->m_testModel.value()?8:0;
371 		switch( m_voice[i]->m_waveFormModel.value() )
372 		{
373 			default: break;
374 			case voiceObject::NoiseWave:	data8 += 128; break;
375 			case voiceObject::SquareWave:	data8 += 64; break;
376 			case voiceObject::SawWave:		data8 += 32; break;
377 			case voiceObject::TriangleWave:	data8 += 16; break;
378 		}
379 		sidreg[base+4] = data8&0x00FF;
380 		// ad
381 		data16 = (int)m_voice[i]->m_attackModel.value();
382 
383 		data8 = (data16&0x0F)<<4;
384 		data16 = (int)m_voice[i]->m_decayModel.value();
385 
386 		data8 += (data16&0x0F);
387 		sidreg[base+5] = data8&0x00FF;
388 		// sr
389 		data16 = (int)m_voice[i]->m_sustainModel.value();
390 
391 		data8 = (data16&0x0F)<<4;
392 		data16 = (int)m_voice[i]->m_releaseModel.value();
393 
394 		data8 += (data16&0x0F);
395 		sidreg[base+6] = data8&0x00FF;
396 	}
397 	// filtered
398 
399 	// FC (FilterCutoff)
400 	data16 = (int)m_filterFCModel.value();
401 	sidreg[21] = data16&0x0007;
402 	sidreg[22] = (data16>>3)&0x00FF;
403 
404 	// res, filt ex,3,2,1
405 	data16 = (int)m_filterResonanceModel.value();
406 	data8 = (data16&0x000F)<<4;
407 	data8 += m_voice[2]->m_filteredModel.value()?4:0;
408 	data8 += m_voice[1]->m_filteredModel.value()?2:0;
409 	data8 += m_voice[0]->m_filteredModel.value()?1:0;
410 	sidreg[23] = data8&0x00FF;
411 
412 	// mode vol
413 	data16 = (int)m_volumeModel.value();
414 	data8 = data16&0x000F;
415 	data8 += m_voice3OffModel.value()?128:0;
416 
417 	switch( m_filterModeModel.value() )
418 	{
419 		default: break;
420 		case LowPass:	data8 += 16; break;
421 		case BandPass:	data8 += 32; break;
422 		case HighPass:	data8 += 64; break;
423 	}
424 
425 	sidreg[24] = data8&0x00FF;
426 
427 	int num = sid_fillbuffer(sidreg, sid,delta_t,buf, frames);
428 	if(num!=frames)
429 		printf("!!!Not enough samples\n");
430 
431 	for( fpp_t frame = 0; frame < frames; ++frame )
432 	{
433 		sample_t s = float(buf[frame])/32768.0;
434 		for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
435 		{
436 			_working_buffer[frame+offset][ch] = s;
437 		}
438 	}
439 
440 	instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
441 }
442 
443 
444 
445 
deleteNotePluginData(NotePlayHandle * _n)446 void sidInstrument::deleteNotePluginData( NotePlayHandle * _n )
447 {
448 	delete static_cast<cSID *>( _n->m_pluginData );
449 }
450 
451 
452 
453 
instantiateView(QWidget * _parent)454 PluginView * sidInstrument::instantiateView( QWidget * _parent )
455 {
456 	return( new sidInstrumentView( this, _parent ) );
457 }
458 
459 
460 
461 
462 class sidKnob : public Knob
463 {
464 public:
sidKnob(QWidget * _parent)465 	sidKnob( QWidget * _parent ) :
466 			Knob( knobStyled, _parent )
467 	{
468 		setFixedSize( 16, 16 );
469 		setCenterPointX( 7.5 );
470 		setCenterPointY( 7.5 );
471 		setInnerRadius( 2 );
472 		setOuterRadius( 8 );
473 		setTotalAngle( 270.0 );
474 		setLineWidth( 2 );
475 	}
476 };
477 
478 
479 
480 
sidInstrumentView(Instrument * _instrument,QWidget * _parent)481 sidInstrumentView::sidInstrumentView( Instrument * _instrument,
482 							QWidget * _parent ) :
483 	InstrumentView( _instrument, _parent )
484 {
485 
486 	setAutoFillBackground( true );
487 	QPalette pal;
488 	pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) );
489 	setPalette( pal );
490 
491 	m_volKnob = new sidKnob( this );
492 	m_volKnob->setHintText( tr( "Volume:" ), "" );
493 	m_volKnob->move( 7, 64 );
494 
495 	m_resKnob = new sidKnob( this );
496 	m_resKnob->setHintText( tr( "Resonance:" ), "" );
497 	m_resKnob->move( 7 + 28, 64 );
498 
499 	m_cutKnob = new sidKnob( this );
500 	m_cutKnob->setHintText( tr( "Cutoff frequency:" ), "Hz" );
501 	m_cutKnob->move( 7 + 2*28, 64 );
502 
503 	PixmapButton * hp_btn = new PixmapButton( this, NULL );
504 	hp_btn->move( 140, 77 );
505 	hp_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "hpred" ) );
506 	hp_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "hp" ) );
507 	ToolTip::add( hp_btn, tr( "High-Pass filter ") );
508 
509 	PixmapButton * bp_btn = new PixmapButton( this, NULL );
510 	bp_btn->move( 164, 77 );
511 	bp_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "bpred" ) );
512 	bp_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "bp" ) );
513 	ToolTip::add( bp_btn, tr( "Band-Pass filter ") );
514 
515 	PixmapButton * lp_btn = new PixmapButton( this, NULL );
516 	lp_btn->move( 185, 77 );
517 	lp_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "lpred" ) );
518 	lp_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "lp" ) );
519 	ToolTip::add( lp_btn, tr( "Low-Pass filter ") );
520 
521 	m_passBtnGrp = new automatableButtonGroup( this );
522 	m_passBtnGrp->addButton( hp_btn );
523 	m_passBtnGrp->addButton( bp_btn );
524 	m_passBtnGrp->addButton( lp_btn );
525 
526 	m_offButton = new PixmapButton( this, NULL );
527 	m_offButton->setCheckable( true );
528 	m_offButton->move( 207, 77 );
529 	m_offButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "3offred" ) );
530 	m_offButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "3off" ) );
531 	ToolTip::add( m_offButton, tr( "Voice3 Off ") );
532 
533 	PixmapButton * mos6581_btn = new PixmapButton( this, NULL );
534 	mos6581_btn->move( 170, 59 );
535 	mos6581_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "6581red" ) );
536 	mos6581_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "6581" ) );
537 	ToolTip::add( mos6581_btn, tr( "MOS6581 SID ") );
538 
539 	PixmapButton * mos8580_btn = new PixmapButton( this, NULL );
540 	mos8580_btn->move( 207, 59 );
541 	mos8580_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "8580red" ) );
542 	mos8580_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "8580" ) );
543 	ToolTip::add( mos8580_btn, tr( "MOS8580 SID ") );
544 
545 	m_sidTypeBtnGrp = new automatableButtonGroup( this );
546 	m_sidTypeBtnGrp->addButton( mos6581_btn );
547 	m_sidTypeBtnGrp->addButton( mos8580_btn );
548 
549 	for( int i = 0; i < 3; i++ )
550 	{
551 		Knob *ak = new sidKnob( this );
552 		ak->setHintText( tr("Attack:"), "" );
553 		ak->move( 7, 114 + i*50 );
554 		ak->setWhatsThis( tr ( "Attack rate determines how rapidly the output "
555 				"of Voice %1 rises from zero to peak amplitude." ).arg( i+1 ) );
556 
557 		Knob *dk = new sidKnob( this );
558 		dk->setHintText( tr("Decay:") , "" );
559 		dk->move( 7 + 28, 114 + i*50 );
560 		dk->setWhatsThis( tr ( "Decay rate determines how rapidly the output "
561 				"falls from the peak amplitude to the selected Sustain level." ) );
562 
563 		Knob *sk = new sidKnob( this );
564 		sk->setHintText( tr("Sustain:"), "" );
565 		sk->move( 7 + 2*28, 114 + i*50 );
566 		sk->setWhatsThis( tr ( "Output of Voice %1 will remain at the selected "
567 				"Sustain amplitude as long as the note is held." ).arg( i+1 ) );
568 
569 		Knob *rk = new sidKnob( this );
570 		rk->setHintText( tr("Release:"), "" );
571 		rk->move( 7 + 3*28, 114 + i*50 );
572 		rk->setWhatsThis( tr ( "The output of of Voice %1 will fall from "
573 				"Sustain amplitude to zero amplitude at the selected Release "
574 				"rate." ).arg( i+1 ) );
575 
576 		Knob *pwk = new sidKnob( this );
577 		pwk->setHintText( tr("Pulse Width:"), "" );
578 		pwk->move( 7 + 4*28, 114 + i*50 );
579 		pwk->setWhatsThis( tr ( "The Pulse Width resolution allows the width "
580 				"to be smoothly swept with no discernable stepping. The Pulse "
581 				"waveform on Oscillator %1 must be selected to have any audible"
582 				" effect." ).arg( i+1 ) );
583 
584 		Knob *crsk = new sidKnob( this );
585 		crsk->setHintText( tr("Coarse:"), " semitones" );
586 		crsk->move( 147, 114 + i*50 );
587 		crsk->setWhatsThis( tr ( "The Coarse detuning allows to detune Voice "
588 				"%1 one octave up or down." ).arg( i+1 ) );
589 
590 		PixmapButton * pulse_btn = new PixmapButton( this, NULL );
591 		pulse_btn->move( 187, 101 + i*50 );
592 		pulse_btn->setActiveGraphic(
593 			PLUGIN_NAME::getIconPixmap( "pulsered" ) );
594 		pulse_btn->setInactiveGraphic(
595 			PLUGIN_NAME::getIconPixmap( "pulse" ) );
596 		ToolTip::add( pulse_btn, tr( "Pulse Wave" ) );
597 
598 		PixmapButton * triangle_btn = new PixmapButton( this, NULL );
599 		triangle_btn->move( 168, 101 + i*50 );
600 		triangle_btn->setActiveGraphic(
601 			PLUGIN_NAME::getIconPixmap( "trianglered" ) );
602 		triangle_btn->setInactiveGraphic(
603 			PLUGIN_NAME::getIconPixmap( "triangle" ) );
604 		ToolTip::add( triangle_btn, tr( "Triangle Wave" ) );
605 
606 		PixmapButton * saw_btn = new PixmapButton( this, NULL );
607 		saw_btn->move( 207, 101 + i*50 );
608 		saw_btn->setActiveGraphic(
609 			PLUGIN_NAME::getIconPixmap( "sawred" ) );
610 		saw_btn->setInactiveGraphic(
611 			PLUGIN_NAME::getIconPixmap( "saw" ) );
612 		ToolTip::add( saw_btn, tr( "SawTooth" ) );
613 
614 		PixmapButton * noise_btn = new PixmapButton( this, NULL );
615 		noise_btn->move( 226, 101 + i*50 );
616 		noise_btn->setActiveGraphic(
617 			PLUGIN_NAME::getIconPixmap( "noisered" ) );
618 		noise_btn->setInactiveGraphic(
619 			PLUGIN_NAME::getIconPixmap( "noise" ) );
620 		ToolTip::add( noise_btn, tr( "Noise" ) );
621 
622 		automatableButtonGroup * wfbg =
623 			new automatableButtonGroup( this );
624 
625 		wfbg->addButton( pulse_btn );
626 		wfbg->addButton( triangle_btn );
627 		wfbg->addButton( saw_btn );
628 		wfbg->addButton( noise_btn );
629 
630 		int syncRingWidth[] = { 3, 1, 2 };
631 
632 		PixmapButton * sync_btn = new PixmapButton( this, NULL );
633 		sync_btn->setCheckable( true );
634 		sync_btn->move( 207, 134 + i*50 );
635 		sync_btn->setActiveGraphic(
636 			PLUGIN_NAME::getIconPixmap( "syncred" ) );
637 		sync_btn->setInactiveGraphic(
638 			PLUGIN_NAME::getIconPixmap( "sync" ) );
639 		ToolTip::add( sync_btn, tr( "Sync" ) );
640 		sync_btn->setWhatsThis( tr ( "Sync synchronizes the fundamental "
641 			"frequency of Oscillator %1 with the fundamental frequency of "
642 			"Oscillator %2 producing \"Hard Sync\" effects." ).arg( i+1 )
643 			.arg( syncRingWidth[i] ) );
644 
645 		PixmapButton * ringMod_btn = new PixmapButton( this, NULL );
646 		ringMod_btn->setCheckable( true );
647 		ringMod_btn->move( 170, 116 + i*50 );
648 		ringMod_btn->setActiveGraphic(
649 			PLUGIN_NAME::getIconPixmap( "ringred" ) );
650 		ringMod_btn->setInactiveGraphic(
651 			PLUGIN_NAME::getIconPixmap( "ring" ) );
652 		ToolTip::add( ringMod_btn, tr( "Ring-Mod" ) );
653 		ringMod_btn->setWhatsThis( tr ( "Ring-mod replaces the Triangle "
654 			"Waveform output of Oscillator %1 with a \"Ring Modulated\" "
655 			"combination of Oscillators %1 and %2." ).arg( i+1 )
656 			.arg( syncRingWidth[i] ) );
657 
658 		PixmapButton * filter_btn = new PixmapButton( this, NULL );
659 		filter_btn->setCheckable( true );
660 		filter_btn->move( 207, 116 + i*50 );
661 		filter_btn->setActiveGraphic(
662 			PLUGIN_NAME::getIconPixmap( "filterred" ) );
663 		filter_btn->setInactiveGraphic(
664 			PLUGIN_NAME::getIconPixmap( "filter" ) );
665 		ToolTip::add( filter_btn, tr( "Filtered" ) );
666 		filter_btn->setWhatsThis( tr ( "When Filtered is on, Voice %1 will be "
667 			"processed through the Filter. When Filtered is off, Voice %1 "
668 			"appears directly at the output, and the Filter has no effect on "
669 			"it." ).arg( i+1 ) );
670 
671 		PixmapButton * test_btn = new PixmapButton( this, NULL );
672 		test_btn->setCheckable( true );
673 		test_btn->move( 170, 134 + i*50 );
674 		test_btn->setActiveGraphic(
675 			PLUGIN_NAME::getIconPixmap( "testred" ) );
676 		test_btn->setInactiveGraphic(
677 			PLUGIN_NAME::getIconPixmap( "test" ) );
678 		ToolTip::add( test_btn, tr( "Test" ) );
679 		test_btn->setWhatsThis( tr ( "Test, when set, resets and locks "
680 			"Oscillator %1 at zero until Test is turned off." ).arg( i+1 ) );
681 
682 		m_voiceKnobs[i] = voiceKnobs( ak, dk, sk, rk, pwk, crsk, wfbg,
683 								sync_btn, ringMod_btn, filter_btn, test_btn );
684 	}
685 }
686 
687 
~sidInstrumentView()688 sidInstrumentView::~sidInstrumentView()
689 {
690 }
691 
updateKnobHint()692 void sidInstrumentView::updateKnobHint()
693 {
694 	sidInstrument * k = castModel<sidInstrument>();
695 
696 	for( int i = 0; i < 3; ++i )
697 	{
698 		m_voiceKnobs[i].m_attKnob->setHintText( tr( "Attack:" ) + " ", " (" +
699 				QString::fromLatin1( attackTime[(int)k->m_voice[i]->
700 				m_attackModel.value()] ) + ")" );
701 		ToolTip::add( m_voiceKnobs[i].m_attKnob,
702 						attackTime[(int)k->m_voice[i]->m_attackModel.value()] );
703 
704 		m_voiceKnobs[i].m_decKnob->setHintText( tr( "Decay:" ) + " ", " (" +
705 				QString::fromLatin1( decRelTime[(int)k->m_voice[i]->
706 				m_decayModel.value()] ) + ")" );
707 		ToolTip::add( m_voiceKnobs[i].m_decKnob,
708 						decRelTime[(int)k->m_voice[i]->m_decayModel.value()] );
709 
710 		m_voiceKnobs[i].m_relKnob->setHintText( tr( "Release:" ) + " ", " (" +
711 				QString::fromLatin1( decRelTime[(int)k->m_voice[i]->
712 				m_releaseModel.value()] )  + ")" );
713 		ToolTip::add( m_voiceKnobs[i].m_relKnob,
714 						decRelTime[(int)k->m_voice[i]->m_releaseModel.value()]);
715 
716 		m_voiceKnobs[i].m_pwKnob->setHintText( tr( "Pulse Width:" )+ " ", " (" +
717 				QString::number(  (double)k->m_voice[i]->
718 				m_pulseWidthModel.value() / 40.95 ) + "%)" );
719 		ToolTip::add( m_voiceKnobs[i].m_pwKnob,
720 				QString::number( (double)k->m_voice[i]->
721 				m_pulseWidthModel.value() / 40.95 ) + "%" );
722 	}
723 	m_cutKnob->setHintText( tr( "Cutoff frequency:" ) + " ", " (" +
724 				QString::number ( (int) ( 9970.0 / 2047.0 *
725 				(double)k->m_filterFCModel.value() + 30.0 ) ) + "Hz)" );
726 	ToolTip::add( m_cutKnob, QString::number( (int) ( 9970.0 / 2047.0 *
727 					 (double)k->m_filterFCModel.value() + 30.0 ) ) + "Hz" );
728 }
729 
730 
731 
732 
updateKnobToolTip()733 void sidInstrumentView::updateKnobToolTip()
734 {
735 	sidInstrument * k = castModel<sidInstrument>();
736 	for( int i = 0; i < 3; ++i )
737 	{
738 		ToolTip::add( m_voiceKnobs[i].m_sustKnob,
739 				QString::number( (int)k->m_voice[i]->m_sustainModel.value() ) );
740 		ToolTip::add( m_voiceKnobs[i].m_crsKnob,
741 				QString::number( (int)k->m_voice[i]->m_coarseModel.value() ) +
742 				" semitones" );
743 	}
744 	ToolTip::add( m_volKnob,
745 					QString::number( (int)k->m_volumeModel.value() ) );
746 	ToolTip::add( m_resKnob,
747 					QString::number( (int)k->m_filterResonanceModel.value() ) );
748 }
749 
750 
751 
752 
modelChanged()753 void sidInstrumentView::modelChanged()
754 {
755 	sidInstrument * k = castModel<sidInstrument>();
756 
757 	m_volKnob->setModel( &k->m_volumeModel );
758 	m_resKnob->setModel( &k->m_filterResonanceModel );
759 	m_cutKnob->setModel( &k->m_filterFCModel );
760 	m_passBtnGrp->setModel( &k->m_filterModeModel );
761 	m_offButton->setModel(  &k->m_voice3OffModel );
762 	m_sidTypeBtnGrp->setModel(  &k->m_chipModel );
763 
764 	for( int i = 0; i < 3; ++i )
765 	{
766 		m_voiceKnobs[i].m_attKnob->setModel(
767 					&k->m_voice[i]->m_attackModel );
768 		m_voiceKnobs[i].m_decKnob->setModel(
769 					&k->m_voice[i]->m_decayModel );
770 		m_voiceKnobs[i].m_sustKnob->setModel(
771 					&k->m_voice[i]->m_sustainModel );
772 		m_voiceKnobs[i].m_relKnob->setModel(
773 					&k->m_voice[i]->m_releaseModel );
774 		m_voiceKnobs[i].m_pwKnob->setModel(
775 					&k->m_voice[i]->m_pulseWidthModel );
776 		m_voiceKnobs[i].m_crsKnob->setModel(
777 					&k->m_voice[i]->m_coarseModel );
778 		m_voiceKnobs[i].m_waveFormBtnGrp->setModel(
779 					&k->m_voice[i]->m_waveFormModel );
780 		m_voiceKnobs[i].m_syncButton->setModel(
781 					&k->m_voice[i]->m_syncModel );
782 		m_voiceKnobs[i].m_ringModButton->setModel(
783 					&k->m_voice[i]->m_ringModModel );
784 		m_voiceKnobs[i].m_filterButton->setModel(
785 					&k->m_voice[i]->m_filteredModel );
786 		m_voiceKnobs[i].m_testButton->setModel(
787 					&k->m_voice[i]->m_testModel );
788 	}
789 
790 	for( int i = 0; i < 3; ++i )
791 	{
792 		connect( &k->m_voice[i]->m_attackModel, SIGNAL( dataChanged() ),
793 			this, SLOT( updateKnobHint() ) );
794 		connect( &k->m_voice[i]->m_decayModel, SIGNAL( dataChanged() ),
795 			this, SLOT( updateKnobHint() ) );
796 		connect( &k->m_voice[i]->m_releaseModel, SIGNAL( dataChanged() ),
797 			this, SLOT( updateKnobHint() ) );
798 		connect( &k->m_voice[i]->m_pulseWidthModel, SIGNAL( dataChanged() ),
799 			this, SLOT( updateKnobHint() ) );
800 		connect( &k->m_voice[i]->m_sustainModel, SIGNAL( dataChanged() ),
801 			this, SLOT( updateKnobToolTip() ) );
802 		connect( &k->m_voice[i]->m_coarseModel, SIGNAL( dataChanged() ),
803 			this, SLOT( updateKnobToolTip() ) );
804 	}
805 
806 	connect( &k->m_volumeModel, SIGNAL( dataChanged() ),
807 		this, SLOT( updateKnobToolTip() ) );
808 	connect( &k->m_filterResonanceModel, SIGNAL( dataChanged() ),
809 		this, SLOT( updateKnobToolTip() ) );
810 	connect( &k->m_filterFCModel, SIGNAL( dataChanged() ),
811 		this, SLOT( updateKnobHint() ) );
812 
813 	updateKnobHint();
814 	updateKnobToolTip();
815 }
816 
817 
818 
819 
820 
821 extern "C"
822 {
823 
824 // necessary for getting instance out of shared lib
lmms_plugin_main(Model *,void * _data)825 Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
826 {
827 	return( new sidInstrument(
828 				static_cast<InstrumentTrack *>( _data ) ) );
829 }
830 
831 
832 }
833 
834 
835 
836 
837