From 27d0761b9c4db7414d0ec948a18d63697b71ff64 Mon Sep 17 00:00:00 2001 From: aj_genius Date: Thu, 20 Nov 2003 21:29:46 +0000 Subject: [PATCH] reworked JACK plugin to allow multiple instance allow changing of port count improve the gui to fit ports in smaller space auto update when ports are changed for eg from another app --- SpiralSound/Plugins/JackPlugin/JackPlugin.C | 424 +++++++++++------- SpiralSound/Plugins/JackPlugin/JackPlugin.h | 120 +++-- .../Plugins/JackPlugin/JackPluginGUI.C | 411 +++++++++++++---- .../Plugins/JackPlugin/JackPluginGUI.h | 60 ++- 4 files changed, 728 insertions(+), 287 deletions(-) diff --git a/SpiralSound/Plugins/JackPlugin/JackPlugin.C b/SpiralSound/Plugins/JackPlugin/JackPlugin.C index 381b5f7..11bc160 100644 --- a/SpiralSound/Plugins/JackPlugin/JackPlugin.C +++ b/SpiralSound/Plugins/JackPlugin/JackPlugin.C @@ -25,76 +25,182 @@ using namespace std; -static const HostInfo* host; -JackClient* JackClient::m_Singleton = NULL; -bool JackClient::m_Attached = false; -long unsigned int JackClient::m_BufferSize = 0; -long unsigned int JackClient::m_SampleRate = 0; -void (*JackClient::RunCallback)(void*, bool m)=NULL; -void *JackClient::RunContext = NULL; -jack_client_t *JackClient::m_Client = NULL; -map JackClient::m_InputPortMap; -map JackClient::m_OutputPortMap; - -int JackPlugin::m_RefCount = 0; -int JackPlugin::m_NoExecuted = 0; +static int JackInstances = 0; +static int JackProcessInstance = -1; +///////////////////////////////////////////////////////////////////////////////////////////// +inline void JackClient::JackProcess_i(jack_nframes_t nframes) +{ + SetBufferSize(nframes); + + for (int n=0; nPort)) + { + sample_t *in = (sample_t *) jack_port_get_buffer(m_InputPortMap[n]->Port, nframes); + memcpy (m_InputPortMap[n]->Buf, in, sizeof (sample_t) * GetBufferSize()); + } + } + + for (int n=0; nPort)) + { + if (m_OutputPortMap[n]->Buf) + { + sample_t *out = (sample_t *) jack_port_get_buffer(m_OutputPortMap[n]->Port, nframes); + memcpy (out, m_OutputPortMap[n]->Buf, sizeof (sample_t) * GetBufferSize()); + } + else // no output availible, clear + { + sample_t *out = (sample_t *) jack_port_get_buffer(m_OutputPortMap[n]->Port, nframes); + memset (out, 0, sizeof (sample_t) * GetBufferSize()); + } + } + } + + if (RunCallback&&RunContext) + { + if (JackProcessInstance==-1) + JackProcessInstance = m_JackInstanceID; + + if (JackProcessInstance==m_JackInstanceID) + RunCallback(RunContext,true); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +inline void JackClient::SampleRateChange_i(jack_nframes_t nframes) +{ + SetSampleRate(nframes); +} + +///////////////////////////////////////////////////////////////////////////////////////////// + +inline void JackClient::JackShutdown_i() +{ + cerr<<"Shutdown"<PortNo = NewPortNumber; + NewPort->Name=Name; + NewPort->Buf=NULL; + NewPort->Port = jack_port_register (m_Client, Name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + + m_InputPortMap[NewPortNumber]=NewPort; +} + +void JackClient::AddOutputPort(int NewPortNumber) +{ + char Name[256]; + JackPort *NewPort; + + if (!(m_Client)) return; + + NewPort = new JackPort; + + sprintf(Name,"Out%d", NewPortNumber); + + NewPort->PortNo = NewPortNumber; + NewPort->Name=Name; + NewPort->Buf=NULL; + NewPort->Port = jack_port_register (m_Client, Name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + m_OutputPortMap[NewPortNumber]=NewPort; +} + +void JackClient::RemoveInputPort(int PortNumber) +{ + char Name[256]; + JackPort *OldPort; + + if (!(m_Client)) return; + + OldPort = m_InputPortMap[PortNumber]; + m_InputPortMap[PortNumber] = NULL; + jack_port_unregister (m_Client, OldPort->Port); + delete OldPort; +} + +void JackClient::RemoveOutputPort(int PortNumber) +{ + char Name[256]; + JackPort *OldPort; + + if (!(m_Client)) return; + + OldPort = m_OutputPortMap[PortNumber]; + m_OutputPortMap[PortNumber] = NULL; + jack_port_unregister (m_Client, OldPort->Port); + delete OldPort; +} + bool JackClient::Attach() { + char JackClientName[256]; + if (m_Attached) return true; - if (!(m_Client = jack_client_new("SSM"))) + sprintf(JackClientName,"SSM%d",GetJackInstanceID()); + if (!(m_Client = jack_client_new(JackClientName))) { cerr<<"jack server not running?"<Name=Name; - NewPort->Buf=NULL; - NewPort->Port = jack_port_register (m_Client, Name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); - m_InputPortMap[n]=NewPort; - } + m_InputPortMap.clear(); + for (int n=0; nName=Name; - NewPort->Buf=NULL; - NewPort->Port = jack_port_register (m_Client, Name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); - m_OutputPortMap[n]=NewPort; - } + m_OutputPortMap.clear(); + for (int n=0; nPort)) - { - sample_t *in = (sample_t *) jack_port_get_buffer(m_InputPortMap[n]->Port, nframes); - memcpy (m_InputPortMap[n]->Buf, in, sizeof (sample_t) * m_BufferSize); - } - } - - for (int n=0; nConnected) - { - if (m_OutputPortMap[n]->Buf) - { - sample_t *out = (sample_t *) jack_port_get_buffer(m_OutputPortMap[n]->Port, nframes); - memcpy (out, m_OutputPortMap[n]->Buf, sizeof (sample_t) * m_BufferSize); - } - else // no output availible, clear - { - sample_t *out = (sample_t *) jack_port_get_buffer(m_OutputPortMap[n]->Port, nframes); - memset (out, 0, sizeof (sample_t) * m_BufferSize); - } - } - } - - if(RunCallback&&RunContext) - { - // do the work - RunCallback(RunContext, true); - } - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////////// - -int JackClient::OnSRateChange(long unsigned int n, void *o) -{ - m_SampleRate=n; - return 0; -} + if (JackProcessInstance==m_JackInstanceID) + JackProcessInstance = -1; -///////////////////////////////////////////////////////////////////////////////////////////// - -void JackClient::OnJackShutdown(void *o) -{ - cerr<<"Shutdown"<Name<<"]"<Name<<"]"<ConnectedTo!="") { @@ -249,7 +298,7 @@ void JackClient::ConnectInput(int n, const string &JackPort) void JackClient::ConnectOutput(int n, const string &JackPort) { if (!IsAttached()) return; - cerr<<"JackClient::ConnectOutput: connecting source ["<Name<<"] to dest ["<Name<<"] to dest ["<ConnectedTo!="") { @@ -270,7 +319,7 @@ void JackClient::ConnectOutput(int n, const string &JackPort) void JackClient::DisconnectInput(int n) { if (!IsAttached()) return; - cerr<<"JackClient::DisconnectInput: Disconnecting input "<ConnectedTo!="") { @@ -287,7 +336,7 @@ void JackClient::DisconnectInput(int n) void JackClient::DisconnectOutput(int n) { if (!IsAttached()) return; - cerr<<"JackClient::DisconnectInput: Disconnecting input "<ConnectedTo!="") { @@ -312,7 +361,6 @@ void JackClient::SetOutputBuf(int ID, float* s) if(m_OutputPortMap.find(ID)!=m_OutputPortMap.end()) m_OutputPortMap[ID]->Buf=s; } -///////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// extern "C" { @@ -343,32 +391,44 @@ JackPlugin::JackPlugin() : m_UpdateNames(false), m_Connected(false) { - m_RefCount++; - + m_JackClient=new JackClient; + + m_JackInstanceID = JackInstances ; + JackInstances++; + + m_JackClient->SetJackInstanceID(m_JackInstanceID); + // we are an output m_IsTerminal = true; m_PluginInfo.Name="Jack"; - m_PluginInfo.Width=200; - m_PluginInfo.Height=325; - m_PluginInfo.NumInputs=NUM_OUTPUTS; - m_PluginInfo.NumOutputs=NUM_INPUTS; + m_PluginInfo.Width=225; + m_PluginInfo.Height=230; + m_PluginInfo.NumInputs=0; + m_PluginInfo.NumOutputs=0; - for (int n=0; nGetJackOutputCount(); + + for (int n=0; nGetJackInputCount(); n++) + { char Temp[256]; - sprintf(Temp,"SSM Output %d",n); + sprintf(Temp,"SSM Input %d",n); m_PluginInfo.PortTips.push_back(Temp); - } + } - for (int n=0; nGetJackOutputCount(); + + for (int n=0; nGetJackOutputCount(); n++) { char Temp[256]; - sprintf(Temp,"SSM Input %d",n); + sprintf(Temp,"SSM Output %d",n); m_PluginInfo.PortTips.push_back(Temp); - } - - m_AudioCH->Register("Num",&m_GUIArgs.Num); + } + + m_AudioCH->Register("NumInputs",&m_GUIArgs.NumInputs); + m_AudioCH->Register("NumOutputs",&m_GUIArgs.NumOutputs); m_AudioCH->RegisterData("Port",ChannelHandler::INPUT,&m_GUIArgs.Port,sizeof(m_GUIArgs.Port)); m_AudioCH->Register("NumInputPortNames",&m_NumInputPortNames,ChannelHandler::OUTPUT); m_AudioCH->Register("NumOutputPortNames",&m_NumOutputPortNames,ChannelHandler::OUTPUT); @@ -380,12 +440,9 @@ m_Connected(false) JackPlugin::~JackPlugin() { - m_RefCount--; - if (m_RefCount==0) - { - JackClient::Get()->Detach(); - JackClient::PackUpAndGoHome(); - } + m_JackClient->Detach(); + delete m_JackClient; + m_JackClient=NULL; } PluginInfo &JackPlugin::Initialise(const HostInfo *Host) @@ -393,7 +450,7 @@ PluginInfo &JackPlugin::Initialise(const HostInfo *Host) PluginInfo& Info= SpiralPlugin::Initialise(Host); host=Host; - if (m_RefCount==1) JackClient::Get()->SetCallback(cb_Update,m_Parent); + m_JackClient->SetCallback(cb_Update,m_Parent); return Info; } @@ -405,23 +462,70 @@ SpiralGUIType *JackPlugin::CreateGUI() this,m_AudioCH,m_HostInfo); } +void JackPlugin::SetNumberPorts (int nInputs, int nOutputs) { + UpdatePluginInfoWithHost(); + RemoveAllInputs (); + RemoveAllOutputs (); + m_PluginInfo.NumInputs = 0; + m_PluginInfo.NumOutputs = 0; + m_PluginInfo.PortTips.clear (); + CreatePorts (nInputs, nOutputs, true); + UpdatePluginInfoWithHost (); +} + +void JackPlugin::CreatePorts (int nInputs, int nOutputs, bool AddPorts) { + m_PluginInfo.PortTips.clear(); + + m_PluginInfo.NumInputs = nInputs; + m_JackClient->SetJackInputCount(nInputs); + + for (int n=0; nSetJackOutputCount(nOutputs); + + for (int n=0; nIsCommandWaiting(); + int command = (commandwaiting)?(int)m_AudioCH->GetCommand():0; + + if (commandwaiting) + { + switch (command) { + case SET_PORT_COUNT : + SetNumberPorts (m_GUIArgs.NumInputs, m_GUIArgs.NumOutputs); + } + } + + // we want to process this whether we are connected to stuff or not - JackClient* pJack=JackClient::Get(); + JackClient* pJack=m_JackClient; // connect the buffers up if we are plugged into something - for (int n=0; nGetJackOutputCount(); n++) { if (InputExists(n)) { @@ -433,24 +537,27 @@ void JackPlugin::ExecuteCommands() } } - for (int n=0; nGetJackInputCount(); n++) { - pJack->SetInputBuf(n,(float*)GetOutputBuf(n)->GetBuffer()); - } - - if (m_AudioCH->IsCommandWaiting()) + if (OutputExists(n)) + { + pJack->SetInputBuf(n,(float*)GetOutputBuf(n)->GetBuffer()); + } + else + { + pJack->SetInputBuf(n,NULL); + } + } + + if (commandwaiting) { - switch (m_AudioCH->GetCommand()) + switch (command) { - //case ATTACH : Attach(); break; - //case DETACH : Detach(); break; - //case CONNECTINPUT : pJack->ConnectInput(m_GUIArgs.Num,m_GUIArgs.Port); break; - //case CONNECTOUTPUT : pJack->ConnectOutput(m_GUIArgs.Num,m_GUIArgs.Port); break; case UPDATE_NAMES : { int c=0; - vector InputNames,OutputNames; + std::vector InputNames,OutputNames; GetPortNames(InputNames,OutputNames); for (vector::iterator i=InputNames.begin(); i!=InputNames.end(); ++i) @@ -461,7 +568,7 @@ void JackPlugin::ExecuteCommands() c=0; - for (vector::iterator i=OutputNames.begin(); + for (std::vector::iterator i=OutputNames.begin(); i!=OutputNames.end(); ++i) { strcpy(m_OutputPortNames[c],i->c_str()); @@ -472,8 +579,25 @@ void JackPlugin::ExecuteCommands() m_NumOutputPortNames=OutputNames.size(); } break; + + case CHECK_PORT_CHANGES : + if ((m_JackClient->IsAttached()) && (!m_JackClient->CheckingPortChanges)) { + m_JackClient->CheckingPortChanges = true; + + for (int n=0; nm_OutputPortMap[n]->Port)!=m_JackClient->m_OutputPortMap[n]->Connected) + m_JackClient->m_OutputPortsChanged.push_back(m_JackClient->m_OutputPortMap[n]); + + if (jack_port_connected(m_JackClient->m_InputPortMap[n]->Port)!=m_JackClient->m_InputPortMap[n]->Connected) + m_JackClient->m_InputPortsChanged.push_back(m_JackClient->m_InputPortMap[n]); + } + + m_JackClient->CheckingPortChanges = false; + } + + break; default : break; } } - m_Connected=JackClient::Get()->IsAttached(); + m_Connected=m_JackClient->IsAttached(); } diff --git a/SpiralSound/Plugins/JackPlugin/JackPlugin.h b/SpiralSound/Plugins/JackPlugin/JackPlugin.h index 50e1661..a43e3b1 100644 --- a/SpiralSound/Plugins/JackPlugin/JackPlugin.h +++ b/SpiralSound/Plugins/JackPlugin/JackPlugin.h @@ -19,28 +19,34 @@ #include "../SpiralPlugin.h" #include "../../RiffWav.h" #include -#include #include +using namespace std; + typedef jack_default_audio_sample_t sample_t; #ifndef JackPLUGIN #define JackPLUGIN -const int NUM_INPUTS = 8; -const int NUM_OUTPUTS = 8; -const int MAX_INPUTPORTS = 256; -const int MAX_OUTPUTPORTS = 256; +const int MAX_PORTS = 64; +const int MIN_PORTS = 2; class JackClient { public: - static JackClient *Get() { if(!m_Singleton) m_Singleton=new JackClient; return m_Singleton; } - static void PackUpAndGoHome() { if(m_Singleton) { delete m_Singleton; m_Singleton=NULL; } } + JackClient(); + virtual ~JackClient(); + + void AddInputPort(int NewPortNumber); + void AddOutputPort(int NewPortNumber); + + void RemoveInputPort(int PortNumber); + void RemoveOutputPort(int PortNumber); bool Attach(); void Detach(); bool IsAttached() { return m_Attached; } + void SetAttached(bool Attached) { m_Attached = Attached; } void SetCallback(void(*Run)(void*, bool m),void *Context) { RunCallback=Run; RunContext=Context; } void GetPortNames(std::vector &InputNames,std::vector &OutputNames); void ConnectInput(int n, const std::string &JackPort); @@ -51,17 +57,16 @@ public: std::string GetOutputName(int ID) { return m_OutputPortMap[ID]->Name; } void SetInputBuf(int ID, float* s); void SetOutputBuf(int ID, float* s); - -protected: - JackClient(); - ~JackClient(); - - static int Process(jack_nframes_t nframes, void *o); - static int OnBufSizeChange(long unsigned int n, void *o); - static int OnSRateChange(long unsigned int n, void *o); - static void OnJackShutdown(void *o); - -private: + int GetJackInstanceID() { return m_JackInstanceID; } + void SetJackInstanceID(int JackInstanceID) { m_JackInstanceID=JackInstanceID; } + int GetBufferSize() { return m_BufferSize; } + void SetBufferSize(jack_nframes_t BufferSize) { m_BufferSize=BufferSize; } + int GetSampleRate() { return m_BufferSize; } + void SetSampleRate(jack_nframes_t SampleRate) { m_SampleRate=SampleRate; } + int GetJackInputCount() { return m_JackInputCount; } + void SetJackInputCount(int JackInputCount) { m_JackInputCount=JackInputCount; } + int GetJackOutputCount() { return m_JackOutputCount; } + void SetJackOutputCount(int JackOutputCount) { m_JackOutputCount=JackOutputCount; } class JackPort { @@ -69,23 +74,42 @@ private: JackPort::JackPort() : Connected(false),Buf(NULL),Port(NULL) {} - std::string Name; + int PortNo; + std::string Name; bool Connected; float* Buf; jack_port_t* Port; std::string ConnectedTo; }; - static JackClient* m_Singleton; - static jack_client_t* m_Client; - static std::map m_InputPortMap; - static std::map m_OutputPortMap; - static long unsigned int m_BufferSize; - static long unsigned int m_SampleRate; - static bool m_Attached; + jack_client_t* m_Client; + std::map m_InputPortMap; + std::map m_OutputPortMap; + + //// Kludge for GUI //// + bool CheckingPortChanges; + std::vector m_OutputPortsChanged; + std::vector m_InputPortsChanged; - static void(*RunCallback)(void*, bool m); - static void *RunContext; + //// inline Callbacks //// + inline void JackProcess_i(jack_nframes_t nframes); + inline void SampleRateChange_i(jack_nframes_t nframes); + inline void JackShutdown_i(); + + //// static Callbacks //// + static int JackProcess(jack_nframes_t nframes, void *jack_client) { ((JackClient *)jack_client)->JackProcess_i(nframes); return 0;} + static int SampleRateChange(jack_nframes_t nframes, void *jack_client) { ((JackClient *)jack_client)->SampleRateChange_i(nframes); return 0;} + static void JackShutdown(void *jack_client) { ((JackClient *)jack_client)->JackShutdown_i();} +private: + jack_nframes_t m_BufferSize; + jack_nframes_t m_SampleRate; + bool m_Attached; + int m_JackInputCount; + int m_JackOutputCount; + int m_JackInstanceID; + + void(*RunCallback)(void*, bool m); + void *RunContext; }; /////////////////////////////////////////////////// @@ -103,32 +127,42 @@ public: virtual void StreamOut(std::ostream &s) {} virtual void StreamIn(std::istream &s) {} - enum GUICommands{NONE,ATTACH,DETACH,CONNECTINPUT,CONNECTOUTPUT,UPDATE_NAMES}; + JackClient *GetJackClient() { return m_JackClient; } + + void SetNumberPorts (int nInputs, int nOutputs); + + enum GUICommands{NONE,UPDATE_NAMES,SET_PORT_COUNT,CHECK_PORT_CHANGES}; struct GUIArgs { + int NumInputs; + int NumOutputs; int Num; char Port[256]; }; - + + void Attach() { m_JackClient->Attach(); } + void Detach() { m_JackClient->Detach(); } private: + const HostInfo* host; + GUIArgs m_GUIArgs; // slightly clumsy, but we have to share this data with the gui int m_NumInputPortNames; - char m_InputPortNames[MAX_INPUTPORTS][256]; + char m_InputPortNames[MAX_PORTS][256]; int m_NumOutputPortNames; - char m_OutputPortNames[MAX_OUTPUTPORTS][256]; - - void Attach() { JackClient::Get()->Attach(); } - void Detach() { JackClient::Get()->Detach(); } - void GetPortNames(std::vector &InputNames,std::vector &OutputNames) { JackClient::Get()->GetPortNames(InputNames,OutputNames); } - void ConnectInput(int n, const std::string &JackPort) { JackClient::Get()->ConnectInput(n,JackPort); } - void ConnectOutput(int n, const std::string &JackPort) { JackClient::Get()->ConnectOutput(n,JackPort); } - - static int m_RefCount; - static int m_NoExecuted; - bool m_UpdateNames; - bool m_Connected; + char m_OutputPortNames[MAX_PORTS][256]; + + void GetPortNames(std::vector &InputNames,std::vector &OutputNames) { m_JackClient->GetPortNames(InputNames,OutputNames); } + void ConnectInput(int n, const std::string &JackPort) { m_JackClient->ConnectInput(n,JackPort); } + void ConnectOutput(int n, const std::string &JackPort) { m_JackClient->ConnectOutput(n,JackPort); } + + void CreatePorts (int nInputs, int nOutputs, bool ); + + bool m_UpdateNames; + bool m_Connected; + JackClient *m_JackClient; + int m_JackInstanceID; }; #endif diff --git a/SpiralSound/Plugins/JackPlugin/JackPluginGUI.C b/SpiralSound/Plugins/JackPlugin/JackPluginGUI.C index 7a1e304..9d4eae7 100644 --- a/SpiralSound/Plugins/JackPlugin/JackPluginGUI.C +++ b/SpiralSound/Plugins/JackPlugin/JackPluginGUI.C @@ -24,11 +24,9 @@ using namespace std; -static int Numbers[255]; - //////////////////////////////////////////////////////////////////////// -int OptionsList(const vector &List) +int OptionsList(const std::vector &List) { Fl_Double_Window *Win = new Fl_Double_Window(300,300); Fl_Button *Ok = new Fl_Button(10,275,40,20,"Ok"); @@ -37,7 +35,7 @@ int OptionsList(const vector &List) Cancel->labelsize(10); Fl_Hold_Browser* Browser = new Fl_Hold_Browser(5,5,290,265,""); - for (vector::const_iterator i = List.begin(); + for (std::vector::const_iterator i = List.begin(); i!=List.end(); i++) { Browser->add(i->c_str()); @@ -77,102 +75,357 @@ int OptionsList(const vector &List) JackPluginGUI::JackPluginGUI(int w, int h,JackPlugin *o,ChannelHandler *ch,const HostInfo *Info) : SpiralPluginGUI(w,h,o,ch) { - for (int n=0; n<255; n++) Numbers[n]=n; + m_GUIColour = (Fl_Color)Info->GUI_COLOUR; + m_JackPlugin =o; + m_JackClient = o->GetJackClient(); - m_Indicator = new Fl_LED_Button(85,15,30,30,""); + m_Indicator = new Fl_LED_Button(w/2 - 15,15,30,30,""); m_Indicator->value(0); m_Indicator->color(FL_RED); + add(m_Indicator); + + m_Remove = new Fl_Button(5,15,25,25,"-"); + m_Remove->box (FL_PLASTIC_UP_BOX); + m_Remove->color (m_GUIColour); + m_Remove->type(0); + m_Remove->labelsize(2); + m_Remove->selection_color (m_GUIColour); + m_Remove->callback((Fl_Callback*)cb_Remove, this); + add(m_Remove); - m_Attach = new Fl_Button(5,40,190,20,"Attach"); + m_Add = new Fl_Button(30,15,25,25,"+"); + m_Add->box (FL_PLASTIC_UP_BOX); + m_Add->color (m_GUIColour); + m_Add->type(0); + m_Add->labelsize(2); + m_Add->selection_color (m_GUIColour); + m_Add->callback((Fl_Callback*)cb_Add, this); + add(m_Add); + + m_Attach = new Fl_Button(5,45,w - 10,20,"Attach"); + m_Attach->box (FL_PLASTIC_UP_BOX); + m_Attach->color (m_GUIColour); m_Attach->type(0); m_Attach->labelsize(10); - m_Attach->callback((Fl_Callback*)cb_Attach); + m_Attach->selection_color (m_GUIColour); + m_Attach->callback((Fl_Callback*)cb_Attach, this); + add(m_Attach); - m_Detach = new Fl_Button(5,60,190,20,"Detach"); + m_Detach = new Fl_Button(5,65,w - 10,20,"Detach"); + m_Detach->box (FL_PLASTIC_UP_BOX); + m_Detach->color (m_GUIColour); m_Detach->type(0); m_Detach->labelsize(10); - m_Detach->callback((Fl_Callback*)cb_Detach); + m_Detach->selection_color (m_GUIColour); + m_Detach->callback((Fl_Callback*)cb_Detach, this); + add(m_Detach); - int yoff=80; + m_Scroll = new Fl_Scroll(5, 90, w - 10, h - 102); + m_Scroll->box(FL_PLASTIC_DOWN_BOX); + m_Scroll->type(Fl_Scroll::VERTICAL_ALWAYS); + m_Scroll->position(0, 0); + add(m_Scroll); + + m_OutputPack = new Fl_Pack(15, 90, 85, h - 102); + m_Scroll->add(m_OutputPack); - for (int n=0; nlabelsize(8); - //m_OutputLabel[n]->labeltype(FL_ENGRAVED_LABEL); - m_OutputButton[n] = new Fl_Button(5,n*30+yoff+10,95,20,"None"); - m_OutputButton[n]->type(1); - m_OutputButton[n]->labelsize(8); - m_OutputButton[n]->callback((Fl_Callback*)cb_OutputConnect,&Numbers[n]); - } + m_InputPack = new Fl_Pack(110, 90, 85, h - 102); + m_Scroll->add(m_InputPack); - for (int n=0; nGetJackInputCount(); n++) { - sprintf(m_InputName[n],"Input %d",n); - m_InputLabel[n] = new Fl_Box(100,n*30+yoff,95,10,m_InputName[n]); - m_InputLabel[n]->labelsize(8); - //m_InputLabel[n]->labeltype(FL_ENGRAVED_LABEL); - m_InputButton[n] = new Fl_Button(100,n*30+yoff+10,95,20,"None"); - m_InputButton[n]->type(1); - m_InputButton[n]->labelsize(8); - m_InputButton[n]->callback((Fl_Callback*)cb_InputConnect,&Numbers[n]); + AddOutput(); + AddInput(); } - + end(); } void JackPluginGUI::UpdateValues(SpiralPlugin *o) { - } void JackPluginGUI::Update() { + if (m_GUICH->GetBool("Connected")) { + m_JackClient->CheckingPortChanges = true; + + for (unsigned int n=0; nm_OutputPortsChanged.size(); n++) { + m_JackClient->m_OutputPortsChanged[n]->Connected = jack_port_connected(m_JackClient->m_OutputPortsChanged[n]->Port); + + if (jack_port_connected(m_JackClient->m_OutputPortsChanged[n]->Port)) { + if (m_JackClient->m_OutputPortsChanged[n]->ConnectedTo!="") { + m_OutputButton[n]->label(m_JackClient->m_OutputPortsChanged[n]->ConnectedTo.c_str()); + } + else + { + const char** connections = jack_port_get_all_connections(m_JackClient->m_Client,m_JackClient->m_OutputPortsChanged[n]->Port); + if (connections) { + m_OutputButton[m_JackClient->m_OutputPortsChanged[n]->PortNo]->label(connections[0]); + free(connections); + } + } + m_OutputButton[m_JackClient->m_OutputPortsChanged[n]->PortNo]->value(1); + } + else + { + m_OutputButton[m_JackClient->m_OutputPortsChanged[n]->PortNo]->value(0); + m_OutputButton[m_JackClient->m_OutputPortsChanged[n]->PortNo]->label("None"); + } + } + + m_JackClient->m_OutputPortsChanged.clear(); + + for (unsigned int n=0; nm_InputPortsChanged.size(); n++) { + m_JackClient->m_InputPortsChanged[n]->Connected = jack_port_connected(m_JackClient->m_InputPortsChanged[n]->Port); + + if (m_JackClient->m_InputPortsChanged[n]->Connected) { + if (m_JackClient->m_InputPortsChanged[n]->ConnectedTo!="") { + m_InputButton[n]->label(m_JackClient->m_InputPortsChanged[n]->ConnectedTo.c_str()); + } + else + { + const char** connections = jack_port_get_all_connections(m_JackClient->m_Client,m_JackClient->m_InputPortsChanged[n]->Port); + if (connections) { + m_InputButton[m_JackClient->m_InputPortsChanged[n]->PortNo]->label(connections[0]); + free(connections); + } + } + m_InputButton[m_JackClient->m_InputPortsChanged[n]->PortNo]->value(1); + } + else + { + m_InputButton[m_JackClient->m_InputPortsChanged[n]->PortNo]->value(0); + m_InputButton[m_JackClient->m_InputPortsChanged[n]->PortNo]->label("None"); + } + + } + + m_JackClient->m_InputPortsChanged.clear(); + + m_JackClient->CheckingPortChanges = false; + } + + m_GUICH->SetCommand (JackPlugin::CHECK_PORT_CHANGES); + m_Indicator->value(m_GUICH->GetBool("Connected")); redraw(); } //// Callbacks //// -inline void JackPluginGUI::cb_Attach_i(Fl_Button* o, void* v) +void JackPluginGUI::RemoveOutput() { + int n =(int) m_InputName.size() - 1; + + if (m_OutputName[n]) + { + delete(m_OutputName[n]); + m_OutputName[n] = NULL; + m_OutputName.pop_back(); + } + + if (m_OutputLabel[n]) + { + m_OutputPack->remove(m_OutputLabel[n]); + delete(m_OutputLabel[n]); + m_OutputLabel[n] = NULL; + m_OutputLabel.pop_back(); + m_OutputPack->redraw(); + m_Scroll->redraw(); + } + + if (m_OutputButton[n]) + { + m_OutputPack->remove(m_OutputButton[n]); + delete(m_OutputButton[n]); + m_OutputButton[n] = NULL; + m_OutputButton.pop_back(); + m_OutputPack->redraw(); + m_Scroll->redraw(); + } +} + +void JackPluginGUI::RemoveInput() { + int n = (int) m_InputName.size() - 1; + + if (m_InputName[n]) + { + delete(m_InputName[n]); + m_InputName[n] = NULL; + m_InputName.pop_back(); + } + + if (m_InputLabel[n]) + { + m_InputPack->remove(m_InputLabel[n]); + delete(m_InputLabel[n]); + m_InputLabel[n] = NULL; + m_InputLabel.pop_back(); + m_InputPack->redraw(); + m_Scroll->redraw(); + } + + if (m_InputButton[n]) + { + m_InputPack->remove(m_InputButton[n]); + delete(m_InputButton[n]); + m_InputButton[n] = NULL; + m_InputButton.pop_back(); + m_InputPack->redraw(); + m_Scroll->redraw(); + + } +} + +void JackPluginGUI::AddOutput() { + int n = (int) m_OutputName.size(); + char *NewName = new char [256]; + + sprintf(NewName,"Output %d",n); + m_OutputName.push_back(NewName); + + m_OutputLabel.push_back(new Fl_Box(0,n*30,90,10,m_OutputName[n])); + m_OutputLabel[n]->labelsize(8); + m_OutputPack->add(m_OutputLabel[n]); + + m_OutputButton.push_back(new Fl_Button(0,n*30+10,90,20,"None")); + m_OutputButton[n]->type(1); + m_OutputButton[n]->labelsize(8); + m_OutputButton[n]->callback((Fl_Callback*)cb_OutputConnect,this); + m_OutputPack->add(m_OutputButton[n]); + + redraw(); + Fl::check(); +} + +void JackPluginGUI::AddInput() { + int n = (int) m_InputName.size(); + char *NewName = new char [256]; + + sprintf(NewName,"Input %d",n); + m_InputName.push_back(NewName); + + m_InputLabel.push_back(new Fl_Box(95,n*30,90,10, m_InputName[n])); + m_InputLabel[n]->labelsize(8); + m_InputPack->add(m_InputLabel[n]); + + m_InputButton.push_back(new Fl_Button(95,n*30+10,90,20,"None")); + m_InputButton[n]->type(1); + m_InputButton[n]->labelsize(8); + m_InputButton[n]->callback((Fl_Callback*)cb_InputConnect,this); + m_InputPack->add(m_InputButton[n]); + + redraw(); + Fl::check(); +} + +inline void JackPluginGUI::cb_Remove_i(Fl_Button* o) { - //m_GUICH->SetCommand(JackPlugin::ATTACH); - JackClient::Get()->Attach(); + int n = (int) m_InputName.size(); + + if (n > MIN_PORTS) + { + RemoveOutput() ; + RemoveInput() ; + + m_GUICH->Set ("NumInputs", n-1); + m_GUICH->Set ("NumOutputs", n-1); + m_GUICH->SetCommand (JackPlugin::SET_PORT_COUNT); + m_GUICH->Wait (); + + /* removing connections live must be called directly from here in the GUI thread */ + if (m_GUICH->GetBool("Connected")) { + m_JackClient->RemoveInputPort(n-1); + m_JackClient->RemoveOutputPort(n-1); + } + + if (n > 19) { + resize (x(), y(), w(), h()-7); + + m_Indicator->resize (x()+w()/2 - 15,y()+15,30,30); + m_Remove->resize (x()+5,y()+15,25,25); + m_Add->resize (x()+30,y()+15,25,25); + m_Attach->resize (x()+5,y()+45,w() - 10,20); + m_Detach->resize (x()+5,y()+65,w() - 10,20); + m_Scroll->resize (x()+5, y()+90, w() - 10, h() - 102); + m_OutputPack->resize (x()+15, y()+90, 85, h() - 102); + m_InputPack->resize (x()+110, y()+90, 85, h() - 102); + } + } } -void JackPluginGUI::cb_Attach(Fl_Button* o, void* v) -{ ((JackPluginGUI*)(o->parent()))->cb_Attach_i(o,v);} -inline void JackPluginGUI::cb_Detach_i(Fl_Button* o, void* v) +inline void JackPluginGUI::cb_Add_i(Fl_Button* o) { - //m_GUICH->SetCommand(JackPlugin::DETACH); - - for (int n=0; nGetBool("Connected")) { + m_JackClient->AddInputPort(n); + m_JackClient->AddOutputPort(n); + } + + m_GUICH->Set ("NumInputs", n+1); + m_GUICH->Set ("NumOutputs", n+1); + m_GUICH->SetCommand (JackPlugin::SET_PORT_COUNT); + m_GUICH->Wait (); + + AddOutput() ; + AddInput() ; + + if (n > 20) { + resize (x(), y(), w(), h()+12); + + m_Indicator->resize (x()+w()/2 - 15,y()+15,30,30); + m_Remove->resize (x()+5,y()+15,25,25); + m_Add->resize (x()+30,y()+15,25,25); + m_Attach->resize (x()+5,y()+45,w() - 10,20); + m_Detach->resize (x()+5,y()+65,w() - 10,20); + m_Scroll->resize (x()+5, y()+90, w() - 10, h() - 102); + m_OutputPack->resize (x()+15, y()+90, 85, h() - 102); + m_InputPack->resize (x()+110, y()+90, 85, h() - 102); + } + } +} + +inline void JackPluginGUI::cb_Attach_i(Fl_Button* o) +{ + m_JackPlugin->Attach(); +} + +inline void JackPluginGUI::cb_Detach_i(Fl_Button* o) +{ + for (int n=0; n<(int)m_OutputName.size(); n++) { m_OutputButton[n]->value(false); m_OutputButton[n]->label("None"); } - for (int n=0; nvalue(false); m_InputButton[n]->label("None"); } - JackClient::Get()->Detach(); + m_JackPlugin->Detach(); } -void JackPluginGUI::cb_Detach(Fl_Button* o, void* v) -{ ((JackPluginGUI*)(o->parent()))->cb_Detach_i(o,v);} -inline void JackPluginGUI::cb_OutputConnect_i(Fl_Button* o, void* v) +inline void JackPluginGUI::cb_OutputConnect_i(Fl_Button* o) { - if (o->value()) + int index=0; + std::vector::iterator it = std::find( m_OutputButton.begin(), m_OutputButton.end(), o ); + + if ( it != m_OutputButton.end() ) + index = std::distance( m_OutputButton.begin(), it ); + + if ((o->value()) && m_GUICH->GetBool("Connected")) { m_GUICH->SetCommand(JackPlugin::UPDATE_NAMES); m_GUICH->Wait(); // bit of a hack for multithreaded safety int ninputs=m_GUICH->GetInt("NumOutputPortNames"); - char inputs[MAX_INPUTPORTS][256]; + char inputs[MAX_PORTS][256]; m_GUICH->GetData("InputPortNames",inputs); vector Inputs; @@ -181,37 +434,43 @@ inline void JackPluginGUI::cb_OutputConnect_i(Fl_Button* o, void* v) // connect this plugin's output to a jack input if (choice>0) - { - //m_GUICH->Set("Num",(*(int*)v)); - //m_GUICH->SetData("Port",inputs[choice-1]); - //m_GUICH->SetCommand(JackPlugin::CONNECTOUTPUT); - - JackClient::Get()->ConnectOutput((*(int*)v),inputs[choice-1]); + { + m_JackClient->ConnectOutput(index,inputs[choice-1]); o->label(inputs[choice-1]); o->redraw(); } + else { + o->label("None"); + o->value(0); + o->redraw(); + } } else { - JackClient::Get()->DisconnectOutput((*(int*)v)); + m_JackClient->DisconnectOutput(index); o->label("None"); + o->value(0); o->redraw(); } } -void JackPluginGUI::cb_OutputConnect(Fl_Button* o, void* v) -{ ((JackPluginGUI*)(o->parent()))->cb_OutputConnect_i(o,v);} -inline void JackPluginGUI::cb_InputConnect_i(Fl_Button* o, void* v) +inline void JackPluginGUI::cb_InputConnect_i(Fl_Button* o) { - if (o->value()) + int index=0; + std::vector::iterator it = std::find( m_InputButton.begin(), m_InputButton.end(), o ); + + if ( it != m_InputButton.end() ) + index = std::distance( m_InputButton.begin(), it ); + + if ((o->value()) && (m_JackClient) && (m_JackClient->IsAttached())) { m_GUICH->SetCommand(JackPlugin::UPDATE_NAMES); m_GUICH->Wait(); // bit of a hack for multithreaded safety int noutputs=m_GUICH->GetInt("NumOutputPortNames"); - char outputs[MAX_OUTPUTPORTS][256]; + char outputs[MAX_PORTS][256]; m_GUICH->GetData("OutputPortNames",outputs); vector Outputs; @@ -220,40 +479,36 @@ inline void JackPluginGUI::cb_InputConnect_i(Fl_Button* o, void* v) // connect this plugin's input to a jack output if (choice>0) - { - //m_GUICH->Set("Num",(*(int*)v)); - //m_GUICH->SetData("Port",outputs[choice-1]); - //m_GUICH->SetCommand(JackPlugin::CONNECTINPUT); - - JackClient::Get()->ConnectInput((*(int*)v),outputs[choice-1]); + { + m_JackClient->ConnectInput(index,outputs[choice-1]); o->label(outputs[choice-1]); o->redraw(); } + else { + o->label("None"); + o->value(0); + o->redraw(); + } } else { - JackClient::Get()->DisconnectInput((*(int*)v)); + m_JackClient->DisconnectInput(index); o->label("None"); + o->value(0); o->redraw(); } } -void JackPluginGUI::cb_InputConnect(Fl_Button* o, void* v) -{ ((JackPluginGUI*)(o->parent()))->cb_InputConnect_i(o,v);} const string JackPluginGUI::GetHelpText(const string &loc){ return string("") + "JACK is the Jack Audio Connection Kit, and allows multiple Linux audio\n" + "apps to be connected together and run simultaneously in a low latency.\n" + "environment.\n\n" - + "This plugin allows you to connect up to 8 inputs and outputs to other\n" - + "JACK apps (providing a server is running)\n" - + "You can use the JackPlugin to connect the ports, or a external program\n" - + "such as the excellent qjackconnect app. Be aware however that if you\n" - + "connect from an external app, the port GUI does not update itself yet.\n" - + "Another problem yet to be fixed is that ssm will get confused if more\n" - + "than one JackPlugin is being used. It's best to use one central\n" - + "JackPlugin at this time.\n\n" + + "This plugin allows you to connect up to 64 inputs and outputs to other\n" + + "JACK apps (providing a server is running and your system can handle it)\n" + + "You can use the JackPlugin to connect the ports, or an external program\n" + + "such as the excellent qjackconnect app.\n\n" + "When using JACK, make sure the buffer size and samplerate are set to\n" + "match the JACK server, otherwise glitchy playback, and/or crashes may\n" + "result"; diff --git a/SpiralSound/Plugins/JackPlugin/JackPluginGUI.h b/SpiralSound/Plugins/JackPlugin/JackPluginGUI.h index ff62dc3..f754e43 100644 --- a/SpiralSound/Plugins/JackPlugin/JackPluginGUI.h +++ b/SpiralSound/Plugins/JackPlugin/JackPluginGUI.h @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "../Widgets/Fl_Knob.H" #include "../Widgets/Fl_LED_Button.H" #include "JackPlugin.h" @@ -30,6 +32,8 @@ #ifndef JACK_GUI_H #define JACK_GUI_H +using namespace std; + class JackPluginGUI : public SpiralPluginGUI { public: @@ -42,28 +46,52 @@ protected: const std::string GetHelpText(const std::string &loc); private: + JackClient *m_JackClient; + JackPlugin *m_JackPlugin; + + Fl_Color m_GUIColour; Fl_LED_Button *m_Indicator; + + Fl_Button *m_Remove; + Fl_Button *m_Add; + Fl_Button *m_Attach; Fl_Button *m_Detach; - char m_InputName[NUM_INPUTS][256]; - Fl_Box *m_InputLabel[NUM_INPUTS]; - Fl_Button *m_InputButton[NUM_INPUTS]; + Fl_Scroll *m_Scroll; + Fl_Pack *m_InputPack; + Fl_Pack *m_OutputPack; + + std::vector m_InputName; + std::vector m_InputLabel; + std::vector m_InputButton; - char m_OutputName[NUM_INPUTS][256]; - Fl_Box *m_OutputLabel[NUM_OUTPUTS]; - Fl_Button *m_OutputButton[NUM_OUTPUTS]; - - //// Callbacks //// - inline void cb_Attach_i(Fl_Button* o, void* v); - static void cb_Attach(Fl_Button* o, void* v); - inline void cb_Detach_i(Fl_Button* o, void* v); - static void cb_Detach(Fl_Button* o, void* v); - inline void cb_OutputConnect_i(Fl_Button* o, void* v); - static void cb_OutputConnect(Fl_Button* o, void* v); - inline void cb_InputConnect_i(Fl_Button* o, void* v); - static void cb_InputConnect(Fl_Button* o, void* v); + std::vector m_OutputName; + std::vector m_OutputLabel; + std::vector m_OutputButton; + + void RemoveInput() ; + void RemoveOutput() ; + + void AddInput() ; + void AddOutput() ; + + //// inline Callbacks //// + inline void cb_Add_i(Fl_Button* o); + inline void cb_Remove_i(Fl_Button* o); + inline void cb_Attach_i(Fl_Button* o); + inline void cb_Detach_i(Fl_Button* o); + inline void cb_OutputConnect_i(Fl_Button* o); + inline void cb_InputConnect_i(Fl_Button* o); + + //// Static Callbacks //// + static void cb_Add(Fl_Button* o, JackPluginGUI* v) {v->cb_Add_i(o);} + static void cb_Remove(Fl_Button* o, JackPluginGUI* v) {v->cb_Remove_i(o);} + static void cb_Attach(Fl_Button* o, JackPluginGUI* v) {v->cb_Attach_i(o);} + static void cb_Detach(Fl_Button* o, JackPluginGUI* v) {v->cb_Detach_i(o);} + static void cb_OutputConnect(Fl_Button* o, JackPluginGUI* v) {v->cb_OutputConnect_i(o);} + static void cb_InputConnect(Fl_Button* o, JackPluginGUI* v) {v->cb_InputConnect_i(o);} }; #endif