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.

1597 lines
47KB

  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_Frozen(false),
  64. m_NextID(0)
  65. {
  66. /* Shared Audio State Information */
  67. m_Info.BUFSIZE = SpiralInfo::BUFSIZE;
  68. m_Info.SAMPLERATE = SpiralInfo::SAMPLERATE;
  69. m_Info.PAUSED = false;
  70. /* obsolete - REMOVE SOON */
  71. m_Info.FRAGSIZE = SpiralInfo::FRAGSIZE;
  72. m_Info.FRAGCOUNT = SpiralInfo::FRAGCOUNT;
  73. m_Info.OUTPUTFILE = SpiralInfo::OUTPUTFILE;
  74. m_Info.MIDIFILE = SpiralInfo::MIDIFILE;
  75. m_Info.POLY = SpiralInfo::POLY;
  76. /* Shared GUI Preferences Information */
  77. m_Info.GUI_COLOUR = SpiralInfo::GUI_COLOUR;
  78. m_Info.SCOPE_BG_COLOUR = SpiralInfo::SCOPE_BG_COLOUR;
  79. m_Info.SCOPE_FG_COLOUR = SpiralInfo::SCOPE_FG_COLOUR;
  80. m_Info.SCOPE_SEL_COLOUR = SpiralInfo::SCOPE_SEL_COLOUR;
  81. m_Info.SCOPE_IND_COLOUR = SpiralInfo::SCOPE_IND_COLOUR;
  82. m_Info.SCOPE_MRK_COLOUR = SpiralInfo::SCOPE_MRK_COLOUR;
  83. m_Info.GUICOL_Device = SpiralInfo::GUICOL_Device;
  84. m_Info.GUIDEVICE_Box = SpiralInfo::GUIDEVICE_Box;
  85. for (int n=0; n<512; n++) Numbers[n]=n;
  86. m_CH.Register("Frozen",&m_Frozen);
  87. }
  88. //////////////////////////////////////////////////////////
  89. SynthModular::~SynthModular()
  90. {
  91. ClearUp();
  92. PluginManager::Get()->PackUpAndGoHome();
  93. system("rm -f ___temp.ssmcopytmp");
  94. }
  95. //////////////////////////////////////////////////////////
  96. void SynthModular::ClearUp()
  97. {
  98. FreezeAll();
  99. for(map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
  100. i!=m_DeviceWinMap.end(); i++)
  101. {
  102. //Stop processing of audio if any
  103. if (i->second->m_Device)
  104. {
  105. if (i->second->m_Device->Kill());
  106. }
  107. i->second->m_DeviceGUI->Clear();
  108. if (i->second->m_DeviceGUI->GetPluginWindow())
  109. {
  110. i->second->m_DeviceGUI->GetPluginWindow()->hide();
  111. }
  112. //Delete Device
  113. delete i->second->m_Device;
  114. i->second->m_Device=NULL;
  115. }
  116. m_Canvas->Clear();
  117. m_DeviceWinMap.clear();
  118. m_NextID=0;
  119. ThawAll();
  120. }
  121. //////////////////////////////////////////////////////////
  122. void SynthModular::Update()
  123. {
  124. m_CH.UpdateDataNow();
  125. if (m_Frozen) return;
  126. // for all the plugins
  127. for(map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
  128. i!=m_DeviceWinMap.end(); i++)
  129. {
  130. if (i->second->m_Device && i->second->m_Device->IsDead())
  131. {
  132. //Delete Device
  133. delete i->second->m_Device;
  134. i->second->m_Device=NULL;
  135. //Erase Device from DeviceWinMap
  136. m_DeviceWinMap.erase(i);
  137. }
  138. else if (i->second->m_Device) // if it's not a comment
  139. {
  140. #ifdef DEBUG_PLUGINS
  141. cerr<<"Updating channelhandler of plugin "<<i->second->m_PluginID<<endl;
  142. #endif
  143. // updates the data from the gui thread, if it's not blocking
  144. i->second->m_Device->UpdateChannelHandler();
  145. #ifdef DEBUG_PLUGINS
  146. cerr<<"Finished updating"<<endl;
  147. #endif
  148. // If this is an audio device see if we always need to ProcessAudio here
  149. if ((!m_ResetingAudioThread))
  150. {
  151. if (i->second->m_Device->IsAudioDriver())
  152. {
  153. AudioDriver *driver = ((AudioDriver *)i->second->m_Device);
  154. if (driver->ProcessType() == AudioDriver::ALWAYS)
  155. {
  156. driver->ProcessAudio();
  157. }
  158. }
  159. // run any commands we've received from the GUI's
  160. i->second->m_Device->ExecuteCommands();
  161. }
  162. }
  163. }
  164. // run the plugins (only ones connected to anything)
  165. list<int> ExecutionOrder = m_Canvas->GetGraph()->GetSortedList();
  166. for (list<int>::reverse_iterator i=ExecutionOrder.rbegin();
  167. i!=ExecutionOrder.rend(); i++)
  168. {
  169. // use the graphsort order to remove internal latency
  170. map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(*i);
  171. if (di!=m_DeviceWinMap.end() && di->second->m_Device && (! di->second->m_Device->IsDead()) && (!m_Info.PAUSED || m_ResetingAudioThread))
  172. {
  173. #ifdef DEBUG_PLUGINS
  174. cerr<<"Executing plugin "<<di->second->m_PluginID<<endl;
  175. #endif
  176. if (m_ResetingAudioThread)
  177. {
  178. di->second->m_Device->Reset();
  179. }
  180. else
  181. {
  182. di->second->m_Device->Execute();
  183. // If this is an audio device see if we need to ProcessAudio here
  184. if (di->second->m_Device->IsAudioDriver())
  185. {
  186. AudioDriver *driver = ((AudioDriver *)di->second->m_Device);
  187. if (driver->ProcessType() == AudioDriver::MANUAL)
  188. {
  189. driver->ProcessAudio();
  190. }
  191. }
  192. }
  193. #ifdef DEBUG_PLUGINS
  194. cerr<<"Finished executing"<<endl;
  195. #endif
  196. }
  197. }
  198. //we can safely turn this off here
  199. m_ResetingAudioThread = false;
  200. }
  201. //////////////////////////////////////////////////////////
  202. void SynthModular::UpdatePluginGUIs()
  203. {
  204. // see if any need deleting
  205. for (map<int,DeviceWin*>::iterator i=m_DeviceWinMap.begin();
  206. i!=m_DeviceWinMap.end(); i++)
  207. {
  208. if (i->second->m_DeviceGUI && i->second->m_DeviceGUI->GetPluginWindow())
  209. {
  210. SpiralPluginGUI *GUI=(SpiralPluginGUI *)i->second->m_DeviceGUI->GetPluginWindow();
  211. GUI->Update();
  212. }
  213. if (i->second->m_DeviceGUI && i->second->m_DeviceGUI->Killed())
  214. {
  215. bool erase = true;
  216. //Stop processing of audio if any
  217. if (i->second->m_Device)
  218. {
  219. if (i->second->m_Device->Kill());
  220. erase = false;
  221. }
  222. //Clear GUI Device
  223. i->second->m_DeviceGUI->Clear();
  224. // Hide Device GUI FIRST
  225. if (i->second->m_DeviceGUI->GetPluginWindow())
  226. {
  227. i->second->m_DeviceGUI->GetPluginWindow()->hide();
  228. }
  229. //Remove Device GUI from canvas
  230. m_Canvas->RemoveDevice(i->second->m_DeviceGUI);
  231. //Delete Device GUI - must delete here or sometimes plugin will randomly crash
  232. delete i->second->m_DeviceGUI;
  233. i->second->m_DeviceGUI = NULL;
  234. //Erase from winmap if no audio to do it
  235. if (erase)
  236. m_DeviceWinMap.erase(i);
  237. }
  238. }
  239. m_Canvas->Poll();
  240. if (m_HostNeedsUpdate)
  241. {
  242. cout << "Updating SampleRate to: " << SpiralInfo::SAMPLERATE << " and Buffer Size to: " << SpiralInfo::BUFSIZE << " to match current Audio Driver." << endl;
  243. UpdateHostInfo();
  244. m_HostNeedsUpdate = false;
  245. }
  246. }
  247. //////////////////////////////////////////////////////////
  248. SpiralWindowType *SynthModular::CreateWindow()
  249. {
  250. m_TopWindow = new SpiralWindowType(MAIN_WIDTH, MAIN_HEIGHT, LABEL.c_str());
  251. m_TopWindow->user_data((void*)(this));
  252. //m_TopWindow->resizable(m_TopWindow);
  253. m_MainMenu = new Fl_Menu_Bar (0, 0, MAIN_WIDTH, 20, "");
  254. m_MainMenu->user_data((void*)(this));
  255. m_MainMenu->box(FL_PLASTIC_UP_BOX);
  256. m_MainMenu->textsize (10);
  257. m_MainMenu->add ("File/New", 0, cb_New, (void*)(this), FL_MENU_DIVIDER);
  258. m_MainMenu->add ("File/Load", 0, cb_Load, (void*)(this), 0);
  259. m_MainMenu->add ("File/Save As", 0, cb_Save, (void*)(this), 0);
  260. m_MainMenu->add ("File/Merge", 0, cb_Merge, (void*)(this), FL_MENU_DIVIDER);
  261. m_MainMenu->add ("File/Exit", 0, cb_Close, (void*)(this), 0);
  262. m_MainMenu->add ("Edit/Cut", 0, cb_Cut, (void*)(this), 0);
  263. m_MainMenu->add ("Edit/Copy", 0, cb_Copy, (void*)(this), 0);
  264. m_MainMenu->add ("Edit/Paste", 0, cb_Paste, (void*)(this), 0);
  265. m_MainMenu->add ("Edit/Delete", 0, cb_Delete, (void*)(this), FL_MENU_DIVIDER);
  266. //m_MainMenu->add ("Edit/Toolbars/Plugins", 0, cb_Undefined, (void*)(this), 0);
  267. //m_MainMenu->add ("Edit/Toolbars/Function", 0, cb_Undefined, (void*)(this), 0);
  268. m_MainMenu->add ("Edit/Options", 0, cb_Options, (void*)(this), 0);
  269. m_MainMenu->add ("Plugins/dummy", 0, NULL, NULL, 0);
  270. m_MainMenu->add ("Audio/Pause", 0, cb_PlayPause, NULL, 0);
  271. m_MainMenu->add ("Audio/Reset", 0, cb_Reset, NULL, 0);
  272. //m_MainMenu->add ("Help/Plugins/dummy", 0, NULL, NULL, 0);
  273. //m_MainMenu->add ("Help/Credits", 0, NULL, (void*)(this), 0);
  274. //m_MainMenu->add ("Help/About", 0, NULL, (void*)(this), 0);
  275. m_TopWindow->add (m_MainMenu);
  276. int but = 50;
  277. int ToolbarHeight = but + 0;
  278. m_Topbar = new Fl_Pack (0, 20, MAIN_WIDTH, ToolbarHeight, "");
  279. m_Topbar->user_data((void*)(this));
  280. m_Topbar->type(FL_HORIZONTAL);
  281. m_Topbar->color(SpiralInfo::GUICOL_Button);
  282. m_TopWindow->add(m_Topbar);
  283. m_ToolbarPanel = new Fl_Pack (0, 20, but*6, ToolbarHeight, "");
  284. m_ToolbarPanel->user_data((void*)(this));
  285. m_ToolbarPanel->type(FL_VERTICAL);
  286. m_ToolbarPanel->color(SpiralInfo::GUICOL_Button);
  287. m_Topbar->add(m_ToolbarPanel);
  288. m_Toolbar = new Fl_Pack (0, 20, but*6, but, "");
  289. m_Toolbar->user_data((void*)(this));
  290. m_Toolbar->type(FL_HORIZONTAL);
  291. m_Toolbar->color(SpiralInfo::GUICOL_Button);
  292. m_ToolbarPanel->add(m_Toolbar);
  293. m_Load = new Fl_Button (0, 0, but, but, "");
  294. m_Load->user_data ((void*)(this));
  295. Fl_Pixmap *tPix = new Fl_Pixmap(load_xpm);
  296. m_Load->image(tPix->copy());
  297. delete tPix;
  298. m_Load->type(0);
  299. m_Load->box(FL_PLASTIC_UP_BOX);
  300. m_Load->color(SpiralInfo::GUICOL_Button);
  301. m_Load->selection_color(SpiralInfo::GUICOL_Tool);
  302. m_Load->labelsize (1);
  303. m_Load->tooltip("Load a patch file");
  304. m_Load->callback((Fl_Callback*)cb_Load);
  305. m_Toolbar->add(m_Load);
  306. m_Save = new Fl_Button(0, 0, but, but, "");
  307. m_Save->user_data ((void*)(this));
  308. tPix = new Fl_Pixmap(save_xpm);
  309. m_Save->image(tPix->copy());
  310. delete tPix;
  311. m_Save->type(0);
  312. m_Save->box(FL_PLASTIC_UP_BOX);
  313. m_Save->color(SpiralInfo::GUICOL_Button);
  314. m_Save->selection_color(SpiralInfo::GUICOL_Tool);
  315. m_Save->labelsize (1);
  316. m_Save->tooltip("Save a patch file");
  317. m_Save->callback((Fl_Callback*)cb_Save);
  318. m_Toolbar->add(m_Save);
  319. m_New = new Fl_Button(0, 0, but, but, "");
  320. m_New->user_data ((void*)(this));
  321. tPix = new Fl_Pixmap(new_xpm);
  322. m_New->image(tPix->copy());
  323. delete tPix;
  324. m_New->type(0);
  325. m_New->box(FL_PLASTIC_UP_BOX);
  326. m_New->color(SpiralInfo::GUICOL_Button);
  327. m_New->selection_color(SpiralInfo::GUICOL_Tool);
  328. m_New->labelsize (1);
  329. m_New->tooltip("New patch");
  330. m_New->callback((Fl_Callback*)cb_New);
  331. m_Toolbar->add(m_New);
  332. m_Options = new Fl_Button(0, 0, but, but, "");
  333. m_Options->user_data ((void*)(this));
  334. tPix = new Fl_Pixmap(options_xpm);
  335. m_Options->image(tPix->copy());
  336. delete tPix;
  337. m_Options->type(0);
  338. m_Options->box(FL_PLASTIC_UP_BOX);
  339. m_Options->color(SpiralInfo::GUICOL_Button);
  340. m_Options->selection_color(SpiralInfo::GUICOL_Tool);
  341. m_Options->labelsize (1);
  342. m_Options->tooltip("Options");
  343. m_Options->callback((Fl_Callback*)cb_Options);
  344. m_Toolbar->add(m_Options);
  345. m_NewComment = new Fl_Button(0, 0, but, but, "");
  346. tPix = new Fl_Pixmap(comment_xpm);
  347. m_NewComment->image(tPix->copy());
  348. delete tPix;
  349. m_NewComment->type(0);
  350. m_NewComment->box(FL_PLASTIC_UP_BOX);
  351. m_NewComment->color(SpiralInfo::GUICOL_Button);
  352. m_NewComment->selection_color(SpiralInfo::GUICOL_Tool);
  353. m_NewComment->labelsize (1);
  354. m_NewComment->tooltip("New comment");
  355. m_NewComment->callback((Fl_Callback*)cb_NewComment);
  356. m_Toolbar->add(m_NewComment);
  357. m_PlayResetGroup = new Fl_Pack (0, 0, but, but, "");
  358. m_PlayResetGroup->color(SpiralInfo::GUICOL_Button);
  359. m_Toolbar->add(m_PlayResetGroup);
  360. m_PlayPause = new Fl_Button(0, 0, but, but/2, "@||");
  361. m_PlayPause->user_data((void*)(this));
  362. m_PlayPause->type(0);
  363. m_PlayPause->box(FL_PLASTIC_UP_BOX);
  364. m_PlayPause->color(SpiralInfo::GUICOL_Button);
  365. m_PlayPause->selection_color(SpiralInfo::GUICOL_Tool);
  366. m_PlayPause->labelsize (10);
  367. m_PlayPause->tooltip("Pause");
  368. m_PlayPause->callback((Fl_Callback*)cb_PlayPause);
  369. m_PlayResetGroup->add(m_PlayPause);
  370. m_Reset = new Fl_Button(0, 0, but, but/2, "Reset");
  371. m_Reset->box(FL_PLASTIC_UP_BOX);
  372. m_Reset->color(SpiralInfo::GUICOL_Button);
  373. m_Reset->user_data((void*)(this));
  374. m_Reset->selection_color(SpiralInfo::GUICOL_Tool);
  375. m_Reset->labelsize (10);
  376. m_Reset->tooltip("Reset Audio State of all Plugins");
  377. m_Reset->callback((Fl_Callback*)cb_Reset);
  378. m_PlayResetGroup->add(m_Reset);
  379. m_GroupFiller = new Fl_Group (0, 0, 0, ToolbarHeight, "");
  380. m_GroupFiller->color(SpiralInfo::GUICOL_Button);
  381. m_Topbar->add (m_GroupFiller);
  382. m_GroupTab = new Fl_Tabs (0, 0, MAIN_WIDTH-m_GroupFiller->w()-but*6, ToolbarHeight, "");
  383. m_GroupTab->user_data ((void*)(this));
  384. m_GroupTab->box(FL_PLASTIC_DOWN_BOX);
  385. m_GroupTab->color(SpiralInfo::GUICOL_Button);
  386. m_GroupTab->callback((Fl_Callback*)cb_GroupTab);
  387. m_Topbar->add (m_GroupTab);
  388. m_Topbar->resizable(m_GroupTab);
  389. /////////////////
  390. ToolbarHeight += 20; // Stretch this a bit to allow room for the menu-bar too.
  391. m_CanvasScroll = new Fl_Scroll (0, ToolbarHeight, MAIN_WIDTH, MAIN_HEIGHT-ToolbarHeight, "");
  392. m_TopWindow->add(m_CanvasScroll);
  393. m_TopWindow->resizable(m_CanvasScroll);
  394. m_Canvas = new Fl_Canvas(-5000, -5000, 10000, 10000, "");
  395. m_Canvas->type(1);
  396. m_Canvas->box(FL_FLAT_BOX);
  397. m_Canvas->labeltype(FL_ENGRAVED_LABEL);
  398. m_Canvas->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE);
  399. m_Canvas->color(SpiralInfo::GUICOL_Canvas);
  400. m_Canvas->user_data((void*)(this));
  401. m_Canvas->SetConnectionCallback((Fl_Callback*)cb_Connection);
  402. m_Canvas->SetUnconnectCallback((Fl_Callback*)cb_Unconnect);
  403. m_Canvas->SetAddDeviceCallback((Fl_Callback*)cb_NewDeviceFromCanvasMenu);
  404. m_Canvas->SetCutDeviceGroupCallback((Fl_Callback*)cb_Cut);
  405. m_Canvas->SetCopyDeviceGroupCallback((Fl_Callback*)cb_Copy);
  406. m_Canvas->SetPasteDeviceGroupCallback((Fl_Callback*)cb_Paste);
  407. m_Canvas->SetMergePatchCallback((Fl_Callback*)cb_Merge);
  408. m_CanvasScroll->add(m_Canvas);
  409. m_SettingsWindow = new SettingsWindow;
  410. m_SettingsWindow->RegisterApp(this);
  411. return m_TopWindow;
  412. }
  413. //////////////////////////////////////////////////////////
  414. vector<string> SynthModular::BuildPluginList (const string &Path) {
  415. // Scan plugin path for plugins.
  416. DIR *dp;
  417. struct dirent *ep;
  418. struct stat sb;
  419. void *handle;
  420. string fullpath;
  421. const char *path = Path.c_str();
  422. vector<string> ret;
  423. dp = opendir(path);
  424. if (!dp) {
  425. cerr << "WARNING: Could not open path " << path << endl;
  426. }
  427. else {
  428. while ((ep = readdir(dp))) {
  429. // Need full path
  430. fullpath = path;
  431. fullpath.append(ep->d_name);
  432. // Stat file to get type
  433. if (!stat(fullpath.c_str(), &sb)) {
  434. // We only want regular files
  435. if (S_ISREG(sb.st_mode)) {
  436. // We're not fussed about resolving symbols yet, since we are just
  437. // checking if it's a DLL.
  438. handle = dlopen(fullpath.c_str(), RTLD_LAZY);
  439. if (!handle) {
  440. cerr << "WARNING: File " << path << ep->d_name
  441. << " could not be examined" << endl;
  442. cerr << "dlerror() output:" << endl;
  443. cerr << dlerror() << endl;
  444. }
  445. else {
  446. // It's a DLL. Add name to list
  447. ret.push_back(ep->d_name);
  448. }
  449. }
  450. }
  451. }
  452. }
  453. return ret;
  454. }
  455. void SynthModular::LoadPlugins (string pluginPath) {
  456. int Width = 35;
  457. int Height = 35;
  458. int SWidth = 256;
  459. int SHeight = 256;
  460. Fl_Pixmap pic (SSM_xpm);
  461. Fl_Double_Window* Splash = new Fl_Double_Window ((Fl::w()/2) - (SWidth/2), (Fl::h()/2) - (SHeight/2),
  462. SWidth, SHeight, "SSM");
  463. Splash->border(0);
  464. Fl_Box* pbut = new Fl_Box (0, 8, SWidth, SHeight, "");
  465. pbut->box (FL_NO_BOX);
  466. pic.label (pbut);
  467. Fl_Box *splashtext = new Fl_Box (5, SHeight-20, 200, 20, "Loading...");
  468. splashtext->labelsize (10);
  469. splashtext->box (FL_NO_BOX);
  470. splashtext->align (FL_ALIGN_INSIDE | FL_ALIGN_LEFT);
  471. Splash->add (pbut);
  472. Splash->add (splashtext);
  473. Splash->show();
  474. int ID=-1;
  475. vector<string> PluginVector;
  476. if (SpiralInfo::USEPLUGINLIST) PluginVector = SpiralInfo::PLUGINVEC;
  477. else {
  478. if (pluginPath.empty()) PluginVector = BuildPluginList (SpiralInfo::PLUGIN_PATH);
  479. else {
  480. string::iterator i = pluginPath.end() - 1;
  481. if (*i != '/') pluginPath += '/';
  482. PluginVector = BuildPluginList (pluginPath);
  483. }
  484. }
  485. for (vector<string>::iterator i=PluginVector.begin(); i!=PluginVector.end(); i++) {
  486. string Fullpath;
  487. if (pluginPath=="") Fullpath=SpiralInfo::PLUGIN_PATH+*i;
  488. else Fullpath = pluginPath + *"/" + *i;
  489. ID = PluginManager::Get()->LoadPlugin (Fullpath.c_str());
  490. if (ID!=PluginError) {
  491. #ifdef DEBUG_PLUGINS
  492. cerr << ID << " = Plugin [" << *i << "]" << endl;
  493. #endif
  494. Fl_ToolButton *NewButton = new Fl_ToolButton (0, 0, Width, Height, "");
  495. // we can't set user data, because the callback uses it
  496. // NewButton->user_data ((void*)(this));
  497. NewButton->labelsize (1);
  498. Fl_Pixmap *tPix = new Fl_Pixmap (PluginManager::Get()->GetPlugin(ID)->GetIcon());
  499. NewButton->image(tPix->copy(tPix->w(),tPix->h()));
  500. delete tPix;
  501. string GroupName = PluginManager::Get()->GetPlugin(ID)->GetGroupName();
  502. Fl_Pack* the_group=NULL;
  503. // find or create this group, and add an icon
  504. map<string,Fl_Pack*>::iterator gi = m_PluginGroupMap.find (GroupName);
  505. if (gi == m_PluginGroupMap.end()) {
  506. the_group = new Fl_Pack (m_GroupTab->x(), 16, m_GroupTab->w(), m_GroupTab->h() - 15, GroupName.c_str());
  507. the_group->type(FL_HORIZONTAL);
  508. the_group->labelsize(8);
  509. the_group->color(SpiralInfo::GUICOL_Button);
  510. the_group->user_data((void*)(this));
  511. //m_GroupTab->add(the_group);
  512. m_GroupTab->value(the_group);
  513. m_PluginGroupMap[GroupName]=the_group;
  514. }
  515. else the_group = gi->second;
  516. NewButton->type (0);
  517. NewButton->box (FL_NO_BOX);
  518. NewButton->down_box (FL_NO_BOX);
  519. //NewButton->color(SpiralInfo::GUICOL_Button);
  520. //NewButton->selection_color(SpiralInfo::GUICOL_Button);
  521. the_group->add (NewButton);
  522. // we need to keep tooltips stored outside their widgets - widgets just have a pointer
  523. // I haven't done anything about cleaning up these strings - which may cause memory leaks?
  524. // But m_DeviceVec - which, I assume, would be used to keep track of / clean up the dynamicly
  525. // created NewButton widgets isn't cleaned up either, so we might have 2 memory leaks
  526. // involved? - but then again, they might be automatically deallocated because they're
  527. // in another widget, in which case there's just one memory leak to deal with. (andy)
  528. string* PluginName = new string (*i);
  529. // find the first slash, if there is one, and get rid of everything before and including it
  530. unsigned int p = PluginName->find ('/');
  531. if (p < PluginName->length()) PluginName->erase (0, p);
  532. // find last . and get rid of everything after and including it
  533. p = PluginName->rfind ('.');
  534. unsigned int l = PluginName->length ();
  535. if (p < l) PluginName->erase (p, l);
  536. NewButton->tooltip (PluginName->c_str());
  537. // Slashes have significance to the menu widgets, remove them from the GroupName
  538. while ((p = GroupName.find ('/')) < PluginName->length())
  539. GroupName = GroupName.replace (p, 1, " and ");
  540. string MenuEntry = "Plugins/" + GroupName + "/" + *PluginName;
  541. m_MainMenu->add (MenuEntry.c_str(), 0, cb_NewDeviceFromMenu, &Numbers[ID], 0);
  542. // when help is working better - this will put the plugins into the help menu
  543. // MenuEntry = "Help/" + MenuEntry;
  544. // m_MainMenu->add (MenuEntry.c_str(), 0, NULL, &Numbers[ID], 0);
  545. // Add the plugins to the canvas menu
  546. m_Canvas->AddPluginName (MenuEntry, PluginManager::Get()->GetPlugin(ID)->ID);
  547. // this overwrites the widget's user_data with that specified for the callback
  548. // so we can't use it for other purposes
  549. NewButton->callback ((Fl_Callback*)cb_NewDevice, &Numbers[ID]);
  550. NewButton->show();
  551. // Nothing else ever touches m_DeviceVec - is this right??? (andy)
  552. m_DeviceVec.push_back (NewButton);
  553. the_group->redraw();
  554. // m_NextPluginButton++;
  555. Fl::check();
  556. splashtext->label (PluginName->c_str());
  557. Splash->redraw();
  558. }
  559. }
  560. map<string,Fl_Pack*>::iterator PlugGrp;
  561. for (PlugGrp = m_PluginGroupMap.begin(); PlugGrp!= m_PluginGroupMap.end(); ++PlugGrp) {
  562. m_GroupTab->add (PlugGrp->second);
  563. PlugGrp->second->add (new Fl_Box (0, 0, 600, 100, ""));
  564. }
  565. // try to show the SpiralSound group
  566. PlugGrp = m_PluginGroupMap.find("SpiralSound");
  567. // can't find it - show the first plugin group
  568. if (PlugGrp==m_PluginGroupMap.end()) PlugGrp=m_PluginGroupMap.begin();
  569. m_GroupTab->value(PlugGrp->second);
  570. bool found_dummy;
  571. int i;
  572. do {
  573. found_dummy = false;
  574. for (i=0; i<m_MainMenu->size(); i++) {
  575. if (m_MainMenu->text (i) != NULL) {
  576. found_dummy = (strcmp ("dummy", m_MainMenu->text (i)) == 0);
  577. if (found_dummy) break;
  578. }
  579. }
  580. if (found_dummy) m_MainMenu->remove (i);
  581. } while (found_dummy);
  582. Splash->hide();
  583. delete Splash;
  584. }
  585. //////////////////////////////////////////////////////////
  586. DeviceGUIInfo SynthModular::BuildDeviceGUIInfo(PluginInfo &PInfo)
  587. {
  588. DeviceGUIInfo Info;
  589. int Height=50;
  590. // tweak the size if we have too many ins/outs
  591. if (PInfo.NumInputs>4 || PInfo.NumOutputs>4)
  592. {
  593. if (PInfo.NumInputs<PInfo.NumOutputs)
  594. {
  595. Height=PInfo.NumOutputs*10+5;
  596. }
  597. else
  598. {
  599. Height=PInfo.NumInputs*10+5;
  600. }
  601. }
  602. // Make the guiinfo struct
  603. Info.XPos = 0;
  604. Info.YPos = 0;
  605. Info.Width = 40;
  606. Info.Height = Height;
  607. Info.NumInputs = PInfo.NumInputs;
  608. Info.NumOutputs = PInfo.NumOutputs;
  609. Info.Name = PInfo.Name;
  610. Info.PortTips = PInfo.PortTips;
  611. Info.PortTypes = PInfo.PortTypes;
  612. return Info;
  613. }
  614. //////////////////////////////////////////////////////////
  615. DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y)
  616. {
  617. DeviceWin *nlw = new DeviceWin;
  618. const HostsideInfo* Plugin=PluginManager::Get()->GetPlugin(n);
  619. if (!Plugin) return NULL;
  620. nlw->m_Device=Plugin->CreateInstance();
  621. if (!nlw->m_Device) return NULL;
  622. nlw->m_Device->SetBlockingCallback(cb_Blocking);
  623. nlw->m_Device->SetUpdateCallback(cb_Update);
  624. nlw->m_Device->SetParent((void*)this);
  625. if ( nlw->m_Device->IsAudioDriver() )
  626. {
  627. AudioDriver *driver = ((AudioDriver*)nlw->m_Device);
  628. driver->SetChangeBufferAndSampleRateCallback(cb_ChangeBufferAndSampleRate);
  629. }
  630. PluginInfo PInfo = nlw->m_Device->Initialise(&m_Info);
  631. SpiralGUIType *temp = nlw->m_Device->CreateGUI();
  632. Fl_Pixmap *Pix = new Fl_Pixmap(Plugin->GetIcon());
  633. nlw->m_PluginID = n;
  634. if (temp) temp->position(x+10,y);
  635. DeviceGUIInfo Info=BuildDeviceGUIInfo(PInfo);
  636. Info.XPos = x; //TOOLBOX_WIDTH+(rand()%400);
  637. Info.YPos = y; //rand()%400;
  638. nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix, nlw->m_Device->IsTerminal());
  639. Fl_Canvas::SetDeviceCallbacks(nlw->m_DeviceGUI, m_Canvas);
  640. m_Canvas->add(nlw->m_DeviceGUI);
  641. m_Canvas->redraw();
  642. return nlw;
  643. }
  644. //////////////////////////////////////////////////////////
  645. void SynthModular::AddDevice(int n, int x=-1, int y=-1)
  646. {
  647. //cerr<<"Adding "<<m_NextID<<endl;
  648. if (x==-1)
  649. {
  650. x = m_CanvasScroll->x()+50;
  651. y = m_CanvasScroll->y()+50;
  652. }
  653. DeviceWin* temp = NewDeviceWin(n,x,y);
  654. if (temp)
  655. {
  656. int ID=m_NextID++;
  657. //cerr<<"adding device "<<ID<<endl;
  658. temp->m_DeviceGUI->SetID(ID);
  659. temp->m_Device->SetUpdateInfoCallback(ID,cb_UpdatePluginInfo);
  660. m_DeviceWinMap[ID]=temp;
  661. }
  662. }
  663. //////////////////////////////////////////////////////////
  664. DeviceWin* SynthModular::NewComment(int n, int x=-1, int y=-1)
  665. {
  666. DeviceWin *nlw = new DeviceWin;
  667. if (x==-1)
  668. {
  669. x = m_CanvasScroll->x()+50;
  670. y = m_CanvasScroll->y()+50;
  671. }
  672. nlw->m_Device=NULL;
  673. nlw->m_PluginID = COMMENT_ID;
  674. DeviceGUIInfo Info;
  675. Info.XPos = x;
  676. Info.YPos = y;
  677. Info.Width = 50;
  678. Info.Height = 20;
  679. Info.NumInputs = 0;
  680. Info.NumOutputs = 0;
  681. Info.Name = "";
  682. nlw->m_DeviceGUI = new Fl_CommentGUI(Info, NULL, NULL);
  683. Fl_Canvas::SetDeviceCallbacks(nlw->m_DeviceGUI, m_Canvas);
  684. m_Canvas->add(nlw->m_DeviceGUI);
  685. m_Canvas->redraw();
  686. return nlw;
  687. }
  688. //////////////////////////////////////////////////////////
  689. void SynthModular::AddComment(int n)
  690. {
  691. //cerr<<"Adding "<<m_NextID<<endl;
  692. DeviceWin* temp = NewComment(n);
  693. if (temp)
  694. {
  695. int ID=m_NextID++;
  696. //cerr<<"adding comment "<<ID<<endl;
  697. temp->m_DeviceGUI->SetID(ID);
  698. m_DeviceWinMap[ID]=temp;
  699. }
  700. }
  701. //////////////////////////////////////////////////////////
  702. void SynthModular::cb_ChangeBufferAndSampleRate_i(long int NewBufferSize, long int NewSamplerate)
  703. {
  704. if (SpiralInfo::BUFSIZE != NewBufferSize)
  705. {
  706. // update the settings
  707. SpiralInfo::BUFSIZE = NewBufferSize;
  708. m_HostNeedsUpdate = true;
  709. }
  710. if (SpiralInfo::SAMPLERATE != NewSamplerate)
  711. {
  712. SpiralInfo::SAMPLERATE = NewSamplerate;
  713. m_HostNeedsUpdate = true;
  714. }
  715. }
  716. void SynthModular::UpdateHostInfo()
  717. {
  718. /* Pause Audio */
  719. FreezeAll();
  720. /* update the settings */
  721. m_Info.BUFSIZE = SpiralInfo::BUFSIZE;
  722. m_Info.SAMPLERATE = SpiralInfo::SAMPLERATE;
  723. /* obsolete - REMOVE SOON */
  724. m_Info.FRAGSIZE = SpiralInfo::FRAGSIZE;
  725. m_Info.FRAGCOUNT = SpiralInfo::FRAGCOUNT;
  726. m_Info.OUTPUTFILE = SpiralInfo::OUTPUTFILE;
  727. m_Info.MIDIFILE = SpiralInfo::MIDIFILE;
  728. m_Info.POLY = SpiralInfo::POLY;
  729. /* Reset all plugin ports/buffers befure Resuming */
  730. ResetAudio();
  731. }
  732. //////////////////////////////////////////////////////////
  733. // called when a callback output plugin wants to run the audio thread
  734. void SynthModular::cb_Update(void* o, bool mode)
  735. {
  736. m_CallbackUpdateMode=mode;
  737. ((SynthModular*)o)->Update();
  738. }
  739. // called by a blocking output plugin to notify the engine its ready to
  740. // take control of the update timing (so take the brakes off)
  741. void SynthModular::cb_Blocking(void* o, bool mode)
  742. {
  743. m_BlockingOutputPluginIsReady=mode;
  744. }
  745. //////////////////////////////////////////////////////////
  746. iostream &SynthModular::StreamPatchIn(iostream &s, bool paste, bool merge)
  747. {
  748. //if we are merging as opposed to loading a new patch
  749. //we have no need to pause audio
  750. if (!merge && !paste)
  751. FreezeAll();
  752. //if we are pasting we don't have any of the file version
  753. //or saving information. since its internal we didn't
  754. //need it, but we do have other things we might need to load
  755. bool has_file_path;
  756. char file_path[1024];
  757. string m_FromFilePath;
  758. string dummy,dummy2;
  759. int ver;
  760. if (paste)
  761. {
  762. m_Copied.devices>>has_file_path;
  763. if (has_file_path)
  764. {
  765. m_Copied.devices.getline(file_path, 1024);
  766. m_FromFilePath = file_path;
  767. cerr << file_path << endl;
  768. }
  769. }
  770. else
  771. {
  772. s>>dummy>>dummy>>dummy>>ver;
  773. if (ver>FILE_VERSION)
  774. {
  775. SpiralInfo::Alert("Bad file, or more recent version.");
  776. ThawAll();
  777. return s;
  778. }
  779. if (ver>2)
  780. {
  781. int MainWinX,MainWinY,MainWinW,MainWinH;
  782. int EditWinX,EditWinY,EditWinW,EditWinH;
  783. s>>MainWinX>>MainWinY>>MainWinW>>MainWinH;
  784. s>>EditWinX>>EditWinY>>EditWinW>>EditWinH;
  785. //o.m_MainWindow->resize(MainWinX,MainWinY,MainWinW,MainWinH);
  786. //o.m_EditorWindow->resize(EditWinX,EditWinY,EditWinW,EditWinH);
  787. }
  788. if (merge)
  789. m_FromFilePath = m_MergeFilePath;
  790. }
  791. //wether pasting or merging we need to clear the current
  792. //selection so we can replace it with the new devices
  793. if (paste || merge)
  794. Fl_Canvas::ClearSelection(m_Canvas);
  795. int Num, ID, PluginID, x,y,ps,px,py;
  796. if (paste)
  797. {
  798. Num = m_Copied.devicecount;
  799. }
  800. else
  801. {
  802. s>>dummy>>Num;
  803. }
  804. for(int n=0; n<Num; n++)
  805. {
  806. #ifdef DEBUG_STREAM
  807. cerr<<"Loading Device "<<n<<endl;
  808. #endif
  809. s>>dummy; // "Device"
  810. s>>ID;
  811. s>>dummy2; // "Plugin"
  812. s>>PluginID;
  813. s>>x>>y;
  814. string Name;
  815. if (paste || ver>3)
  816. {
  817. // load the device name
  818. int size;
  819. char Buf[1024];
  820. s>>size;
  821. s.ignore(1);
  822. if (size > 0) {
  823. s.get(Buf,size+1);
  824. Name=Buf;
  825. } else {
  826. Name = "";
  827. }
  828. }
  829. #ifdef DEBUG_STREAM
  830. cerr<<dummy<<" "<<ID<<" "<<dummy2<<" "<<PluginID<<" "<<x<<" "<<y<<endl;
  831. #endif
  832. if (paste || ver>1) s>>ps>>px>>py;
  833. //if we are merging a patch or pasting we will change duplicate ID's
  834. if (!paste && !merge)
  835. {
  836. // Check we're not duplicating an ID
  837. if (m_DeviceWinMap.find(ID)!=m_DeviceWinMap.end())
  838. {
  839. SpiralInfo::Alert("Duplicate device ID found in file - aborting load");
  840. ThawAll();
  841. return s;
  842. }
  843. }
  844. if (PluginID==COMMENT_ID)
  845. {
  846. DeviceWin* temp = NewComment(PluginID, x, y);
  847. if (temp)
  848. {
  849. if (paste || merge)
  850. {
  851. m_Copied.m_DeviceIds[ID] = m_NextID++;
  852. ID = m_Copied.m_DeviceIds[ID];
  853. }
  854. temp->m_DeviceGUI->SetID(ID);
  855. m_DeviceWinMap[ID]=temp;
  856. ((Fl_CommentGUI*)(m_DeviceWinMap[ID]->m_DeviceGUI))->StreamIn(s); // load the plugin
  857. if (paste || merge)
  858. Fl_Canvas::AppendSelection(ID, m_Canvas);
  859. else
  860. if (m_NextID<=ID) m_NextID=ID+1;
  861. }
  862. }
  863. else
  864. {
  865. DeviceWin* temp = NewDeviceWin(PluginID, x, y);
  866. if (temp)
  867. {
  868. int oldID=ID;
  869. if (paste || merge)
  870. {
  871. m_Copied.m_DeviceIds[ID] = m_NextID++;
  872. ID = m_Copied.m_DeviceIds[ID];
  873. }
  874. temp->m_DeviceGUI->SetID(ID);
  875. if (paste || ver>3)
  876. {
  877. // set the titlebars
  878. temp->m_DeviceGUI->SetName(Name);
  879. }
  880. temp->m_Device->SetUpdateInfoCallback(ID,cb_UpdatePluginInfo);
  881. m_DeviceWinMap[ID]=temp;
  882. m_DeviceWinMap[ID]->m_Device->StreamIn(s); // load the plugin
  883. // load external files
  884. if (paste || merge)
  885. m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FromFilePath+"_files/", oldID);
  886. else
  887. m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FilePath+"_files/");
  888. if ((paste || ver>1) && m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow())
  889. {
  890. // set the GUI up with the loaded values
  891. // looks messy, but if we do it here, the plugin and it's gui can remain
  892. // totally seperated.
  893. ((SpiralPluginGUI*)(m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()))->
  894. UpdateValues(m_DeviceWinMap[ID]->m_Device);
  895. // updates the data in the channel buffers, so the values don't
  896. // get overwritten in the next tick. (should maybe be somewhere else)
  897. m_DeviceWinMap[ID]->m_Device->GetChannelHandler()->FlushChannels();
  898. // position the plugin window in the main window
  899. //m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()->position(px,py);
  900. if (ps)
  901. {
  902. m_DeviceWinMap[ID]->m_DeviceGUI->Maximise();
  903. // reposition after maximise
  904. m_DeviceWinMap[ID]->m_DeviceGUI->position(x,y);
  905. }
  906. else m_DeviceWinMap[ID]->m_DeviceGUI->Minimise();
  907. if (paste || merge)
  908. Fl_Canvas::AppendSelection(ID, m_Canvas);
  909. }
  910. if (!paste && !merge)
  911. if (m_NextID<=ID) m_NextID=ID+1;
  912. }
  913. else
  914. {
  915. // can't really recover if the plugin ID doesn't match a plugin, as
  916. // we have no idea how much data in the stream belongs to this plugin
  917. SpiralInfo::Alert("Error in stream, can't really recover data from here on.");
  918. return s;
  919. }
  920. }
  921. }
  922. if (!paste && !merge)
  923. {
  924. s>>*m_Canvas;
  925. ThawAll();
  926. }
  927. return s;
  928. }
  929. iostream &operator>>(iostream &s, SynthModular &o)
  930. {
  931. return o.StreamPatchIn(s, false, false);
  932. }
  933. //////////////////////////////////////////////////////////
  934. ostream &operator<<(ostream &s, SynthModular &o)
  935. {
  936. o.FreezeAll();
  937. s<<"SpiralSynthModular File Ver "<<FILE_VERSION<<endl;
  938. // make external files dir
  939. bool ExternalDirUsed=false;
  940. string command("mkdir '"+o.m_FilePath+"_files'");
  941. system(command.c_str());
  942. if (FILE_VERSION>2)
  943. {
  944. s<<o.m_TopWindow->x()<<" "<<o.m_TopWindow->y()<<" ";
  945. s<<o.m_TopWindow->w()<<" "<<o.m_TopWindow->h()<<" ";
  946. s<<0<<" "<<0<<" ";
  947. s<<0<<" "<<0<<endl;
  948. }
  949. // save out the SynthModular
  950. s<<"SectionList"<<endl;
  951. s<<o.m_DeviceWinMap.size()<<endl;
  952. for(map<int,DeviceWin*>::iterator i=o.m_DeviceWinMap.begin();
  953. i!=o.m_DeviceWinMap.end(); i++)
  954. {
  955. if (i->second->m_DeviceGUI && ((i->second->m_Device) || (i->second->m_PluginID==COMMENT_ID)))
  956. {
  957. s<<endl;
  958. s<<"Device ";
  959. s<<i->first<<" "; // save the id
  960. s<<"Plugin ";
  961. s<<i->second->m_PluginID<<endl;
  962. s<<i->second->m_DeviceGUI->x()<<" ";
  963. s<<i->second->m_DeviceGUI->y()<<" ";
  964. s<<i->second->m_DeviceGUI->GetName().size()<<" ";
  965. s<<i->second->m_DeviceGUI->GetName()<<" ";
  966. if (i->second->m_DeviceGUI->GetPluginWindow())
  967. {
  968. s<<i->second->m_DeviceGUI->GetPluginWindow()->visible()<<" ";
  969. s<<i->second->m_DeviceGUI->GetPluginWindow()->x()<<" ";
  970. s<<i->second->m_DeviceGUI->GetPluginWindow()->y()<<" ";
  971. }
  972. else
  973. {
  974. s<<0<<" "<<0<<" "<<0;
  975. }
  976. s<<endl;
  977. if (i->second->m_PluginID==COMMENT_ID)
  978. {
  979. // save the comment gui
  980. ((Fl_CommentGUI*)(i->second->m_DeviceGUI))->StreamOut(s);
  981. }
  982. else
  983. {
  984. // save the plugin
  985. i->second->m_Device->StreamOut(s);
  986. }
  987. s<<endl;
  988. // save external files
  989. if (i->second->m_Device && i->second->m_Device->SaveExternalFiles(o.m_FilePath+"_files/"))
  990. {
  991. ExternalDirUsed=true;
  992. }
  993. }
  994. }
  995. s<<endl<<*o.m_Canvas<<endl;
  996. // remove it if it wasn't used
  997. if (!ExternalDirUsed)
  998. {
  999. // i guess rmdir won't work if there is something in the dir
  1000. // anyway, but best to be on the safe side. (could do rm -rf) :)
  1001. string command("rmdir "+o.m_FilePath+"_files");
  1002. system(command.c_str());
  1003. }
  1004. o.ThawAll();
  1005. return s;
  1006. }
  1007. //////////////////////////////////////////////////////////////////////////////////////////
  1008. // Callbacks
  1009. /////////////////////////////////
  1010. // File Menu & associated buttons
  1011. // New
  1012. inline void SynthModular::cb_New_i (Fl_Widget *o, void *v) {
  1013. if (m_DeviceWinMap.size()>0 && !Pawfal_YesNo ("New - Lose changes to current patch?"))
  1014. return;
  1015. m_TopWindow->label (TITLEBAR.c_str());
  1016. ClearUp();
  1017. }
  1018. void SynthModular::cb_New (Fl_Widget *o, void *v) {
  1019. ((SynthModular*)(o->user_data()))->cb_New_i (o, v);
  1020. }
  1021. // Load
  1022. inline void SynthModular::cb_Load_i (Fl_Widget *o, void *v) {
  1023. if (m_DeviceWinMap.size()>0 && !Pawfal_YesNo ("Load - Lose changes to current patch?"))
  1024. return;
  1025. char *fn=fl_file_chooser ("Load a patch", "*.ssm", NULL);
  1026. if (fn && fn!='\0') {
  1027. ifstream in (fn);
  1028. if (in) {
  1029. fstream inf;
  1030. inf.open (fn, ios::in);
  1031. m_FilePath = fn;
  1032. ClearUp();
  1033. inf >> *this;
  1034. inf.close();
  1035. TITLEBAR = LABEL + " " + fn;
  1036. m_TopWindow->label (TITLEBAR.c_str());
  1037. }
  1038. }
  1039. }
  1040. void SynthModular::cb_Load(Fl_Widget *o, void *v) {
  1041. ((SynthModular*)(o->user_data()))->cb_Load_i (o, v);
  1042. }
  1043. // Save
  1044. inline void SynthModular::cb_Save_i (Fl_Widget *o, void *v) {
  1045. char *fn=fl_file_chooser("Save a patch", "*.ssm", NULL);
  1046. if (fn && fn!='\0') {
  1047. ifstream ifl (fn);
  1048. if (ifl) {
  1049. if (!Pawfal_YesNo ("File [%s] exists, overwrite?", fn))
  1050. return;
  1051. }
  1052. ofstream of (fn);
  1053. if (of) {
  1054. m_FilePath = fn;
  1055. of << *this;
  1056. TITLEBAR = LABEL + " " + fn;
  1057. m_TopWindow->label (TITLEBAR.c_str());
  1058. }
  1059. else {
  1060. fl_message (string ("Error saving " + string(fn)).c_str());
  1061. }
  1062. }
  1063. }
  1064. void SynthModular::cb_Save (Fl_Widget *o, void *v) {
  1065. ((SynthModular*)(o->user_data()))->cb_Save_i (o, v);
  1066. }
  1067. // Merge
  1068. inline void SynthModular::cb_Merge_i (Fl_Widget *o, void *v) {
  1069. char *fn = fl_file_chooser ("Merge a patch", "*.ssm", NULL);
  1070. if (fn && fn!='\0') {
  1071. ifstream in (fn);
  1072. if (in) {
  1073. fstream inf;
  1074. inf.open (fn, ios::in);
  1075. m_MergeFilePath = fn;
  1076. StreamPatchIn (inf, false, true);
  1077. m_Canvas->StreamSelectionWiresIn (inf, m_Copied.m_DeviceIds, true, false);
  1078. inf.close();
  1079. }
  1080. }
  1081. }
  1082. void SynthModular::cb_Merge (Fl_Widget *o, void *v) {
  1083. ((SynthModular*)(o->parent()->user_data()))->cb_Merge_i (o, v);
  1084. }
  1085. // Close
  1086. inline void SynthModular::cb_Close_i (Fl_Widget *o, void *v) {
  1087. m_SettingsWindow->hide();
  1088. delete m_SettingsWindow;
  1089. m_TopWindow->hide();
  1090. delete m_TopWindow;
  1091. }
  1092. void SynthModular::cb_Close (Fl_Widget *o, void *v) {
  1093. ((SynthModular*)(o->user_data()))->cb_Close_i (o, v);
  1094. }
  1095. /////////////////////////////////
  1096. // Edit Menu
  1097. // Cut
  1098. inline void SynthModular::cb_Cut_i(Fl_Widget *o, void *v) {
  1099. if (! m_Canvas->HaveSelection()) return;
  1100. // show some warning here
  1101. cb_Copy_i (o, v); // should we be calling an inline function here??????
  1102. for (unsigned int i=0; i<m_Canvas->Selection().m_DeviceIds.size(); i++) {
  1103. int ID = m_Canvas->Selection().m_DeviceIds[i];
  1104. Fl_DeviceGUI::Kill(m_DeviceWinMap[ID]->m_DeviceGUI);
  1105. }
  1106. Fl_Canvas::ClearSelection(m_Canvas);
  1107. }
  1108. void SynthModular::cb_Cut (Fl_Widget *o, void *v) {
  1109. ((SynthModular*)(o->parent()->user_data()))->cb_Cut_i (o, v);
  1110. }
  1111. // Copy
  1112. inline void SynthModular::cb_Copy_i (Fl_Widget *o, void *v) {
  1113. if (! m_Canvas->HaveSelection()) return;
  1114. m_Copied.devices.open ("___temp.ssmcopytmp", ios::out);
  1115. m_Copied.devicecount = 0;
  1116. m_Copied.m_DeviceIds.clear();
  1117. if (m_FilePath != "") {
  1118. m_Copied.devices << true << " " << m_FilePath << endl;
  1119. }
  1120. else m_Copied.devices << false << endl;
  1121. for (unsigned int i=0; i<m_Canvas->Selection().m_DeviceIds.size(); i++) {
  1122. int ID = m_Canvas->Selection().m_DeviceIds[i];
  1123. std::map<int,DeviceWin*>::iterator i = m_DeviceWinMap.find(ID);
  1124. m_Copied.m_DeviceIds[ID] = ID;
  1125. m_Copied.devicecount += 1;
  1126. m_Copied.devices << "Device " << i->first << " " ; // save the id
  1127. m_Copied.devices << "Plugin " <<i->second->m_PluginID << endl;
  1128. m_Copied.devices << i->second->m_DeviceGUI->x() << " ";
  1129. m_Copied.devices << i->second->m_DeviceGUI->y() << " ";
  1130. m_Copied.devices << i->second->m_DeviceGUI->GetName().size() << " ";
  1131. m_Copied.devices << i->second->m_DeviceGUI->GetName() << " ";
  1132. if (i->second->m_DeviceGUI->GetPluginWindow()) {
  1133. m_Copied.devices << i->second->m_DeviceGUI->GetPluginWindow()->visible() << " ";
  1134. m_Copied.devices << i->second->m_DeviceGUI->GetPluginWindow()->x() << " ";
  1135. m_Copied.devices << i->second->m_DeviceGUI->GetPluginWindow()->y() << " ";
  1136. }
  1137. else m_Copied.devices << 0 << " " << 0 << " " << 0;
  1138. m_Copied.devices << endl;
  1139. if (i->second->m_PluginID == COMMENT_ID) {
  1140. // save the comment gui
  1141. ((Fl_CommentGUI*)(i->second->m_DeviceGUI))->StreamOut (m_Copied.devices);
  1142. }
  1143. else {
  1144. // save the plugin
  1145. i->second->m_Device->StreamOut (m_Copied.devices);
  1146. }
  1147. m_Copied.devices<<endl;
  1148. }
  1149. m_Canvas->StreamSelectionWiresOut(m_Copied.devices);
  1150. m_Copied.devices.close();
  1151. Fl_Canvas::EnablePaste (m_Canvas);
  1152. }
  1153. void SynthModular::cb_Copy (Fl_Widget *o, void *v) {
  1154. ((SynthModular*)(o->parent()->user_data()))->cb_Copy_i (o, v);
  1155. }
  1156. // Paste
  1157. inline void SynthModular::cb_Paste_i (Fl_Widget *o, void *v) {
  1158. if (m_Copied.devicecount <= 0) return;
  1159. m_Copied.devices.open ("___temp.ssmcopytmp", ios::in);
  1160. StreamPatchIn(m_Copied.devices, true, false);
  1161. m_Canvas->StreamSelectionWiresIn (m_Copied.devices, m_Copied.m_DeviceIds, false, true);
  1162. m_Copied.devices.close();
  1163. }
  1164. void SynthModular::cb_Paste (Fl_Widget *o, void *v) {
  1165. ((SynthModular*)(o->parent()->user_data()))->cb_Paste_i (o, v);
  1166. }
  1167. // Delete
  1168. inline void SynthModular::cb_Delete_i (Fl_Widget *o, void *v) {
  1169. m_Canvas->DeleteSelection();
  1170. }
  1171. void SynthModular::cb_Delete (Fl_Widget *o, void *v) {
  1172. ((SynthModular*)(o->user_data()))->cb_Delete_i (o, v);
  1173. }
  1174. // Options
  1175. inline void SynthModular::cb_Options_i (Fl_Widget *o, void *v) {
  1176. m_SettingsWindow->show();
  1177. }
  1178. void SynthModular::cb_Options (Fl_Widget* o, void* v) {
  1179. ((SynthModular*)(o->user_data()))->cb_Options_i (o, v);
  1180. }
  1181. /////////////////////////////////
  1182. // Plugin Menu
  1183. // This callback has the name that the callback for the canvas menu
  1184. // used to have please note - that is now NewDeviceFromCanvasMenu
  1185. inline void SynthModular::cb_NewDeviceFromMenu_i (Fl_Widget *o, void *v) {
  1186. AddDevice (*((int*)v));
  1187. }
  1188. void SynthModular::cb_NewDeviceFromMenu (Fl_Widget *o, void *v) {
  1189. ((SynthModular*)(o->user_data()))->cb_NewDeviceFromMenu_i (o, v);
  1190. }
  1191. // (Plugin Buttons)
  1192. inline void SynthModular::cb_NewDevice_i (Fl_Button *o, void *v) {
  1193. AddDevice (*((int*)v));
  1194. }
  1195. void SynthModular::cb_NewDevice (Fl_Button *o, void *v) {
  1196. ((SynthModular*)(o->parent()->user_data()))->cb_NewDevice_i (o, v);
  1197. }
  1198. // (Plugin Canvas Menu)
  1199. inline void SynthModular::cb_NewDeviceFromCanvasMenu_i (Fl_Canvas* o, void* v) {
  1200. AddDevice(*((int*)v),*((int*)v+1),*((int*)v+2));
  1201. }
  1202. void SynthModular::cb_NewDeviceFromCanvasMenu(Fl_Canvas* o, void* v) {
  1203. ((SynthModular*)(o->user_data()))->cb_NewDeviceFromCanvasMenu_i(o,v);
  1204. }
  1205. /////////////////////////////////
  1206. // Audio Menu
  1207. // Play / Pause
  1208. inline void SynthModular::cb_PlayPause_i (Fl_Widget *o, void *v) {
  1209. string oldname = m_PlayPause->tooltip ();
  1210. if (m_Info.PAUSED) {
  1211. m_PlayPause->label ("@||");
  1212. m_PlayPause->tooltip ("Pause");
  1213. ResumeAudio();
  1214. }
  1215. else {
  1216. m_PlayPause->label ("@>");
  1217. m_PlayPause->tooltip ("Play");
  1218. PauseAudio();
  1219. }
  1220. for (int i=0; i<m_MainMenu->size(); i++) {
  1221. if (m_MainMenu->text (i) != NULL) {
  1222. if (oldname == m_MainMenu->text (i)) {
  1223. m_MainMenu->replace (i, m_PlayPause->tooltip());
  1224. break;
  1225. }
  1226. }
  1227. }
  1228. }
  1229. void SynthModular::cb_PlayPause (Fl_Widget *o, void *v) {
  1230. ((SynthModular*)(o->user_data()))->cb_PlayPause_i (o, v);
  1231. }
  1232. // Reset
  1233. inline void SynthModular::cb_Reset_i (Fl_Widget *o, void *v) {
  1234. ResetAudio();
  1235. }
  1236. void SynthModular::cb_Reset (Fl_Widget *o, void *v) {
  1237. ((SynthModular*)(o->user_data()))->cb_Reset_i (o, v);
  1238. }
  1239. //////////////////////////////////////////////////////////
  1240. inline void SynthModular::cb_NewComment_i(Fl_Button* o, void* v)
  1241. {
  1242. AddComment(-1);
  1243. }
  1244. void SynthModular::cb_NewComment(Fl_Button* o, void* v)
  1245. {((SynthModular*)(o->parent()->user_data()))->cb_NewComment_i(o,v);}
  1246. //////////////////////////////////////////////////////////
  1247. inline void SynthModular::cb_GroupTab_i(Fl_Tabs* o, void* v)
  1248. {
  1249. m_GroupTab->redraw();
  1250. }
  1251. void SynthModular::cb_GroupTab(Fl_Tabs* o, void* v)
  1252. {((SynthModular*)(o->parent()->user_data()))->cb_GroupTab_i(o,v);}
  1253. //////////////////////////////////////////////////////////
  1254. inline void SynthModular::cb_Connection_i(Fl_Canvas* o, void* v)
  1255. {
  1256. CanvasWire *Wire;
  1257. Wire=(CanvasWire*)v;
  1258. map<int,DeviceWin*>::iterator si=m_DeviceWinMap.find(Wire->OutputID);
  1259. if (si==m_DeviceWinMap.end())
  1260. {
  1261. char num[32];
  1262. sprintf(num,"%d",Wire->OutputID);
  1263. SpiralInfo::Alert("Warning: Connection problem - can't find source "+string(num));
  1264. return;
  1265. }
  1266. map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(Wire->InputID);
  1267. if (di==m_DeviceWinMap.end())
  1268. {
  1269. char num[32];
  1270. sprintf(num,"%d",Wire->InputID);
  1271. SpiralInfo::Alert("Warning: Connection problem - can't find destination "+string(num));
  1272. return;
  1273. }
  1274. Sample *sample=NULL;
  1275. if (!si->second->m_Device->GetOutput(Wire->OutputPort,&sample))
  1276. {
  1277. char num[32];
  1278. sprintf(num,"%d,%d",Wire->OutputID,Wire->OutputPort);
  1279. SpiralInfo::Alert("Warning: Connection problem - can't find source output "+string(num));
  1280. return;
  1281. }
  1282. if (!di->second->m_Device->SetInput(Wire->InputPort,(const Sample*)sample))
  1283. {
  1284. char num[32];
  1285. sprintf(num,"%d,%d",Wire->InputID,Wire->InputPort);
  1286. SpiralInfo::Alert("Warning: Connection problem - can't find source input "+string(num));
  1287. return;
  1288. }
  1289. }
  1290. void SynthModular::cb_Connection(Fl_Canvas* o, void* v)
  1291. {((SynthModular*)(o->user_data()))->cb_Connection_i(o,v);}
  1292. //////////////////////////////////////////////////////////
  1293. inline void SynthModular::cb_Unconnect_i(Fl_Canvas* o, void* v)
  1294. {
  1295. CanvasWire *Wire;
  1296. Wire=(CanvasWire*)v;
  1297. //cerr<<Wire->InputID<<" "<<Wire->InputPort<<endl;
  1298. map<int,DeviceWin*>::iterator di=m_DeviceWinMap.find(Wire->InputID);
  1299. if (di==m_DeviceWinMap.end())
  1300. {
  1301. //cerr<<"Can't find destination device "<<Wire->InputID<<endl;
  1302. return;
  1303. }
  1304. SpiralPlugin *Plugin=di->second->m_Device;
  1305. if (Plugin && !Plugin->SetInput(Wire->InputPort,NULL))
  1306. { cerr<<"Can't find destination device's Input"<<endl; return; }
  1307. }
  1308. void SynthModular::cb_Unconnect(Fl_Canvas* o, void* v)
  1309. {((SynthModular*)(o->user_data()))->cb_Unconnect_i(o,v);}
  1310. //////////////////////////////////////////////////////////
  1311. void SynthModular::cb_UpdatePluginInfo(int ID, void *PInfo)
  1312. {
  1313. map<int,DeviceWin*>::iterator i=m_DeviceWinMap.find(ID);
  1314. if (i!=m_DeviceWinMap.end())
  1315. {
  1316. DeviceGUIInfo Info=BuildDeviceGUIInfo(*((PluginInfo*)PInfo));
  1317. (*i).second->m_DeviceGUI->Setup(Info);
  1318. (*i).second->m_DeviceGUI->redraw();
  1319. }
  1320. }
  1321. //////////////////////////////////////////////////////////
  1322. void SynthModular::LoadPatch(const char *fn)
  1323. {
  1324. ifstream in(fn);
  1325. if (in)
  1326. {
  1327. fstream inf;
  1328. inf.open(fn, std::ios::in);
  1329. m_FilePath=fn;
  1330. ClearUp();
  1331. inf>>*this;
  1332. inf.close();
  1333. TITLEBAR=LABEL+" "+fn;
  1334. m_TopWindow->label(TITLEBAR.c_str());
  1335. }
  1336. }