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.

1271 lines
33KB

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