diff --git a/GUI/Widgets/Fl_Canvas.C b/GUI/Widgets/Fl_Canvas.C index c13d3e6..2fefa37 100644 --- a/GUI/Widgets/Fl_Canvas.C +++ b/GUI/Widgets/Fl_Canvas.C @@ -38,8 +38,10 @@ m_UpdateTimer(0) { m_IncompleteWire.OutputChild=-1; m_IncompleteWire.OutputPort=-1; + m_IncompleteWire.OutputTerminal=false; m_IncompleteWire.InputChild=-1; m_IncompleteWire.InputPort=-1; + m_IncompleteWire.InputTerminal=false; m_BG=NULL; m_BGData=NULL; @@ -364,6 +366,7 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value m_IncompleteWire.OutputChild=ChildNum; m_IncompleteWire.OutputPort=Port; m_IncompleteWire.OutputID=Device->GetID(); + m_IncompleteWire.OutputTerminal=Device->IsTerminal(); } else { @@ -378,6 +381,7 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value m_IncompleteWire.InputChild=ChildNum; m_IncompleteWire.InputPort=Port; m_IncompleteWire.InputID=Device->GetID(); + m_IncompleteWire.InputTerminal=Device->IsTerminal(); } else { @@ -392,7 +396,8 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value // send the connect callback cb_Connection(this,(void*)&m_IncompleteWire); - m_Graph.AddConnection(m_IncompleteWire.OutputID,m_IncompleteWire.InputID); + m_Graph.AddConnection(m_IncompleteWire.OutputID,m_IncompleteWire.OutputTerminal, + m_IncompleteWire.InputID,m_IncompleteWire.InputTerminal); // Turn on both ports Fl_DeviceGUI* ODGUI = (Fl_DeviceGUI*)(child(m_IncompleteWire.OutputChild)); @@ -557,30 +562,65 @@ istream &operator>>(istream &s, Fl_Canvas &o) { int NumWires; s>>NumWires; - - for(int n=0; n>version; + s>>NumWires; - s>>NewWire.OutputID; - s>>NewWire.OutputChild; - s>>NewWire.OutputPort; - s>>NewWire.InputID; - s>>NewWire.InputChild; - s>>NewWire.InputPort; - - o.m_WireVec.push_back(NewWire); - - // Notify connection by callback - o.cb_Connection(&o,(void*)&NewWire); - o.m_Graph.AddConnection(NewWire.OutputID,NewWire.InputID); + for(int n=0; nAddConnection(NewWire.OutputPort+ - ((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs); - ((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort); - } + s>>NewWire.OutputID; + s>>NewWire.OutputChild; + s>>NewWire.OutputPort; + s>>NewWire.OutputTerminal; + s>>NewWire.InputID; + s>>NewWire.InputChild; + s>>NewWire.InputPort; + s>>NewWire.InputTerminal; + + o.m_WireVec.push_back(NewWire); + + // Notify connection by callback + o.cb_Connection(&o,(void*)&NewWire); + o.m_Graph.AddConnection(NewWire.OutputID,NewWire.OutputTerminal,NewWire.InputID,NewWire.InputTerminal); + + // Turn on both ports + ((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->AddConnection(NewWire.OutputPort+ + ((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs); + ((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort); + } + } + else + { + for(int n=0; n>NewWire.OutputID; + s>>NewWire.OutputChild; + s>>NewWire.OutputPort; + s>>NewWire.InputID; + s>>NewWire.InputChild; + s>>NewWire.InputPort; + + o.m_WireVec.push_back(NewWire); + + // Notify connection by callback + o.cb_Connection(&o,(void*)&NewWire); + o.m_Graph.AddConnection(NewWire.OutputID,false,NewWire.InputID,false); + + // Turn on both ports + ((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->AddConnection(NewWire.OutputPort+ + ((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs); + ((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort); + } + } return s; } @@ -588,6 +628,9 @@ istream &operator>>(istream &s, Fl_Canvas &o) ostream &operator<<(ostream &s, Fl_Canvas &o) { + int version=0; + s<<-1<<" "<::iterator i=o.m_WireVec.begin(); @@ -596,9 +639,11 @@ ostream &operator<<(ostream &s, Fl_Canvas &o) s<OutputID<<" "; s<OutputChild<<" "; s<OutputPort<<" "; + s<OutputTerminal<<" "; s<InputID<<" "; s<InputChild<<" "; - s<InputPort<InputPort<<" "; + s<InputTerminal< RootNodes; + bool FoundRoot=false; + for (map::iterator i=m_Graph.begin(); i!=m_Graph.end(); i++) { // if there are no outputs, this must be a root if (i->second.Outputs.empty()) { + FoundRoot=true; RecursiveWalk(i->first); } } + // no roots found - try looking for a terminal node and recursing from + // there, this makes circular graphs work. + if (!FoundRoot) + { + for (map::iterator i=m_Graph.begin(); + i!=m_Graph.end(); i++) + { + // if there are no outputs, this must be a root + if (i->second.IsTerminal) + { + RecursiveWalk(i->first); + } + } + } + #ifdef GRAPHSORT_TRACE for(list::iterator i=m_Sorted.begin(); i!=m_Sorted.end(); i++) @@ -121,12 +139,13 @@ void GraphSort::Dump() } } -void GraphSort::AddConnection(int SID, int DID) +void GraphSort::AddConnection(int SID, bool STerminal, int DID, bool DTerminal) { map::iterator si=m_Graph.find(SID); if (si==m_Graph.end()) { Node newnode; + newnode.IsTerminal = STerminal; m_Graph[SID]=newnode; #ifdef GRAPHSORT_TRACE cerr<<"added "< &GetSortedList(); void Sort(); - void AddConnection(int SID, int DID); + void AddConnection(int SID, bool STerminal, int DID, bool DTerminal); void RemoveConnection(int SID, int DID); void Clear(); void Dump(); @@ -43,6 +43,7 @@ public: { list Inputs; list Outputs; + bool IsTerminal; }; private: diff --git a/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.C b/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.C index b9e3dee..2030648 100644 --- a/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.C +++ b/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.C @@ -81,6 +81,7 @@ inline void FilterPluginGUI::cb_Cutoff_i(Fl_Slider* o, void* v) float value=100.0f-o->value(); m_GUICH->Set("Cutoff",(float)(value*value)+10.0f); } + void FilterPluginGUI::cb_Cutoff(Fl_Slider* o, void* v) { ((FilterPluginGUI*)(o->parent()))->cb_Cutoff_i(o,v); } @@ -99,3 +100,10 @@ inline void FilterPluginGUI::cb_RevResonance_i(Fl_Button* o, void* v) void FilterPluginGUI::cb_RevResonance(Fl_Button* o, void* v) { ((FilterPluginGUI*)(o->parent()))->cb_RevResonance_i(o,v); } +const string FilterPluginGUI::GetHelpText(const string &loc){ + return string("") + + "The standard SpiralSynth filter, based on the (zxforms design).\n" + + "Quite a meaty sound - low pass only, nice for bass modulations.\n" + + "With variable emphasis/cutoff CV's.\n\n" + + "It's also pretty fast, and well tested in SpiralSynth."; +} diff --git a/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.h b/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.h index 4cca4d3..7b2a6eb 100644 --- a/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.h +++ b/SpiralSound/Plugins/FilterPlugin/FilterPluginGUI.h @@ -36,6 +36,9 @@ public: virtual void UpdateValues(SpiralPlugin *o); +protected: + const string GetHelpText(const string &loc); + private: Fl_Group *GUIFilterGroup; diff --git a/SpiralSound/Plugins/JackPlugin/JackPlugin.C b/SpiralSound/Plugins/JackPlugin/JackPlugin.C index 9659ff6..7e43c15 100644 --- a/SpiralSound/Plugins/JackPlugin/JackPlugin.C +++ b/SpiralSound/Plugins/JackPlugin/JackPlugin.C @@ -154,7 +154,7 @@ int JackClient::Process(jack_nframes_t nframes, void *o) } } } - + if(RunCallback&&RunContext) { // do the work @@ -345,6 +345,9 @@ m_Connected(false) { m_RefCount++; + // we are an output + m_IsTerminal = true; + m_PluginInfo.Name="Jack"; m_PluginInfo.Width=200; m_PluginInfo.Height=325; @@ -390,7 +393,7 @@ PluginInfo &JackPlugin::Initialise(const HostInfo *Host) PluginInfo& Info= SpiralPlugin::Initialise(Host); host=Host; - JackClient::Get()->SetCallback(cb_Update,m_Parent); + if (m_RefCount==1) JackClient::Get()->SetCallback(cb_Update,m_Parent); return Info; } @@ -409,6 +412,11 @@ void JackPlugin::Execute() void JackPlugin::ExecuteCommands() { + // only do this once per set of plugins + m_NoExecuted++; + if (m_NoExecuted!=m_RefCount) return; + m_NoExecuted=0; + // we want to process this whether we are connected to stuff or not JackClient* pJack=JackClient::Get(); diff --git a/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.C b/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.C index 27e70ce..8ed7a43 100644 --- a/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.C +++ b/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.C @@ -71,3 +71,12 @@ inline void MoogFilterPluginGUI::cb_Resonance_i(Fl_Knob* o, void* v) { m_GUICH->Set("Resonance",(float)(o->value())); } void MoogFilterPluginGUI::cb_Resonance(Fl_Knob* o, void* v) { ((MoogFilterPluginGUI*)(o->parent()))->cb_Resonance_i(o,v); } + +const string MoogFilterPluginGUI::GetHelpText(const string &loc){ + return string("") + + "Classic moog filter. Very different sound to the other filters,\n" + + "needless to say, very squelchy. As well as lowpass, band and high\n" + + "pass are simultaneously calculated too. The emphasis can be pushed\n" + + "into self oscillation (careful of the speakers). In this way, it\n" + + "can be used to generate sinewave oscillations."; +} diff --git a/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.h b/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.h index 2c41550..79c478b 100644 --- a/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.h +++ b/SpiralSound/Plugins/MoogFilterPlugin/MoogFilterPluginGUI.h @@ -36,6 +36,9 @@ public: virtual void UpdateValues(SpiralPlugin *o); +protected: + const string GetHelpText(const string &loc); + private: Fl_Group *GUIFilterGroup; diff --git a/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.C b/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.C index 6e63d25..b46609f 100644 --- a/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.C +++ b/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.C @@ -372,3 +372,19 @@ void OscillatorPluginGUI::cb_pop(Fl_Button* o, void* v) { ((OscillatorPluginGUI*)(o->parent()))->cb_pop_i(o,v); } +const string OscillatorPluginGUI::GetHelpText(const string &loc){ + return string("") + + "The Oscillator generates raw waveforms from CV controls. Three wave \n" + + "shapes are included, Square wave, Triangle wave and white noise.\n\n" + + "In the square and triangle shapes, the Frequency CV controls the pitch \n" + + "of the signal generated, and the pulsewidth turns the squarewave into \n" + + "a pulse wave of varying harmonics, and the triangle wave into a sawtooth,\n" + + "or reverse sawtooth wave.\n\n" + + "The sample & hold CV changes the time between samples with the white noise.\n" + + "This is usful for making the Oscillator into a random CV generator.\n\n" + + "The plugin window allows you to select the wave shape, set the octave and\n" + + "fine tune the frequency. There are also controls to set the pulsewidth,\n" + + "sample and hold manually, and control the modulation depth of the input CV's.\n\n" + + "The frequency can be set extremely low on this oscillator, so you can use\n" + + "it as an LFO for controlling other plugins."; +} diff --git a/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.h b/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.h index 87f5e33..9a5d260 100644 --- a/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.h +++ b/SpiralSound/Plugins/OscillatorPlugin/OscillatorPluginGUI.h @@ -37,6 +37,9 @@ public: virtual void UpdateValues(SpiralPlugin *o); +protected: + const string GetHelpText(const string &loc); + private: Fl_Check_Button *ShapeSquare; diff --git a/SpiralSound/Plugins/OutputPlugin/OutputPlugin.C b/SpiralSound/Plugins/OutputPlugin/OutputPlugin.C index 2e6425b..e350f9d 100644 --- a/SpiralSound/Plugins/OutputPlugin/OutputPlugin.C +++ b/SpiralSound/Plugins/OutputPlugin/OutputPlugin.C @@ -87,7 +87,10 @@ OutputPlugin::OutputPlugin() : m_Volume(1.0f) { m_RefCount++; - + + // we are an output. + m_IsTerminal=true; + m_PluginInfo.Name="OSS"; m_PluginInfo.Width=100; m_PluginInfo.Height=100; diff --git a/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.C b/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.C index 0d9e15c..23d3dee 100644 --- a/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.C +++ b/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.C @@ -80,3 +80,17 @@ inline void SVFilterPluginGUI::cb_Reset_i(Fl_Button* o, void* v) { } void SVFilterPluginGUI::cb_Reset(Fl_Button* o, void* v) { ((SVFilterPluginGUI*)(o->parent()))->cb_Reset_i(o,v); } + +const string SVFilterPluginGUI::GetHelpText(const string &loc){ + return string("") + + "A State Variable Filter. First thing to say is, it's a bit\n" + + "broken. Seems to generate glitchy noise when the cutoff is \n" + + "modulated. Possibly a range bug on the cutoff too.\n" + + "On the other hand, I like some of the noises it seems to\n" + + "make, so it's here anyway (I'll fix it some day).\n\n" + + "Works pretty well at band,high and peaking useful for creating\n" + + "some different sounds.\n\n" + + "Note: Comes with a reset button, so if you break it pushing\n" + + "the emphasis up too high, you can reset the cooeficients\n" + + "(which fixes it)."; +} diff --git a/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.h b/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.h index 3f69328..89dea3b 100644 --- a/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.h +++ b/SpiralSound/Plugins/SVFilterPlugin/SVFilterPluginGUI.h @@ -36,6 +36,9 @@ public: virtual void UpdateValues(SpiralPlugin *o); +protected: + const string GetHelpText(const string &loc); + private: Fl_Group *GUIFilterGroup; diff --git a/SpiralSound/Plugins/SpiralPlugin.C b/SpiralSound/Plugins/SpiralPlugin.C index 4758628..5a03aca 100644 --- a/SpiralSound/Plugins/SpiralPlugin.C +++ b/SpiralSound/Plugins/SpiralPlugin.C @@ -30,6 +30,7 @@ SpiralPlugin::SpiralPlugin() cb_Update=NULL; m_Parent=NULL; m_HostID=-1; + m_IsTerminal=false; m_AudioCH = new ChannelHandler; } diff --git a/SpiralSound/Plugins/SpiralPlugin.h b/SpiralSound/Plugins/SpiralPlugin.h index 06f6d84..156e8b0 100644 --- a/SpiralSound/Plugins/SpiralPlugin.h +++ b/SpiralSound/Plugins/SpiralPlugin.h @@ -104,6 +104,8 @@ public: void SetParent(void *s) { m_Parent=s; } void UpdateChannelHandler(); + // is the plugin connected to an external device (oss/alsa/jack) + bool IsTerminal() { return m_IsTerminal; } ChannelHandler *GetChannelHandler() { return m_AudioCH; } @@ -153,6 +155,8 @@ protected: // tell the engine that we are taking control of the // timing for output. void (*cb_Blocking)(void*o ,bool m); + + bool m_IsTerminal; private: diff --git a/SpiralSound/Plugins/SpiralPluginGUI.C b/SpiralSound/Plugins/SpiralPluginGUI.C index 5f5de35..4d02044 100644 --- a/SpiralSound/Plugins/SpiralPluginGUI.C +++ b/SpiralSound/Plugins/SpiralPluginGUI.C @@ -21,6 +21,8 @@ #include #include #include +#include +#include static const int GUI_COLOUR = 154; static const int GUIBG_COLOUR = 144; @@ -83,14 +85,15 @@ inline void SpiralPluginGUI::cb_Help_i(Fl_Button* o, void* v) { int w=300,h=200; m_HelpWin = new Fl_Double_Window(w,h,"Help"); - - Fl_Multiline_Output* text= new Fl_Multiline_Output(0,0,w,h); - text->value(GetHelpText(SpiralInfo::LOCALE).c_str()); + + Fl_Text_Display* text = new Fl_Text_Display(0,0,10,10); + text->buffer(new Fl_Text_Buffer); + text->insert(GetHelpText(SpiralInfo::LOCALE).c_str()); text->textsize(10); - text->set_output(); m_HelpWin->add(text); - + m_HelpWin->resizable(text); m_HelpWin->show(); + text->size(w,h); // hack to get the text widget to appear??? } else { diff --git a/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.C b/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.C index a458852..6f867f9 100644 --- a/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.C +++ b/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.C @@ -504,3 +504,17 @@ void WaveTablePluginGUI::cb_pop(Fl_Button* o, void* v) { ((WaveTablePluginGUI*)o->parent())->cb_pop_i(o,v); } +const string WaveTablePluginGUI::GetHelpText(const string &loc){ + return string("") + + "The WaveTable plugin is a fast multifunction oscillator with a variety \n" + + "of wave shapes:\n" + + "Sine, Square, Saw, Reverse Saw, Triangle, Two pulse shapes and an inverse\n" + + "sinewave.\n\n" + + "These wave shapes are internally represented as samples, rather than\n" + + "being continually calculated like the conventional oscillator. This \n" + + "makes the plugin fast, but restricts the modulations you can do on the\n" + + "wave forms (no pulsewidth).\n\n" + + "The oscillator can be pitched very low for use as a LFO CV generator,\n" + + "using any of the supported wave shapes. User wave shapes are planned,\n" + + "so you will be able to load your own samples in."; +} diff --git a/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.h b/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.h index f4906f5..eb39a5e 100644 --- a/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.h +++ b/SpiralSound/Plugins/WaveTablePlugin/WaveTablePluginGUI.h @@ -36,6 +36,9 @@ public: virtual void UpdateValues(SpiralPlugin* o); +protected: + const string GetHelpText(const string &loc); + private: Fl_Check_Button *ShapeSquare; diff --git a/SpiralSound/Plugins/XFadePlugin/XFadePluginGUI.C b/SpiralSound/Plugins/XFadePlugin/XFadePluginGUI.C index 1178558..3c6fdab 100644 --- a/SpiralSound/Plugins/XFadePlugin/XFadePluginGUI.C +++ b/SpiralSound/Plugins/XFadePlugin/XFadePluginGUI.C @@ -49,7 +49,7 @@ void XFadePluginGUI::UpdateValues(SpiralPlugin *o) inline void XFadePluginGUI::cb_Mix_i(Fl_Slider* o, void* v) { - m_GUICH->Set("Mix",o->value()); + m_GUICH->Set("Mix",(float)o->value()); } void XFadePluginGUI::cb_Mix(Fl_Slider* o, void* v) { ((XFadePluginGUI*)(o->parent()))->cb_Mix_i(o,v); } diff --git a/SpiralSynthModular.C b/SpiralSynthModular.C index 0fb2c69..9d424fb 100644 --- a/SpiralSynthModular.C +++ b/SpiralSynthModular.C @@ -567,7 +567,7 @@ DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y) Info.XPos = x; //TOOLBOX_WIDTH+(rand()%400); Info.YPos = y; //rand()%400; - nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix); + nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix, nlw->m_Device->IsTerminal()); m_Canvas->add(nlw->m_DeviceGUI); m_Canvas->redraw();