2 * Copyleft (C) 2002 David Griffiths <dave@pawfal.org>
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.
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.
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.
23 #include <sys/types.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;
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():
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()
109 PluginManager::Get()->PackUpAndGoHome();
110 system("rm -f ___temp.ssmcopytmp");
113 //////////////////////////////////////////////////////////
115 void SynthModular::ClearUp()
119 for(map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
120 i!=m_DeviceWinMap.end(); i++)
122 //Stop processing of audio if any
123 if (i->second->m_Device)
125 if (i->second->m_Device->Kill());
127 i->second->m_DeviceGUI->Clear();
129 if (i->second->m_DeviceGUI->GetPluginWindow())
131 i->second->m_DeviceGUI->GetPluginWindow()->hide();
135 delete i->second->m_Device;
136 i->second->m_Device=NULL;
140 m_DeviceWinMap.clear();
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++)
157 if (i->second->m_Device && i->second->m_Device->IsDead())
160 delete i->second->m_Device;
161 i->second->m_Device=NULL;
163 //Erase Device from DeviceWinMap
164 m_DeviceWinMap.erase(i);
166 else if (i->second->m_Device) // if it's not a comment
169 cerr<<"Updating channelhandler of plugin "<<i->second->m_PluginID<<endl;
172 // updates the data from the gui thread, if it's not blocking
173 i->second->m_Device->UpdateChannelHandler();
176 cerr<<"Finished updating"<<endl;
179 // If this is an audio device see if we always need to ProcessAudio here
180 if ((!m_ResetingAudioThread))
182 if (i->second->m_Device->IsAudioDriver())
184 AudioDriver *driver = ((AudioDriver *)i->second->m_Device);
186 if (driver->ProcessType() == AudioDriver::ALWAYS)
188 driver->ProcessAudio();
192 // run any commands we've received from the GUI's
193 i->second->m_Device->ExecuteCommands();
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++)
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))
208 cerr<<"Executing plugin "<<di->second->m_PluginID<<endl;
211 if (m_ResetingAudioThread)
213 di->second->m_Device->Reset();
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())
222 AudioDriver *driver = ((AudioDriver *)di->second->m_Device);
224 if (driver->ProcessType() == AudioDriver::MANUAL)
226 driver->ProcessAudio();
232 cerr<<"Finished executing"<<endl;
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++)
249 if (i->second->m_DeviceGUI && i->second->m_DeviceGUI->GetPluginWindow())
251 SpiralPluginGUI *GUI=(SpiralPluginGUI *)i->second->m_DeviceGUI->GetPluginWindow();
255 if (i->second->m_DeviceGUI && i->second->m_DeviceGUI->Killed())
259 //Stop processing of audio if any
260 if (i->second->m_Device)
262 if (i->second->m_Device->Kill());
267 i->second->m_DeviceGUI->Clear();
269 // Hide Device GUI FIRST
270 if (i->second->m_DeviceGUI->GetPluginWindow())
272 i->second->m_DeviceGUI->GetPluginWindow()->hide();
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
284 m_DeviceWinMap.erase(i);
290 if (m_HostNeedsUpdate)
292 cout << "Updating SampleRate to: " << SpiralInfo::SAMPLERATE << " and Buffer Size to: " << SpiralInfo::BUFSIZE << " to match current Audio Driver." << endl;
294 m_HostNeedsUpdate = false;
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);
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());
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());
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());
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());
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());
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);
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, "");
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);
485 //////////////////////////////////////////////////////////
487 vector<string> SynthModular::BuildPluginList (const string &Path) {
488 // Scan plugin path for plugins.
494 const char *path = Path.c_str();
499 cerr << "WARNING: Could not open path " << path << endl;
502 while ((ep = readdir(dp))) {
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);
515 cerr << "WARNING: File " << path << ep->d_name
516 << " could not be examined" << endl;
517 cerr << "dlerror() output:" << endl;
518 cerr << dlerror() << endl;
521 // It's a DLL. Add name to list
522 ret.push_back(ep->d_name);
531 void SynthModular::LoadPlugins (string pluginPath) {
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");
540 Fl_Box* pbut = new Fl_Box (0, 8, SWidth, SHeight, "");
541 pbut->box (FL_NO_BOX);
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);
548 Splash->add (splashtext);
551 vector<string> PluginVector;
552 if (SpiralInfo::USEPLUGINLIST) PluginVector = SpiralInfo::PLUGINVEC;
554 if (pluginPath.empty()) PluginVector = BuildPluginList (SpiralInfo::PLUGIN_PATH);
556 string::iterator i = pluginPath.end() - 1;
557 if (*i != '/') pluginPath += '/';
558 PluginVector = BuildPluginList (pluginPath);
561 for (vector<string>::iterator i=PluginVector.begin(); i!=PluginVector.end(); i++) {
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) {
568 cerr << ID << " = Plugin [" << *i << "]" << endl;
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()));
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;
591 else the_group = gi->second;
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]);
629 // Nothing else ever touches m_DeviceVec - is this right??? (andy)
630 m_DeviceVec.push_back (NewButton);
632 // m_NextPluginButton++;
634 splashtext->label (PluginName->c_str());
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, ""));
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);
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;
658 if (found_dummy) m_MainMenu->remove (i);
659 } while (found_dummy);
664 //////////////////////////////////////////////////////////
666 DeviceGUIInfo SynthModular::BuildDeviceGUIInfo(PluginInfo &PInfo)
671 // tweak the size if we have too many ins/outs
672 if (PInfo.NumInputs>4 || PInfo.NumOutputs>4)
674 if (PInfo.NumInputs<PInfo.NumOutputs)
676 Height=PInfo.NumOutputs*10+5;
680 Height=PInfo.NumInputs*10+5;
684 // Make the guiinfo struct
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;
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() )
717 AudioDriver *driver = ((AudioDriver*)nlw->m_Device);
718 driver->SetChangeBufferAndSampleRateCallback(cb_ChangeBufferAndSampleRate);
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());
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);
741 //////////////////////////////////////////////////////////
743 void SynthModular::AddDevice(int n, int x=-1, int y=-1)
745 //cerr<<"Adding "<<m_NextID<<endl;
749 x = m_CanvasScroll->x()+50;
750 y = m_CanvasScroll->y()+50;
753 DeviceWin* temp = NewDeviceWin(n,x,y);
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;
764 //////////////////////////////////////////////////////////
766 DeviceWin* SynthModular::NewComment(int n, int x=-1, int y=-1)
768 DeviceWin *nlw = new DeviceWin;
772 x = m_CanvasScroll->x()+50;
773 y = m_CanvasScroll->y()+50;
777 nlw->m_PluginID = COMMENT_ID;
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);
798 //////////////////////////////////////////////////////////
800 void SynthModular::AddComment(int n)
802 //cerr<<"Adding "<<m_NextID<<endl;
803 DeviceWin* temp = NewComment(n);
807 //cerr<<"adding comment "<<ID<<endl;
808 temp->m_DeviceGUI->SetID(ID);
809 m_DeviceWinMap[ID]=temp;
813 //////////////////////////////////////////////////////////
815 void SynthModular::cb_ChangeBufferAndSampleRate_i(long int NewBufferSize, long int NewSamplerate)
817 if (SpiralInfo::BUFSIZE != NewBufferSize)
819 // update the settings
820 SpiralInfo::BUFSIZE = NewBufferSize;
821 m_HostNeedsUpdate = true;
824 if (SpiralInfo::SAMPLERATE != NewSamplerate)
826 SpiralInfo::SAMPLERATE = NewSamplerate;
827 m_HostNeedsUpdate = true;
832 void SynthModular::UpdateHostInfo()
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 */
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)
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
882 char file_path[1024];
883 string m_FromFilePath;
890 m_Copied.devices>>has_file_path;
894 m_Copied.devices.getline(file_path, 1024);
895 m_FromFilePath = file_path;
896 cerr << file_path << endl;
902 s>>dummy>>dummy>>dummy>>ver;
904 if (ver>FILE_VERSION)
906 SpiralInfo::Alert("Bad file, or more recent version.");
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);
924 m_FromFilePath = m_MergeFilePath;
927 //wether pasting or merging we need to clear the current
928 //selection so we can replace it with the new devices
930 Fl_Canvas::ClearSelection(m_Canvas);
932 int Num, ID, PluginID, x,y,ps,px,py;
936 Num = m_Copied.devicecount;
943 for(int n=0; n<Num; n++)
946 cerr<<"Loading Device "<<n<<endl;
949 s>>dummy; // "Device"
951 s>>dummy2; // "Plugin"
959 // load the device name
973 cerr<<dummy<<" "<<ID<<" "<<dummy2<<" "<<PluginID<<" "<<x<<" "<<y<<endl;
976 if (paste || ver>1) s>>ps>>px>>py;
978 //if we are merging a patch or pasting we will change duplicate ID's
979 if (!paste && !merge)
981 // Check we're not duplicating an ID
982 if (m_DeviceWinMap.find(ID)!=m_DeviceWinMap.end())
984 SpiralInfo::Alert("Duplicate device ID found in file - aborting load");
990 if (PluginID==COMMENT_ID)
992 DeviceWin* temp = NewComment(PluginID, x, y);
997 m_Copied.m_DeviceIds[ID] = m_NextID++;
998 ID = m_Copied.m_DeviceIds[ID];
1001 temp->m_DeviceGUI->SetID(ID);
1002 m_DeviceWinMap[ID]=temp;
1003 ((Fl_CommentGUI*)(m_DeviceWinMap[ID]->m_DeviceGUI))->StreamIn(s); // load the plugin
1006 Fl_Canvas::AppendSelection(ID, m_Canvas);
1008 if (m_NextID<=ID) m_NextID=ID+1;
1014 DeviceWin* temp = NewDeviceWin(PluginID, x, y);
1020 m_Copied.m_DeviceIds[ID] = m_NextID++;
1022 ID = m_Copied.m_DeviceIds[ID];
1025 temp->m_DeviceGUI->SetID(ID);
1029 // set the titlebars
1030 temp->m_DeviceGUI->SetName(Name);
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
1039 m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FromFilePath+"_files/", oldID);
1041 m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FilePath+"_files/");
1043 if ((paste || ver>1) && m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow())
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);
1060 m_DeviceWinMap[ID]->m_DeviceGUI->Maximise();
1061 // reposition after maximise
1062 m_DeviceWinMap[ID]->m_DeviceGUI->position(x,y);
1064 else m_DeviceWinMap[ID]->m_DeviceGUI->Minimise();
1067 Fl_Canvas::AppendSelection(ID, m_Canvas);
1070 if (!paste && !merge)
1071 if (m_NextID<=ID) m_NextID=ID+1;
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.");
1083 if (!paste && !merge)
1092 iostream &operator>>(iostream &s, SynthModular &o)
1094 return o.StreamPatchIn(s, false, false);
1098 //////////////////////////////////////////////////////////
1100 ostream &operator<<(ostream &s, SynthModular &o)
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());
1113 s<<o.m_TopWindow->x()<<" "<<o.m_TopWindow->y()<<" ";
1114 s<<o.m_TopWindow->w()<<" "<<o.m_TopWindow->h()<<" ";
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++)
1126 if (i->second->m_DeviceGUI && ((i->second->m_Device) || (i->second->m_PluginID==COMMENT_ID)))
1130 s<<i->first<<" "; // save the id
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())
1140 s<<i->second->m_DeviceGUI->GetPluginWindow()->visible()<<" ";
1141 s<<i->second->m_DeviceGUI->GetPluginWindow()->x()<<" ";
1142 s<<i->second->m_DeviceGUI->GetPluginWindow()->y()<<" ";
1146 s<<0<<" "<<0<<" "<<0;
1151 if (i->second->m_PluginID==COMMENT_ID)
1153 // save the comment gui
1154 ((Fl_CommentGUI*)(i->second->m_DeviceGUI))->StreamOut(s);
1159 i->second->m_Device->StreamOut(s);
1163 // save external files
1164 if (i->second->m_Device && i->second->m_Device->SaveExternalFiles(o.m_FilePath+"_files/"))
1166 ExternalDirUsed=true;
1171 s<<endl<<*o.m_Canvas<<endl;
1173 // remove it if it wasn't used
1174 if (!ExternalDirUsed)
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());
1187 //////////////////////////////////////////////////////////////////////////////////////////
1191 /////////////////////////////////
1192 // File Menu & associated buttons
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?"))
1199 m_TopWindow->label (TITLEBAR.c_str());
1203 void SynthModular::cb_New (Fl_Widget *o, void *v) {
1204 ((SynthModular*)(o->user_data()))->cb_New_i (o, v);
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?"))
1212 char *fn=fl_file_chooser ("Load a patch", "*.ssm", NULL);
1213 if (fn && fn!='\0') {
1217 inf.open (fn, ios::in);
1222 TITLEBAR = LABEL + " " + fn;
1223 m_TopWindow->label (TITLEBAR.c_str());
1228 void SynthModular::cb_Load(Fl_Widget *o, void *v) {
1229 ((SynthModular*)(o->user_data()))->cb_Load_i (o, v);
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') {
1239 if (!Pawfal_YesNo ("File [%s] exists, overwrite?", fn))
1246 TITLEBAR = LABEL + " " + fn;
1247 m_TopWindow->label (TITLEBAR.c_str());
1250 fl_message ( "%s", string ("Error saving " + string(fn)).c_str());
1255 void SynthModular::cb_Save (Fl_Widget *o, void *v) {
1256 ((SynthModular*)(o->user_data()))->cb_Save_i (o, v);
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') {
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);
1276 void SynthModular::cb_Merge (Fl_Widget *o, void *v) {
1277 ((SynthModular*)(o->parent()->user_data()))->cb_Merge_i (o, v);
1282 inline void SynthModular::cb_Close_i (Fl_Widget *o, void *v) {
1283 m_SettingsWindow->hide();
1284 delete m_SettingsWindow;
1285 m_TopWindow->hide();
1289 void SynthModular::cb_Close (Fl_Widget *o, void *v) {
1290 ((SynthModular*)(o->user_data()))->cb_Close_i (o, v);
1293 /////////////////////////////////
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);
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);
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;
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() << " ";
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);
1348 j->second->m_Device->StreamOut (m_Copied.devices);
1350 m_Copied.devices<<endl;
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);
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);
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);
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 /////////////////////////////////
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);
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 /////////////////////////////////
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");
1443 m_PlayPause->label ("@>");
1444 m_PlayPause->tooltip ("Play");
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());
1458 void SynthModular::cb_PlayPause (Fl_Widget *o, void *v) {
1459 ((SynthModular*)(o->user_data()))->cb_PlayPause_i (o, v);
1464 inline void SynthModular::cb_Reset_i (Fl_Widget *o, void *v) {
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)
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)
1496 Wire=(CanvasWire*)v;
1498 map<int,DeviceWin*>::iterator si=m_DeviceWinMap.find(Wire->OutputID);
1499 if (si==m_DeviceWinMap.end())
1502 sprintf(num,"%d",Wire->OutputID);
1503 SpiralInfo::Alert("Warning: Connection problem - can't find source "+string(num));
1507 map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(Wire->InputID);
1508 if (di==m_DeviceWinMap.end())
1511 sprintf(num,"%d",Wire->InputID);
1512 SpiralInfo::Alert("Warning: Connection problem - can't find destination "+string(num));
1516 Sample *sample=NULL;
1518 if (!si->second->m_Device->GetOutput(Wire->OutputPort,&sample))
1521 sprintf(num,"%d,%d",Wire->OutputID,Wire->OutputPort);
1522 SpiralInfo::Alert("Warning: Connection problem - can't find source output "+string(num));
1526 if (!di->second->m_Device->SetInput(Wire->InputPort,(const Sample*)sample))
1529 sprintf(num,"%d,%d",Wire->InputID,Wire->InputPort);
1530 SpiralInfo::Alert("Warning: Connection problem - can't find source input "+string(num));
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)
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())
1549 //cerr<<"Can't find destination device "<<Wire->InputID<<endl;
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())
1567 DeviceGUIInfo Info=BuildDeviceGUIInfo(*((PluginInfo*)PInfo));
1569 (*i).second->m_DeviceGUI->Setup(Info);
1570 (*i).second->m_DeviceGUI->redraw();
1574 //////////////////////////////////////////////////////////
1576 void SynthModular::LoadPatch(const char *fn)
1583 inf.open(fn, std::ios::in);
1592 TITLEBAR=LABEL+" "+fn;
1593 m_TopWindow->label(TITLEBAR.c_str());