Fix scope error with newer GCC.
[spiralsynthmodular.git] / SpiralSynthModular.C
blob5f1a8de77cbfda29319ab2472be669da5b8880e4
1 /*  SpiralSynthModular
2  *  Copyleft (C) 2002 David Griffiths <dave@pawfal.org>
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.
19 #include <string>
20 #include <iostream>
21 #include <fstream>
22 #include <sstream>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <dirent.h>
26 #include <dlfcn.h>
27 #include <FL/Fl.H>
28 #include <FL/Enumerations.H>
29 #include <FL/Fl_File_Chooser.H>
30 #include <FL/Fl_Box.H>
31 #include <FL/Fl_Tooltip.H>
32 #include "SpiralSynthModular.h"
33 #include "SpiralSound/PluginManager.h"
34 #include "SpiralSound/SpiralInfo.h"
35 #include "SpiralSound/Plugins/SpiralPluginGUI.h"
36 #include "GUI/SSM.xpm"
37 #include "GUI/load.xpm"
38 #include "GUI/save.xpm"
39 #include "GUI/new.xpm"
40 #include "GUI/options.xpm"
41 #include "GUI/comment.xpm"
42 #include "GUI/Widgets/PawfalYesNo.h"
44 //#define DEBUG_PLUGINS
45 //#define DEBUG_STREAM
47 const static string LABEL = "SpiralSynthModular "+VER_STRING;
48 static string TITLEBAR;
50 static const int FILE_VERSION = 4;
51 static int Numbers[512];
53 static const int MAIN_WIDTH     = 700;
54 static const int MAIN_HEIGHT    = 600;
55 static const int SLIDER_WIDTH   = 15;
56 static const int ICON_DEPTH     = 3;
57 static const int COMMENT_ID     = -1;
59 using namespace std;
61 map<int,DeviceWin*> SynthModular::m_DeviceWinMap;
62 bool SynthModular::m_CallbackUpdateMode = false;
63 bool SynthModular::m_BlockingOutputPluginIsReady = false;
65 //////////////////////////////////////////////////////////
67 DeviceWin::~DeviceWin()
71 //////////////////////////////////////////////////////////
73 SynthModular::SynthModular():
74 m_Frozen(false),
75 m_NextID(0)
77         /* Shared Audio State Information  */
78         m_Info.BUFSIZE = SpiralInfo::BUFSIZE;
79         m_Info.SAMPLERATE = SpiralInfo::SAMPLERATE;
80         m_Info.PAUSED = false;
82         /* obsolete - REMOVE SOON  */
83         m_Info.FRAGSIZE = SpiralInfo::FRAGSIZE;
84         m_Info.FRAGCOUNT = SpiralInfo::FRAGCOUNT;
85         m_Info.OUTPUTFILE = SpiralInfo::OUTPUTFILE;
86         m_Info.MIDIFILE = SpiralInfo::MIDIFILE;
87         m_Info.POLY = SpiralInfo::POLY;
89         /* Shared GUI Preferences Information  */
90         m_Info.GUI_COLOUR = SpiralInfo::GUI_COLOUR;
91         m_Info.SCOPE_BG_COLOUR = SpiralInfo::SCOPE_BG_COLOUR;
92         m_Info.SCOPE_FG_COLOUR = SpiralInfo::SCOPE_FG_COLOUR;
93         m_Info.SCOPE_SEL_COLOUR = SpiralInfo::SCOPE_SEL_COLOUR;
94         m_Info.SCOPE_IND_COLOUR = SpiralInfo::SCOPE_IND_COLOUR;
95         m_Info.SCOPE_MRK_COLOUR = SpiralInfo::SCOPE_MRK_COLOUR;
96         m_Info.GUICOL_Device = SpiralInfo::GUICOL_Device;
97         m_Info.GUIDEVICE_Box = SpiralInfo::GUIDEVICE_Box;
99         for (int n=0; n<512; n++) Numbers[n]=n;
101         m_CH.Register("Frozen",&m_Frozen);
104 //////////////////////////////////////////////////////////
106 SynthModular::~SynthModular()
108         ClearUp();
109         PluginManager::Get()->PackUpAndGoHome();
110         system("rm -f ___temp.ssmcopytmp");
113 //////////////////////////////////////////////////////////
115 void SynthModular::ClearUp()
117         FreezeAll();
119         for(map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
120                 i!=m_DeviceWinMap.end(); i++)
121         {
122                 //Stop processing of audio if any
123                 if (i->second->m_Device)
124                 {
125                         if (i->second->m_Device->Kill());
126                 }
127                 i->second->m_DeviceGUI->Clear();
129                 if (i->second->m_DeviceGUI->GetPluginWindow())
130                 {
131                         i->second->m_DeviceGUI->GetPluginWindow()->hide();
132                 }
134                 //Delete Device
135                 delete i->second->m_Device;
136                 i->second->m_Device=NULL;
137         }
139         m_Canvas->Clear();
140         m_DeviceWinMap.clear();
141         m_NextID=0;
143         ThawAll();
146 //////////////////////////////////////////////////////////
147 void SynthModular::Update()
149         m_CH.UpdateDataNow();
151         if (m_Frozen) return;
153         // for all the plugins
154         for(map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
155                 i!=m_DeviceWinMap.end(); i++)
156         {
157                 if (i->second->m_Device && i->second->m_Device->IsDead())
158                 {
159                         //Delete Device
160                         delete i->second->m_Device;
161                         i->second->m_Device=NULL;
163                         //Erase Device from DeviceWinMap
164                         m_DeviceWinMap.erase(i);
165                 }
166                 else if (i->second->m_Device) // if it's not a comment
167                 {
168                         #ifdef DEBUG_PLUGINS
169                         cerr<<"Updating channelhandler of plugin "<<i->second->m_PluginID<<endl;
170                         #endif
172                         // updates the data from the gui thread, if it's not blocking
173                         i->second->m_Device->UpdateChannelHandler();
175                         #ifdef DEBUG_PLUGINS
176                         cerr<<"Finished updating"<<endl;
177                         #endif
179                         // If this is an audio device see if we always need to ProcessAudio here
180                         if ((!m_ResetingAudioThread))
181                         {
182                                 if (i->second->m_Device->IsAudioDriver())
183                                 {
184                                         AudioDriver *driver = ((AudioDriver *)i->second->m_Device);
186                                         if (driver->ProcessType() == AudioDriver::ALWAYS)
187                                         {
188                                                 driver->ProcessAudio();
189                                         }
190                                 }
192                                 // run any commands we've received from the GUI's
193                                 i->second->m_Device->ExecuteCommands();
194                         }
195                 }
196         }
198         // run the plugins (only ones connected to anything)
199         list<int> ExecutionOrder = m_Canvas->GetGraph()->GetSortedList();
200         for (list<int>::reverse_iterator i=ExecutionOrder.rbegin();
201                  i!=ExecutionOrder.rend(); i++)
202         {
203                 // use the graphsort order to remove internal latency
204                 map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(*i);
205                 if (di!=m_DeviceWinMap.end() && di->second->m_Device  && (! di->second->m_Device->IsDead()) && (!m_Info.PAUSED || m_ResetingAudioThread))
206                 {
207                         #ifdef DEBUG_PLUGINS
208                         cerr<<"Executing plugin "<<di->second->m_PluginID<<endl;
209                         #endif
211                         if (m_ResetingAudioThread)
212                         {
213                                 di->second->m_Device->Reset();
214                         }
215                         else
216                         {
217                                 di->second->m_Device->Execute();
219                                 // If this is an audio device see if we need to ProcessAudio here
220                                 if (di->second->m_Device->IsAudioDriver())
221                                 {
222                                         AudioDriver *driver = ((AudioDriver *)di->second->m_Device);
224                                         if (driver->ProcessType() == AudioDriver::MANUAL)
225                                         {
226                                                 driver->ProcessAudio();
227                                         }
228                                 }
229                         }
231                         #ifdef DEBUG_PLUGINS
232                         cerr<<"Finished executing"<<endl;
233                         #endif
234                 }
235         }
237         //we can safely turn this off here
238         m_ResetingAudioThread = false;
241 //////////////////////////////////////////////////////////
243 void SynthModular::UpdatePluginGUIs()
245         // see if any need deleting
246         for (map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
247                  i!=m_DeviceWinMap.end(); i++)
248         {
249                 if (i->second->m_DeviceGUI && i->second->m_DeviceGUI->GetPluginWindow())
250                 {
251                         SpiralPluginGUI *GUI=(SpiralPluginGUI *)i->second->m_DeviceGUI->GetPluginWindow();
252                         GUI->Update();
253                 }
255                 if (i->second->m_DeviceGUI && i->second->m_DeviceGUI->Killed())
256                 {
257                         bool erase = true;
259                         //Stop processing of audio if any
260                         if (i->second->m_Device)
261                         {
262                                 if (i->second->m_Device->Kill());
263                                 erase = false;
264                         }
266                         //Clear GUI Device
267                         i->second->m_DeviceGUI->Clear();
269                         // Hide Device GUI FIRST
270                         if (i->second->m_DeviceGUI->GetPluginWindow())
271                         {
272                                 i->second->m_DeviceGUI->GetPluginWindow()->hide();
273                         }
275                         //Remove Device GUI from canvas
276                         m_Canvas->RemoveDevice(i->second->m_DeviceGUI);
278                         //Delete Device GUI - must delete here or sometimes plugin will randomly crash
279                         delete i->second->m_DeviceGUI;
280                         i->second->m_DeviceGUI = NULL;
282                         //Erase from winmap if no audio to do it
283                         if (erase)
284                                 m_DeviceWinMap.erase(i);
285                 }
286         }
288         m_Canvas->Poll();
290         if (m_HostNeedsUpdate)
291         {
292                 cout << "Updating SampleRate to: " << SpiralInfo::SAMPLERATE << " and Buffer Size to: " << SpiralInfo::BUFSIZE << " to match current Audio Driver." << endl;
293                 UpdateHostInfo();
294                 m_HostNeedsUpdate = false;
295         }
298 //////////////////////////////////////////////////////////
300 SpiralWindowType *SynthModular::CreateWindow()
302         m_TopWindow = new SpiralWindowType(MAIN_WIDTH, MAIN_HEIGHT, LABEL.c_str());
303         m_TopWindow->user_data((void*)(this));
304         //m_TopWindow->resizable(m_TopWindow);
305         m_MainMenu = new Fl_Menu_Bar (0, 0, MAIN_WIDTH, 20, "");
306         m_MainMenu->user_data((void*)(this));
307         m_MainMenu->box(FL_PLASTIC_UP_BOX);
308         m_MainMenu->textsize (10);
309         m_MainMenu->add ("File/New", 0, cb_New, (void*)(this), FL_MENU_DIVIDER);
310         m_MainMenu->add ("File/Load", 0, cb_Load, (void*)(this), 0);
311         m_MainMenu->add ("File/Save As", 0, cb_Save, (void*)(this), 0);
312         m_MainMenu->add ("File/Merge", 0, cb_Merge, (void*)(this), FL_MENU_DIVIDER);
313         m_MainMenu->add ("File/Exit", 0, cb_Close, (void*)(this), 0);
314         m_MainMenu->add ("Edit/Cut", 0, cb_Cut, (void*)(this), 0);
315         m_MainMenu->add ("Edit/Copy", 0, cb_Copy, (void*)(this), 0);
316         m_MainMenu->add ("Edit/Paste", 0, cb_Paste, (void*)(this), 0);
317         m_MainMenu->add ("Edit/Delete", 0, cb_Delete, (void*)(this), FL_MENU_DIVIDER);
318         //m_MainMenu->add ("Edit/Toolbars/Plugins", 0, cb_Undefined, (void*)(this), 0);
319         //m_MainMenu->add ("Edit/Toolbars/Function", 0, cb_Undefined, (void*)(this), 0);
320         m_MainMenu->add ("Edit/Options", 0, cb_Options, (void*)(this), 0);
321         m_MainMenu->add ("Plugins/dummy", 0, NULL, NULL, 0);
322         m_MainMenu->add ("Audio/Pause", 0, cb_PlayPause, NULL, 0);
323         m_MainMenu->add ("Audio/Reset", 0, cb_Reset, NULL, 0);
324         //m_MainMenu->add ("Help/Plugins/dummy", 0, NULL, NULL, 0);
325         //m_MainMenu->add ("Help/Credits", 0, NULL, (void*)(this), 0);
326         //m_MainMenu->add ("Help/About", 0, NULL, (void*)(this), 0);
327         m_TopWindow->add (m_MainMenu);
328         int but = 50;
329         int ToolbarHeight = but + 0;
330         m_Topbar = new Fl_Pack (0, 20, MAIN_WIDTH, ToolbarHeight, "");
331         m_Topbar->user_data((void*)(this));
332         m_Topbar->type(FL_HORIZONTAL);
333         m_Topbar->color(SpiralInfo::GUICOL_Button);
334         m_TopWindow->add(m_Topbar);
336         m_ToolbarPanel = new Fl_Pack (0, 20, but*6, ToolbarHeight, "");
337         m_ToolbarPanel->user_data((void*)(this));
338         m_ToolbarPanel->type(FL_VERTICAL);
339         m_ToolbarPanel->color(SpiralInfo::GUICOL_Button);
340         m_Topbar->add(m_ToolbarPanel);
342         m_Toolbar = new Fl_Pack (0, 20, but*6, but, "");
343         m_Toolbar->user_data((void*)(this));
344         m_Toolbar->type(FL_HORIZONTAL);
345         m_Toolbar->color(SpiralInfo::GUICOL_Button);
346         m_ToolbarPanel->add(m_Toolbar);
348         m_Load = new Fl_Button (0, 0, but, but, "");
349         m_Load->user_data ((void*)(this));
350         Fl_Pixmap *tPix = new Fl_Pixmap(load_xpm);
351         m_Load->image(tPix->copy());
352         delete tPix;
353         m_Load->type(0);
354         m_Load->box(FL_PLASTIC_UP_BOX);
355         m_Load->color(SpiralInfo::GUICOL_Button);
356         m_Load->selection_color(SpiralInfo::GUICOL_Tool);
357         m_Load->labelsize (1);
358         m_Load->tooltip("Load a patch file");
359         m_Load->callback((Fl_Callback*)cb_Load);
360         m_Toolbar->add(m_Load);
362         m_Save = new Fl_Button(0, 0, but, but, "");
363         m_Save->user_data ((void*)(this));
364         tPix = new Fl_Pixmap(save_xpm);
365         m_Save->image(tPix->copy());
366         delete tPix;
367         m_Save->type(0);
368         m_Save->box(FL_PLASTIC_UP_BOX);
369         m_Save->color(SpiralInfo::GUICOL_Button);
370         m_Save->selection_color(SpiralInfo::GUICOL_Tool);
371         m_Save->labelsize (1);
372         m_Save->tooltip("Save a patch file");
373         m_Save->callback((Fl_Callback*)cb_Save);
374         m_Toolbar->add(m_Save);
376         m_New = new Fl_Button(0, 0, but, but, "");
377         m_New->user_data ((void*)(this));
378         tPix = new Fl_Pixmap(new_xpm);
379         m_New->image(tPix->copy());
380         delete tPix;
381         m_New->type(0);
382         m_New->box(FL_PLASTIC_UP_BOX);
383         m_New->color(SpiralInfo::GUICOL_Button);
384         m_New->selection_color(SpiralInfo::GUICOL_Tool);
385         m_New->labelsize (1);
386         m_New->tooltip("New patch");
387         m_New->callback((Fl_Callback*)cb_New);
388         m_Toolbar->add(m_New);
390         m_Options = new Fl_Button(0, 0, but, but, "");
391         m_Options->user_data ((void*)(this));
392         tPix = new Fl_Pixmap(options_xpm);
393         m_Options->image(tPix->copy());
394         delete tPix;
395         m_Options->type(0);
396         m_Options->box(FL_PLASTIC_UP_BOX);
397         m_Options->color(SpiralInfo::GUICOL_Button);
398         m_Options->selection_color(SpiralInfo::GUICOL_Tool);
399         m_Options->labelsize (1);
400         m_Options->tooltip("Options");
401         m_Options->callback((Fl_Callback*)cb_Options);
402         m_Toolbar->add(m_Options);
404         m_NewComment = new Fl_Button(0, 0, but, but, "");
405         tPix = new Fl_Pixmap(comment_xpm);
406         m_NewComment->image(tPix->copy());
407         delete tPix;
408         m_NewComment->type(0);
409         m_NewComment->box(FL_PLASTIC_UP_BOX);
410         m_NewComment->color(SpiralInfo::GUICOL_Button);
411         m_NewComment->selection_color(SpiralInfo::GUICOL_Tool);
412         m_NewComment->labelsize (1);
413         m_NewComment->tooltip("New comment");
414         m_NewComment->callback((Fl_Callback*)cb_NewComment);
415         m_Toolbar->add(m_NewComment);
417         m_PlayResetGroup = new Fl_Pack (0, 0, but, but, "");
418         m_PlayResetGroup->color(SpiralInfo::GUICOL_Button);
419         m_Toolbar->add(m_PlayResetGroup);
421         m_PlayPause = new Fl_Button(0, 0, but, but/2, "@||");
422         m_PlayPause->user_data((void*)(this));
423         m_PlayPause->type(0);
424         m_PlayPause->box(FL_PLASTIC_UP_BOX);
425         m_PlayPause->color(SpiralInfo::GUICOL_Button);
426         m_PlayPause->selection_color(SpiralInfo::GUICOL_Tool);
427         m_PlayPause->labelsize (10);
428         m_PlayPause->tooltip("Pause");
429         m_PlayPause->callback((Fl_Callback*)cb_PlayPause);
430         m_PlayResetGroup->add(m_PlayPause);
432         m_Reset = new Fl_Button(0, 0, but, but/2, "Reset");
433         m_Reset->box(FL_PLASTIC_UP_BOX);
434         m_Reset->color(SpiralInfo::GUICOL_Button);
435         m_Reset->user_data((void*)(this));
436         m_Reset->selection_color(SpiralInfo::GUICOL_Tool);
437         m_Reset->labelsize (10);
438         m_Reset->tooltip("Reset Audio State of all Plugins");
439         m_Reset->callback((Fl_Callback*)cb_Reset);
440         m_PlayResetGroup->add(m_Reset);
442         m_GroupFiller = new Fl_Group (0, 0, 0, ToolbarHeight, "");
443         m_GroupFiller->color(SpiralInfo::GUICOL_Button);
444         m_Topbar->add (m_GroupFiller);
446         m_GroupTab = new Fl_Tabs (0, 0, MAIN_WIDTH-m_GroupFiller->w()-but*6, ToolbarHeight, "");
447         m_GroupTab->user_data ((void*)(this));
448         m_GroupTab->box(FL_PLASTIC_DOWN_BOX);
449         m_GroupTab->color(SpiralInfo::GUICOL_Button);
450         m_GroupTab->callback((Fl_Callback*)cb_GroupTab);
451         m_Topbar->add (m_GroupTab);
453         m_Topbar->resizable(m_GroupTab);
455         /////////////////
457         ToolbarHeight += 20; // Stretch this a bit to allow room for the menu-bar too.
458         m_CanvasScroll = new Fl_Scroll (0, ToolbarHeight, MAIN_WIDTH, MAIN_HEIGHT-ToolbarHeight, "");
459         m_TopWindow->add(m_CanvasScroll);
460         m_TopWindow->resizable(m_CanvasScroll);
462         m_Canvas = new Fl_Canvas(-5000, -5000, 10000, 10000, "");
463         m_Canvas->type(1);
464         m_Canvas->box(FL_FLAT_BOX);
465         m_Canvas->labeltype(FL_ENGRAVED_LABEL);
466         m_Canvas->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE);
467         m_Canvas->color(SpiralInfo::GUICOL_Canvas);
468         m_Canvas->user_data((void*)(this));
469         m_Canvas->SetConnectionCallback((Fl_Callback*)cb_Connection);
470         m_Canvas->SetUnconnectCallback((Fl_Callback*)cb_Unconnect);
471         m_Canvas->SetAddDeviceCallback((Fl_Callback*)cb_NewDeviceFromCanvasMenu);
472         m_Canvas->SetCutDeviceGroupCallback((Fl_Callback*)cb_Cut);
473         m_Canvas->SetCopyDeviceGroupCallback((Fl_Callback*)cb_Copy);
474         m_Canvas->SetPasteDeviceGroupCallback((Fl_Callback*)cb_Paste);
475         m_Canvas->SetMergePatchCallback((Fl_Callback*)cb_Merge);
477         m_CanvasScroll->add(m_Canvas);
479         m_SettingsWindow = new SettingsWindow;
480         m_SettingsWindow->RegisterApp(this);
482         return m_TopWindow;
485 //////////////////////////////////////////////////////////
487 vector<string> SynthModular::BuildPluginList (const string &Path) {
488         // Scan plugin path for plugins.
489         DIR *dp;
490         struct dirent *ep;
491         struct stat sb;
492         void *handle;
493         string fullpath;
494         const char *path = Path.c_str();
495         vector<string> ret;
497         dp = opendir(path);
498         if (!dp) {
499            cerr << "WARNING: Could not open path " << path << endl;
500         }
501         else {
502                 while ((ep = readdir(dp))) {
503                         // Need full path
504                         fullpath = path;
505                         fullpath.append(ep->d_name);
507                         // Stat file to get type
508                         if (!stat(fullpath.c_str(), &sb)) {
509                                 // We only want regular files
510                                 if (S_ISREG(sb.st_mode))  {
511                                         // We're not fussed about resolving symbols yet, since we are just
512                                         // checking if it's a DLL.
513                                         handle = dlopen(fullpath.c_str(), RTLD_LAZY);
514                                         if (!handle) {
515                                                 cerr << "WARNING: File " << path << ep->d_name
516                                                         << " could not be examined" << endl;
517                                                 cerr << "dlerror() output:" << endl;
518                                                 cerr << dlerror() << endl;
519                                         }
520                                         else {
521                                                 // It's a DLL. Add name to list
522                                                 ret.push_back(ep->d_name);
523                                         }
524                                 }
525                         }
526                 }
527         }
528         return ret;
531 void SynthModular::LoadPlugins (string pluginPath) {
532      int Width  = 35;
533      int Height = 35;
534      int SWidth  = 256;
535      int SHeight = 256;
536      Fl_Pixmap pic (SSM_xpm);
537      Fl_Double_Window* Splash = new Fl_Double_Window ((Fl::w()/2) - (SWidth/2), (Fl::h()/2) - (SHeight/2),
538                                                        SWidth, SHeight, "SSM");
539      Splash->border(0);
540      Fl_Box* pbut = new Fl_Box (0, 8, SWidth, SHeight, "");
541      pbut->box (FL_NO_BOX);
542      pic.label (pbut);
543      Fl_Box *splashtext = new Fl_Box (5, SHeight-20, 200, 20, "Loading...");
544      splashtext->labelsize (10);
545      splashtext->box (FL_NO_BOX);
546      splashtext->align (FL_ALIGN_INSIDE | FL_ALIGN_LEFT);
547      Splash->add (pbut);
548      Splash->add (splashtext);
549      Splash->show();
550      int ID=-1;
551      vector<string> PluginVector;
552      if (SpiralInfo::USEPLUGINLIST) PluginVector = SpiralInfo::PLUGINVEC;
553      else {
554         if (pluginPath.empty()) PluginVector = BuildPluginList (SpiralInfo::PLUGIN_PATH);
555         else {
556            string::iterator i = pluginPath.end() - 1;
557            if (*i != '/') pluginPath += '/';
558            PluginVector = BuildPluginList (pluginPath);
559         }
560      }
561      for (vector<string>::iterator i=PluginVector.begin(); i!=PluginVector.end(); i++) {
562          string Fullpath;
563          if (pluginPath=="") Fullpath=SpiralInfo::PLUGIN_PATH+*i;
564          else Fullpath = pluginPath + *"/" + *i;
565          ID = PluginManager::Get()->LoadPlugin (Fullpath.c_str());
566          if (ID!=PluginError) {
567             #ifdef DEBUG_PLUGINS
568             cerr << ID << " = Plugin [" << *i << "]" << endl;
569             #endif
570             Fl_ToolButton *NewButton = new Fl_ToolButton (0, 0, Width, Height, "");
571             // we can't set user data, because the callback uses it
572             // NewButton->user_data ((void*)(this));
573             NewButton->labelsize (1);
574             Fl_Pixmap *tPix = new Fl_Pixmap (PluginManager::Get()->GetPlugin(ID)->GetIcon());
575             NewButton->image(tPix->copy(tPix->w(),tPix->h()));
576             delete tPix;
577             string GroupName = PluginManager::Get()->GetPlugin(ID)->GetGroupName();
578             Fl_Pack* the_group=NULL;
579             // find or create this group, and add an icon
580             map<string,Fl_Pack*>::iterator gi = m_PluginGroupMap.find (GroupName);
581             if (gi == m_PluginGroupMap.end()) {
582                the_group = new Fl_Pack (m_GroupTab->x(), 16, m_GroupTab->w(), m_GroupTab->h() - 15, GroupName.c_str());
583                the_group->type(FL_HORIZONTAL);
584                the_group->labelsize(8);
585                the_group->color(SpiralInfo::GUICOL_Button);
586                the_group->user_data((void*)(this));
587                //m_GroupTab->add(the_group);
588                m_GroupTab->value(the_group);
589                m_PluginGroupMap[GroupName]=the_group;
590             }
591             else the_group = gi->second;
592             NewButton->type (0);
593             NewButton->box (FL_NO_BOX);
594             NewButton->down_box (FL_NO_BOX);
595             //NewButton->color(SpiralInfo::GUICOL_Button);
596             //NewButton->selection_color(SpiralInfo::GUICOL_Button);
597             the_group->add (NewButton);
599             // we need to keep tooltips stored outside their widgets - widgets just have a pointer
600             // I haven't done anything about cleaning up these strings - which may cause memory leaks?
601             // But m_DeviceVec - which, I assume, would be used to keep track of / clean up the dynamicly
602             //     created NewButton widgets isn't cleaned up either, so we might have 2 memory leaks
603             //     involved? - but then again, they might be automatically deallocated because they're
604             //     in another widget, in which case there's just one memory leak to deal with. (andy)
605             string* PluginName = new string (*i);
606             // find the first slash, if there is one, and get rid of everything before and including it
607             unsigned int p = PluginName->find ('/');
608             if (p < PluginName->length()) PluginName->erase (0, p);
609             // find last . and get rid of everything after and including it
610             p = PluginName->rfind ('.');
611             unsigned int l = PluginName->length ();
612             if (p < l) PluginName->erase (p, l);
613             NewButton->tooltip (PluginName->c_str());
614             // Slashes have significance to the menu widgets, remove them from the GroupName
615             while ((p = GroupName.find ('/')) < PluginName->length())
616                   GroupName = GroupName.replace (p, 1, " and ");
617             string MenuEntry = "Plugins/" + GroupName + "/" + *PluginName;
618             m_MainMenu->add (MenuEntry.c_str(), 0, cb_NewDeviceFromMenu, &Numbers[ID], 0);
619             // when help is working better - this will put the plugins into the help menu
620             // MenuEntry = "Help/" + MenuEntry;
621             // m_MainMenu->add (MenuEntry.c_str(), 0, NULL, &Numbers[ID], 0);
623             // Add the plugins to the canvas menu
624             m_Canvas->AddPluginName (MenuEntry, PluginManager::Get()->GetPlugin(ID)->ID);
625             // this overwrites the widget's user_data with that specified for the callback
626             // so we can't use it for other purposes
627             NewButton->callback ((Fl_Callback*)cb_NewDevice, &Numbers[ID]);
628             NewButton->show();
629             // Nothing else ever touches m_DeviceVec - is this right??? (andy)
630             m_DeviceVec.push_back (NewButton);
631             the_group->redraw();
632             // m_NextPluginButton++;
633             Fl::check();
634             splashtext->label (PluginName->c_str());
635             Splash->redraw();
636          }
637      }
638      map<string,Fl_Pack*>::iterator PlugGrp;
639      for (PlugGrp = m_PluginGroupMap.begin(); PlugGrp!= m_PluginGroupMap.end(); ++PlugGrp) {
640          m_GroupTab->add (PlugGrp->second);
641          PlugGrp->second->add (new Fl_Box (0, 0, 600, 100, ""));
642      }
643      // try to show the SpiralSound group
644      PlugGrp = m_PluginGroupMap.find("SpiralSound");
645      // can't find it - show the first plugin group
646      if (PlugGrp==m_PluginGroupMap.end()) PlugGrp=m_PluginGroupMap.begin();
647      m_GroupTab->value(PlugGrp->second);
648      bool found_dummy;
649      int i;
650      do {
651         found_dummy = false;
652         for (i=0; i<m_MainMenu->size(); i++) {
653             if (m_MainMenu->text (i) != NULL) {
654                found_dummy = (strcmp ("dummy", m_MainMenu->text (i)) == 0);
655                if (found_dummy) break;
656             }
657         }
658         if (found_dummy) m_MainMenu->remove (i);
659      } while (found_dummy);
660      Splash->hide();
661      delete Splash;
664 //////////////////////////////////////////////////////////
666 DeviceGUIInfo SynthModular::BuildDeviceGUIInfo(PluginInfo &PInfo)
668         DeviceGUIInfo Info;
669         int Height=50;
671         // tweak the size if we have too many ins/outs
672         if (PInfo.NumInputs>4 || PInfo.NumOutputs>4)
673         {
674                 if (PInfo.NumInputs<PInfo.NumOutputs)
675                 {
676                         Height=PInfo.NumOutputs*10+5;
677                 }
678                 else
679                 {
680                         Height=PInfo.NumInputs*10+5;
681                 }
682         }
684         // Make the guiinfo struct
685         Info.XPos       = 0;
686         Info.YPos       = 0;
687         Info.Width      = 40;
688         Info.Height     = Height;
689         Info.NumInputs  = PInfo.NumInputs;
690         Info.NumOutputs = PInfo.NumOutputs;
691         Info.Name       = PInfo.Name;
692         Info.PortTips   = PInfo.PortTips;
693         Info.PortTypes  = PInfo.PortTypes;
695         return Info;
698 //////////////////////////////////////////////////////////
700 DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y)
702         DeviceWin *nlw = new DeviceWin;
703         const HostsideInfo* Plugin=PluginManager::Get()->GetPlugin(n);
705         if (!Plugin) return NULL;
707         nlw->m_Device=Plugin->CreateInstance();
709         if (!nlw->m_Device) return NULL;
711         nlw->m_Device->SetBlockingCallback(cb_Blocking);
712         nlw->m_Device->SetUpdateCallback(cb_Update);
713         nlw->m_Device->SetParent((void*)this);
715         if ( nlw->m_Device->IsAudioDriver() )
716         {
717                 AudioDriver *driver = ((AudioDriver*)nlw->m_Device);
718                 driver->SetChangeBufferAndSampleRateCallback(cb_ChangeBufferAndSampleRate);
719         }
721         PluginInfo PInfo    = nlw->m_Device->Initialise(&m_Info);
722         SpiralGUIType *temp = nlw->m_Device->CreateGUI();
723         Fl_Pixmap *Pix      = new Fl_Pixmap(Plugin->GetIcon());
724         nlw->m_PluginID     = n;
726         if (temp) temp->position(x+10,y);
728         DeviceGUIInfo Info=BuildDeviceGUIInfo(PInfo);
730         Info.XPos       = x; //TOOLBOX_WIDTH+(rand()%400);
731         Info.YPos       = y; //rand()%400;
733         nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix, nlw->m_Device->IsTerminal());
734         Fl_Canvas::SetDeviceCallbacks(nlw->m_DeviceGUI, m_Canvas);
735         m_Canvas->add(nlw->m_DeviceGUI);
736         m_Canvas->redraw();
738         return nlw;
741 //////////////////////////////////////////////////////////
743 void SynthModular::AddDevice(int n, int x=-1, int y=-1)
745         //cerr<<"Adding "<<m_NextID<<endl;
747         if (x==-1)
748         {
749                 x = m_CanvasScroll->x()+50;
750                 y = m_CanvasScroll->y()+50;
751         }
753         DeviceWin* temp = NewDeviceWin(n,x,y);
754         if (temp)
755         {
756                 int ID=m_NextID++;
757                 //cerr<<"adding device "<<ID<<endl;
758                 temp->m_DeviceGUI->SetID(ID);
759                 temp->m_Device->SetUpdateInfoCallback(ID,cb_UpdatePluginInfo);
760                 m_DeviceWinMap[ID]=temp;
761         }
764 //////////////////////////////////////////////////////////
766 DeviceWin* SynthModular::NewComment(int n, int x=-1, int y=-1)
768         DeviceWin *nlw = new DeviceWin;
770         if (x==-1)
771         {
772                 x = m_CanvasScroll->x()+50;
773                 y = m_CanvasScroll->y()+50;
774         }
776         nlw->m_Device=NULL;
777         nlw->m_PluginID  = COMMENT_ID;
779         DeviceGUIInfo Info;
781         Info.XPos       = x;
782         Info.YPos       = y;
783         Info.Width      = 50;
784         Info.Height     = 20;
785         Info.NumInputs  = 0;
786         Info.NumOutputs = 0;
787         Info.Name = "";
789         nlw->m_DeviceGUI = new Fl_CommentGUI(Info, NULL, NULL);
791         Fl_Canvas::SetDeviceCallbacks(nlw->m_DeviceGUI, m_Canvas);
792         m_Canvas->add(nlw->m_DeviceGUI);
793         m_Canvas->redraw();
795         return nlw;
798 //////////////////////////////////////////////////////////
800 void SynthModular::AddComment(int n)
802         //cerr<<"Adding "<<m_NextID<<endl;
803         DeviceWin* temp = NewComment(n);
804         if (temp)
805         {
806                 int ID=m_NextID++;
807                 //cerr<<"adding comment "<<ID<<endl;
808                 temp->m_DeviceGUI->SetID(ID);
809                 m_DeviceWinMap[ID]=temp;
810         }
813 //////////////////////////////////////////////////////////
815 void SynthModular::cb_ChangeBufferAndSampleRate_i(long int NewBufferSize, long int NewSamplerate)
817         if (SpiralInfo::BUFSIZE != NewBufferSize)
818         {
819                 // update the settings
820                 SpiralInfo::BUFSIZE    = NewBufferSize;
821                 m_HostNeedsUpdate = true;
822         }
824         if (SpiralInfo::SAMPLERATE != NewSamplerate)
825         {
826                 SpiralInfo::SAMPLERATE = NewSamplerate;
827                 m_HostNeedsUpdate = true;
828         }
832 void SynthModular::UpdateHostInfo()
834         /* Pause Audio */
835         FreezeAll();
837         /* update the settings */
838         m_Info.BUFSIZE    = SpiralInfo::BUFSIZE;
839         m_Info.SAMPLERATE = SpiralInfo::SAMPLERATE;
841         /* obsolete - REMOVE SOON  */
842         m_Info.FRAGSIZE   = SpiralInfo::FRAGSIZE;
843         m_Info.FRAGCOUNT  = SpiralInfo::FRAGCOUNT;
844         m_Info.OUTPUTFILE = SpiralInfo::OUTPUTFILE;
845         m_Info.MIDIFILE   = SpiralInfo::MIDIFILE;
846         m_Info.POLY       = SpiralInfo::POLY;
848         /* Reset all plugin ports/buffers befure Resuming */
849         ResetAudio();
852 //////////////////////////////////////////////////////////
854 // called when a callback output plugin wants to run the audio thread
855 void SynthModular::cb_Update(void* o, bool mode)
857         m_CallbackUpdateMode=mode;
858         ((SynthModular*)o)->Update();
861 // called by a blocking output plugin to notify the engine its ready to
862 // take control of the update timing (so take the brakes off)
863 void SynthModular::cb_Blocking(void* o, bool mode)
865         m_BlockingOutputPluginIsReady=mode;
868 //////////////////////////////////////////////////////////
870 iostream &SynthModular::StreamPatchIn(iostream &s, bool paste, bool merge)
872         //if we are merging as opposed to loading a new patch
873         //we have no need to pause audio
874         if (!merge && !paste)
875                 FreezeAll();
877         //if we are pasting we don't have any of the file version
878         //or saving information. since its internal we didn't
879         //need it, but we do have other things we might need to load
881         bool has_file_path;
882         char file_path[1024];
883         string m_FromFilePath;
885         string dummy,dummy2;            
886         int ver;
888         if (paste)
889         {
890                 m_Copied.devices>>has_file_path;
892                 if (has_file_path)
893                 {
894                   m_Copied.devices.getline(file_path, 1024);
895                   m_FromFilePath = file_path;
896                   cerr << file_path << endl;
897                 }  
898                   
899         }
900         else
901         {
902                 s>>dummy>>dummy>>dummy>>ver;
904                 if (ver>FILE_VERSION)
905                 {
906                         SpiralInfo::Alert("Bad file, or more recent version.");
907                         ThawAll();
908                         return s;
909                 }
911                 if (ver>2)
912                 {
913                         int MainWinX,MainWinY,MainWinW,MainWinH;
914                         int EditWinX,EditWinY,EditWinW,EditWinH;
916                         s>>MainWinX>>MainWinY>>MainWinW>>MainWinH;
917                         s>>EditWinX>>EditWinY>>EditWinW>>EditWinH;
919                         //o.m_MainWindow->resize(MainWinX,MainWinY,MainWinW,MainWinH);
920                         //o.m_EditorWindow->resize(EditWinX,EditWinY,EditWinW,EditWinH);
921                 }
922                 
923                 if (merge)
924                         m_FromFilePath = m_MergeFilePath;
925         }
927         //wether pasting or merging we need to clear the current 
928         //selection so we can replace it with the new devices
929         if (paste || merge)
930                 Fl_Canvas::ClearSelection(m_Canvas);
931         
932         int Num, ID, PluginID, x,y,ps,px,py;
933         
934         if (paste)
935         {
936                 Num = m_Copied.devicecount;
937         }
938         else
939         {
940                 s>>dummy>>Num;
941         }
943         for(int n=0; n<Num; n++)
944         {
945                 #ifdef DEBUG_STREAM
946                 cerr<<"Loading Device "<<n<<endl;
947                 #endif
949                 s>>dummy; // "Device"
950                 s>>ID;
951                 s>>dummy2; // "Plugin"
952                 s>>PluginID;
953                 s>>x>>y;
955                 string Name;
957                 if (paste || ver>3)
958                 {
959                         // load the device name
960                         int size;
961                         char Buf[1024];
962                         s>>size;
963                         s.ignore(1);
964                         if (size > 0) {
965                                 s.get(Buf,size+1);
966                                 Name=Buf;
967                         } else {
968                                 Name = "";
969                         }
970                 }
972                 #ifdef DEBUG_STREAM
973                 cerr<<dummy<<" "<<ID<<" "<<dummy2<<" "<<PluginID<<" "<<x<<" "<<y<<endl;
974                 #endif
976                 if (paste || ver>1) s>>ps>>px>>py;
977                 
978                 //if we are merging a patch or pasting we will change duplicate ID's
979                 if (!paste && !merge)
980                 {
981                         // Check we're not duplicating an ID
982                         if (m_DeviceWinMap.find(ID)!=m_DeviceWinMap.end())
983                         {
984                                 SpiralInfo::Alert("Duplicate device ID found in file - aborting load");
985                                 ThawAll();
986                                 return s;
987                         }
988                 }
990                 if (PluginID==COMMENT_ID)
991                 {
992                         DeviceWin* temp = NewComment(PluginID, x, y);
993                         if (temp)
994                         {
995                                 if (paste || merge)
996                                 {
997                                         m_Copied.m_DeviceIds[ID] = m_NextID++;
998                                         ID = m_Copied.m_DeviceIds[ID];
999                                 }       
1001                                 temp->m_DeviceGUI->SetID(ID);
1002                                 m_DeviceWinMap[ID]=temp;
1003                                 ((Fl_CommentGUI*)(m_DeviceWinMap[ID]->m_DeviceGUI))->StreamIn(s); // load the plugin
1005                                 if (paste || merge)
1006                                         Fl_Canvas::AppendSelection(ID, m_Canvas);
1007                                 else
1008                                         if (m_NextID<=ID) m_NextID=ID+1;
1010                         }
1011                 }
1012                 else
1013                 {
1014                         DeviceWin* temp = NewDeviceWin(PluginID, x, y);
1015                         if (temp)
1016                         {
1017                                 int oldID=ID;
1018                                 if (paste || merge)
1019                                 {
1020                                         m_Copied.m_DeviceIds[ID] = m_NextID++;
1022                                         ID = m_Copied.m_DeviceIds[ID];
1023                                 }
1025                                 temp->m_DeviceGUI->SetID(ID);
1027                                 if (paste || ver>3)
1028                                 {
1029                                         // set the titlebars
1030                                         temp->m_DeviceGUI->SetName(Name);
1031                                 }
1033                                 temp->m_Device->SetUpdateInfoCallback(ID,cb_UpdatePluginInfo);
1034                                 m_DeviceWinMap[ID]=temp;
1035                                 m_DeviceWinMap[ID]->m_Device->StreamIn(s); // load the plugin
1037                                 // load external files
1038                                 if (paste || merge)
1039                                         m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FromFilePath+"_files/", oldID);
1040                                 else
1041                                         m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FilePath+"_files/");
1043                                 if ((paste || ver>1) && m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow())
1044                                 {
1045                                         // set the GUI up with the loaded values
1046                                         // looks messy, but if we do it here, the plugin and it's gui can remain
1047                                         // totally seperated.
1048                                         ((SpiralPluginGUI*)(m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()))->
1049                                                 UpdateValues(m_DeviceWinMap[ID]->m_Device);
1051                                         // updates the data in the channel buffers, so the values don't
1052                                         // get overwritten in the next tick. (should maybe be somewhere else)
1053                                         m_DeviceWinMap[ID]->m_Device->GetChannelHandler()->FlushChannels();
1055                                         // position the plugin window in the main window
1056                                         //m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()->position(px,py);
1058                                         if (ps)
1059                                         {
1060                                                 m_DeviceWinMap[ID]->m_DeviceGUI->Maximise();
1061                                                 // reposition after maximise
1062                                                 m_DeviceWinMap[ID]->m_DeviceGUI->position(x,y);
1063                                         }
1064                                         else m_DeviceWinMap[ID]->m_DeviceGUI->Minimise();
1065                                         
1066                                         if (paste || merge)
1067                                                 Fl_Canvas::AppendSelection(ID, m_Canvas);
1068                                 }
1070                                 if (!paste && !merge)
1071                                         if (m_NextID<=ID) m_NextID=ID+1;
1072                         }
1073                         else
1074                         {
1075                                 // can't really recover if the plugin ID doesn't match a plugin, as
1076                             // we have no idea how much data in the stream belongs to this plugin
1077                                 SpiralInfo::Alert("Error in stream, can't really recover data from here on.");
1078                                 return s;
1079                         }
1080                 }
1081         }
1083         if (!paste && !merge)
1084         {
1085                 s>>*m_Canvas;
1086                 ThawAll();
1087         }
1088         
1089         return s;
1092 iostream &operator>>(iostream &s, SynthModular &o)
1094         return o.StreamPatchIn(s, false, false);
1098 //////////////////////////////////////////////////////////
1100 ostream &operator<<(ostream &s, SynthModular &o)
1102         o.FreezeAll();
1104         s<<"SpiralSynthModular File Ver "<<FILE_VERSION<<endl;
1106         // make external files dir
1107         bool ExternalDirUsed=false;
1108         string command("mkdir '"+o.m_FilePath+"_files'");
1109         system(command.c_str());
1111         if (FILE_VERSION>2)
1112         {
1113                 s<<o.m_TopWindow->x()<<" "<<o.m_TopWindow->y()<<" ";
1114                 s<<o.m_TopWindow->w()<<" "<<o.m_TopWindow->h()<<" ";
1115                 s<<0<<" "<<0<<" ";
1116                 s<<0<<" "<<0<<endl;
1117         }
1119         // save out the SynthModular
1120         s<<"SectionList"<<endl;
1121         s<<o.m_DeviceWinMap.size()<<endl;
1123         for(map<int,DeviceWin*>::iterator i=o.m_DeviceWinMap.begin();
1124                 i!=o.m_DeviceWinMap.end(); i++)
1125         {
1126                 if (i->second->m_DeviceGUI && ((i->second->m_Device) || (i->second->m_PluginID==COMMENT_ID)))
1127                 {
1128                         s<<endl;
1129                         s<<"Device ";
1130                         s<<i->first<<" "; // save the id
1131                         s<<"Plugin ";
1132                         s<<i->second->m_PluginID<<endl;
1133                         s<<i->second->m_DeviceGUI->x()<<" ";
1134                         s<<i->second->m_DeviceGUI->y()<<" ";
1135                         s<<i->second->m_DeviceGUI->GetName().size()<<" ";
1136                         s<<i->second->m_DeviceGUI->GetName()<<" ";
1138                         if (i->second->m_DeviceGUI->GetPluginWindow())
1139                         {
1140                                 s<<i->second->m_DeviceGUI->GetPluginWindow()->visible()<<" ";
1141                                 s<<i->second->m_DeviceGUI->GetPluginWindow()->x()<<" ";
1142                                 s<<i->second->m_DeviceGUI->GetPluginWindow()->y()<<" ";
1143                         }
1144                         else
1145                         {
1146                                 s<<0<<" "<<0<<" "<<0;
1147                         }
1149                         s<<endl;
1151                         if (i->second->m_PluginID==COMMENT_ID)
1152                         {
1153                                 // save the comment gui
1154                                 ((Fl_CommentGUI*)(i->second->m_DeviceGUI))->StreamOut(s);
1155                         }
1156                         else
1157                         {
1158                                 // save the plugin
1159                                 i->second->m_Device->StreamOut(s);
1160                         }
1161                         s<<endl;
1163                         // save external files
1164                         if (i->second->m_Device && i->second->m_Device->SaveExternalFiles(o.m_FilePath+"_files/"))
1165                         {
1166                                 ExternalDirUsed=true;
1167                         }
1168                 }
1169         }
1171         s<<endl<<*o.m_Canvas<<endl;
1173         // remove it if it wasn't used
1174         if (!ExternalDirUsed)
1175         {
1176                 // i guess rmdir won't work if there is something in the dir
1177                 // anyway, but best to be on the safe side. (could do rm -rf) :)
1178                 string command("rmdir "+o.m_FilePath+"_files");
1179                 system(command.c_str());
1180         }
1182         o.ThawAll();
1184         return s;
1187 //////////////////////////////////////////////////////////////////////////////////////////
1189 // Callbacks
1191 /////////////////////////////////
1192 // File Menu & associated buttons
1194 // New
1196 inline void SynthModular::cb_New_i (Fl_Widget *o, void *v) {
1197        if (m_DeviceWinMap.size()>0 && !Pawfal_YesNo ("New - Lose changes to current patch?"))
1198           return;
1199        m_TopWindow->label (TITLEBAR.c_str());
1200        ClearUp();
1203 void SynthModular::cb_New (Fl_Widget *o, void *v) {
1204      ((SynthModular*)(o->user_data()))->cb_New_i (o, v);
1207 // Load
1209 inline void SynthModular::cb_Load_i (Fl_Widget *o, void *v) {
1210        if (m_DeviceWinMap.size()>0 && !Pawfal_YesNo ("Load - Lose changes to current patch?"))
1211           return;
1212        char *fn=fl_file_chooser ("Load a patch", "*.ssm", NULL);
1213        if (fn && fn!='\0') {
1214           ifstream in (fn);
1215           if (in) {
1216              fstream inf;
1217              inf.open (fn, ios::in);
1218              m_FilePath = fn;
1219              ClearUp();
1220              inf >> *this;
1221              inf.close();
1222              TITLEBAR = LABEL + " " + fn;
1223              m_TopWindow->label (TITLEBAR.c_str());
1224           }
1225        }
1228 void SynthModular::cb_Load(Fl_Widget *o, void *v) {
1229      ((SynthModular*)(o->user_data()))->cb_Load_i (o, v);
1232 // Save
1234 inline void SynthModular::cb_Save_i (Fl_Widget *o, void *v) {
1235        char *fn=fl_file_chooser("Save a patch", "*.ssm", NULL);
1236        if (fn && fn!='\0') {
1237           ifstream ifl (fn);
1238           if (ifl) {
1239              if (!Pawfal_YesNo ("File [%s] exists, overwrite?", fn))
1240                 return;
1241           }
1242           ofstream of (fn);
1243           if (of) {
1244              m_FilePath = fn;
1245              of << *this;
1246              TITLEBAR = LABEL + " " + fn;
1247              m_TopWindow->label (TITLEBAR.c_str());
1248           }
1249           else {
1250               fl_message ( "%s", string ("Error saving " + string(fn)).c_str());
1251           }
1252        }
1255 void SynthModular::cb_Save (Fl_Widget *o, void *v) {
1256      ((SynthModular*)(o->user_data()))->cb_Save_i (o, v);
1259 // Merge
1261 inline void SynthModular::cb_Merge_i (Fl_Widget *o, void *v) {
1262        char *fn = fl_file_chooser ("Merge a patch", "*.ssm", NULL);
1263        if (fn && fn!='\0') {
1264           ifstream in (fn);
1265           if (in) {
1266              fstream inf;
1267              inf.open (fn, ios::in);
1268              m_MergeFilePath = fn;
1269              StreamPatchIn (inf, false, true);
1270              m_Canvas->StreamSelectionWiresIn (inf, m_Copied.m_DeviceIds, true, false);
1271              inf.close();
1272           }
1273        }
1276 void SynthModular::cb_Merge (Fl_Widget *o, void *v) {
1277      ((SynthModular*)(o->parent()->user_data()))->cb_Merge_i (o, v);
1280 // Close
1282 inline void SynthModular::cb_Close_i (Fl_Widget *o, void *v) {
1283        m_SettingsWindow->hide();
1284        delete m_SettingsWindow;
1285        m_TopWindow->hide();
1286        delete m_TopWindow;
1289 void SynthModular::cb_Close (Fl_Widget *o, void *v) {
1290      ((SynthModular*)(o->user_data()))->cb_Close_i (o, v);
1293 /////////////////////////////////
1294 // Edit Menu
1296 // Cut
1298 inline void SynthModular::cb_Cut_i(Fl_Widget *o, void *v) {
1299        if (! m_Canvas->HaveSelection()) return;
1300        // show some warning here
1301        cb_Copy_i (o, v);  // should we be calling an inline function here??????
1302        for (unsigned int i=0; i<m_Canvas->Selection().m_DeviceIds.size(); i++) {
1303            int ID = m_Canvas->Selection().m_DeviceIds[i];
1304            Fl_DeviceGUI::Kill(m_DeviceWinMap[ID]->m_DeviceGUI);
1305        }
1306        Fl_Canvas::ClearSelection(m_Canvas);
1309 void SynthModular::cb_Cut (Fl_Widget *o, void *v) {
1310      ((SynthModular*)(o->parent()->user_data()))->cb_Cut_i (o, v);
1313 // Copy
1315 inline void SynthModular::cb_Copy_i (Fl_Widget *o, void *v) {
1316        if (! m_Canvas->HaveSelection()) return;
1317        m_Copied.devices.open ("___temp.ssmcopytmp", ios::out);
1318        m_Copied.devicecount = 0;
1319        m_Copied.m_DeviceIds.clear();
1320        if (m_FilePath != "") {
1321            m_Copied.devices << true << " "  << m_FilePath << endl;
1322        }    
1323        else m_Copied.devices << false << endl;
1324        for (unsigned int i=0; i<m_Canvas->Selection().m_DeviceIds.size(); i++) {
1325            int ID = m_Canvas->Selection().m_DeviceIds[i];
1326            std::map<int,DeviceWin*>::iterator j = m_DeviceWinMap.find(ID);
1327            m_Copied.m_DeviceIds[ID] = ID;
1328            m_Copied.devicecount += 1;
1329            m_Copied.devices << "Device " << j->first << " " ; // save the id
1330            m_Copied.devices << "Plugin " <<j->second->m_PluginID << endl;
1331            m_Copied.devices << j->second->m_DeviceGUI->x() << " ";
1332            m_Copied.devices << j->second->m_DeviceGUI->y() << " ";
1333            m_Copied.devices << j->second->m_DeviceGUI->GetName().size() << " ";
1334            m_Copied.devices << j->second->m_DeviceGUI->GetName() << " ";
1335            if (j->second->m_DeviceGUI->GetPluginWindow()) {
1336               m_Copied.devices << j->second->m_DeviceGUI->GetPluginWindow()->visible() << " ";
1337               m_Copied.devices << j->second->m_DeviceGUI->GetPluginWindow()->x() << " ";
1338               m_Copied.devices << j->second->m_DeviceGUI->GetPluginWindow()->y() << " ";
1339            }
1340            else m_Copied.devices << 0 << " " << 0 << " " << 0;
1341            m_Copied.devices << endl;
1342            if (j->second->m_PluginID == COMMENT_ID) {
1343               // save the comment gui
1344               ((Fl_CommentGUI*)(j->second->m_DeviceGUI))->StreamOut (m_Copied.devices);
1345            }
1346            else {
1347               // save the plugin
1348               j->second->m_Device->StreamOut (m_Copied.devices);
1349            }
1350            m_Copied.devices<<endl;
1351        }
1352        m_Canvas->StreamSelectionWiresOut(m_Copied.devices);
1353        m_Copied.devices.close();
1354        Fl_Canvas::EnablePaste (m_Canvas);
1357 void SynthModular::cb_Copy (Fl_Widget *o, void *v) {
1358      ((SynthModular*)(o->parent()->user_data()))->cb_Copy_i (o, v);
1361 // Paste
1363 inline void SynthModular::cb_Paste_i (Fl_Widget *o, void *v) {
1364        if (m_Copied.devicecount <= 0) return;
1365        m_Copied.devices.open ("___temp.ssmcopytmp", ios::in);
1366        StreamPatchIn(m_Copied.devices, true, false);
1367        m_Canvas->StreamSelectionWiresIn (m_Copied.devices, m_Copied.m_DeviceIds, false, true);
1368        m_Copied.devices.close();
1371 void SynthModular::cb_Paste (Fl_Widget *o, void *v) {
1372      ((SynthModular*)(o->parent()->user_data()))->cb_Paste_i (o, v);
1375 // Delete
1377 inline void SynthModular::cb_Delete_i (Fl_Widget *o, void *v) {
1378        m_Canvas->DeleteSelection();
1381 void SynthModular::cb_Delete (Fl_Widget *o, void *v) {
1382      ((SynthModular*)(o->user_data()))->cb_Delete_i (o, v);
1385 // Options
1387 inline void SynthModular::cb_Options_i (Fl_Widget *o, void *v) {
1388        m_SettingsWindow->show();
1391 void SynthModular::cb_Options (Fl_Widget* o, void* v) {
1392      ((SynthModular*)(o->user_data()))->cb_Options_i (o, v);
1395 /////////////////////////////////
1396 // Plugin Menu
1398 // This callback has the name that the callback for the canvas menu
1399 // used to have please note - that is now NewDeviceFromCanvasMenu
1401 inline void SynthModular::cb_NewDeviceFromMenu_i (Fl_Widget *o, void *v) {
1402        AddDevice (*((int*)v));
1405 void SynthModular::cb_NewDeviceFromMenu (Fl_Widget *o, void *v) {
1406      ((SynthModular*)(o->user_data()))->cb_NewDeviceFromMenu_i (o, v);
1409 // (Plugin Buttons)
1411 inline void SynthModular::cb_NewDevice_i (Fl_Button *o, void *v) {
1412        AddDevice (*((int*)v));
1415 void SynthModular::cb_NewDevice (Fl_Button *o, void *v) {
1416      ((SynthModular*)(o->parent()->user_data()))->cb_NewDevice_i (o, v);
1419 // (Plugin Canvas Menu)
1421 inline void SynthModular::cb_NewDeviceFromCanvasMenu_i (Fl_Canvas* o, void* v) {
1422        AddDevice(*((int*)v),*((int*)v+1),*((int*)v+2));
1425 void SynthModular::cb_NewDeviceFromCanvasMenu(Fl_Canvas* o, void* v) {
1426      ((SynthModular*)(o->user_data()))->cb_NewDeviceFromCanvasMenu_i(o,v);
1430 /////////////////////////////////
1431 // Audio Menu
1433 // Play / Pause
1435 inline void SynthModular::cb_PlayPause_i (Fl_Widget *o, void *v) {
1436        string oldname = m_PlayPause->tooltip ();
1437        if (m_Info.PAUSED) {
1438           m_PlayPause->label ("@||");
1439           m_PlayPause->tooltip ("Pause");
1440           ResumeAudio();
1441        }
1442        else {
1443           m_PlayPause->label ("@>");
1444           m_PlayPause->tooltip ("Play");
1445           PauseAudio();
1446        }
1447        for (int i=0; i<m_MainMenu->size(); i++) {
1448             if (m_MainMenu->text (i) != NULL) {
1449                if (oldname == m_MainMenu->text (i)) {
1450                   m_MainMenu->replace (i, m_PlayPause->tooltip());
1451                   break;
1452                }
1453             }
1454         }
1458 void SynthModular::cb_PlayPause (Fl_Widget *o, void *v) {
1459      ((SynthModular*)(o->user_data()))->cb_PlayPause_i (o, v);
1462 // Reset
1464 inline void SynthModular::cb_Reset_i (Fl_Widget *o, void *v) {
1465        ResetAudio();
1468 void SynthModular::cb_Reset (Fl_Widget *o, void *v) {
1469      ((SynthModular*)(o->user_data()))->cb_Reset_i (o, v);
1472 //////////////////////////////////////////////////////////
1474 inline void SynthModular::cb_NewComment_i(Fl_Button* o, void* v)
1476         AddComment(-1);
1478 void SynthModular::cb_NewComment(Fl_Button* o, void* v)
1479 {((SynthModular*)(o->parent()->user_data()))->cb_NewComment_i(o,v);}
1481 //////////////////////////////////////////////////////////
1483 inline void SynthModular::cb_GroupTab_i(Fl_Tabs* o, void* v)
1485         m_GroupTab->redraw();
1488 void SynthModular::cb_GroupTab(Fl_Tabs* o, void* v)
1489 {((SynthModular*)(o->parent()->user_data()))->cb_GroupTab_i(o,v);}
1491 //////////////////////////////////////////////////////////
1493 inline void SynthModular::cb_Connection_i(Fl_Canvas* o, void* v)
1495         CanvasWire *Wire;
1496         Wire=(CanvasWire*)v;
1498         map<int,DeviceWin*>::iterator si=m_DeviceWinMap.find(Wire->OutputID);
1499         if (si==m_DeviceWinMap.end())
1500         {
1501                 char num[32];
1502                 sprintf(num,"%d",Wire->OutputID);
1503                 SpiralInfo::Alert("Warning: Connection problem - can't find source "+string(num));
1504                 return;
1505         }
1507         map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(Wire->InputID);
1508         if (di==m_DeviceWinMap.end())
1509         {
1510                 char num[32];
1511                 sprintf(num,"%d",Wire->InputID);
1512                 SpiralInfo::Alert("Warning: Connection problem - can't find destination "+string(num));
1513                 return;
1514         }
1516         Sample *sample=NULL;
1518         if (!si->second->m_Device->GetOutput(Wire->OutputPort,&sample))
1519         {
1520                 char num[32];
1521                 sprintf(num,"%d,%d",Wire->OutputID,Wire->OutputPort);
1522                 SpiralInfo::Alert("Warning: Connection problem - can't find source output "+string(num));
1523                 return;
1524         }
1526         if (!di->second->m_Device->SetInput(Wire->InputPort,(const Sample*)sample))
1527         {
1528                 char num[32];
1529                 sprintf(num,"%d,%d",Wire->InputID,Wire->InputPort);
1530                 SpiralInfo::Alert("Warning: Connection problem - can't find source input "+string(num));
1531                 return;
1532         }
1534 void SynthModular::cb_Connection(Fl_Canvas* o, void* v)
1535 {((SynthModular*)(o->user_data()))->cb_Connection_i(o,v);}
1537 //////////////////////////////////////////////////////////
1539 inline void SynthModular::cb_Unconnect_i(Fl_Canvas* o, void* v)
1541         CanvasWire *Wire;
1542         Wire=(CanvasWire*)v;
1544         //cerr<<Wire->InputID<<" "<<Wire->InputPort<<endl;
1546         map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(Wire->InputID);
1547         if (di==m_DeviceWinMap.end())
1548         {
1549                 //cerr<<"Can't find destination device "<<Wire->InputID<<endl;
1550                 return;
1551         }
1553         SpiralPlugin *Plugin=di->second->m_Device;
1554         if (Plugin && !Plugin->SetInput(Wire->InputPort,NULL))
1555         { cerr<<"Can't find destination device's Input"<<endl; return;  }
1557 void SynthModular::cb_Unconnect(Fl_Canvas* o, void* v)
1558 {((SynthModular*)(o->user_data()))->cb_Unconnect_i(o,v);}
1560 //////////////////////////////////////////////////////////
1562 void SynthModular::cb_UpdatePluginInfo(int ID, void *PInfo)
1564         map<int,DeviceWin*>::iterator i=m_DeviceWinMap.find(ID);
1565         if (i!=m_DeviceWinMap.end())
1566         {
1567                 DeviceGUIInfo Info=BuildDeviceGUIInfo(*((PluginInfo*)PInfo));
1569                 (*i).second->m_DeviceGUI->Setup(Info);
1570                 (*i).second->m_DeviceGUI->redraw();
1571         }
1574 //////////////////////////////////////////////////////////
1576 void SynthModular::LoadPatch(const char *fn)
1578         ifstream in(fn);
1580         if (in)
1581         {
1582                 fstream inf;
1583                 inf.open(fn, std::ios::in);
1585                 m_FilePath=fn;
1587                 ClearUp();
1588                 inf>>*this;
1590                 inf.close();
1592                 TITLEBAR=LABEL+" "+fn;
1593                 m_TopWindow->label(TITLEBAR.c_str());
1594         }