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.

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