/* SpiralPlugin * Copyleft (C) 2000 David Griffiths * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "JackPlugin.h" #include "JackPluginGUI.h" #include #include #include #include using namespace std; //////////////////////////////////////////////////////////////////////// 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"); Ok->labelsize(10); Fl_Button *Cancel = new Fl_Button(50,275,40,20,"Cancel"); Cancel->labelsize(10); Fl_Hold_Browser* Browser = new Fl_Hold_Browser(5,5,290,265,""); for (std::vector::const_iterator i = List.begin(); i!=List.end(); i++) { Browser->add(i->c_str()); } Win->show(); int Choice=-1; for (;;) { Fl::wait(); Fl_Widget* o = Fl::readqueue(); if (o==Ok || o==Browser) { Choice=Browser->value(); Win->hide(); delete Win; break; } if (o==Cancel) { Choice=-1; Win->hide(); delete Win; break; } if (o==Win) break; } return Choice; } //////////////////////////////////////////////////////////////////////// JackPluginGUI::JackPluginGUI(int w, int h,JackPlugin *o,ChannelHandler *ch,const HostInfo *Info) : SpiralPluginGUI(w,h,o,ch) { m_GUIColour = (Fl_Color)Info->GUI_COLOUR; m_JackPlugin =o; m_JackClient = o->GetJackClient(); 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_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->selection_color (m_GUIColour); m_Attach->callback((Fl_Callback*)cb_Attach, this); add(m_Attach); 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->selection_color (m_GUIColour); m_Detach->callback((Fl_Callback*)cb_Detach, this); add(m_Detach); 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); m_InputPack = new Fl_Pack(110, 90, 85, h - 102); m_Scroll->add(m_InputPack); for (int n=0; nGetJackInputCount(); n++) { AddOutput(); AddInput(); } end(); } void JackPluginGUI::UpdateValues(SpiralPlugin *o) { //To make sure buttons match ports on loading a patch if (! m_GUICH->GetBool("Connected")) { int i, numbuttons = (int) m_InputName.size(), numports = m_JackClient->GetJackInputCount(); if (numbuttons > numports) { for (int i=numbuttons-numports; i > 0; i--) { RemoveOutput() ; RemoveInput() ; } } if (numbuttons < numports) { for (int i=0; i < numports-numbuttons; i++) { AddOutput() ; AddInput() ; } } } } 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 //// 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) { 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); } } } inline void JackPluginGUI::cb_Add_i(Fl_Button* o) { int n = (int) m_OutputName.size(); if (n <= MAX_PORTS) { /* Adding connections live must be called directly from here in the GUI thread */ if (m_GUICH->GetBool("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; n<(int)m_InputName.size(); n++) { m_InputButton[n]->value(false); m_InputButton[n]->label("None"); } m_JackPlugin->Detach(); } inline void JackPluginGUI::cb_OutputConnect_i(Fl_Button* o) { 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_PORTS][256]; m_GUICH->GetData("InputPortNames",inputs); vector Inputs; for (int n=0; n0) { m_JackClient->ConnectOutput(index,inputs[choice-1]); o->label(inputs[choice-1]); o->redraw(); } else { o->label("None"); o->value(0); o->redraw(); } } else { m_JackClient->DisconnectOutput(index); o->label("None"); o->value(0); o->redraw(); } } inline void JackPluginGUI::cb_InputConnect_i(Fl_Button* o) { 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_PORTS][256]; m_GUICH->GetData("OutputPortNames",outputs); vector Outputs; for (int n=0; n0) { m_JackClient->ConnectInput(index,outputs[choice-1]); o->label(outputs[choice-1]); o->redraw(); } else { o->label("None"); o->value(0); o->redraw(); } } else { m_JackClient->DisconnectInput(index); o->label("None"); o->value(0); o->redraw(); } } 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 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"; }