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.

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