1 /*  SpiralSynth
2  *  Copyright (C) 2000 David Griffiths <dave@blueammonite.f9.co.uk>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18 
19 #include "Synth.h"
20 
21 #include <FL/Fl.H>
22 #include <FL/Enumerations.H>
23 #include <fstream>
24 #include <sstream>
25 #include <string>
26 #include <time.h>
27 #include <stdlib.h>
28 
29 #include "Oscillator.h"
30 #include "Output.h"
31 #include "Midi.h"
32 
Synth()33 Synth::Synth():
34 m_Oct(5),
35 m_Databuf(NULL),
36 m_Databuf2(NULL),
37 m_Databuf3(NULL),
38 m_OscControlbuf1(NULL),
39 m_OscControlbuf2(NULL),
40 m_OscControlbuf3(NULL),
41 m_EnvControlbuf(NULL),
42 m_LFOControlbuf(NULL),
43 m_BothControlbuf(NULL),
44 m_Osc1GUI(&m_Osc1),
45 m_Osc2GUI(&m_Osc2),
46 m_Osc3GUI(&m_Osc3),
47 m_Env1GUI(&m_Env1),
48 m_Env2GUI(&m_Env2),
49 m_Env3GUI(&m_Env3),
50 m_FilterGUI(&m_Filter),
51 m_MixerGUI(&m_Mixer),
52 m_Mixer2GUI(&m_Mixer2),
53 m_DelayGUI(&m_Delay),
54 m_FilterEnvGUI(&m_FilterEnv),
55 m_LFOGUI(&m_LFO),
56 m_OutGUI(&m_Out)
57 {
58 	cerr<<"Allocating Buffer Memory"<<endl;
59 
60 	AllocMem();
61 
62 	m_VoiceNote =   new int[SynthInfo::POLY];
63 	m_VoiceOctave = new int[SynthInfo::POLY];
64 	for (int n=0; n<SynthInfo::POLY; n++)
65 	{
66 		m_VoiceNote[n] = -1;
67 		m_VoiceOctave[n] = -1;
68 	}
69 
70 	if (!m_Databuf || !m_Databuf2 || !m_Databuf3 || !m_OscControlbuf1
71 	    || !m_OscControlbuf2 || !m_OscControlbuf3 || !m_EnvControlbuf
72 		|| !m_LFOControlbuf || !m_BothControlbuf )
73 	{
74 		cerr<<"Not enough memory availible"<<endl;
75 		exit(0);
76 	}
77 
78 	m_KeyVoice = new char[SynthInfo::POLY];
79 
80 	for (int n=0; n<SynthInfo::POLY; n++)
81 	{
82 		m_KeyVoice[n]=' ';
83 	}
84 
85 	LoadPatch(1);
86 }
87 
~Synth()88 Synth::~Synth()
89 {
90 	CloseMidi();
91 
92 	delete[] m_Databuf;
93 	delete[] m_Databuf2;
94 	delete[] m_Databuf3;
95 	delete[] m_OscControlbuf1;
96 	delete[] m_OscControlbuf2;
97 	delete[] m_OscControlbuf3;
98 	delete[] m_EnvControlbuf;
99     delete[] m_LFOControlbuf;
100 	delete[] m_BothControlbuf;
101 	delete[] m_XModbuf1;
102 	delete[] m_XModbuf2;
103 	delete[] m_VoiceNote;
104 	delete[] m_VoiceOctave;
105 	delete[] m_KeyVoice;
106 }
107 
AllocMem()108 void Synth::AllocMem()
109 {
110 	m_Databuf = new short[SynthInfo::BUFSIZE];
111 	m_Databuf2 = new short[SynthInfo::BUFSIZE];
112 	m_Databuf3 = new short[SynthInfo::BUFSIZE];
113 	m_OscControlbuf1 = new short[SynthInfo::BUFSIZE];
114 	m_OscControlbuf2 = new short[SynthInfo::BUFSIZE];
115 	m_OscControlbuf3 = new short[SynthInfo::BUFSIZE];
116 	m_EnvControlbuf = new short[SynthInfo::BUFSIZE];
117     m_LFOControlbuf = new short[SynthInfo::BUFSIZE];
118 	m_BothControlbuf = new short[SynthInfo::BUFSIZE];
119 
120 	m_XModbuf1 = new short*[SynthInfo::POLY];
121 	m_XModbuf2 = new short*[SynthInfo::POLY];
122 
123 	for (int n=0; n<SynthInfo::POLY; n++)
124 	{
125 		m_XModbuf1[n] = new short[SynthInfo::BUFSIZE];
126 		m_XModbuf2[n] = new short[SynthInfo::BUFSIZE];
127 	}
128 }
129 
CreateWindow()130 Fl_Window *Synth::CreateWindow()
131 {
132  Fl_Window* w;
133   { Fl_Window* o = Window = new Fl_Window(785, 395);
134     w = o;
135 	o->color(SynthInfo::GUIBG_COLOUR);
136 
137 	// put part guis here
138 	m_Osc1GUI.CreateGUI(5,5,"Osc1");
139 	m_Osc2GUI.CreateGUI(5,120,"Osc2");
140 	m_Osc3GUI.CreateGUI(5,235,"Osc3");
141 	m_Env1GUI.CreateGUI(255,5,"Envelope1");
142 	m_Env2GUI.CreateGUI(255,120,"Envelope2");
143 	m_Env3GUI.CreateGUI(255,235,"Envelope3");
144 	m_FilterGUI.CreateGUI(485,5,"Filter");
145 	m_MixerGUI.CreateGUI(380,5,"Mixer 1&2");
146 	m_Mixer2GUI.CreateGUI(380,120,"Mixer [1,2]&3");
147 	m_DelayGUI.CreateGUI(610,5,"Delay");
148 	m_FilterEnvGUI.CreateGUI(485,120,"Extra Envelope");
149 	m_LFOGUI.CreateGUI(610,120,"LFO");
150 	m_OutGUI.CreateGUI(695,5,"Output");
151 	m_PatchBank.CreateGUI(5,350,"Patch Bank");
152 	m_LFORouteGUI.CreateGUI(610,292,"LFO Route");
153 	m_EnvRouteGUI.CreateGUI(610,235,"Envelope Route");
154 
155 	m_Scope.CreateGUI(380,235,"Scope");
156   }
157 
158  return w;
159 }
160 
161 void Synth::Trigger(int octave, int note, int vol=128)
162 {
163 	// Move to the next voice
164 	m_CurrentVoice++;
165 
166 	if (m_CurrentVoice>=SynthInfo::POLY)
167 	{
168 		m_CurrentVoice=0;
169 	}
170 
171 	// if it's not free
172 	if (m_VoiceNote[m_CurrentVoice]!=-1)
173 	{
174 		// look for a free voice
175 		for (int n=0; n<SynthInfo::POLY; n++)
176 		{
177 			if (m_VoiceNote[n]==-1 && m_VoiceOctave[n]==-1)
178 			{
179 				m_CurrentVoice=n;
180 			}
181 		}
182 	}
183 
184 	m_VoiceNote[m_CurrentVoice]=note;
185 	m_VoiceOctave[m_CurrentVoice]=octave;
186 
187 	m_Osc1.NoteTrigger(m_CurrentVoice,octave,note,vol); m_Env1.Trigger(m_CurrentVoice);
188 	m_Osc2.NoteTrigger(m_CurrentVoice,octave,note,vol); m_Env2.Trigger(m_CurrentVoice);
189 	m_Osc3.NoteTrigger(m_CurrentVoice,octave,note,vol); m_Env3.Trigger(m_CurrentVoice);
190 	m_FilterEnv.Trigger(m_CurrentVoice);
191 }
192 
193 void Synth::UnTrigger(int octave=-1,int note=-1)
194 {
195 	// clear all voices
196 	if (octave==-1 && note==-1)
197 	{
198 		for (int n=0; n<SynthInfo::POLY; n++)
199 		{
200 			m_Env1.UnTrigger(n);
201 			m_Env2.UnTrigger(n);
202 			m_Env3.UnTrigger(n);
203 			m_FilterEnv.UnTrigger(n);
204 			m_VoiceNote[n]=-1;
205 			m_VoiceOctave[n]=-1;
206 		}
207 	}
208 	else
209 	{
210 		// look for the voice and clear it
211 		for (int n=0; n<SynthInfo::POLY; n++)
212 		{
213 			if (m_VoiceOctave[n]==octave && m_VoiceNote[n]==note)
214 			{
215 				m_Env1.UnTrigger(n);
216 				m_Env2.UnTrigger(n);
217 				m_Env3.UnTrigger(n);
218 				m_FilterEnv.UnTrigger(n);
219 				m_VoiceNote[n]=-1;
220 				m_VoiceOctave[n]=-1;
221 			}
222 		}
223 	}
224 
225 }
226 
DoIdle()227 void Synth::DoIdle()
228 {
229 	if (Fl::event_key(FL_F+1)) m_Oct=0;
230 	if (Fl::event_key(FL_F+2)) m_Oct=1;
231 	if (Fl::event_key(FL_F+3)) m_Oct=2;
232 	if (Fl::event_key(FL_F+4)) m_Oct=3;
233 	if (Fl::event_key(FL_F+5)) m_Oct=4;
234 	if (Fl::event_key(FL_F+6)) m_Oct=5;
235 	if (Fl::event_key(FL_F+7)) m_Oct=6;
236 	if (Fl::event_key(FL_F+8)) m_Oct=7;
237 	if (Fl::event_key(FL_F+9)) m_Oct=8;
238 	if (Fl::event_key(FL_F+10)) m_Oct=9;
239 	if (Fl::event_key(FL_F+11)) m_Oct=10;
240 
241 	int note=0;
242 	int oct=0;
243 	char KeyChar=0;
244 	bool KeyPressed=false;
245 
246 	for (int key=0; key<SynthInfo::NKEYS; key++)
247 	{
248 		KeyChar=SynthInfo::KEYMAP.c_str()[key];
249 
250 		// check if a key's been pressed
251 		if (Fl::event_key(KeyChar))
252 		{
253 			KeyPressed=true;
254 
255 			// check it was pressed last time
256 			bool Found=false;
257 			for (int n=0; n<SynthInfo::POLY; n++)
258 			{
259 				if (m_KeyVoice[n]==KeyChar)
260 				{
261 					Found=true;
262 				}
263 			}
264 
265 			if (!Found)
266 			{
267 				Trigger(m_Oct+oct,note);
268 				m_KeyVoice[m_CurrentVoice]=KeyChar;
269 			}
270 		}
271 		else // it's not pressed down
272 		{
273 			//see if the note was pressed down last time
274 			for (int n=0; n<SynthInfo::POLY; n++)
275 			{
276 				if (m_KeyVoice[n]==KeyChar)
277 				{
278 					UnTrigger(m_Oct+oct,note);
279 					m_KeyVoice[n]=' ';
280 				}
281 			}
282 		}
283 
284 		note++;
285 		if (note>11)
286 		{
287 			note=0;
288 			oct++;
289 		}
290 	}
291 	// reset if no keys have been pressed
292 	/*if (!KeyPressed)
293 	{
294 		if (LastKeyPressed!=0)
295 		{
296 			UnTrigger();
297 		}
298 		LastKeyPressed=0;
299 	}*/
300 
301 	m_CurrentEvent=m_Midi.GetEvent();
302 
303 	if (m_CurrentEvent.GetType()==MidiEvent::ON)
304 	{
305 		Trigger(m_CurrentEvent.GetOctave(),
306 		        m_CurrentEvent.GetNote(),
307 				m_CurrentEvent.GetVolume());
308 	}
309 
310 	if (m_CurrentEvent.GetType()==MidiEvent::OFF)
311 	{
312 		UnTrigger(m_CurrentEvent.GetOctave(),m_CurrentEvent.GetNote());
313 	}
314 
315 	float f;
316 	m_Out.ClearBuffer();
317 
318 	m_LFO.GetOutput(0,m_LFOControlbuf);
319 
320 	// run the synth!
321 	for (int Voice=0; Voice<SynthInfo::POLY; Voice++)
322 	{
323 		// Xmod routing
324 		if (m_Mixer.GetType()==Mixer::XMOD)
325 		{
326 			m_Osc2.ModulateFreq(m_XModbuf1[Voice]);
327 		}
328 
329 		if (m_Mixer2.GetType()==Mixer::XMOD)
330 		{
331 			m_Osc3.ModulateFreq(m_XModbuf2[Voice]);
332 		}
333 
334 		m_FilterEnv.GetOutput(Voice, m_EnvControlbuf);
335 
336 		for (int n=0; n<SynthInfo::BUFSIZE; n++)
337 			m_BothControlbuf[n] = m_LFOControlbuf[n]+m_EnvControlbuf[n];
338 
339 		m_Osc1.GetOutput(Voice, m_Databuf);
340 		m_Osc2.GetOutput(Voice, m_Databuf2);
341 		m_Osc3.GetOutput(Voice, m_Databuf3);
342 		m_Env1.GetOutput(Voice, m_OscControlbuf1);
343 		m_Env2.GetOutput(Voice, m_OscControlbuf2);
344 		m_Env3.GetOutput(Voice, m_OscControlbuf3);
345 		m_OscAmp1.GetOutput(m_OscControlbuf1,m_Databuf);
346 		m_OscAmp2.GetOutput(m_OscControlbuf2,m_Databuf2);
347 		m_OscAmp3.GetOutput(m_OscControlbuf3,m_Databuf3);
348 
349 		// store the buffer here for feeding back into the oscillator
350 		if (m_Mixer.GetType()==Mixer::XMOD)
351 		{
352 			CopyBuffer(m_Databuf,m_XModbuf1[Voice]);
353 		}
354 
355 		m_Mixer.GetOutput(m_Databuf2,m_Databuf);
356 
357 		// store the buffer here for feeding back into the oscillator
358 		if (m_Mixer2.GetType()==Mixer::XMOD)
359 		{
360 			CopyBuffer(m_Databuf2,m_XModbuf2[Voice]);
361 		}
362 
363 		m_Mixer2.GetOutput(m_Databuf3,m_Databuf);
364 		m_Filter.GetOutput(Voice,m_Databuf);
365 
366 		m_Out.Send(m_Databuf);
367 	}
368 
369 	m_Delay.GetOutput(m_Out.GetBuffer());
370 
371 	m_Scope.Display(m_Out.GetBuffer());
372 
373 	m_Out.Play();
374 
375 	m_CurrentPatch=m_PatchBank.GetOutput();
376 
377 	if (m_CurrentPatch>=0)
378 	{
379 		m_Filter.Reset();
380 		if (m_PatchBank.IsSaving())
381 		{
382 			SavePatch(m_CurrentPatch);
383 		}
384 		else
385 		{
386 			LoadPatch(m_CurrentPatch);
387 			UpdateValues();
388 		}
389 	}
390 
391 	if (m_CurrentPatch==-2)
392 	{
393 		Randomise();
394 	}
395 
396 	Route();
397 }
398 
SavePatch(int n)399 void Synth::SavePatch(int n)
400 {
401 	char buf[1024];
402 	ostrstream os(buf,1024,ios::out);
403 	os<<*this<<"| ";
404 	WritePatch(n,buf);
405 }
406 
LoadPatch(int n)407 void Synth::LoadPatch(int n)
408 {
409 	char buf[1024];
410 	if (ReadPatch(n,buf))
411 	{
412 		istrstream is(buf);
413 		is>>*this;
414 	}
415 }
416 
417 
ReadPatch(int n,char * out)418 int Synth::ReadPatch(int n, char *out)
419 {
420 	string Patchfn(SynthInfo::Get()->GetHomeDir()+"/.SpiralPatches.bank");
421 	ifstream is(Patchfn.c_str());
422 
423 	if (!is.good()) // make a patch file
424 	{
425 		cerr<<"SpiralPatches.bank not found - writing new patch file."<<endl;
426 
427 		ofstream os(Patchfn.c_str());
428 		for (int n=0; n<NUM_PATCHES; n++)
429 			os<<*this<<"| ";
430 
431 		return 0;
432 	}
433 
434 	char c=0;
435 	int count=0;
436 
437 	while(is.good() && count!=n)
438 	{
439 		is.get(c);
440 		//cerr<<"c="<<c<<" count="<<count<<endl;
441 		if (c=='|') count++;
442 	}
443 
444 	if(is.good())
445 	{
446 		is.get(out,1024);
447 	}
448 
449 	return 1;
450 }
451 
WritePatch(int n,char * in)452 void Synth::WritePatch(int n, char *in)
453 {
454 	// todo: get rid of this cheesy implementation
455 	string Patchfn(SynthInfo::Get()->GetHomeDir()+"/.SpiralPatches.bank");
456 	string Tempfn(SynthInfo::Get()->GetHomeDir()+"/temp");
457 	ifstream is(Patchfn.c_str());
458 	ofstream os(Tempfn.c_str());
459 
460 	char c=0;
461 	int count=0;
462 	int count2=0;
463 	bool IgnorePatch=false;
464 
465 	while(is.good())
466 	{
467 		is.get(c);
468 
469 		while (count==n) // this is the patch to overwrite
470 		{
471 			c=in[count2++];
472 
473 			if (c=='|') // end of patch
474 			{
475 				IgnorePatch=true;
476 				count++;
477 				os<<c<<" ";
478 				is.get(c);
479 			}
480 			else os<<c;
481 		}
482 
483 		if (IgnorePatch) // step through until end
484 		{
485 
486 			if (c=='|')
487 			{
488 				IgnorePatch=false;
489 				count++;
490 			}
491 		}
492 		else // normal write mode
493 		{
494 			if (c=='|')
495 			{
496 				count++;
497 			}
498 
499 			os<<c;
500 		}
501 
502 	}
503 
504 	string command("mv -f "+Tempfn+" "+Patchfn);
505 	system(command.c_str());
506 }
507 
Route()508 void Synth::Route()
509 {
510 	m_Osc1.ModulateFreq(NULL);
511 	m_Osc1.ModulatePulseWidth(NULL);
512 	m_Osc2.ModulateFreq(NULL);
513 	m_Osc2.ModulatePulseWidth(NULL);
514 	m_Filter.ModulateCutoff(NULL);
515 	m_Filter.ModulateResonance(NULL);
516 
517 	if (m_LFORouteGUI.Query(RouteGUI::OSC1FREQ))
518 		m_Osc1.ModulateFreq(m_LFOControlbuf);
519 
520 	if (m_LFORouteGUI.Query(RouteGUI::OSC1PW))
521 		m_Osc1.ModulatePulseWidth(m_LFOControlbuf);
522 
523 	if (m_LFORouteGUI.Query(RouteGUI::OSC2FREQ))
524 		m_Osc2.ModulateFreq(m_LFOControlbuf);
525 
526 	if (m_LFORouteGUI.Query(RouteGUI::OSC2PW))
527 		m_Osc2.ModulatePulseWidth(m_LFOControlbuf);
528 
529 	if (m_LFORouteGUI.Query(RouteGUI::FILTERC))
530 		m_Filter.ModulateCutoff(m_LFOControlbuf);
531 
532 	if (m_LFORouteGUI.Query(RouteGUI::FILTERR))
533 		m_Filter.ModulateResonance(m_LFOControlbuf);
534 
535 
536 	if (m_EnvRouteGUI.Query(RouteGUI::OSC1FREQ))
537 		m_Osc1.ModulateFreq(m_EnvControlbuf);
538 
539 	if (m_EnvRouteGUI.Query(RouteGUI::OSC1PW))
540 		m_Osc1.ModulatePulseWidth(m_EnvControlbuf);
541 
542 	if (m_EnvRouteGUI.Query(RouteGUI::OSC2FREQ))
543 		m_Osc2.ModulateFreq(m_EnvControlbuf);
544 
545 	if (m_EnvRouteGUI.Query(RouteGUI::OSC2PW))
546 		m_Osc2.ModulatePulseWidth(m_EnvControlbuf);
547 
548 	if (m_EnvRouteGUI.Query(RouteGUI::FILTERC))
549 		m_Filter.ModulateCutoff(m_EnvControlbuf);
550 
551 	if (m_EnvRouteGUI.Query(RouteGUI::FILTERR))
552 		m_Filter.ModulateResonance(m_EnvControlbuf);
553 
554 
555 	if (m_LFORouteGUI.Query(RouteGUI::OSC1FREQ) &&
556 	    m_EnvRouteGUI.Query(RouteGUI::OSC1FREQ))
557 		m_Osc1.ModulateFreq(m_BothControlbuf);
558 
559 	if (m_LFORouteGUI.Query(RouteGUI::OSC1PW) &&
560 		m_EnvRouteGUI.Query(RouteGUI::OSC1PW))
561 		m_Osc1.ModulatePulseWidth(m_BothControlbuf);
562 
563 	if (m_LFORouteGUI.Query(RouteGUI::OSC2FREQ) &&
564 		m_EnvRouteGUI.Query(RouteGUI::OSC2FREQ))
565 		m_Osc2.ModulateFreq(m_BothControlbuf);
566 
567 	if (m_LFORouteGUI.Query(RouteGUI::OSC2PW) &&
568 		m_EnvRouteGUI.Query(RouteGUI::OSC2PW))
569 		m_Osc2.ModulatePulseWidth(m_BothControlbuf);
570 
571 	if (m_LFORouteGUI.Query(RouteGUI::FILTERC) &&
572 		m_EnvRouteGUI.Query(RouteGUI::FILTERC))
573 		m_Filter.ModulateCutoff(m_BothControlbuf);
574 
575 	if (m_LFORouteGUI.Query(RouteGUI::FILTERR) &&
576 		m_EnvRouteGUI.Query(RouteGUI::FILTERR))
577 		m_Filter.ModulateResonance(m_BothControlbuf);
578 }
579 
UpdateValues()580 void Synth::UpdateValues()
581 {
582 	m_Osc1GUI.UpdateValues();
583 	m_Osc2GUI.UpdateValues();
584 	m_Osc3GUI.UpdateValues();
585 	m_Env1GUI.UpdateValues();
586 	m_Env2GUI.UpdateValues();
587 	m_Env3GUI.UpdateValues();
588 	m_FilterGUI.UpdateValues();
589 	m_MixerGUI.UpdateValues();
590 	m_Mixer2GUI.UpdateValues();
591 	m_DelayGUI.UpdateValues();
592 	m_FilterEnvGUI.UpdateValues();
593 	m_LFOGUI.UpdateValues();
594 	m_LFORouteGUI.UpdateValues();
595 	m_EnvRouteGUI.UpdateValues();
596 }
597 
Randomise()598 void Synth::Randomise()
599 {
600 	m_Osc1.Randomise();
601 	m_Osc2.Randomise();
602 	m_Osc3.Randomise();
603 	m_Env1.Randomise();
604 	m_Env2.Randomise();
605 	m_Env3.Randomise();
606 	m_Filter.Randomise();
607 	m_Mixer.Randomise();
608 	m_Mixer2.Randomise();
609 	m_FilterEnv.Randomise();
610 	m_LFO.Randomise();
611 	m_LFORouteGUI.Randomise();
612 	m_EnvRouteGUI.Randomise();
613 
614 	UpdateValues();
615 }
616 
617 istream &operator>>(istream &s, Synth &o)
618 {
619 	char t;
620 	s>>o.m_Osc1>>o.m_Osc2>>o.m_Osc3>>o.m_Env1>>o.m_Env2>>
621 	o.m_Env3>>o.m_Filter>>o.m_Mixer>>o.m_Mixer2>>o.m_Delay>>
622 	o.m_FilterEnv>>o.m_LFO>>o.m_LFORouteGUI>>o.m_EnvRouteGUI;
623 	return s;
624 }
625 
626 ostream &operator<<(ostream &s, Synth &o)
627 {
628 	s<<" "<<o.m_Osc1<<o.m_Osc2<<o.m_Osc3<<o.m_Env1
629 	<<o.m_Env2<<o.m_Env3<<o.m_Filter<<o.m_Mixer
630 	<<o.m_Mixer2<<o.m_Delay<<o.m_FilterEnv<<o.m_LFO
631 	<<o.m_LFORouteGUI<<o.m_EnvRouteGUI<<endl;
632 	return s;
633 }
634 
main(int argc,char ** argv)635 int main(int argc, char **argv)
636 {
637 	srand(time(NULL));
638 	SynthInfo::Get()->LoadPrefs();
639 
640 	Synth *synth=new Synth;
641 
642 	Fl::visual(FL_DOUBLE|FL_RGB);
643 	synth->CreateWindow();
644     synth->Window->show(argc, argv);
645 
646     for (;;)
647 	{
648     	if (!Fl::check()) break;
649 		synth->DoIdle();
650   	}
651 
652 	delete synth;
653 
654 	return 1;
655 }
656