You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1243 lines
32KB

  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.
  17. */
  18. #include <string>
  19. #include <iostream>
  20. #include <fstream>
  21. #include <sstream>
  22. #include <sys/types.h>
  23. #include <sys/stat.h>
  24. #include <dirent.h>
  25. #include <dlfcn.h>
  26. #include <FL/Fl.H>
  27. #include <FL/Enumerations.H>
  28. #include <FL/fl_file_chooser.h>
  29. #include <FL/Fl_Box.h>
  30. #include <FL/Fl_Tooltip.h>
  31. #include "SpiralSynthModular.h"
  32. #include "SpiralSound/PluginManager.h"
  33. #include "SpiralSound/SpiralInfo.h"
  34. #include "SpiralSound/Plugins/SpiralPluginGUI.h"
  35. #include "GUI/SSM.xpm"
  36. #include "GUI/load.xpm"
  37. #include "GUI/save.xpm"
  38. #include "GUI/new.xpm"
  39. #include "GUI/options.xpm"
  40. #include "GUI/edit.xpm"
  41. #include "GUI/comment.xpm"
  42. #include "GUI/Widgets/PawfalYesNo.h"
  43. //#define DEBUG_PLUGINS
  44. //#define DEBUG_STREAM
  45. const static string LABEL = "SpiralSynthModular "+VER_STRING;
  46. static string TITLEBAR;
  47. static const int FILE_VERSION = 4;
  48. static int Numbers[512];
  49. static const int MAIN_WIDTH = 700;
  50. static const int MAIN_HEIGHT = 600;
  51. static const int SLIDER_WIDTH = 15;
  52. static const int ICON_DEPTH = 3;
  53. static const int COMMENT_ID = -1;
  54. using namespace std;
  55. map<int,DeviceWin*> SynthModular::m_DeviceWinMap;
  56. bool SynthModular::m_CallbackUpdateMode = false;
  57. bool SynthModular::m_BlockingOutputPluginIsReady = false;
  58. //////////////////////////////////////////////////////////
  59. DeviceWin::~DeviceWin()
  60. {
  61. }
  62. //////////////////////////////////////////////////////////
  63. SynthModular::SynthModular():
  64. m_NextID(0),
  65. m_PauseAudio(false)
  66. {
  67. m_Info.BUFSIZE = SpiralInfo::BUFSIZE;
  68. m_Info.FRAGSIZE = SpiralInfo::FRAGSIZE;
  69. m_Info.FRAGCOUNT = SpiralInfo::FRAGCOUNT;
  70. m_Info.SAMPLERATE = SpiralInfo::SAMPLERATE;
  71. m_Info.OUTPUTFILE = SpiralInfo::OUTPUTFILE;
  72. m_Info.MIDIFILE = SpiralInfo::MIDIFILE;
  73. m_Info.POLY = SpiralInfo::POLY;
  74. m_Info.GUI_COLOUR = SpiralInfo::GUI_COLOUR;
  75. m_Info.SCOPE_BG_COLOUR = SpiralInfo::SCOPE_BG_COLOUR;
  76. m_Info.SCOPE_FG_COLOUR = SpiralInfo::SCOPE_FG_COLOUR;
  77. m_Info.SCOPE_SEL_COLOUR = SpiralInfo::SCOPE_SEL_COLOUR;
  78. m_Info.SCOPE_IND_COLOUR = SpiralInfo::SCOPE_IND_COLOUR;
  79. m_Info.SCOPE_MRK_COLOUR = SpiralInfo::SCOPE_MRK_COLOUR;
  80. //m_Info.SpiralInfo::GUICOL_Button = SpiralInfo::SpiralInfo::GUICOL_Button;
  81. for (int n=0; n<512; n++) Numbers[n]=n;
  82. m_CH.Register("PauseAudio",&m_PauseAudio);
  83. }
  84. //////////////////////////////////////////////////////////
  85. SynthModular::~SynthModular()
  86. {
  87. ClearUp();
  88. PluginManager::Get()->PackUpAndGoHome();
  89. }
  90. //////////////////////////////////////////////////////////
  91. void SynthModular::ClearUp()
  92. {
  93. PauseAudio();
  94. for(map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
  95. i!=m_DeviceWinMap.end(); i++)
  96. {
  97. if (i->second->m_DeviceGUI->GetPluginWindow())
  98. {
  99. i->second->m_DeviceGUI->GetPluginWindow()->hide();
  100. //m_MainWindow->remove(i->second->m_DeviceGUI->GetPluginWindow());
  101. }
  102. // deleted by Canvas::Remove()? seems to cause random crashes
  103. //delete i->second->m_DeviceGUI;
  104. delete i->second->m_Device;
  105. i->second->m_Device=NULL;
  106. }
  107. m_Canvas->Clear();
  108. m_DeviceWinMap.clear();
  109. m_NextID=0;
  110. ResumeAudio();
  111. }
  112. //////////////////////////////////////////////////////////
  113. void SynthModular::Update()
  114. {
  115. m_CH.UpdateDataNow();
  116. if (m_PauseAudio) return;
  117. // for all the plugins
  118. for(map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
  119. i!=m_DeviceWinMap.end(); i++)
  120. {
  121. if (i->second->m_Device) // if it's not a comment
  122. {
  123. #ifdef DEBUG_PLUGINS
  124. cerr<<"Updating channelhandler of plugin "<<i->second->m_PluginID<<endl;
  125. #endif
  126. // updates the data from the gui thread, if it's not blocking
  127. i->second->m_Device->UpdateChannelHandler();
  128. #ifdef DEBUG_PLUGINS
  129. cerr<<"Finished updating"<<endl;
  130. #endif
  131. // run any commands we've received from the GUI's
  132. i->second->m_Device->ExecuteCommands();
  133. }
  134. }
  135. // run the plugins (only ones connected to anything)
  136. list<int> ExecutionOrder = m_Canvas->GetGraph()->GetSortedList();
  137. for (list<int>::reverse_iterator i=ExecutionOrder.rbegin();
  138. i!=ExecutionOrder.rend(); i++)
  139. {
  140. // use the graphsort order to remove internal latency
  141. map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(*i);
  142. if (di!=m_DeviceWinMap.end() && di->second->m_Device)
  143. {
  144. #ifdef DEBUG_PLUGINS
  145. cerr<<"Executing plugin "<<di->second->m_PluginID<<endl;
  146. #endif
  147. di->second->m_Device->Execute();
  148. #ifdef DEBUG_PLUGINS
  149. cerr<<"Finished executing"<<endl;
  150. #endif
  151. }
  152. }
  153. }
  154. //////////////////////////////////////////////////////////
  155. void SynthModular::UpdatePluginGUIs()
  156. {
  157. // see if any need deleting
  158. for (map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
  159. i!=m_DeviceWinMap.end(); i++)
  160. {
  161. if (i->second->m_DeviceGUI->GetPluginWindow())
  162. {
  163. SpiralPluginGUI *GUI=(SpiralPluginGUI *)i->second->m_DeviceGUI->GetPluginWindow();
  164. GUI->Update();
  165. }
  166. if (i->second->m_DeviceGUI->Killed())
  167. {
  168. PauseAudio();
  169. if (i->second->m_Device)
  170. {
  171. delete i->second->m_Device;
  172. i->second->m_Device=NULL;
  173. }
  174. if (i->second->m_DeviceGUI->GetPluginWindow())
  175. {
  176. i->second->m_DeviceGUI->GetPluginWindow()->hide();
  177. //m_MainWindow->remove(i->second->m_DeviceGUI->GetPluginWindow());
  178. }
  179. i->second->m_DeviceGUI->Clear();
  180. m_Canvas->RemoveDevice(i->second->m_DeviceGUI);
  181. // deleted by Canvas::Remove()? seems to cause random crashes
  182. //delete i->second->m_DeviceGUI;
  183. m_DeviceWinMap.erase(i);
  184. ResumeAudio();
  185. break;
  186. }
  187. }
  188. m_Canvas->Poll();
  189. }
  190. //////////////////////////////////////////////////////////
  191. SpiralWindowType *SynthModular::CreateWindow()
  192. {
  193. m_TopWindow = new SpiralWindowType(MAIN_WIDTH, MAIN_HEIGHT, LABEL.c_str());
  194. //m_TopWindow->resizable(m_TopWindow);
  195. int but=50;
  196. int ToolbarHeight=but+0;
  197. m_Topbar = new Fl_Pack (0, 0, MAIN_WIDTH, ToolbarHeight, "");
  198. m_Topbar->user_data((void*)(this));
  199. m_Topbar->type(FL_HORIZONTAL);
  200. m_Topbar->color(SpiralInfo::GUICOL_Button);
  201. m_TopWindow->add(m_Topbar);
  202. m_ToolbarPanel = new Fl_Pack (0, 0, but*5, ToolbarHeight, "");
  203. m_ToolbarPanel->user_data((void*)(this));
  204. m_ToolbarPanel->type(FL_VERTICAL);
  205. m_ToolbarPanel->color(SpiralInfo::GUICOL_Button);
  206. m_Topbar->add(m_ToolbarPanel);
  207. m_Toolbar = new Fl_Pack (0, 0, but*5, but, "");
  208. m_Toolbar->user_data((void*)(this));
  209. m_Toolbar->type(FL_HORIZONTAL);
  210. m_Toolbar->color(SpiralInfo::GUICOL_Button);
  211. m_ToolbarPanel->add(m_Toolbar);
  212. m_Load = new Fl_Button(0, 0, but, but, "");
  213. Fl_Pixmap *tPix = new Fl_Pixmap(load_xpm);
  214. m_Load->image(tPix->copy());
  215. delete tPix;
  216. m_Load->type(0);
  217. m_Load->box(FL_PLASTIC_UP_BOX);
  218. m_Load->color(SpiralInfo::GUICOL_Button);
  219. m_Load->selection_color(SpiralInfo::GUICOL_Tool);
  220. m_Load->labelsize (1);
  221. m_Load->tooltip("Load a patch file");
  222. m_Load->callback((Fl_Callback*)cb_Load);
  223. m_Toolbar->add(m_Load);
  224. m_Save = new Fl_Button(0, 0, but, but, "");
  225. tPix = new Fl_Pixmap(save_xpm);
  226. m_Save->image(tPix->copy());
  227. delete tPix;
  228. m_Save->type(0);
  229. m_Save->box(FL_PLASTIC_UP_BOX);
  230. m_Save->color(SpiralInfo::GUICOL_Button);
  231. m_Save->selection_color(SpiralInfo::GUICOL_Tool);
  232. m_Save->labelsize (1);
  233. m_Save->tooltip("Save a patch file");
  234. m_Save->callback((Fl_Callback*)cb_Save);
  235. m_Toolbar->add(m_Save);
  236. m_New = new Fl_Button(0, 0, but, but, "");
  237. tPix = new Fl_Pixmap(new_xpm);
  238. m_New->image(tPix->copy());
  239. delete tPix;
  240. m_New->type(0);
  241. m_New->box(FL_PLASTIC_UP_BOX);
  242. m_New->color(SpiralInfo::GUICOL_Button);
  243. m_New->selection_color(SpiralInfo::GUICOL_Tool);
  244. m_New->labelsize (1);
  245. m_New->tooltip("New patch");
  246. m_New->callback((Fl_Callback*)cb_New);
  247. m_Toolbar->add(m_New);
  248. m_Options = new Fl_Button(0, 0, but, but, "");
  249. tPix = new Fl_Pixmap(options_xpm);
  250. m_Options->image(tPix->copy());
  251. delete tPix;
  252. m_Options->type(0);
  253. m_Options->box(FL_PLASTIC_UP_BOX);
  254. m_Options->color(SpiralInfo::GUICOL_Button);
  255. m_Options->selection_color(SpiralInfo::GUICOL_Tool);
  256. m_Options->labelsize (1);
  257. m_Options->tooltip("Options");
  258. m_Options->callback((Fl_Callback*)cb_Rload);
  259. m_Toolbar->add(m_Options);
  260. m_NewComment = new Fl_Button(0, 0, but, but, "");
  261. tPix = new Fl_Pixmap(comment_xpm);
  262. m_NewComment->image(tPix->copy());
  263. delete tPix;
  264. m_NewComment->type(0);
  265. m_NewComment->box(FL_PLASTIC_UP_BOX);
  266. m_NewComment->color(SpiralInfo::GUICOL_Button);
  267. m_NewComment->selection_color(SpiralInfo::GUICOL_Tool);
  268. m_NewComment->labelsize (1);
  269. m_NewComment->tooltip("New comment");
  270. m_NewComment->callback((Fl_Callback*)cb_NewComment);
  271. m_Toolbar->add(m_NewComment);
  272. m_GroupFiller = new Fl_Group (0, 0, 0, ToolbarHeight, "");
  273. m_GroupFiller->color(SpiralInfo::GUICOL_Button);
  274. m_Topbar->add (m_GroupFiller);
  275. m_GroupTab = new Fl_Tabs (0, 0, MAIN_WIDTH-m_GroupFiller->w()-but*5, ToolbarHeight, "");
  276. m_GroupTab->user_data ((void*)(this));
  277. m_GroupTab->box(FL_PLASTIC_DOWN_BOX);
  278. m_GroupTab->color(SpiralInfo::GUICOL_Button);
  279. m_GroupTab->callback((Fl_Callback*)cb_GroupTab);
  280. m_Topbar->add (m_GroupTab);
  281. m_Topbar->resizable(m_GroupTab);
  282. /////////////////
  283. m_CanvasScroll = new Fl_Scroll(0, ToolbarHeight, MAIN_WIDTH, MAIN_HEIGHT-ToolbarHeight, "");
  284. m_TopWindow->add(m_CanvasScroll);
  285. m_TopWindow->resizable(m_CanvasScroll);
  286. m_Canvas = new Fl_Canvas(-5000, -5000, 10000, 10000, "");
  287. m_Canvas->type(1);
  288. m_Canvas->box(FL_FLAT_BOX);
  289. m_Canvas->labeltype(FL_ENGRAVED_LABEL);
  290. m_Canvas->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE);
  291. m_Canvas->color(SpiralInfo::GUICOL_Canvas);
  292. m_Canvas->user_data((void*)(this));
  293. m_Canvas->SetConnectionCallback((Fl_Callback*)cb_Connection);
  294. m_Canvas->SetUnconnectCallback((Fl_Callback*)cb_Unconnect);
  295. m_Canvas->SetAddDeviceCallback((Fl_Callback*)cb_NewDeviceFromMenu);
  296. m_CanvasScroll->add(m_Canvas);
  297. m_SettingsWindow = new SettingsWindow;
  298. m_SettingsWindow->RegisterApp(this);
  299. return m_TopWindow;
  300. }
  301. //////////////////////////////////////////////////////////
  302. vector<string> SynthModular::BuildPluginList(const string &Path)
  303. {
  304. // Scan plugin path for plugins.
  305. DIR *dp;
  306. struct dirent *ep;
  307. struct stat sb;
  308. void *handle;
  309. string fullpath;
  310. const char *path = Path.c_str();
  311. vector<string> ret;
  312. dp = opendir(path);
  313. if (!dp)
  314. {
  315. cerr << "WARNING: Could not open path " << path << endl;
  316. }
  317. else
  318. {
  319. while ((ep = readdir(dp)))
  320. {
  321. // Need full path
  322. fullpath = path;
  323. fullpath.append(ep->d_name);
  324. // Stat file to get type
  325. if (!stat(fullpath.c_str(), &sb))
  326. {
  327. // We only want regular files
  328. if (S_ISREG(sb.st_mode))
  329. {
  330. // We're not fussed about resolving symbols yet, since we are just
  331. // checking if it's a DLL.
  332. handle = dlopen(fullpath.c_str(), RTLD_LAZY);
  333. if (!handle)
  334. {
  335. cerr << "WARNING: File " << path << ep->d_name
  336. << " could not be examined" << endl;
  337. cerr << "dlerror() output:" << endl;
  338. cerr << dlerror() << endl;
  339. }
  340. else
  341. {
  342. // It's a DLL. Add name to list
  343. ret.push_back(ep->d_name);
  344. }
  345. }
  346. }
  347. }
  348. }
  349. return ret;
  350. }
  351. void SynthModular::LoadPlugins(string pluginPath)
  352. {
  353. int Width = 35;
  354. int Height = 35;
  355. int SWidth = 256;
  356. int SHeight = 256;
  357. Fl_Pixmap pic(SSM_xpm);
  358. Fl_Double_Window* Splash = new Fl_Double_Window((Fl::w()/2)-(SWidth/2),
  359. (Fl::h()/2)-(SHeight/2),
  360. SWidth,SHeight,"SSM");
  361. Splash->border(0);
  362. Fl_Box* pbut = new Fl_Box(0,8,SWidth,SHeight,"");
  363. pbut->box(FL_NO_BOX);
  364. pic.label(pbut);
  365. Fl_Box *splashtext = new Fl_Box(5,SHeight-20,200,20,"Loading...");
  366. splashtext->labelsize(10);
  367. splashtext->box(FL_NO_BOX);
  368. splashtext->align(FL_ALIGN_INSIDE|FL_ALIGN_LEFT);
  369. Splash->add(pbut);
  370. Splash->add(splashtext);
  371. Splash->show();
  372. int ID=-1;
  373. vector<string> PluginVector;
  374. if (SpiralInfo::USEPLUGINLIST)
  375. {
  376. PluginVector=SpiralInfo::PLUGINVEC;
  377. }
  378. else
  379. {
  380. if (pluginPath.empty())
  381. PluginVector=BuildPluginList(SpiralInfo::PLUGIN_PATH);
  382. else {
  383. string::iterator i = pluginPath.end() - 1;
  384. if (*i != '/') pluginPath += '/';
  385. PluginVector=BuildPluginList(pluginPath);
  386. }
  387. }
  388. for (vector<string>::iterator i=PluginVector.begin();
  389. i!=PluginVector.end(); i++)
  390. {
  391. string Fullpath;
  392. if (pluginPath=="")
  393. {
  394. Fullpath=SpiralInfo::PLUGIN_PATH+*i;
  395. }
  396. else
  397. {
  398. Fullpath=pluginPath+*"/"+*i;
  399. }
  400. ID=PluginManager::Get()->LoadPlugin(Fullpath.c_str());
  401. if (ID!=PluginError)
  402. {
  403. #ifdef DEBUG_PLUGINS
  404. cerr << ID << " = Plugin [" << *i << "]" << endl;
  405. #endif
  406. Fl_ToolButton *NewButton = new Fl_ToolButton(0,0,Width,Height,"");
  407. NewButton->user_data((void*)(this));
  408. NewButton->labelsize(1);
  409. Fl_Pixmap *tPix = new Fl_Pixmap(PluginManager::Get()->GetPlugin(ID)->GetIcon());
  410. NewButton->image(tPix->copy(tPix->w(),tPix->h()));
  411. delete tPix;
  412. string GroupName = PluginManager::Get()->GetPlugin(ID)->GetGroupName();
  413. Fl_Pack* the_group=NULL;
  414. // find or create this group, and add an icon
  415. map<string,Fl_Pack*>::iterator gi=m_PluginGroupMap.find(GroupName);
  416. if (gi==m_PluginGroupMap.end())
  417. {
  418. the_group = new Fl_Pack (m_GroupTab->x(), 16, m_GroupTab->w(), m_GroupTab->h()-15, GroupName.c_str());
  419. the_group->type(FL_HORIZONTAL);
  420. the_group->labelsize(8);
  421. the_group->color(SpiralInfo::GUICOL_Button);
  422. the_group->user_data((void*)(this));
  423. //m_GroupTab->add(the_group);
  424. m_GroupTab->value(the_group);
  425. m_PluginGroupMap[GroupName]=the_group;
  426. }
  427. else
  428. {
  429. the_group=gi->second;
  430. }
  431. NewButton->type(0);
  432. NewButton->box(FL_NO_BOX);
  433. NewButton->down_box(FL_NO_BOX);
  434. //NewButton->color(SpiralInfo::GUICOL_Button);
  435. //NewButton->selection_color(SpiralInfo::GUICOL_Button);
  436. the_group->add(NewButton);
  437. string tooltip=*i;
  438. // find the first / if there is one, and get rid of everything before and including it
  439. unsigned int p = tooltip.find ('/');
  440. if (p < tooltip.length()) tooltip.erase (0, p);
  441. // find last . and get rid of everything after and including it
  442. p = tooltip.rfind ('.');
  443. unsigned int l = tooltip.length ();
  444. if (p < l) tooltip.erase (p, l);
  445. m_Canvas->AddPluginName (tooltip, PluginManager::Get()->GetPlugin(ID)->ID);
  446. splashtext->label (tooltip.c_str());
  447. Splash->redraw();
  448. NewButton->tooltip (tooltip.c_str());
  449. NewButton->callback((Fl_Callback*)cb_NewDevice,&Numbers[ID]);
  450. NewButton->show();
  451. m_DeviceVec.push_back(NewButton);
  452. the_group->redraw();
  453. // m_NextPluginButton++;
  454. Fl::check();
  455. }
  456. }
  457. map<string,Fl_Pack*>::iterator PlugGrp;
  458. for (PlugGrp = m_PluginGroupMap.begin(); PlugGrp!= m_PluginGroupMap.end(); ++PlugGrp)
  459. {
  460. m_GroupTab->add(PlugGrp->second);
  461. PlugGrp->second->add(new Fl_Box(0,0,600,100,""));
  462. }
  463. // try to show the SpiralSound group
  464. PlugGrp = m_PluginGroupMap.find("SpiralSound");
  465. // can't find it - show the first plugin group
  466. if (PlugGrp==m_PluginGroupMap.end()) PlugGrp=m_PluginGroupMap.begin();
  467. m_GroupTab->value(PlugGrp->second);
  468. Splash->hide();
  469. delete Splash;
  470. }
  471. //////////////////////////////////////////////////////////
  472. DeviceGUIInfo SynthModular::BuildDeviceGUIInfo(PluginInfo &PInfo)
  473. {
  474. DeviceGUIInfo Info;
  475. int Height=50;
  476. // tweak the size if we have too many ins/outs
  477. if (PInfo.NumInputs>4 || PInfo.NumOutputs>4)
  478. {
  479. if (PInfo.NumInputs<PInfo.NumOutputs)
  480. {
  481. Height=PInfo.NumOutputs*10+5;
  482. }
  483. else
  484. {
  485. Height=PInfo.NumInputs*10+5;
  486. }
  487. }
  488. // Make the guiinfo struct
  489. Info.XPos = 0;
  490. Info.YPos = 0;
  491. Info.Width = 40;
  492. Info.Height = Height;
  493. Info.NumInputs = PInfo.NumInputs;
  494. Info.NumOutputs = PInfo.NumOutputs;
  495. Info.Name = PInfo.Name;
  496. Info.PortTips = PInfo.PortTips;
  497. Info.PortTypes = PInfo.PortTypes;
  498. return Info;
  499. }
  500. //////////////////////////////////////////////////////////
  501. DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y)
  502. {
  503. DeviceWin *nlw = new DeviceWin;
  504. const HostsideInfo* Plugin=PluginManager::Get()->GetPlugin(n);
  505. if (!Plugin) return NULL;
  506. nlw->m_Device=Plugin->CreateInstance();
  507. if (!nlw->m_Device)
  508. {
  509. return NULL;
  510. }
  511. nlw->m_Device->SetBlockingCallback(cb_Blocking);
  512. nlw->m_Device->SetUpdateCallback(cb_Update);
  513. nlw->m_Device->SetParent((void*)this);
  514. PluginInfo PInfo = nlw->m_Device->Initialise(&m_Info);
  515. SpiralGUIType *temp = nlw->m_Device->CreateGUI();
  516. Fl_Pixmap *Pix = new Fl_Pixmap(Plugin->GetIcon());
  517. nlw->m_PluginID = n;
  518. if (temp)
  519. {
  520. temp->position(x+10,y);
  521. }
  522. DeviceGUIInfo Info=BuildDeviceGUIInfo(PInfo);
  523. Info.XPos = x; //TOOLBOX_WIDTH+(rand()%400);
  524. Info.YPos = y; //rand()%400;
  525. nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix, nlw->m_Device->IsTerminal());
  526. m_Canvas->add(nlw->m_DeviceGUI);
  527. m_Canvas->redraw();
  528. return nlw;
  529. }
  530. //////////////////////////////////////////////////////////
  531. void SynthModular::AddDevice(int n, int x=-1, int y=-1)
  532. {
  533. //cerr<<"Adding "<<m_NextID<<endl;
  534. if (x==-1)
  535. {
  536. x = m_CanvasScroll->x()+50;
  537. y = m_CanvasScroll->y()+50;
  538. }
  539. DeviceWin* temp = NewDeviceWin(n,x,y);
  540. if (temp)
  541. {
  542. int ID=m_NextID++;
  543. //cerr<<"adding device "<<ID<<endl;
  544. temp->m_DeviceGUI->SetID(ID);
  545. temp->m_Device->SetUpdateInfoCallback(ID,cb_UpdatePluginInfo);
  546. m_DeviceWinMap[ID]=temp;
  547. }
  548. }
  549. //////////////////////////////////////////////////////////
  550. DeviceWin* SynthModular::NewComment(int n, int x=-1, int y=-1)
  551. {
  552. DeviceWin *nlw = new DeviceWin;
  553. if (x==-1)
  554. {
  555. x = m_CanvasScroll->x()+50;
  556. y = m_CanvasScroll->y()+50;
  557. }
  558. nlw->m_Device=NULL;
  559. nlw->m_PluginID = COMMENT_ID;
  560. DeviceGUIInfo Info;
  561. Info.XPos = x;
  562. Info.YPos = y;
  563. Info.Width = 50;
  564. Info.Height = 20;
  565. Info.NumInputs = 0;
  566. Info.NumOutputs = 0;
  567. Info.Name = "";
  568. nlw->m_DeviceGUI = new Fl_CommentGUI(Info, NULL, NULL);
  569. m_Canvas->add(nlw->m_DeviceGUI);
  570. m_Canvas->redraw();
  571. return nlw;
  572. }
  573. //////////////////////////////////////////////////////////
  574. void SynthModular::AddComment(int n)
  575. {
  576. //cerr<<"Adding "<<m_NextID<<endl;
  577. DeviceWin* temp = NewComment(n);
  578. if (temp)
  579. {
  580. int ID=m_NextID++;
  581. //cerr<<"adding comment "<<ID<<endl;
  582. temp->m_DeviceGUI->SetID(ID);
  583. m_DeviceWinMap[ID]=temp;
  584. }
  585. }
  586. //////////////////////////////////////////////////////////
  587. void SynthModular::UpdateHostInfo()
  588. {
  589. // used to use string streams, but this seems to cause a compiler bug
  590. // at the moment, so fall back to using a temporary file
  591. //std::stringstream str;
  592. fstream ofs("___temp.ssmtmp",ios::out);
  593. //str<<*this;
  594. ofs<<*this;
  595. ClearUp();
  596. // update the settings
  597. m_Info.BUFSIZE = SpiralInfo::BUFSIZE;
  598. m_Info.FRAGSIZE = SpiralInfo::FRAGSIZE;
  599. m_Info.FRAGCOUNT = SpiralInfo::FRAGCOUNT;
  600. m_Info.SAMPLERATE = SpiralInfo::SAMPLERATE;
  601. m_Info.OUTPUTFILE = SpiralInfo::OUTPUTFILE;
  602. m_Info.MIDIFILE = SpiralInfo::MIDIFILE;
  603. m_Info.POLY = SpiralInfo::POLY;
  604. fstream ifs("___temp.ssmtmp",ios::in);
  605. //str>>*this;
  606. ifs>>*this;
  607. system("rm -f ___temp.ssmtmp");
  608. }
  609. //////////////////////////////////////////////////////////
  610. // called when a callback output plugin wants to run the audio thread
  611. void SynthModular::cb_Update(void* o, bool mode)
  612. {
  613. m_CallbackUpdateMode=mode;
  614. ((SynthModular*)o)->Update();
  615. }
  616. // called by a blocking output plugin to notify the engine its ready to
  617. // take control of the update timing (so take the brakes off)
  618. void SynthModular::cb_Blocking(void* o, bool mode)
  619. {
  620. m_BlockingOutputPluginIsReady=mode;
  621. }
  622. //////////////////////////////////////////////////////////
  623. istream &operator>>(istream &s, SynthModular &o)
  624. {
  625. o.PauseAudio();
  626. string dummy,dummy2;
  627. int ver;
  628. s>>dummy>>dummy>>dummy>>ver;
  629. if (ver>FILE_VERSION)
  630. {
  631. SpiralInfo::Alert("Bad file, or more recent version.");
  632. return s;
  633. }
  634. if (ver>2)
  635. {
  636. int MainWinX,MainWinY,MainWinW,MainWinH;
  637. int EditWinX,EditWinY,EditWinW,EditWinH;
  638. s>>MainWinX>>MainWinY>>MainWinW>>MainWinH;
  639. s>>EditWinX>>EditWinY>>EditWinW>>EditWinH;
  640. //o.m_MainWindow->resize(MainWinX,MainWinY,MainWinW,MainWinH);
  641. //o.m_EditorWindow->resize(EditWinX,EditWinY,EditWinW,EditWinH);
  642. }
  643. int Num, ID, PluginID, x,y,ps,px,py;
  644. s>>dummy>>Num;
  645. for(int n=0; n<Num; n++)
  646. {
  647. #ifdef DEBUG_STREAM
  648. cerr<<"Loading Device "<<n<<endl;
  649. #endif
  650. s>>dummy; // "Device"
  651. s>>ID;
  652. s>>dummy2; // "Plugin"
  653. s>>PluginID;
  654. s>>x>>y;
  655. string Name;
  656. if (ver>3)
  657. {
  658. // load the device name
  659. int size;
  660. char Buf[1024];
  661. s>>size;
  662. s.ignore(1);
  663. if (size > 0) {
  664. s.get(Buf,size+1);
  665. Name=Buf;
  666. } else {
  667. Name = "";
  668. }
  669. }
  670. #ifdef DEBUG_STREAM
  671. cerr<<dummy<<" "<<ID<<" "<<dummy2<<" "<<PluginID<<" "<<x<<" "<<y<<endl;
  672. #endif
  673. if (ver>1) s>>ps>>px>>py;
  674. // Check we're not duplicating an ID
  675. if (o.m_DeviceWinMap.find(ID)!=o.m_DeviceWinMap.end())
  676. {
  677. SpiralInfo::Alert("Duplicate device ID found in file - aborting load");
  678. return s;
  679. }
  680. if (PluginID==COMMENT_ID)
  681. {
  682. DeviceWin* temp = o.NewComment(PluginID, x, y);
  683. if (temp)
  684. {
  685. temp->m_DeviceGUI->SetID(ID);
  686. o.m_DeviceWinMap[ID]=temp;
  687. ((Fl_CommentGUI*)(o.m_DeviceWinMap[ID]->m_DeviceGUI))->StreamIn(s); // load the plugin
  688. if (o.m_NextID<=ID) o.m_NextID=ID+1;
  689. }
  690. }
  691. else
  692. {
  693. DeviceWin* temp = o.NewDeviceWin(PluginID, x, y);
  694. if (temp)
  695. {
  696. temp->m_DeviceGUI->SetID(ID);
  697. if (ver>3)
  698. {
  699. // set the titlebars
  700. temp->m_DeviceGUI->SetName(Name);
  701. }
  702. temp->m_Device->SetUpdateInfoCallback(ID,o.cb_UpdatePluginInfo);
  703. o.m_DeviceWinMap[ID]=temp;
  704. o.m_DeviceWinMap[ID]->m_Device->StreamIn(s); // load the plugin
  705. // load external files
  706. o.m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(o.m_FilePath+"_files/");
  707. if (ver>1 && o.m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow())
  708. {
  709. // set the GUI up with the loaded values
  710. // looks messy, but if we do it here, the plugin and it's gui can remain
  711. // totally seperated.
  712. ((SpiralPluginGUI*)(o.m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()))->
  713. UpdateValues(o.m_DeviceWinMap[ID]->m_Device);
  714. // updates the data in the channel buffers, so the values don't
  715. // get overwritten in the next tick. (should maybe be somewhere else)
  716. o.m_DeviceWinMap[ID]->m_Device->GetChannelHandler()->FlushChannels();
  717. // position the plugin window in the main window
  718. //o.m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()->position(px,py);
  719. if (ps)
  720. {
  721. o.m_DeviceWinMap[ID]->m_DeviceGUI->Maximise();
  722. // reposition after maximise
  723. o.m_DeviceWinMap[ID]->m_DeviceGUI->position(x,y);
  724. }
  725. else o.m_DeviceWinMap[ID]->m_DeviceGUI->Minimise();
  726. }
  727. if (o.m_NextID<=ID) o.m_NextID=ID+1;
  728. }
  729. else
  730. {
  731. // can't really recover if the plugin ID doesn't match a plugin, as
  732. // we have no idea how much data in the stream belongs to this plugin
  733. SpiralInfo::Alert("Error in stream, can't really recover data from here on.");
  734. return s;
  735. }
  736. }
  737. }
  738. s>>*o.m_Canvas;
  739. o.ResumeAudio();
  740. return s;
  741. }
  742. //////////////////////////////////////////////////////////
  743. ostream &operator<<(ostream &s, SynthModular &o)
  744. {
  745. o.PauseAudio();
  746. s<<"SpiralSynthModular File Ver "<<FILE_VERSION<<endl;
  747. // make external files dir
  748. bool ExternalDirUsed=false;
  749. string command("mkdir "+o.m_FilePath+"_files");
  750. system(command.c_str());
  751. if (FILE_VERSION>2)
  752. {
  753. s<<o.m_TopWindow->x()<<" "<<o.m_TopWindow->y()<<" ";
  754. s<<o.m_TopWindow->w()<<" "<<o.m_TopWindow->h()<<" ";
  755. s<<0<<" "<<0<<" ";
  756. s<<0<<" "<<0<<endl;
  757. }
  758. // save out the SynthModular
  759. s<<"SectionList"<<endl;
  760. s<<o.m_DeviceWinMap.size()<<endl;
  761. for(map<int,DeviceWin*>::iterator i=o.m_DeviceWinMap.begin();
  762. i!=o.m_DeviceWinMap.end(); i++)
  763. {
  764. s<<endl;
  765. s<<"Device ";
  766. s<<i->first<<" "; // save the id
  767. s<<"Plugin ";
  768. s<<i->second->m_PluginID<<endl;
  769. s<<i->second->m_DeviceGUI->x()<<" ";
  770. s<<i->second->m_DeviceGUI->y()<<" ";
  771. s<<i->second->m_DeviceGUI->GetName().size()<<" ";
  772. s<<i->second->m_DeviceGUI->GetName()<<" ";
  773. if (i->second->m_DeviceGUI->GetPluginWindow())
  774. {
  775. s<<i->second->m_DeviceGUI->GetPluginWindow()->visible()<<" ";
  776. s<<i->second->m_DeviceGUI->GetPluginWindow()->x()<<" ";
  777. s<<i->second->m_DeviceGUI->GetPluginWindow()->y()<<" ";
  778. }
  779. else
  780. {
  781. s<<0<<" "<<0<<" "<<0;
  782. }
  783. s<<endl;
  784. if (i->second->m_PluginID==COMMENT_ID)
  785. {
  786. // save the comment gui
  787. ((Fl_CommentGUI*)(i->second->m_DeviceGUI))->StreamOut(s);
  788. }
  789. else
  790. {
  791. // save the plugin
  792. i->second->m_Device->StreamOut(s);
  793. }
  794. s<<endl;
  795. // save external files
  796. if (i->second->m_Device && i->second->m_Device->SaveExternalFiles(o.m_FilePath+"_files/"))
  797. {
  798. ExternalDirUsed=true;
  799. }
  800. }
  801. s<<endl<<*o.m_Canvas<<endl;
  802. // remove it if it wasn't used
  803. if (!ExternalDirUsed)
  804. {
  805. // i guess rmdir won't work if there is something in the dir
  806. // anyway, but best to be on the safe side. (could do rm -rf) :)
  807. string command("rmdir "+o.m_FilePath+"_files");
  808. system(command.c_str());
  809. }
  810. o.ResumeAudio();
  811. return s;
  812. }
  813. //////////////////////////////////////////////////////////
  814. inline void SynthModular::cb_Close_i(Fl_Window* o, void* v)
  815. {
  816. m_SettingsWindow->hide();
  817. delete m_SettingsWindow;
  818. m_TopWindow->hide();
  819. delete m_TopWindow;
  820. o->hide();
  821. }
  822. void SynthModular::cb_Close(Fl_Window* o, void* v)
  823. {((SynthModular*)(o->user_data()))->cb_Close_i(o,v);}
  824. //////////////////////////////////////////////////////////
  825. inline void SynthModular::cb_Load_i(Fl_Button* o, void* v)
  826. {
  827. if (m_DeviceWinMap.size()>0 && !Pawfal_YesNo("Load - Lose changes to current patch?"))
  828. {
  829. return;
  830. }
  831. char *fn=fl_file_chooser("Load a patch", "*.ssm", NULL);
  832. if (fn && fn!='\0')
  833. {
  834. ifstream inf(fn);
  835. if (inf)
  836. {
  837. m_FilePath=fn;
  838. ClearUp();
  839. inf>>*this;
  840. TITLEBAR=LABEL+" "+fn;
  841. m_TopWindow->label(TITLEBAR.c_str());
  842. }
  843. }
  844. }
  845. void SynthModular::cb_Load(Fl_Button* o, void* v)
  846. {((SynthModular*)(o->parent()->user_data()))->cb_Load_i(o,v);}
  847. //////////////////////////////////////////////////////////
  848. inline void SynthModular::cb_Save_i(Fl_Button* o, void* v)
  849. {
  850. char *fn=fl_file_chooser("Save a patch", "*.ssm", NULL);
  851. if (fn && fn!='\0')
  852. {
  853. ifstream ifl(fn);
  854. if (ifl)
  855. {
  856. if (!Pawfal_YesNo("File [%s] exists, overwrite?",fn))
  857. {
  858. return;
  859. }
  860. }
  861. ofstream of(fn);
  862. if (of)
  863. {
  864. m_FilePath=fn;
  865. of<<*this;
  866. TITLEBAR=LABEL+" "+fn;
  867. m_TopWindow->label(TITLEBAR.c_str());
  868. }
  869. else
  870. {
  871. fl_message(string("Error saving "+string(fn)).c_str());
  872. }
  873. }
  874. }
  875. void SynthModular::cb_Save(Fl_Button* o, void* v)
  876. {((SynthModular*)(o->parent()->user_data()))->cb_Save_i(o,v);}
  877. //////////////////////////////////////////////////////////
  878. inline void SynthModular::cb_New_i(Fl_Button* o, void* v)
  879. {
  880. if (m_DeviceWinMap.size()>0 && !Pawfal_YesNo("New - Lose changes to current patch?"))
  881. {
  882. return;
  883. }
  884. m_TopWindow->label(TITLEBAR.c_str());
  885. ClearUp();
  886. }
  887. void SynthModular::cb_New(Fl_Button* o, void* v)
  888. {((SynthModular*)(o->parent()->user_data()))->cb_New_i(o,v);}
  889. //////////////////////////////////////////////////////////
  890. inline void SynthModular::cb_NewDevice_i(Fl_Button* o, void* v)
  891. {
  892. AddDevice(*((int*)v));
  893. }
  894. void SynthModular::cb_NewDevice(Fl_Button* o, void* v)
  895. {((SynthModular*)(o->parent()->user_data()))->cb_NewDevice_i(o,v);}
  896. //////////////////////////////////////////////////////////
  897. inline void SynthModular::cb_NewDeviceFromMenu_i(Fl_Canvas* o, void* v)
  898. {
  899. AddDevice(*((int*)v),*((int*)v+1),*((int*)v+2));
  900. }
  901. void SynthModular::cb_NewDeviceFromMenu(Fl_Canvas* o, void* v)
  902. {((SynthModular*)(o->user_data()))->cb_NewDeviceFromMenu_i(o,v);}
  903. //////////////////////////////////////////////////////////
  904. inline void SynthModular::cb_NewComment_i(Fl_Button* o, void* v)
  905. {
  906. AddComment(-1);
  907. }
  908. void SynthModular::cb_NewComment(Fl_Button* o, void* v)
  909. {((SynthModular*)(o->parent()->user_data()))->cb_NewComment_i(o,v);}
  910. //////////////////////////////////////////////////////////
  911. inline void SynthModular::cb_GroupTab_i(Fl_Tabs* o, void* v)
  912. {
  913. m_GroupTab->redraw();
  914. }
  915. void SynthModular::cb_GroupTab(Fl_Tabs* o, void* v)
  916. {((SynthModular*)(o->parent()->user_data()))->cb_GroupTab_i(o,v);}
  917. //////////////////////////////////////////////////////////
  918. inline void SynthModular::cb_Rload_i(Fl_Button* o, void* v)
  919. {
  920. m_SettingsWindow->show();
  921. /*PluginManager::Get()->UnloadAll();
  922. m_ToolBox->remove(m_ToolPack);
  923. delete m_ToolPack;
  924. m_ToolPack = new Fl_Pack(5,20,TOOLBOX_WIDTH-10, TOOLBOX_HEIGHT-40,"");
  925. m_ToolPack->type(FL_VERTICAL);
  926. m_ToolPack->box(FL_NO_BOX);
  927. m_ToolPack->color(SpiralInfo::GUICOL_Tool);
  928. m_ToolPack->user_data((void*)(this));
  929. m_ToolBox->add(m_ToolPack);
  930. m_ToolBox->redraw();
  931. LoadPlugins();*/
  932. }
  933. void SynthModular::cb_Rload(Fl_Button* o, void* v)
  934. {((SynthModular*)(o->parent()->user_data()))->cb_Rload_i(o,v);}
  935. //////////////////////////////////////////////////////////
  936. inline void SynthModular::cb_Connection_i(Fl_Canvas* o, void* v)
  937. {
  938. CanvasWire *Wire;
  939. Wire=(CanvasWire*)v;
  940. map<int,DeviceWin*>::iterator si=m_DeviceWinMap.find(Wire->OutputID);
  941. if (si==m_DeviceWinMap.end())
  942. {
  943. char num[32];
  944. sprintf(num,"%d",Wire->OutputID);
  945. SpiralInfo::Alert("Warning: Connection problem - can't find source "+string(num));
  946. return;
  947. }
  948. map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(Wire->InputID);
  949. if (di==m_DeviceWinMap.end())
  950. {
  951. char num[32];
  952. sprintf(num,"%d",Wire->InputID);
  953. SpiralInfo::Alert("Warning: Connection problem - can't find destination "+string(num));
  954. return;
  955. }
  956. Sample *sample=NULL;
  957. if (!si->second->m_Device->GetOutput(Wire->OutputPort,&sample))
  958. {
  959. char num[32];
  960. sprintf(num,"%d,%d",Wire->OutputID,Wire->OutputPort);
  961. SpiralInfo::Alert("Warning: Connection problem - can't find source output "+string(num));
  962. return;
  963. }
  964. if (!di->second->m_Device->SetInput(Wire->InputPort,(const Sample*)sample))
  965. {
  966. char num[32];
  967. sprintf(num,"%d,%d",Wire->InputID,Wire->InputPort);
  968. SpiralInfo::Alert("Warning: Connection problem - can't find source input "+string(num));
  969. return;
  970. }
  971. }
  972. void SynthModular::cb_Connection(Fl_Canvas* o, void* v)
  973. {((SynthModular*)(o->user_data()))->cb_Connection_i(o,v);}
  974. //////////////////////////////////////////////////////////
  975. inline void SynthModular::cb_Unconnect_i(Fl_Canvas* o, void* v)
  976. {
  977. CanvasWire *Wire;
  978. Wire=(CanvasWire*)v;
  979. //cerr<<Wire->InputID<<" "<<Wire->InputPort<<endl;
  980. map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(Wire->InputID);
  981. if (di==m_DeviceWinMap.end())
  982. {
  983. //cerr<<"Can't find destination device "<<Wire->InputID<<endl;
  984. return;
  985. }
  986. SpiralPlugin *Plugin=di->second->m_Device;
  987. if (Plugin && !Plugin->SetInput(Wire->InputPort,NULL))
  988. { cerr<<"Can't find destination device's Input"<<endl; return; }
  989. }
  990. void SynthModular::cb_Unconnect(Fl_Canvas* o, void* v)
  991. {((SynthModular*)(o->user_data()))->cb_Unconnect_i(o,v);}
  992. //////////////////////////////////////////////////////////
  993. void SynthModular::cb_UpdatePluginInfo(int ID, void *PInfo)
  994. {
  995. map<int,DeviceWin*>::iterator i=m_DeviceWinMap.find(ID);
  996. if (i!=m_DeviceWinMap.end())
  997. {
  998. DeviceGUIInfo Info=BuildDeviceGUIInfo(*((PluginInfo*)PInfo));
  999. (*i).second->m_DeviceGUI->Setup(Info);
  1000. (*i).second->m_DeviceGUI->redraw();
  1001. }
  1002. }
  1003. //////////////////////////////////////////////////////////
  1004. void SynthModular::LoadPatch(const char *fn)
  1005. {
  1006. ifstream inf(fn);
  1007. if (inf)
  1008. {
  1009. m_FilePath=fn;
  1010. ClearUp();
  1011. inf>>*this;
  1012. TITLEBAR=LABEL+" "+fn;
  1013. m_TopWindow->label(TITLEBAR.c_str());
  1014. }
  1015. }
  1016. //////////////////////////////////////////////////////////