├── README ├── flowCanvas └── flowcanvas.cpp ├── guiViewSample ├── guiViewSample.cpp ├── sample.wav ├── waveview.cpp └── waveview.hpp ├── jackClient ├── README └── jackClient.cpp ├── ladspaHost ├── README └── ladspaHost.cpp ├── ladspaHostGuiBrowser ├── Makefile ├── README ├── main.cpp ├── sila.cpp ├── sila.desktop ├── sila.h ├── sila_browser.cpp └── sila_ui.cpp ├── loopedSample ├── README ├── loopedSample.cpp └── sample.wav ├── pureDataHost ├── README ├── libpd.pc ├── main.cpp ├── puredatahost.cpp └── puredatahost.hpp └── writingSoundfileToDisk ├── README └── writingSoundfileToDisk.cpp /README: -------------------------------------------------------------------------------- 1 | Open Audio Programming Tutorials 2 | 3 | This repo exists as a place to host example code, where they can be used 4 | as tutorials for learning C++ audio programming. It would be great to 5 | get feedback and patches from the community to ensure the examples stay 6 | up to date! 7 | -------------------------------------------------------------------------------- /flowCanvas/flowcanvas.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // Author: Harry van Haaren, 2012 10 | // Compile: g++ -Wall -I/usr/include/ganv -I/usr/include/flowcanvas flowcanvas.cpp `pkg-config gtkmm-2.4 ganv-1 flowcanvas --cflags --libs` 11 | 12 | using namespace std; 13 | 14 | class UI : public Ganv::Canvas 15 | { 16 | public: 17 | UI() 18 | : Ganv::Canvas(800,600) 19 | { 20 | box1 = new Ganv::Module( *this, "source", 10, 20 ); 21 | box2 = new Ganv::Module( *this, "destination", 30, 80 ); 22 | 23 | box1Out = new Ganv::Port( *box1, "out", false, 0x244678FF); 24 | box1Out = new Ganv::Port( *box2, "in", true, 0x244678FF); 25 | 26 | signal_connect.connect (sigc::mem_fun(this, &UI::nodeConnect )); 27 | signal_disconnect.connect (sigc::mem_fun(this, &UI::nodeDisconnect )); 28 | } 29 | 30 | void nodeConnect(Ganv::Node* port1, Ganv::Node* port2) 31 | { 32 | // don't need to keep the pointer, we disconnect using both "ends" 33 | new Ganv::Edge(*this, port1, port2, 0x244678FF); 34 | } 35 | void nodeDisconnect(Ganv::Node* port1, Ganv::Node* port2) 36 | { 37 | remove_edge(port1, port2); 38 | } 39 | 40 | private: 41 | Ganv::Module* box1; 42 | Ganv::Module* box2; 43 | 44 | Ganv::Port* box1Out; 45 | Ganv::Port* box2In; 46 | }; 47 | 48 | int main(int argc, char**argv) 49 | { 50 | 51 | Glib::thread_init(); 52 | Gtk::Main kit(argc, argv); 53 | 54 | UI ui; 55 | 56 | Gtk::Window window; 57 | 58 | window.add( ui.widget() ); 59 | 60 | window.show_all(); 61 | 62 | kit.run( window ); 63 | 64 | return 0; 65 | 66 | } 67 | -------------------------------------------------------------------------------- /guiViewSample/guiViewSample.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Contact: Harry van Haaren 3 | // Compile: g++ -oguiViewSample waveview.cpp guiViewSample.cpp `pkg-config --cflags --libs sndfile gtkmm-2.4` 4 | 5 | // In this tutorial you will learn how to create a simple Gtkmm window, 6 | // and then show a custom widget inside it to draw a waveform of a sample 7 | // Please read the "loopedSample" tutorial for info on how to read samples 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // to get access to the GUI functions 14 | #include 15 | 16 | // include our custom widget! 17 | #include "waveview.hpp" 18 | 19 | // get a vector, that's an Array that we can dynamically resize. It contains 20 | // floats in this case, so we can store audio samples in it. 21 | std::vector sampleVector; 22 | 23 | int loadSample() 24 | { 25 | // create a "Sndfile" handle, it's part of the sndfile library we 26 | // use to load samples 27 | SndfileHandle fileHandle( "sample.wav" , SFM_READ, SF_FORMAT_WAV | SF_FORMAT_FLOAT , 1 , 44100); 28 | 29 | // get the number of frames in the sample 30 | int size = fileHandle.frames(); 31 | 32 | if ( size == 0 ) 33 | { 34 | // file doesn't exist or is of incompatible type, main handles the -1 35 | return -1; 36 | } 37 | 38 | // get some more info of the sample 39 | int channels = fileHandle.channels(); 40 | int samplerate = fileHandle.samplerate(); 41 | 42 | // we declared sampleVector earlier, now we resize it 43 | sampleVector.resize(size); 44 | 45 | // this tells sndfile to 46 | fileHandle.read( &sampleVector.at(0) , size ); 47 | 48 | std::cout << "Loaded a file with " << channels << " channels, and a samplerate of " << 49 | samplerate << " with " << size << " samples, so its duration is " << 50 | size / samplerate << " seconds long." << std::endl; 51 | 52 | return 0; 53 | } 54 | 55 | 56 | int main(int argc, char** argv) 57 | { 58 | std::cout << "GUI View Sample tutorial" << std::endl; 59 | 60 | int loadNotOK = loadSample(); 61 | 62 | if ( loadNotOK ) 63 | { 64 | std::cout << "Error in sample loading function, check the sample\ 65 | exists in the directory the program is running!" << std::endl; 66 | return 0; // quit, as we didn't setup correctly 67 | } 68 | 69 | // create an instance of Gtk::Main, we use it to make the GUI run later 70 | Gtk::Main kit(argc, argv); 71 | 72 | // create a "Window" 73 | Gtk::Window window; 74 | 75 | // set some attributes 76 | window.set_default_size(300,150); 77 | window.set_position(Gtk::WIN_POS_CENTER); 78 | 79 | // create an instance of our custom widget 80 | GWaveView waveview; 81 | 82 | // add the widget to the window 83 | window.add(waveview); 84 | 85 | // tell the window to show its contents 86 | window.show_all(); 87 | 88 | // tell our widget to draw the sampleVector 89 | waveview.draw(sampleVector); 90 | 91 | // now call on the Gtk::Main to run, and it does the work for us. 92 | kit.run(window); 93 | 94 | // note that until the Window is closed (and the Gtk::Main quits) the 95 | // program will stop on the line above this, and the program will exit 96 | // as soon as the window is closed! 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /guiViewSample/sample.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harryhaaren/openAudioProgrammingTutorials/56c970ef994c97adbea8b05a6fa3b765dd70613d/guiViewSample/sample.wav -------------------------------------------------------------------------------- /guiViewSample/waveview.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "waveview.hpp" 4 | 5 | GWaveView::GWaveView() 6 | { 7 | // Gives "Exposure" events to the widget, we need the for when we want 8 | // to redraw the widget! 9 | add_events(Gdk::EXPOSURE_MASK); 10 | 11 | // set default widget size 12 | set_size_request( 400, 100 ); 13 | 14 | // set our flag so we copy the data when we call draw() 15 | copiedSample = false; 16 | } 17 | 18 | 19 | void GWaveView::draw(const std::vector& inSample) 20 | { 21 | if ( !copiedSample ) 22 | { 23 | sample = inSample; 24 | copiedSample = true; 25 | } 26 | 27 | // force our program to redraw the entire widget. 28 | Glib::RefPtr win = get_window(); 29 | if (win) 30 | { 31 | Gdk::Rectangle r(0, 0, get_allocation().get_width(),get_allocation().get_height()); 32 | win->invalidate_rect(r, false); 33 | } 34 | } 35 | 36 | bool GWaveView::on_expose_event(GdkEventExpose* event) 37 | { 38 | // This is where we draw on the window 39 | Glib::RefPtr window = get_window(); 40 | 41 | if(window) // Only run if Window does exist 42 | { 43 | Gtk::Allocation allocation = get_allocation(); 44 | width = allocation.get_width(); 45 | height = allocation.get_height(); 46 | 47 | // coordinates for the center of the window 48 | int xc, yc; 49 | xc = width / 2; 50 | yc = height / 2; 51 | 52 | Cairo::RefPtr cr = window->create_cairo_context(); 53 | 54 | // clip to the area indicated by the expose event so that we only redraw 55 | // the portion of the window that needs to be redrawn 56 | cr->rectangle(event->area.x, event->area.y,event->area.width, event->area.height); 57 | cr->clip(); 58 | 59 | int x = 0; 60 | int y = 0; 61 | int xSize = width; 62 | int ySize = height; 63 | 64 | // works but a bit simple 65 | cr -> move_to( x , y ); 66 | cr -> line_to( x + xSize, y ); 67 | cr -> line_to( x + xSize, y + ySize ); 68 | cr -> line_to( x , y + ySize ); 69 | cr -> close_path(); 70 | 71 | // Draw outline shape 72 | cr -> set_source_rgb (0.1,0.1,0.1); 73 | cr -> fill(); 74 | 75 | 76 | // don't draw every sample 77 | int sampleCountForDrawing = -1; 78 | 79 | float currentTop = 0.f; 80 | float previousTop = 0.f; 81 | float currentSample = 0.f; 82 | 83 | // loop for drawing each Point on the widget. 84 | for (long index=0; index <(long)sample.size(); index++ ) 85 | { 86 | // get the current sample 87 | float currentSample = sample.at(index); 88 | 89 | if ( currentSample > 0 && currentTop < currentSample ) 90 | { 91 | currentTop = currentSample; 92 | } 93 | sampleCountForDrawing--; 94 | 95 | if ( sampleCountForDrawing < 0 ) 96 | { 97 | float drawSample = currentTop; 98 | 99 | int xCoord = x + ( xSize * ( float(index) / sample.size() ) ); 100 | 101 | cr->move_to( xCoord, y + (ySize/2) - (previousTop * ySize ) ); // top 102 | cr->line_to( xCoord, y + (ySize/2) + (drawSample * ySize ) ); // bottom 103 | 104 | sampleCountForDrawing = 15; 105 | previousTop = drawSample; 106 | currentTop = 0; 107 | } 108 | 109 | } 110 | 111 | cr -> set_source_rgb (1.0,1.0,1.0); 112 | cr->stroke(); 113 | } 114 | return true; 115 | } // on_expose_event() 116 | 117 | -------------------------------------------------------------------------------- /guiViewSample/waveview.hpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef GWAVEVIEW 4 | #define GWAVEVIEW 5 | 6 | #include 7 | #include 8 | 9 | class GWaveView : public Gtk::DrawingArea 10 | { 11 | public: 12 | GWaveView(); 13 | 14 | void draw(const std::vector& inSample); 15 | 16 | protected: 17 | int width; 18 | int height; 19 | 20 | // keep track of if we have the data from the sample 21 | bool copiedSample; 22 | 23 | // keep a *copy* of the vector 24 | std::vector sample; 25 | 26 | // this is the GTK "draw" function, it's where we'll draw lines onto the widget 27 | bool on_expose_event(GdkEventExpose* event); 28 | }; 29 | 30 | #endif // GWAVEVIEW 31 | 32 | -------------------------------------------------------------------------------- /jackClient/README: -------------------------------------------------------------------------------- 1 | JackClient 2 | 3 | What we're going to do today is create a simple JACK client. Its going 4 | to create 2 ports, one input, and one output. Then it will copy the data 5 | from one port to the other. 6 | 7 | This is essentially a useless JACK client, but it provides a good 8 | overview of how JACK programming works, and this basic functionality 9 | is essential for any fancy programs: read Ardour, QTractor, QSynth, 10 | Yoshimi etc *all* have a JACK process callback just like this one! 11 | -------------------------------------------------------------------------------- /jackClient/jackClient.cpp: -------------------------------------------------------------------------------- 1 | // Contact: Harry van Haaren 2 | // Compile: g++ jackClient.cpp `pkg-config --cflags --libs jack` 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // declare two "jack_port_t" pointers, which will each represent a port 9 | // in the JACK graph (ie: Connections tab in QJackCtl) 10 | jack_port_t* inputPort = 0; 11 | jack_port_t* outputPort = 0; 12 | 13 | 14 | // this function is the main audio processing loop, JACK calls this function 15 | // every time that it wants "nframes" number of frames to be processed. 16 | // nframes is usually between 64 and 256, but you should always program 17 | // so that you can work with any amount of frames per process() call! 18 | int process(jack_nframes_t nframes, void* ) 19 | { 20 | // this is a little tricky, port_get_buffer() will return a pointer to 21 | // the data that we will use, so cast it to (float*), so that we 22 | // can use the data as floating point numbers. JACK will always pass 23 | // floating point samples around, the reason that we have to cast it 24 | // ourselves is so that it could be changed in the future... don't worry 25 | // about it too much. 26 | float* inputBuffer = (float*)jack_port_get_buffer ( inputPort , nframes); 27 | float* outputBuffer= (float*)jack_port_get_buffer ( outputPort, nframes); 28 | 29 | 30 | // this is the intresting part, we work with each sample of audio data 31 | // one by one, copying them across. Try multiplying the input by 0.5, 32 | // it will decrease the volume... 33 | for ( int i = 0; i < (int) nframes; i++) 34 | { 35 | // copy data from input to output. Note this is not optimized for speed! 36 | outputBuffer[i] = inputBuffer[i]; 37 | } 38 | 39 | return 0; 40 | } 41 | 42 | int main() 43 | { 44 | std::cout << "JACK client tutorial" << std::endl; 45 | 46 | // create a JACK client and activate 47 | jack_client_t* client = jack_client_open ("JackClientTutorial", 48 | JackNullOption, 49 | 0, 50 | 0 ); 51 | 52 | // register the process callback : JACK "calls us back" when there is 53 | // work to be done, and the "process" function does that work. 54 | jack_set_process_callback (client, process , 0); 55 | 56 | // register two ports, one input one output, both of AUDIO type 57 | inputPort = jack_port_register ( client, 58 | "input", 59 | JACK_DEFAULT_AUDIO_TYPE, 60 | JackPortIsInput, 61 | 0 ); 62 | 63 | outputPort = jack_port_register ( client, 64 | "output", 65 | JACK_DEFAULT_AUDIO_TYPE, 66 | JackPortIsOutput, 67 | 0 ); 68 | 69 | // activate the client, ie: enable it for processing 70 | jack_activate(client); 71 | 72 | // pause for 30 seconds, letting process() do it's thing 73 | sleep(30); 74 | 75 | // tell JACK to stop processing the client 76 | jack_deactivate(client); 77 | 78 | // close the client 79 | jack_client_close(client); 80 | 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /ladspaHost/README: -------------------------------------------------------------------------------- 1 | Ladspa Host 2 | 3 | For this tutorial we're going to have to use some slightly more advanced 4 | features of glibmm, loading a plugin file, and then retrieving the 5 | LADSPA details from it. 6 | 7 | Once we have instantiated the plugin (created one processing "unit") 8 | then we "connect" the ports of the plugin to the variables of the host, 9 | and finally we tell it to run for "nframes" amount of audio samples. 10 | 11 | So the structure is pretty logical: 12 | 13 | Load .so plugin file from disk 14 | Retrieve details of plugin from file handle 15 | Instantiate plugin 16 | connect ports 17 | run the plugin 18 | -------------------------------------------------------------------------------- /ladspaHost/ladspaHost.cpp: -------------------------------------------------------------------------------- 1 | 2 | // This tutorial builds upon the JackClient tutorial, so it is a good 3 | // idea to look over there if you're not familiar with a simple JACK client 4 | 5 | // Contact: Harry van Haaren 6 | // Compile: g++ ladspaHost.cpp -o ladspaHost `pkg-config --cflags --libs jack glibmm-2.4` 7 | 8 | 9 | // Intro 10 | // In order to load LADSPA plugins we need to use a library. More 11 | // technically, we need to load a "shared object". We're going to use 12 | // GLibmm to do this, specifically the Module class. 13 | // 14 | // What the glibmm::module class provides is a way to load these shared 15 | // objects in a platform independant way. It also gives us access to 16 | // the interal functions in the object, which is how we're going to 17 | // get access to the audio processing functions. 18 | // 19 | // The LADSPA effect we're going to host is the AM pitchshifter from 20 | // Steve Harris SWH plugin suite. 21 | // Details of plugin: http://plugin.org.uk/ladspa-swh/docs/ladspa-swh.html#tth_sEc2.5 22 | 23 | #include 24 | #include 25 | 26 | // include the LADSPA header file 27 | #include 28 | 29 | // then include Glibmm's "Module", so we can load the plugin 30 | #include 31 | 32 | // here we have the "handle" to the LADSPA effect, this is how we will 33 | // use the effect object later 34 | LADSPA_Handle instanceHandle; 35 | 36 | // next we define some global variables, like glibLadspaModule and LADSPA_Handle 37 | // we use them to load the LADSPA .so library file, and instantiate the plugin 38 | // note these are pointers, so we initialize them to 0 39 | Glib::Module* glibModule = 0; 40 | LADSPA_Descriptor* descriptor = 0; 41 | 42 | // here we define the processing parameters we're going to use later 43 | // note that they're volatile, its to do with threading, don't worry about it for now! 44 | volatile float pitchShiftAmount = 2.0; // octave up 45 | volatile float bufferSize = 3.0; // value in range [1,7] so 3 is pretty standard 46 | volatile float outputLatency = 0.f; // this value will only be written to, not read 47 | 48 | jack_port_t* inputPort = 0; 49 | jack_port_t* outputPort = 0; 50 | 51 | 52 | int process(jack_nframes_t nframes, void* ) 53 | { 54 | float* inputBuffer = (float*)jack_port_get_buffer ( inputPort , nframes); 55 | float* outputBuffer= (float*)jack_port_get_buffer ( outputPort, nframes); 56 | 57 | // connect the JACK audio buffers to the LADSPA instance 58 | descriptor->connect_port( instanceHandle , 2, inputBuffer ); // input 59 | descriptor->connect_port( instanceHandle , 3, outputBuffer ); // output 60 | 61 | // here we call run(): its going to perform the needed processing, reading 62 | // the input *directly* from JACK, and writing the output *directly* to jack 63 | descriptor->run( instanceHandle , nframes); 64 | 65 | return 0; 66 | } 67 | 68 | int main() 69 | { 70 | std::cout << "LADSPA Host tutorial" << std::endl; 71 | 72 | // create jack client 73 | jack_client_t* client = jack_client_open ("LadspaHost", 74 | JackNullOption, 75 | 0, 76 | 0 ); 77 | 78 | // get some info about JACK 79 | int samplerate = jack_get_sample_rate( client ); 80 | 81 | 82 | // LADSPA stuff starts here! 83 | // ensure you have the file there! Try the following command: 84 | // $ ls /usr/lib/ladspa/ | grep "am_pitchshift" 85 | glibModule = new Glib::Module( "/usr/lib/ladspa/am_pitchshift_1433.so" ); 86 | 87 | // check that the module is valid 88 | if ( !glibModule ) 89 | { 90 | std::cout << "Error loading module, Quitting NOW!" << std::endl; 91 | return -1; 92 | } 93 | 94 | std::cout << "Module loading OK, now getting descriptor function" << std::endl; 95 | 96 | LADSPA_Descriptor_Function descriptorFunction; 97 | void* tmpFunc; 98 | bool found = glibModule->get_symbol("ladspa_descriptor", tmpFunc ); 99 | 100 | if ( !found ) 101 | { 102 | std::cout << "ERROR: Could not find LADSPA_Descriptor_Function, probably due to non LADSPA .so file. Quitting now!" << std::endl; 103 | return -1; 104 | } 105 | 106 | // we have a valid LADSPA object loaded 107 | std::cout << "Casting Descriptor_Function now!" << std::endl; 108 | descriptorFunction = (LADSPA_Descriptor_Function) tmpFunc; 109 | 110 | // finally get the descriptor from the descriptorFunction 111 | // the "0" here means we want to load the first plugin from the .so file 112 | // The AM PitchShifter has only one LADSPA effect in it, but calf.so has many! 113 | descriptor = const_cast( descriptorFunction( 0 ) ); 114 | 115 | // here we create the actual plugin instance, using the descriptor and 116 | // storing the actual instance in the "instanceHandle" variable 117 | instanceHandle = descriptor->instantiate( descriptor ,samplerate ); 118 | 119 | 120 | // so far handling each LADSPA effect was the same: 121 | // -load the library 122 | // -extract the descriptor_function 123 | // -get & cast the descriptor itself 124 | // -create an instance, using the descriptor 125 | // 126 | // From now on, things get specific to each LADSPA effect, based on what 127 | // ports it has, and how many. To get information about a plugin, use the 128 | // "analyseplugin" tool: 129 | // $ analyseplugin "/usr/lib/ladspa/am_pitchshift_1433.so" 130 | // 131 | // We are shown a printout of the effects ports & details, we are now 132 | // going to write the code to interact with those ports 133 | 134 | // this function connects a ports of the LADSPA instance to a certain 135 | // memory address of your program. It allows you to provide input & output 136 | // of control & audio data to / from the plugin! 137 | descriptor->connect_port( instanceHandle , 0, (float*)&pitchShiftAmount ); 138 | descriptor->connect_port( instanceHandle , 1, (float*)&bufferSize ); 139 | // ports 2 and 3 are audio input & output, we'll connect them in the 140 | // process() function that JACK calls 141 | descriptor->connect_port( instanceHandle , 4, (float*)&outputLatency ); 142 | 143 | // LADSPA stuff finishes here! 144 | 145 | // set up JACK & start processing 146 | jack_set_process_callback (client, process , 0); 147 | 148 | inputPort = jack_port_register ( client, 149 | "input", 150 | JACK_DEFAULT_AUDIO_TYPE, 151 | JackPortIsInput, 152 | 0 ); 153 | 154 | outputPort = jack_port_register ( client, 155 | "output", 156 | JACK_DEFAULT_AUDIO_TYPE, 157 | JackPortIsOutput, 158 | 0 ); 159 | 160 | jack_activate(client); 161 | 162 | sleep(90); 163 | 164 | jack_deactivate(client); 165 | 166 | jack_client_close(client); 167 | 168 | return 0; 169 | } 170 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/Makefile: -------------------------------------------------------------------------------- 1 | 2 | NAME = sila 3 | VER = 0.2 4 | PREFIX = /usr 5 | BIN_DIR = $(PREFIX)/bin 6 | SHARE_DIR = $(PREFIX)/share 7 | DESKAPPS_DIR = $(SHARE_DIR)/applications 8 | DEBNAME = $(NAME)_$(VER) 9 | CREATEDEB = yes '' | dh_make -s -n -e $(USER)@org -p $(DEBNAME) -c gpl >/dev/null 10 | DIRS = $(BIN_DIR) $(DESKAPPS_DIR) 11 | BUILDDEB = dpkg-buildpackage -rfakeroot -b 2>/dev/null | grep dpkg-deb 12 | 13 | CXXFLAGS = -Wall -ffast-math `pkg-config --cflags gtkmm-2.4` 14 | LDFLAGS = `pkg-config --libs gtkmm-2.4 jack` -ldl 15 | 16 | OBJECTS = main.o sila.o sila_browser.o sila_ui.o 17 | 18 | .PHONY : all clean install uninstall tar deb 19 | 20 | all : $(NAME) 21 | 22 | clean : 23 | rm -f $(NAME) $(OBJECTS) 24 | 25 | install : all 26 | mkdir -p $(DESTDIR)$(BIN_DIR) 27 | mkdir -p $(DESTDIR)$(DESKAPPS_DIR) 28 | install $(NAME) $(DESTDIR)$(BIN_DIR) 29 | install $(NAME).desktop $(DESTDIR)$(DESKAPPS_DIR) 30 | 31 | uninstall : 32 | @rm -rf $(BIN_DIR)/$(NAME) $(DESKAPPS_DIR)/$(NAME).desktop 33 | 34 | tar : clean 35 | @cd ../ && \ 36 | tar -cf $(NAME)-$(VER).tar.bz2 --bzip2 $(NAME) 37 | @echo "build $(NAME)-$(VER).tar.bz2" 38 | 39 | deb : 40 | @rm -rf ./debian 41 | @echo "create ./debian" 42 | -@ $(CREATEDEB) 43 | @echo "try to build debian package, that may take some time" 44 | -@ if $(BUILDDEB); then echo ". . , done"; else \ 45 | echo "sorry, build fail"; fi 46 | @rm -rf ./debian 47 | 48 | $(OBJECTS) : sila.h 49 | 50 | $(NAME) : $(OBJECTS) 51 | $(CXX) $(OBJECTS) $(LDFLAGS) -o $(NAME) 52 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/README: -------------------------------------------------------------------------------- 1 | Sila 2 | 3 | Note that the "make" tool is used to build this program! 4 | Just type "make" in a terminal that's in the directory and it will build 5 | 6 | Thanks to Hermann from the Guitarix project for donating this code! 7 | http://sourceforge.net/projects/guitarix/files/web/sila-0.2.tar.bz2/download 8 | 9 | a simple LADSPA host witch load a single LADSPA plugin and create 10 | a interface for it. 11 | plugin could specified by command-line (lib-name UniqueID) or selected 12 | from a LADSPA browser, witch will inform you over the plug-in details. 13 | Additional plug-ins could selected from a menu (Mono , Stereo, Misc) 14 | 15 | Jack connections will be reused when you switch a plug-in. 16 | Available are play/pause/stop. 17 | 18 | sila is created for fast ladspa plug-in check/testing. 19 | 20 | sila use the gtkmm tool-kit. 21 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #include "sila.h" 5 | 6 | LadspaHost l_h; 7 | 8 | 9 | int main(int argc, char **argv) { 10 | 11 | Gtk::Main main(argc, argv); 12 | 13 | l_h.m_window = NULL; 14 | l_h.l_b = new LadspaBrowser(); 15 | l_h.sila_start(argc, argv); 16 | 17 | Gtk::Main::run(); 18 | 19 | delete l_h.l_b; 20 | delete l_h.m_window; 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/sila.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "sila.h" 4 | 5 | /*****************************************************************************/ 6 | 7 | int LadspaHost::compute_callback(jack_nframes_t nframes, void *arg) { 8 | LadspaHost& self = *static_cast(arg); 9 | 10 | if(!self.pause) { 11 | for (int i = 0; i < static_cast(self.l_p.descriptor->PortCount); i++) { 12 | if (LADSPA_IS_PORT_AUDIO(self.l_p.descriptor->PortDescriptors[i])) { 13 | self.l_p.descriptor->connect_port (self.l_p.lhandle, i, 14 | (float *)jack_port_get_buffer(self.ports[i], nframes)); 15 | } 16 | } 17 | 18 | self.l_p.descriptor->run(self.l_p.lhandle, nframes); 19 | } else { //pause 20 | float *inports[self.l_p.n_audio_in]; 21 | float *outports[self.l_p.n_audio_out]; 22 | int in = 0; 23 | int out = 0; 24 | 25 | for (int i = 0; i < static_cast(self.l_p.descriptor->PortCount); i++) { 26 | if (LADSPA_IS_PORT_AUDIO(self.l_p.descriptor->PortDescriptors[i]) && 27 | LADSPA_IS_PORT_INPUT(self.l_p.descriptor->PortDescriptors[i])) { 28 | inports[in] = (float *)jack_port_get_buffer(self.ports[i], nframes); 29 | in++; 30 | } else if (LADSPA_IS_PORT_AUDIO(self.l_p.descriptor->PortDescriptors[i]) && 31 | LADSPA_IS_PORT_OUTPUT(self.l_p.descriptor->PortDescriptors[i])) { 32 | outports[out] = (float *)jack_port_get_buffer(self.ports[i], nframes); 33 | out++; 34 | } 35 | } 36 | if (out>0&&in>0) { 37 | if (out <= in) { 38 | for(int i=0;i in) { 42 | int j = 0; 43 | for(int i=0;i=in)j=0; 47 | } 48 | } 49 | } else if (out>0) { 50 | for(int j=0;j(arg); 62 | self.set_ex(false); 63 | self.set_go_on(true); 64 | self.jack_cleanup(); 65 | delete self.m_window; 66 | self.m_window = NULL; 67 | self.sila_start(0,NULL); 68 | return false; 69 | } 70 | 71 | /*****************************************************************************/ 72 | 73 | int LadspaHost::buffer_size_callback(jack_nframes_t nframes, void *arg) { 74 | LadspaHost& self = *static_cast(arg); 75 | int _bz = jack_get_buffer_size(self.jack_client); 76 | if (_bz != self.BZ) { 77 | self.set_pause(true); 78 | g_idle_add(buffer_changed,(void*)arg); 79 | } 80 | return 0; 81 | } 82 | 83 | /*****************************************************************************/ 84 | 85 | void LadspaHost::shut_down_callback(void *arg) { 86 | LadspaHost& self = *static_cast(arg); 87 | delete [] self.l_p.cpv; 88 | delete [] self.ports; 89 | fprintf(stderr,"Exit: JACK shut us down\n"); 90 | if(!Gtk::Main::instance()->level()) { 91 | delete self.l_b; 92 | delete self.m_window; 93 | exit(1); 94 | } else { 95 | Gtk::Main::quit(); 96 | } 97 | } 98 | 99 | /*****************************************************************************/ 100 | 101 | void LadspaHost::deactivate_jack() { 102 | get_connections(); 103 | jack_deactivate(jack_client); 104 | } 105 | 106 | /*****************************************************************************/ 107 | 108 | void LadspaHost::activate_jack() { 109 | if (jack_activate(jack_client)) { 110 | delete [] l_p.cpv; 111 | delete [] ports; 112 | fprintf(stderr,"Exit: could not activate JACK processing\n"); 113 | if(!Gtk::Main::instance()->level()) { 114 | delete l_b; 115 | delete m_window; 116 | exit(1); 117 | } else { 118 | Gtk::Main::quit(); 119 | } 120 | } else { 121 | std::string portname; 122 | int a = 1; 123 | int b = 1; 124 | int c = 0; 125 | int d = 0; 126 | for (int i = 0; i < static_cast(l_p.descriptor->PortCount); i++) { 127 | if (LADSPA_IS_PORT_CONTROL(l_p.descriptor-> 128 | PortDescriptors[i]) && 129 | LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 130 | continue; 131 | } 132 | if (LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 133 | portname = "SHINPUT"; 134 | c++; 135 | portname += to_string(a); 136 | portname += "_"; 137 | portname += to_string(c); 138 | while(getenv(portname.c_str()) != NULL){ 139 | jack_connect(jack_client, getenv(portname.c_str()), jack_port_name(ports[i])); 140 | portname = "SHINPUT"; 141 | c++; 142 | portname += to_string(a); 143 | portname += "_"; 144 | portname += to_string(c); 145 | } 146 | c--; 147 | a++; 148 | } else if (LADSPA_IS_PORT_OUTPUT(l_p.descriptor->PortDescriptors[i])){ 149 | portname = "SHOUTPUT"; 150 | d++; 151 | portname += to_string(b); 152 | portname += "_"; 153 | portname += to_string(d); 154 | while (getenv(portname.c_str()) != NULL) { 155 | jack_connect(jack_client, jack_port_name(ports[i]), getenv(portname.c_str())); 156 | portname = "SHOUTPUT"; 157 | d++; 158 | portname += to_string(b); 159 | portname += "_"; 160 | portname += to_string(d); 161 | } 162 | d--; 163 | b++; 164 | } 165 | } 166 | } 167 | } 168 | 169 | /*****************************************************************************/ 170 | 171 | bool LadspaHost::init_jack(Glib::ustring *message) { 172 | client_name = "sila_"; 173 | jack_status_t jack_status; 174 | int i; 175 | unsigned long flags; 176 | l_p.n_audio_in = 0; 177 | l_p.n_audio_out = 0; 178 | l_p.cnp = 0; 179 | l_p.cpv = NULL; 180 | ports = NULL; 181 | 182 | client_name += l_p.descriptor->Label; 183 | jack_client = jack_client_open(client_name.c_str(), 184 | JackNullOption, &jack_status); 185 | if (jack_status) { 186 | *message = "Error: could not connect to JACK"; 187 | return false; 188 | } 189 | 190 | try { 191 | ports = new jack_port_t* [l_p.descriptor->PortCount](); 192 | l_p.cpv = new float [l_p.descriptor->PortCount * sizeof(float)]; 193 | } catch(...) { 194 | *message = "Error: could not allocate memory"; 195 | return false; 196 | } 197 | 198 | for (i = 0; i < static_cast(l_p.descriptor->PortCount); i++) { 199 | if (LADSPA_IS_PORT_CONTROL(l_p.descriptor-> 200 | PortDescriptors[i]) && 201 | LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 202 | l_p.cnp++; 203 | continue; 204 | } 205 | 206 | if (LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i]) && 207 | LADSPA_IS_PORT_AUDIO(l_p.descriptor->PortDescriptors[i])) { 208 | flags = JackPortIsInput; 209 | l_p.n_audio_in++; 210 | 211 | } else if (LADSPA_IS_PORT_OUTPUT(l_p.descriptor->PortDescriptors[i]) && 212 | LADSPA_IS_PORT_AUDIO(l_p.descriptor->PortDescriptors[i])){ 213 | flags = JackPortIsOutput; 214 | l_p.n_audio_out++; 215 | } 216 | 217 | ports[i] = 218 | jack_port_register(jack_client, 219 | l_p.descriptor->PortNames[i], 220 | JACK_DEFAULT_AUDIO_TYPE, 221 | flags, 0); 222 | 223 | if (!ports[i]) { 224 | delete [] l_p.cpv; 225 | delete [] ports; 226 | *message = "Error: could not register JACK ports"; 227 | return false; 228 | } 229 | } 230 | 231 | if (!l_p.n_audio_in) { 232 | fprintf(stderr,"Warning: plugin have no input JACK port \n"); 233 | } 234 | if (!l_p.n_audio_out) { 235 | fprintf(stderr,"Warning: plugin have no output JACK port \n"); 236 | } 237 | 238 | jack_set_process_callback(jack_client, compute_callback, this); 239 | jack_set_buffer_size_callback(jack_client, buffer_size_callback, this); 240 | jack_on_shutdown(jack_client, shut_down_callback, this); 241 | 242 | SR = jack_get_sample_rate(jack_client); 243 | BZ = jack_get_buffer_size(jack_client); 244 | 245 | l_p.lhandle = l_p.descriptor->instantiate(l_p.descriptor, SR); 246 | if (!l_p.lhandle) { 247 | *message = "Error: could not instantiate the plugin."; 248 | jack_cleanup(); 249 | set_go_on(true); 250 | return false; 251 | } 252 | 253 | for (i = 0; i < static_cast(l_p.descriptor->PortCount); i++) { 254 | if (LADSPA_IS_PORT_CONTROL(l_p.descriptor-> PortDescriptors[i]) && 255 | LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 256 | l_p.cpv[i] = 0.0; 257 | l_p.descriptor->connect_port(l_p.lhandle, i,&l_p.cpv[i]); 258 | } 259 | if (LADSPA_IS_PORT_CONTROL(l_p.descriptor-> PortDescriptors[i]) && 260 | LADSPA_IS_PORT_OUTPUT(l_p.descriptor->PortDescriptors[i])) { 261 | l_p.cpv[i] = 0.0; 262 | l_p.descriptor->connect_port(l_p.lhandle, i,&l_p.cpv[i] ); 263 | } 264 | } 265 | 266 | if (l_p.descriptor->activate) { 267 | l_p.descriptor->activate(l_p.lhandle); 268 | } 269 | 270 | return true; 271 | } 272 | 273 | /*****************************************************************************/ 274 | 275 | void LadspaHost::get_connections() { 276 | const char** port = NULL; 277 | std::string portname; 278 | int a = 0; 279 | int b = 0; 280 | int c = 0; 281 | int d = 0; 282 | for (int i = 0; i < static_cast(l_p.descriptor->PortCount); i++) { 283 | if (LADSPA_IS_PORT_CONTROL(l_p.descriptor-> PortDescriptors[i]) && 284 | LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 285 | continue; 286 | } 287 | if (LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 288 | a++; 289 | if (jack_port_connected(ports[i])) { 290 | port = jack_port_get_connections(ports[i]); 291 | for ( int j = 0; port[j] != NULL; j++) { 292 | portname = "SHINPUT"; 293 | c++; 294 | portname += to_string(a); 295 | portname += "_"; 296 | portname += to_string(c); 297 | setenv(portname.c_str(),port[j],1); 298 | } 299 | } 300 | } else if (LADSPA_IS_PORT_OUTPUT(l_p.descriptor->PortDescriptors[i])){ 301 | b++; 302 | if (jack_port_connected(ports[i])) { 303 | port = jack_port_get_connections(ports[i]); 304 | for ( int j = 0; port[j] != NULL; j++) { 305 | portname = "SHOUTPUT"; 306 | d++; 307 | portname += to_string(b); 308 | portname += "_"; 309 | portname += to_string(d); 310 | setenv(portname.c_str(),port[j],1); 311 | } 312 | } 313 | } 314 | } 315 | free(port); 316 | } 317 | 318 | /*****************************************************************************/ 319 | 320 | void LadspaHost::jack_cleanup() { 321 | if (jack_client) { 322 | if(!ex) { 323 | get_connections(); 324 | } 325 | 326 | jack_deactivate(jack_client); 327 | 328 | if (l_p.descriptor->deactivate) { 329 | l_p.descriptor->deactivate(l_p.lhandle); 330 | } 331 | if (l_p.descriptor->cleanup) { 332 | l_p.descriptor->cleanup(l_p.lhandle); 333 | } 334 | 335 | for (int i = 0; i < static_cast(l_p.descriptor->PortCount); i++) { 336 | if (LADSPA_IS_PORT_CONTROL(l_p.descriptor-> PortDescriptors[i]) && 337 | LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 338 | continue; 339 | } 340 | if (LADSPA_IS_PORT_INPUT(l_p.descriptor->PortDescriptors[i])) { 341 | jack_port_unregister(jack_client, ports[i]); 342 | } else if (LADSPA_IS_PORT_OUTPUT(l_p.descriptor->PortDescriptors[i])){ 343 | jack_port_unregister(jack_client, ports[i]); 344 | } 345 | } 346 | 347 | if (l_p.PluginHandle) dlclose(l_p.PluginHandle); 348 | jack_client_close(jack_client); 349 | jack_client = NULL; 350 | delete [] l_p.cpv; 351 | delete [] ports; 352 | } 353 | if (ex) { 354 | Gtk::Main::quit(); 355 | } 356 | } 357 | 358 | /*****************************************************************************/ 359 | 360 | bool LadspaHost::load_ladspa_plugin(char *PluginFilename, 361 | int id, Glib::ustring *message) { 362 | 363 | LADSPA_Descriptor_Function phDescriptorFunction; 364 | const LADSPA_Descriptor * phDescriptor; 365 | char * pch; 366 | std::string path; 367 | 368 | pch=strchr(PluginFilename,'/'); 369 | if(pch == NULL) { 370 | char * p = getenv ("LADSPA_PATH"); 371 | if (p != NULL) { 372 | path = getenv ("LADSPA_PATH"); 373 | } else { 374 | setenv("LADSPA_PATH","/usr/lib/ladspa",0); 375 | setenv("LADSPA_PATH","/usr/local/lib/ladspa",0); 376 | path = getenv("LADSPA_PATH"); 377 | } 378 | path += "/"; 379 | path += PluginFilename; 380 | PluginFilename = const_cast(path.c_str()); 381 | } 382 | 383 | l_p.PluginHandle = dlopen(PluginFilename, RTLD_NOW | RTLD_LOCAL); 384 | if (!l_p.PluginHandle) { 385 | *message = dlerror(); 386 | jack_cleanup(); 387 | set_go_on(true); 388 | return false; 389 | } 390 | 391 | phDescriptorFunction 392 | = (LADSPA_Descriptor_Function)dlsym(l_p.PluginHandle, "ladspa_descriptor"); 393 | if (!phDescriptorFunction) { 394 | *message = dlerror(); 395 | jack_cleanup(); 396 | set_go_on(true); 397 | return false; 398 | } 399 | 400 | for (int i = 0;; i++) { 401 | phDescriptor = phDescriptorFunction(i); 402 | if (!phDescriptor) { 403 | *message = "Error: ID not found in the given Plugin"; 404 | jack_cleanup(); 405 | set_go_on(true); 406 | return false; 407 | } else if (static_cast(phDescriptor->UniqueID) == id) { 408 | l_p.descriptor = phDescriptor; 409 | break; 410 | } 411 | } 412 | return true; 413 | } 414 | 415 | /*****************************************************************************/ 416 | 417 | void LadspaHost::file_browser(char **plfile, int *plid) { 418 | *plid = 0; 419 | Glib::ustring pl; 420 | std::string path; 421 | l_b->show_browser(&pl,plid); 422 | char * p = getenv ("LADSPA_PATH"); 423 | if (p != NULL) { 424 | path = getenv ("LADSPA_PATH"); 425 | } else { 426 | setenv("LADSPA_PATH","/usr/lib/ladspa",0); 427 | setenv("LADSPA_PATH","/usr/local/lib/ladspa",0); 428 | path = getenv("LADSPA_PATH"); 429 | } 430 | path += "/"; 431 | path += pl.c_str(); 432 | *plfile = const_cast(path.c_str()); 433 | } 434 | 435 | /*****************************************************************************/ 436 | 437 | bool LadspaHost::cmd_parser(int argc, char **argv, 438 | char **plfile, int *plid, Glib::ustring *message) { 439 | 440 | if (argc >= 3) { 441 | *plfile = argv[1]; 442 | *plid = atoi(argv[2]); 443 | } else { 444 | file_browser(plfile, plid); 445 | } 446 | 447 | if(*plid == 0) { 448 | *message = "Error: no plugin filename and no plugin ID given"; 449 | return false; 450 | } 451 | return true; 452 | } 453 | 454 | /*****************************************************************************/ 455 | 456 | bool LadspaHost::on_delete_event(GdkEventAny*) { 457 | jack_cleanup(); 458 | return false; 459 | } 460 | 461 | /*****************************************************************************/ 462 | 463 | bool LadspaHost::sila_try(int argc, char **argv) { 464 | Glib::ustring message; 465 | char *plfile; 466 | int plid; 467 | 468 | jack_client = 0; 469 | 470 | if(!cmd_parser(argc, argv, &plfile, &plid, &message) || 471 | !load_ladspa_plugin(plfile, plid, &message) || 472 | !init_jack(&message)) { 473 | fprintf(stderr, "%s.\n", message.c_str()); 474 | return false; 475 | } 476 | return true; 477 | } 478 | 479 | /*****************************************************************************/ 480 | 481 | void LadspaHost::sila_start(int argc, char **argv) { 482 | 483 | if (sila_try(argc, argv)) { 484 | set_pause(false); 485 | m_window = new Gtk::Window; 486 | m_window->signal_delete_event().connect( 487 | sigc::mem_fun(*this,&LadspaHost::on_delete_event)); 488 | m_window->add(*create_widgets()); 489 | m_window->resize(500, m_window->get_height()); 490 | m_window->set_title(client_name.c_str()); 491 | m_window->set_position(Gtk::WIN_POS_CENTER); 492 | m_window->show_all_children(); 493 | activate_jack(); 494 | m_window->show(); 495 | set_ex(true); 496 | } else if (go_on) { 497 | set_go_on(false); 498 | sila_start(0,NULL); 499 | } else { 500 | if(!Gtk::Main::instance()->level()) { 501 | delete l_b; 502 | delete l_h.m_window; 503 | exit(1); 504 | } else { 505 | Gtk::Main::quit(); 506 | } 507 | } 508 | } 509 | 510 | /*****************************************************************************/ 511 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/sila.desktop: -------------------------------------------------------------------------------- 1 | 2 | [Desktop Entry] 3 | Name=sila 4 | GenericName=LADSPA_HOST 5 | Comment= LADSPA Host for a single plugin to jack 6 | Exec=sila 7 | Icon= 8 | Categories=AudioVideo;X-Jack; 9 | Terminal=false 10 | Type=Application 11 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/sila.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #ifndef _SILA_H 5 | #define _SILA_H 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /*****************************************************************************/ 24 | 25 | template 26 | inline std::string to_string(const T& t) { 27 | std::stringstream ss; 28 | ss << t; 29 | return ss.str(); 30 | } 31 | 32 | /*****************************************************************************/ 33 | 34 | class LadspaBrowser : public Gtk::Window { 35 | private: 36 | 37 | class LadspaHandle { 38 | public: 39 | Glib::ustring libname; 40 | Glib::ustring labelname; 41 | Glib::ustring UnicID; 42 | }; 43 | 44 | class ModelColumns : public Gtk::TreeModel::ColumnRecord { 45 | public: 46 | ModelColumns() { add(name); 47 | add(label);} 48 | Gtk::TreeModelColumn name; 49 | Gtk::TreeModelColumn label; 50 | }; 51 | 52 | ModelColumns columns; 53 | LadspaHandle plhandle; 54 | Gtk::Dialog window; 55 | Gtk::VBox topBox; 56 | Gtk::Button b1; 57 | Gtk::Image stck1; 58 | Gtk::Image stck2; 59 | Gtk::ScrolledWindow scrollWindow; 60 | Gtk::ScrolledWindow detailWindow; 61 | Gtk::TextView m_TextView; 62 | Gtk::VPaned m_pane; 63 | Glib::RefPtr m_refTextBuffer; 64 | Gtk::TreeView treeview; 65 | Glib::RefPtr model; 66 | void on_pressed1_event(); 67 | void on_response(int); 68 | void on_cursor_changed(); 69 | void create_list(); 70 | void fill_buffers(Glib::ustring); 71 | Glib::ustring analysePlugin(const char * pcPluginFilename, 72 | const char * pcPluginLabel, const int bVerbose); 73 | void unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary); 74 | void * loadLADSPAPluginLibrary(const char * pcPluginFilename); 75 | void * dlopenLADSPA(const char * pcFilename, int iFlag); 76 | void on_treeview_row_activated(const Gtk::TreeModel::Path& path, 77 | Gtk::TreeViewColumn*); 78 | 79 | public: 80 | void show_browser(Glib::ustring *plfile, int *plid); 81 | explicit LadspaBrowser(); 82 | ~LadspaBrowser(); 83 | }; 84 | 85 | /*****************************************************************************/ 86 | 87 | class LadspaHost { 88 | private: 89 | 90 | class LadspaPlug { 91 | public: 92 | int n_audio_in; 93 | int n_audio_out; 94 | int cnp; 95 | float *cpv; 96 | const LADSPA_Descriptor *descriptor; 97 | LADSPA_Handle lhandle; 98 | void *PluginHandle; 99 | }; 100 | 101 | bool go_on; 102 | bool pause; 103 | bool ex; 104 | bool cmd_parser( int argc, char **argv, char **plfile, 105 | int *plid, Glib::ustring *error); 106 | bool init_jack(Glib::ustring *error); 107 | bool load_ladspa_plugin(char *PluginFilename, int id, Glib::ustring *error); 108 | bool sila_try(int argc, char **argv); 109 | bool on_delete_event(GdkEventAny*); 110 | int SR; 111 | int BZ; 112 | jack_client_t *jack_client; 113 | Gtk::Window *create_widgets(); 114 | void file_browser(char **plfile, int *plid); 115 | std::string client_name; 116 | jack_port_t **ports; 117 | void get_connections(); 118 | static int compute_callback(jack_nframes_t nframes, void *arg); 119 | static int buffer_size_callback(jack_nframes_t nframes, void *arg); 120 | static gboolean buffer_changed(void* arg); 121 | static void shut_down_callback(void *arg); 122 | 123 | public: 124 | std::list plug_mono_list; 125 | std::list plug_stereo_list; 126 | std::list plug_misc_list; 127 | LadspaPlug l_p; 128 | LadspaBrowser *l_b; 129 | Gtk::Window *m_window; 130 | void set_ex(bool e_) {ex = e_;} 131 | void set_pause(bool pa_) {pause = pa_;} 132 | bool get_pause() {return pause;} 133 | int get_samplerate() {return SR;} 134 | void set_go_on(bool go_) {go_on = go_;} 135 | void sila_start(int argc, char **argv); 136 | void jack_cleanup(); 137 | void activate_jack(); 138 | void deactivate_jack(); 139 | }; 140 | extern LadspaHost l_h; 141 | 142 | /*****************************************************************************/ 143 | 144 | #endif // _SILA_H 145 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/sila_browser.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "sila.h" 4 | 5 | /*****************************************************************************/ 6 | 7 | 8 | /* this routine will search the LADSPA_PATH for the file. */ 9 | void *LadspaBrowser::dlopenLADSPA(const char * pcFilename, int iFlag) { 10 | 11 | char * pcBuffer = NULL; 12 | const char * pcEnd; 13 | const char * pcLADSPAPath; 14 | const char * pcStart; 15 | int iNeedSlash; 16 | size_t iFilenameLength; 17 | void * pvResult; 18 | iFilenameLength = strlen(pcFilename); 19 | pvResult = NULL; 20 | 21 | pcLADSPAPath = getenv("LADSPA_PATH"); 22 | 23 | if (pcLADSPAPath == NULL) { 24 | setenv("LADSPA_PATH","/usr/lib/ladspa",0); 25 | setenv("LADSPA_PATH","/usr/local/lib/ladspa",0); 26 | pcLADSPAPath = getenv("LADSPA_PATH"); 27 | } 28 | pcStart = pcLADSPAPath; 29 | while (*pcStart != '\0') { 30 | pcEnd = pcStart; 31 | while (*pcEnd != ':' && *pcEnd != '\0') 32 | pcEnd++; 33 | pcBuffer = (char*)malloc(iFilenameLength + 2 + (pcEnd - pcStart)); 34 | if (pcEnd > pcStart) 35 | strncpy(pcBuffer, pcStart, pcEnd - pcStart); 36 | iNeedSlash = 0; 37 | if (pcEnd > pcStart) 38 | if (*(pcEnd - 1) != '/') { 39 | iNeedSlash = 1; 40 | pcBuffer[pcEnd - pcStart] = '/'; 41 | } 42 | strcpy(pcBuffer + iNeedSlash + (pcEnd - pcStart), pcFilename); 43 | pvResult = dlopen(pcBuffer, iFlag); 44 | free(pcBuffer); 45 | return pvResult; 46 | 47 | } 48 | return pvResult; 49 | } 50 | 51 | /*****************************************************************************/ 52 | 53 | void *LadspaBrowser::loadLADSPAPluginLibrary(const char * pcPluginFilename) { 54 | 55 | void * pvPluginHandle; 56 | 57 | pvPluginHandle = dlopenLADSPA(pcPluginFilename, RTLD_NOW); 58 | if (!pvPluginHandle) { 59 | fprintf(stderr, 60 | "Failed to load plugin \"%s\": %s\n", 61 | pcPluginFilename, 62 | dlerror()); 63 | //exit(1); 64 | } 65 | return pvPluginHandle; 66 | } 67 | 68 | /*****************************************************************************/ 69 | 70 | void LadspaBrowser::unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary) { 71 | dlclose(pvLADSPAPluginLibrary); 72 | } 73 | 74 | /*****************************************************************************/ 75 | 76 | Glib::ustring LadspaBrowser::analysePlugin(const char * pcPluginFilename, 77 | const char * pcPluginLabel, const int bVerbose) { 78 | 79 | LADSPA_Descriptor_Function pfDescriptorFunction; 80 | const LADSPA_Descriptor * psDescriptor; 81 | unsigned long lPluginIndex; 82 | unsigned long lPortIndex; 83 | void * pvPluginHandle; 84 | LADSPA_PortRangeHintDescriptor iHintDescriptor; 85 | LADSPA_Data fBound; 86 | LADSPA_Data fDefault; 87 | Glib::ustring details; 88 | std::string collect_details; 89 | std::string collect_first; 90 | int inputports = 0; 91 | int outputports = 0; 92 | 93 | pvPluginHandle = loadLADSPAPluginLibrary(pcPluginFilename); 94 | 95 | dlerror(); 96 | pfDescriptorFunction 97 | = (LADSPA_Descriptor_Function)dlsym(pvPluginHandle, "ladspa_descriptor"); 98 | if (!pfDescriptorFunction) { 99 | const char * pcError = dlerror(); 100 | if (pcError) 101 | fprintf(stderr, 102 | "Unable to find ladspa_descriptor() function in plugin file " 103 | "\"%s\": %s.\n" 104 | "Are you sure this is a LADSPA plugin file?\n", 105 | pcPluginFilename, 106 | pcError); 107 | return pcError; 108 | } 109 | 110 | for (lPluginIndex = 0;; lPluginIndex++) { 111 | inputports = 0; 112 | outputports = 0; 113 | psDescriptor = pfDescriptorFunction(lPluginIndex); 114 | if (!psDescriptor) 115 | break; 116 | if (pcPluginLabel != NULL) 117 | if (strcmp(pcPluginLabel, psDescriptor->Label) != 0) 118 | continue; 119 | 120 | if ( bVerbose == -1) { 121 | collect_details += psDescriptor->Label; 122 | collect_details += "\n"; 123 | } else if (!bVerbose) { 124 | 125 | if (psDescriptor->PortCount == 0) 126 | collect_details +="\tERROR: PLUGIN HAS NO PORTS.\n"; 127 | 128 | collect_details +="\n"; 129 | 130 | for (lPortIndex = 0; 131 | lPortIndex < psDescriptor->PortCount; 132 | lPortIndex++) { 133 | if (LADSPA_IS_PORT_AUDIO(psDescriptor->PortDescriptors[lPortIndex]) && 134 | LADSPA_IS_PORT_INPUT(psDescriptor->PortDescriptors[lPortIndex])) { 135 | inputports ++; 136 | } else if (LADSPA_IS_PORT_AUDIO(psDescriptor->PortDescriptors[lPortIndex]) && 137 | LADSPA_IS_PORT_OUTPUT(psDescriptor->PortDescriptors[lPortIndex])) { 138 | outputports ++; 139 | } 140 | 141 | } 142 | if(inputports == 1 && outputports == 1) { 143 | collect_details += "\n Type : MONO"; 144 | } else if(inputports == 2 && outputports == 2) { 145 | collect_details += "\n Type : STEREO"; 146 | } else { 147 | collect_details += "\n Type : MISC"; 148 | } 149 | 150 | collect_details +="\n Plugin Label: "; 151 | collect_details += psDescriptor->Label; 152 | collect_details +="\n Plugin Unique "; 153 | collect_details +=to_string(psDescriptor->UniqueID); 154 | collect_details +="\n Plugin Name: "; 155 | collect_details +=psDescriptor->Name; 156 | 157 | 158 | 159 | } else { 160 | collect_details +="\n Plugin Name: "; 161 | collect_details +=psDescriptor->Name; 162 | collect_details +="\n Plugin Label: "; 163 | collect_details += psDescriptor->Label; 164 | collect_details += "\n Plugin Unique "; 165 | collect_details +=to_string(psDescriptor->UniqueID); 166 | collect_details +="\n Maker: "; 167 | collect_details +=psDescriptor->Maker; 168 | collect_details +="\n"; 169 | collect_details +=" RT: "; 170 | collect_details +=to_string(psDescriptor->Properties); 171 | collect_details +="\n"; 172 | collect_details +=" Ports: "; 173 | 174 | 175 | if (psDescriptor->PortCount == 0) 176 | collect_details +="\tERROR: PLUGIN HAS NO PORTS.\n"; 177 | else collect_details += to_string(psDescriptor->PortCount); 178 | collect_details +="\n"; 179 | 180 | for (lPortIndex = 0; 181 | lPortIndex < psDescriptor->PortCount; 182 | lPortIndex++) { 183 | collect_details +=" index "; 184 | collect_details +=to_string(lPortIndex); 185 | collect_details += ": "; 186 | collect_details += " "; 187 | collect_details += psDescriptor->PortNames[lPortIndex]; 188 | 189 | 190 | if (LADSPA_IS_PORT_CONTROL 191 | (psDescriptor->PortDescriptors[lPortIndex]) 192 | && LADSPA_IS_PORT_AUDIO 193 | (psDescriptor->PortDescriptors[lPortIndex])){ 194 | collect_details +=", ERROR: CONTROL AND AUDIO"; 195 | } else if (LADSPA_IS_PORT_CONTROL 196 | (psDescriptor->PortDescriptors[lPortIndex])){ 197 | collect_details +=" -> Type control"; 198 | 199 | } else if (LADSPA_IS_PORT_AUDIO 200 | (psDescriptor->PortDescriptors[lPortIndex])){ 201 | collect_details +=" -> Type audio"; 202 | 203 | } else { 204 | collect_details +=", ERROR: NEITHER CONTROL NOR AUDIO"; 205 | } 206 | 207 | if (LADSPA_IS_PORT_INPUT 208 | (psDescriptor->PortDescriptors[lPortIndex]) 209 | && LADSPA_IS_PORT_OUTPUT 210 | (psDescriptor->PortDescriptors[lPortIndex])) 211 | collect_details +="ERROR: INPUT AND OUTPUT"; 212 | else if (LADSPA_IS_PORT_INPUT 213 | (psDescriptor->PortDescriptors[lPortIndex])) 214 | collect_details +=":input "; 215 | else if (LADSPA_IS_PORT_OUTPUT 216 | (psDescriptor->PortDescriptors[lPortIndex])) 217 | collect_details +=":output "; 218 | else 219 | collect_details +="ERROR: NEITHER INPUT NOR OUTPUT"; 220 | 221 | if (LADSPA_IS_PORT_INPUT 222 | (psDescriptor->PortDescriptors[lPortIndex]) 223 | && LADSPA_IS_PORT_AUDIO 224 | (psDescriptor->PortDescriptors[lPortIndex])){ 225 | inputports++; 226 | } else if (LADSPA_IS_PORT_OUTPUT 227 | (psDescriptor->PortDescriptors[lPortIndex]) 228 | && LADSPA_IS_PORT_AUDIO 229 | (psDescriptor->PortDescriptors[lPortIndex])){ 230 | outputports++; 231 | } 232 | 233 | iHintDescriptor 234 | = psDescriptor->PortRangeHints[lPortIndex].HintDescriptor; 235 | 236 | if (LADSPA_IS_HINT_BOUNDED_BELOW(iHintDescriptor) 237 | || LADSPA_IS_HINT_BOUNDED_ABOVE(iHintDescriptor)) { 238 | if (LADSPA_IS_HINT_BOUNDED_BELOW(iHintDescriptor)) { 239 | fBound = psDescriptor->PortRangeHints[lPortIndex].LowerBound; 240 | if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fBound != 0) { 241 | collect_details += to_string(fBound); 242 | collect_details +="*srate"; 243 | } else { 244 | collect_details +=" Range: "; 245 | collect_details += to_string(fBound); 246 | } 247 | } else collect_details +="..."; 248 | collect_details +=" to "; 249 | if (LADSPA_IS_HINT_BOUNDED_ABOVE(iHintDescriptor)) { 250 | fBound = psDescriptor->PortRangeHints[lPortIndex].UpperBound; 251 | if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fBound != 0) { 252 | collect_details += to_string(fBound); 253 | collect_details +="*srate"; 254 | } else { 255 | collect_details += to_string(fBound); 256 | } 257 | } else collect_details +="..."; 258 | } 259 | 260 | if (LADSPA_IS_HINT_TOGGLED(iHintDescriptor)) { 261 | if ((iHintDescriptor 262 | | LADSPA_HINT_DEFAULT_0 263 | | LADSPA_HINT_DEFAULT_1) 264 | != (LADSPA_HINT_TOGGLED 265 | | LADSPA_HINT_DEFAULT_0 266 | | LADSPA_HINT_DEFAULT_1)) 267 | collect_details +=", ERROR: TOGGLED INCOMPATIBLE WITH OTHER HINT"; 268 | else collect_details +=", toggled"; 269 | } 270 | 271 | switch (iHintDescriptor & LADSPA_HINT_DEFAULT_MASK) { 272 | case LADSPA_HINT_DEFAULT_NONE: 273 | break; 274 | case LADSPA_HINT_DEFAULT_MINIMUM: 275 | fDefault = psDescriptor->PortRangeHints[lPortIndex].LowerBound; 276 | if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) { 277 | collect_details += ", default "; 278 | collect_details += to_string(fDefault); 279 | collect_details += "*srate"; 280 | } else { 281 | collect_details += ", default "; 282 | collect_details += to_string(fDefault); 283 | } 284 | break; 285 | case LADSPA_HINT_DEFAULT_LOW: 286 | if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { 287 | fDefault = exp(log(psDescriptor->PortRangeHints[lPortIndex].LowerBound) 288 | * 0.75 289 | + log(psDescriptor->PortRangeHints[lPortIndex].UpperBound) 290 | * 0.25); 291 | } else { 292 | fDefault = (psDescriptor->PortRangeHints[lPortIndex].LowerBound 293 | * 0.75 294 | + psDescriptor->PortRangeHints[lPortIndex].UpperBound 295 | * 0.25); 296 | } 297 | if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) { 298 | collect_details += ", default "; 299 | collect_details += to_string(fDefault); 300 | collect_details += "*srate"; 301 | } else { 302 | collect_details += ", default "; 303 | collect_details += to_string(fDefault); 304 | } 305 | break; 306 | case LADSPA_HINT_DEFAULT_MIDDLE: 307 | if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { 308 | fDefault = sqrt(psDescriptor->PortRangeHints[lPortIndex].LowerBound 309 | * psDescriptor->PortRangeHints[lPortIndex].UpperBound); 310 | } else { 311 | fDefault = 0.5 * (psDescriptor->PortRangeHints[lPortIndex].LowerBound 312 | + psDescriptor->PortRangeHints[lPortIndex].UpperBound); 313 | } 314 | if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) { 315 | collect_details += ", default "; 316 | collect_details += to_string(fDefault); 317 | collect_details += "*srate"; 318 | } else{ 319 | collect_details += ", default "; 320 | collect_details += to_string(fDefault); 321 | } 322 | break; 323 | case LADSPA_HINT_DEFAULT_HIGH: 324 | if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) { 325 | fDefault = exp(log(psDescriptor->PortRangeHints[lPortIndex].LowerBound) 326 | * 0.25 327 | + log(psDescriptor->PortRangeHints[lPortIndex].UpperBound) 328 | * 0.75); 329 | } else { 330 | fDefault = (psDescriptor->PortRangeHints[lPortIndex].LowerBound 331 | * 0.25 332 | + psDescriptor->PortRangeHints[lPortIndex].UpperBound 333 | * 0.75); 334 | } 335 | if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) { 336 | collect_details += ", default "; 337 | collect_details += to_string(fDefault); 338 | collect_details += "*srate"; 339 | }else { 340 | collect_details += ", default "; 341 | collect_details += to_string(fDefault); 342 | } 343 | break; 344 | case LADSPA_HINT_DEFAULT_MAXIMUM: 345 | fDefault = psDescriptor->PortRangeHints[lPortIndex].UpperBound; 346 | if (LADSPA_IS_HINT_SAMPLE_RATE(iHintDescriptor) && fDefault != 0) { 347 | collect_details += ", default "; 348 | collect_details += to_string(fDefault); 349 | collect_details += "*srate"; 350 | } else { 351 | collect_details += ", default "; 352 | collect_details += to_string(fDefault); 353 | } 354 | break; 355 | case LADSPA_HINT_DEFAULT_0: 356 | collect_details +=", default 0"; 357 | break; 358 | case LADSPA_HINT_DEFAULT_1: 359 | collect_details +=", default 1"; 360 | break; 361 | case LADSPA_HINT_DEFAULT_100: 362 | collect_details +=", default 100"; 363 | break; 364 | case LADSPA_HINT_DEFAULT_440: 365 | collect_details +=", default 440"; 366 | break; 367 | default: 368 | collect_details +=", UNKNOWN DEFAULT CODE"; 369 | /* (Not necessarily an error - may be a newer version.) */ 370 | break; 371 | } 372 | 373 | if (LADSPA_IS_HINT_LOGARITHMIC(iHintDescriptor)) 374 | collect_details +=" type logarithmic"; 375 | 376 | if (LADSPA_IS_HINT_INTEGER(iHintDescriptor)) 377 | collect_details +=" type integer"; 378 | collect_details +="\n"; 379 | } 380 | if(inputports == 1 && outputports == 1) { 381 | collect_first = " Type : MONO"; 382 | } else if(inputports == 2 && outputports == 2) { 383 | collect_first = " Type : STEREO"; 384 | } else if(inputports == 0 && outputports == 1) { 385 | collect_first = " Type : MONO SYNTH"; 386 | } else if(inputports == 0 && outputports == 2) { 387 | collect_first = " Type : STEREO SYNTH"; 388 | } else if(inputports < outputports && inputports) { 389 | collect_first = " Type : SPLITTER"; 390 | } else if(inputports > outputports && outputports) { 391 | collect_first = " Type : MERGER"; 392 | } else if(inputports == 0 && outputports == 0) { 393 | collect_first = " Type : NO AUDIO IN OR OUTPUT PORT FOUND"; 394 | } else if(inputports == outputports ) { 395 | collect_first = " Type : MULTI CHANNEL"; 396 | } else { 397 | collect_first = " Type : MISC"; 398 | } 399 | } 400 | } 401 | 402 | if (bVerbose) 403 | collect_details +="\n"; 404 | unloadLADSPAPluginLibrary(pvPluginHandle); 405 | collect_first += collect_details; 406 | details = collect_first.c_str(); 407 | return details; 408 | } 409 | 410 | /*****************************************************************************/ 411 | 412 | void make_menu(const LADSPA_Descriptor * psDescriptor, Glib::ustring str) { 413 | unsigned long lPortIndex; 414 | int inputports = 0; 415 | int outputports = 0; 416 | 417 | for (lPortIndex = 0; 418 | lPortIndex < psDescriptor->PortCount; 419 | lPortIndex++) { 420 | if (LADSPA_IS_PORT_AUDIO(psDescriptor->PortDescriptors[lPortIndex]) && 421 | LADSPA_IS_PORT_INPUT(psDescriptor->PortDescriptors[lPortIndex])) { 422 | inputports ++; 423 | } else if (LADSPA_IS_PORT_AUDIO(psDescriptor->PortDescriptors[lPortIndex]) && 424 | LADSPA_IS_PORT_OUTPUT(psDescriptor->PortDescriptors[lPortIndex])) { 425 | outputports ++; 426 | } 427 | } 428 | if(inputports == 1 && outputports == 1) { 429 | std::string mono_plug = "["; 430 | mono_plug += str.c_str(); 431 | mono_plug += "] "; 432 | mono_plug += psDescriptor->Label; 433 | mono_plug += " "; 434 | mono_plug += to_string(psDescriptor->UniqueID); 435 | l_h.plug_mono_list.push_back(mono_plug); 436 | } else if(inputports == 2 && outputports == 2) { 437 | std::string stereo_plug = "["; 438 | stereo_plug += str.c_str(); 439 | stereo_plug += "] "; 440 | stereo_plug += psDescriptor->Label; 441 | stereo_plug += " "; 442 | stereo_plug += to_string(psDescriptor->UniqueID); 443 | l_h.plug_stereo_list.push_back(stereo_plug); 444 | } else { 445 | std::string misc_plug = "["; 446 | misc_plug += str.c_str(); 447 | misc_plug += "] "; 448 | misc_plug += psDescriptor->Label; 449 | misc_plug += " "; 450 | misc_plug += to_string(psDescriptor->UniqueID); 451 | l_h.plug_misc_list.push_back(misc_plug); 452 | } 453 | } 454 | 455 | void LadspaBrowser::create_list() { 456 | Glib::ustring path; 457 | char * p = getenv ("LADSPA_PATH"); 458 | if (p != NULL) { 459 | path = getenv ("LADSPA_PATH"); 460 | } else { 461 | setenv("LADSPA_PATH","/usr/lib/ladspa",0); 462 | setenv("LADSPA_PATH","/usr/local/lib/ladspa",0); 463 | path = getenv("LADSPA_PATH"); 464 | } 465 | 466 | Glib::RefPtr file = Gio::File::create_for_path(path); 467 | if (file->query_exists()) { 468 | Glib::RefPtr child_enumeration = 469 | file->enumerate_children(G_FILE_ATTRIBUTE_STANDARD_NAME); 470 | std::vector file_names; 471 | Glib::RefPtr file_info; 472 | 473 | while ((file_info = child_enumeration->next_file()) != 0) { 474 | if (file_info->get_name().size() > 2) { // filefilter 475 | if (file_info->get_name().compare(file_info->get_name().size()-2, 2, "so") == 0) 476 | file_names.push_back(file_info->get_name()); 477 | } 478 | } 479 | // sort the vector 480 | std::sort(file_names.begin(), file_names.end()); 481 | // clear the TreeView 482 | model->clear(); 483 | // now populate the TreeView 484 | Gtk::TreeModel::Row row = *(model->append()); 485 | 486 | for (unsigned int i = 0; i < file_names.size(); i++) { 487 | row[columns.name] = file_names[i]; 488 | //fprintf(stderr, " %i %s \n", i,file_names[i].c_str()); 489 | if (i != file_names.size() || i == 0) { 490 | 491 | Glib::ustring str = file_names[i]; 492 | const char * plug = str.c_str(); 493 | //const char* dat ; 494 | void * pvPluginHandle; 495 | LADSPA_Descriptor_Function pfDescriptorFunction; 496 | const LADSPA_Descriptor * psDescriptor; 497 | unsigned long lPluginIndex; 498 | pvPluginHandle = loadLADSPAPluginLibrary(plug); 499 | dlerror(); 500 | pfDescriptorFunction 501 | = (LADSPA_Descriptor_Function)dlsym(pvPluginHandle, "ladspa_descriptor"); 502 | if (!pfDescriptorFunction) { 503 | const char * pcError = dlerror(); 504 | if (pcError) 505 | fprintf(stderr, 506 | "Unable to find ladspa_descriptor() function in plugin file " 507 | "\"%s\": %s.\n" 508 | "Are you sure this is a LADSPA plugin file?\n", 509 | plug, 510 | pcError); 511 | return; 512 | } 513 | for (lPluginIndex = 0;; lPluginIndex++) { 514 | psDescriptor = pfDescriptorFunction(lPluginIndex); 515 | if (!psDescriptor) 516 | break; 517 | //dat = psDescriptor->Label; 518 | Gtk::TreeModel::Row childrow = *(model->append(row.children())); 519 | childrow[columns.name] = to_string(psDescriptor->UniqueID); 520 | childrow[columns.label] = psDescriptor->Label; 521 | make_menu(psDescriptor, str); 522 | 523 | } 524 | // avoid appending a last empty row 525 | if (i != file_names.size()-1 || i == 0) 526 | row = *(model->append()); 527 | unloadLADSPAPluginLibrary(pvPluginHandle); 528 | } 529 | } 530 | } 531 | } 532 | 533 | void LadspaBrowser::on_pressed1_event() { 534 | static bool expand = true; 535 | if(expand) { 536 | treeview.expand_all(); 537 | b1.set_label("Collaps_e Tree"); 538 | b1.set_image (stck1); 539 | expand = false; 540 | } else { 541 | treeview.collapse_all(); 542 | b1.set_label(" _Expand Tree"); 543 | b1.set_image (stck2); 544 | expand = true; 545 | } 546 | } 547 | 548 | /*****************************************************************************/ 549 | 550 | LadspaBrowser::LadspaBrowser() 551 | : window("LADSPA", *this, true), 552 | topBox(false, 0), 553 | scrollWindow() { 554 | window.set_default_size(450,450); 555 | window.set_decorated(true); 556 | window.set_resizable(true); 557 | window.set_gravity(Gdk::GRAVITY_SOUTH); 558 | m_refTextBuffer = Gtk::TextBuffer::create(); 559 | model = Gtk::TreeStore::create(columns); 560 | treeview.set_headers_visible(false); 561 | //treeview.modify_text(treeview.get_state(),Gdk::Color("black")); 562 | //treeview.modify_base(treeview.get_state(),Gdk::Color("blue")); 563 | treeview.set_model(model); 564 | treeview.append_column("", columns.name); 565 | treeview.append_column("", columns.label); 566 | scrollWindow.add(treeview); 567 | scrollWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); 568 | topBox.pack_start(m_pane); 569 | m_pane.add1(scrollWindow); 570 | //topBox.pack_start(scrollWindow); 571 | m_TextView.set_border_window_size(Gtk::TEXT_WINDOW_TOP, 20); 572 | m_TextView.set_editable(false); 573 | //m_TextView.set_wrap_mode(Gtk::WRAP_WORD); 574 | detailWindow.add(m_TextView); 575 | detailWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); 576 | //topBox.pack_end(detailWindow); 577 | m_pane.add2(detailWindow); 578 | m_pane.set_position(250); 579 | window.get_vbox()->set_homogeneous(false); 580 | window.get_vbox()->pack_start(topBox,Gtk::PACK_EXPAND_WIDGET); 581 | //window.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK); 582 | window.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CANCEL); 583 | 584 | stck1.set(Gtk::Stock::JUSTIFY_FILL, Gtk::ICON_SIZE_BUTTON); 585 | stck2.set(Gtk::Stock::JUSTIFY_CENTER, Gtk::ICON_SIZE_BUTTON); 586 | 587 | b1.set_use_stock (true); 588 | b1.set_label (" _Expand Tree"); 589 | b1.set_image (stck2); 590 | 591 | window.get_action_area ()->pack_start(b1,Gtk::PACK_SHRINK); 592 | window.add_button(Gtk::Stock::EXECUTE, Gtk::RESPONSE_APPLY); 593 | b1.signal_activate().connect( 594 | sigc::mem_fun(*this, &LadspaBrowser::on_pressed1_event)); 595 | b1.signal_pressed().connect( 596 | sigc::mem_fun(*this, &LadspaBrowser::on_pressed1_event)); 597 | window.signal_response().connect( 598 | sigc::mem_fun(*this, &LadspaBrowser::on_response)); 599 | treeview.signal_row_activated().connect(sigc::mem_fun(*this, 600 | &LadspaBrowser::on_treeview_row_activated) ); 601 | treeview.signal_cursor_changed().connect(sigc::mem_fun(*this, 602 | &LadspaBrowser::on_cursor_changed) ); 603 | create_list(); 604 | fill_buffers("Select a Plugin"); 605 | } 606 | 607 | /*****************************************************************************/ 608 | 609 | void LadspaBrowser::fill_buffers(Glib::ustring details) 610 | { 611 | 612 | m_refTextBuffer->set_text(Glib::convert(details, "UTF-8", "ISO-8859-1")); 613 | m_TextView.set_buffer(m_refTextBuffer); 614 | } 615 | 616 | /*****************************************************************************/ 617 | 618 | void LadspaBrowser::on_treeview_row_activated(const Gtk::TreeModel::Path& path, 619 | Gtk::TreeViewColumn* /* column */) { 620 | // nothing to do 621 | } 622 | 623 | /*****************************************************************************/ 624 | 625 | void LadspaBrowser::on_cursor_changed() { 626 | Gtk::TreeModel::iterator iter = treeview.get_selection()->get_selected(); 627 | if(iter) { 628 | Gtk::TreeModel::Row row = *iter; 629 | Gtk::TreeModel::Row childrow = *(row.children().begin()); 630 | Glib::ustring str = row[columns.name]; 631 | Glib::ustring str1 = childrow[columns.name]; 632 | const char * plug = str1.c_str(); 633 | const char * lib = str.c_str(); 634 | Glib::ustring det; 635 | if(strcmp(plug, lib) !=0) { 636 | det = analysePlugin(lib,NULL,0); 637 | } else { 638 | str = row[columns.label]; 639 | if(strlen(str.c_str())) { 640 | plug = str.c_str(); 641 | Gtk::TreeModel::Row parent = *(row.parent()); 642 | str1 = parent[columns.name]; 643 | const char * lib = str1.c_str(); 644 | det = analysePlugin(lib,plug,1); 645 | } else { 646 | det = "NO LABEL OR UNIQUE-ID FOUND"; 647 | } 648 | } 649 | fill_buffers(det); 650 | } 651 | } 652 | 653 | /*****************************************************************************/ 654 | 655 | void LadspaBrowser::on_response(int res) { 656 | //fprintf(stderr, " %i response \n", res); 657 | if(res == -10) { 658 | Gtk::TreeModel::iterator iter = treeview.get_selection()->get_selected(); 659 | if(iter) { 660 | 661 | Gtk::TreeModel::Row row = *iter; 662 | Glib::ustring str = plhandle.libname = row[columns.name]; 663 | Gtk::TreeModel::Row childrow = *(row.children().begin()); 664 | Glib::ustring str1 = plhandle.UnicID = childrow[columns.name]; 665 | const char * lib = str.c_str(); 666 | const char * plug = str1.c_str(); 667 | const char * id = str1.c_str(); 668 | Glib::ustring det; 669 | if(strcmp(plug, lib) !=0) { 670 | 671 | str1 = plhandle.labelname = childrow[columns.label]; 672 | plug = str1.c_str(); 673 | std::cout << "load lib= " 674 | << lib << " label= " << plug << " ID= " << id << std::endl; 675 | } else { 676 | str = plhandle.labelname = row[columns.label]; 677 | if(strlen(str.c_str())) { 678 | plug = str.c_str(); 679 | Gtk::TreeModel::Row parent = *(row.parent()); 680 | str1 = plhandle.libname = parent[columns.name]; 681 | lib = str1.c_str(); 682 | std::cout << "load lib=" 683 | << lib << " label= " << plug << " ID= " << id << std::endl; 684 | } 685 | } 686 | } 687 | l_h.set_go_on(true); 688 | } else { 689 | plhandle.labelname = plhandle.libname = plhandle.UnicID = ""; 690 | l_h.set_go_on(false); 691 | } 692 | // model.clear(); 693 | } 694 | 695 | /*****************************************************************************/ 696 | 697 | void LadspaBrowser::show_browser(Glib::ustring *plfile, int *plid) { 698 | window.show_all(); 699 | window.run(); 700 | window.hide(); 701 | 702 | *plfile = plhandle.libname; 703 | *plid = atoi(to_string(plhandle.UnicID).c_str()); 704 | 705 | } 706 | 707 | /*****************************************************************************/ 708 | 709 | LadspaBrowser::~LadspaBrowser() { 710 | } 711 | 712 | /*****************************************************************************/ 713 | 714 | -------------------------------------------------------------------------------- /ladspaHostGuiBrowser/sila_ui.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "sila.h" 4 | 5 | /**********************************************************************/ 6 | 7 | static void set_default_value(LADSPA_PortRangeHintDescriptor hdescriptor, 8 | float lower_bound, float upper_bound, float *dest) { 9 | float default_value = 0.0; 10 | if (LADSPA_IS_HINT_HAS_DEFAULT(hdescriptor)) { 11 | switch (hdescriptor & LADSPA_HINT_DEFAULT_MASK) { 12 | case LADSPA_HINT_DEFAULT_MINIMUM: 13 | default_value = lower_bound; 14 | break; 15 | case LADSPA_HINT_DEFAULT_LOW: 16 | if (LADSPA_IS_HINT_LOGARITHMIC(hdescriptor)) { 17 | default_value = exp(log(lower_bound) * 0.75 + log(upper_bound) * 0.25); 18 | } else { 19 | default_value = lower_bound * 0.75 + upper_bound * 0.25; 20 | } 21 | break; 22 | case LADSPA_HINT_DEFAULT_MIDDLE: 23 | if (LADSPA_IS_HINT_LOGARITHMIC(hdescriptor)) { 24 | default_value = exp(log(lower_bound) * 0.5 + log(upper_bound) * 0.5); 25 | } else { 26 | default_value = lower_bound * 0.5 + upper_bound * 0.5; 27 | } 28 | break; 29 | case LADSPA_HINT_DEFAULT_HIGH: 30 | if (LADSPA_IS_HINT_LOGARITHMIC(hdescriptor)) { 31 | default_value = exp(log(lower_bound) * 0.25 + log(upper_bound) * 0.75); 32 | } else { 33 | default_value = lower_bound * 0.25 + upper_bound * 0.75; 34 | } 35 | break; 36 | case LADSPA_HINT_DEFAULT_MAXIMUM: 37 | default_value = upper_bound; 38 | break; 39 | case LADSPA_HINT_DEFAULT_0: 40 | default_value = 0.0; 41 | break; 42 | case LADSPA_HINT_DEFAULT_1: 43 | default_value = 1.0; 44 | break; 45 | case LADSPA_HINT_DEFAULT_100: 46 | default_value = 100.0; 47 | break; 48 | case LADSPA_HINT_DEFAULT_440: 49 | default_value = 440.0; 50 | break; 51 | } 52 | *dest = default_value; 53 | } 54 | } 55 | 56 | /**********************************************************************/ 57 | 58 | class Slider : public Gtk::HScale { 59 | public: 60 | Slider(int port); 61 | private: 62 | void on_value_changed(); 63 | float *dest; 64 | }; 65 | 66 | Slider::Slider(int port) { 67 | LADSPA_PortRangeHint hint = l_h.l_p.descriptor->PortRangeHints[port]; 68 | LADSPA_PortRangeHintDescriptor hdescriptor = hint.HintDescriptor; 69 | float lower_bound = hint.LowerBound; 70 | float upper_bound = hint.UpperBound; 71 | 72 | 73 | dest = &l_h.l_p.cpv[port]; 74 | 75 | if (LADSPA_IS_HINT_SAMPLE_RATE(hdescriptor)) { 76 | lower_bound *= l_h.get_samplerate(); 77 | upper_bound *= l_h.get_samplerate(); 78 | } 79 | if (LADSPA_IS_HINT_LOGARITHMIC(hdescriptor)) 80 | { 81 | if (lower_bound < FLT_EPSILON) 82 | lower_bound = FLT_EPSILON; 83 | } 84 | 85 | if (LADSPA_IS_HINT_BOUNDED_BELOW(hdescriptor) && 86 | LADSPA_IS_HINT_BOUNDED_ABOVE(hdescriptor)) { 87 | set_range(lower_bound, upper_bound); 88 | } else if (LADSPA_IS_HINT_BOUNDED_BELOW(hdescriptor)) { 89 | set_range(lower_bound, 1.0); 90 | } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hdescriptor)) { 91 | set_range(0.0, upper_bound); 92 | } else { 93 | set_range(-1.0, 1.0); 94 | } 95 | 96 | if (LADSPA_IS_HINT_TOGGLED(hdescriptor)) { 97 | set_range(0.0, 1.0); 98 | set_increments(1.0, 1.0); 99 | } 100 | 101 | if (LADSPA_IS_HINT_INTEGER(hdescriptor)) { 102 | set_increments(1.0, 1.0); 103 | set_digits(0); 104 | } else { 105 | set_increments(0.05, 0.1); 106 | set_digits(2); 107 | } 108 | set_default_value(hdescriptor, lower_bound, upper_bound, dest); 109 | // here we can set initial values if we wish like that 110 | /* if(port == 13 && l_h.l_p.descriptor->UniqueID == 34049) { 111 | *dest = 2990.7; 112 | } else if (port == 12 && l_h.l_p.descriptor->UniqueID == 33918) { 113 | *dest = 3556.56; 114 | }*/ 115 | set_value(*dest); 116 | } 117 | 118 | void Slider::on_value_changed() { 119 | *dest = get_value(); 120 | } 121 | 122 | /**********************************************************************/ 123 | 124 | class TButton : public Gtk::ToggleButton { 125 | public: 126 | TButton(int port); 127 | private: 128 | void on_toggled(); 129 | float *dest; 130 | }; 131 | 132 | TButton::TButton(int port) { 133 | LADSPA_PortRangeHint hint = l_h.l_p.descriptor->PortRangeHints[port]; 134 | LADSPA_PortRangeHintDescriptor hdescriptor = hint.HintDescriptor; 135 | set_label(l_h.l_p.descriptor->PortNames[port]); 136 | 137 | float default_value; 138 | dest = &l_h.l_p.cpv[port]; 139 | 140 | if (LADSPA_IS_HINT_HAS_DEFAULT(hdescriptor)) { 141 | switch (hdescriptor & LADSPA_HINT_DEFAULT_MASK) { 142 | case LADSPA_HINT_DEFAULT_0: 143 | default_value = 0.0; 144 | set_active(false); 145 | break; 146 | case LADSPA_HINT_DEFAULT_1: 147 | default_value = 1.0; 148 | set_active(true); 149 | break; 150 | default: 151 | default_value = 0.0; 152 | set_active(false); 153 | break; 154 | } 155 | *dest = default_value; 156 | } 157 | } 158 | 159 | void TButton::on_toggled() { 160 | if(get_active()) *dest = 1.0; 161 | else *dest = 0.0; 162 | } 163 | 164 | /**********************************************************************/ 165 | 166 | class Spiner : public Gtk::SpinButton { 167 | public: 168 | Spiner(int port); 169 | private: 170 | void on_value_changed(); 171 | float *dest; 172 | }; 173 | 174 | Spiner::Spiner(int port) { 175 | LADSPA_PortRangeHint hint = l_h.l_p.descriptor->PortRangeHints[port]; 176 | LADSPA_PortRangeHintDescriptor hdescriptor = hint.HintDescriptor; 177 | float lower_bound = hint.LowerBound; 178 | float upper_bound = hint.UpperBound; 179 | 180 | dest = &l_h.l_p.cpv[port]; 181 | 182 | if (LADSPA_IS_HINT_SAMPLE_RATE(hdescriptor)) { 183 | lower_bound *= l_h.get_samplerate(); 184 | upper_bound *= l_h.get_samplerate(); 185 | } 186 | if (LADSPA_IS_HINT_LOGARITHMIC(hdescriptor)) 187 | { 188 | if (lower_bound < FLT_EPSILON) 189 | lower_bound = FLT_EPSILON; 190 | } 191 | 192 | if (LADSPA_IS_HINT_BOUNDED_BELOW(hdescriptor) && 193 | LADSPA_IS_HINT_BOUNDED_ABOVE(hdescriptor)) { 194 | set_range(lower_bound, upper_bound); 195 | } else if (LADSPA_IS_HINT_BOUNDED_BELOW(hdescriptor)) { 196 | set_range(lower_bound, 1.0); 197 | } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(hdescriptor)) { 198 | set_range(0.0, upper_bound); 199 | } else { 200 | set_range(-1.0, 1.0); 201 | } 202 | 203 | set_increments(1.0, 1.0); 204 | set_digits(0); 205 | 206 | set_default_value(hdescriptor, lower_bound, upper_bound, dest); 207 | // here we can set initial values if we wish like that 208 | /* if(port == 13 && l_h.l_p.descriptor->UniqueID == 34049) { 209 | *dest = 2990; 210 | } else if (port == 12 && l_h.l_p.descriptor->UniqueID == 33918) { 211 | *dest = 3556; 212 | }*/ 213 | set_value(*dest); 214 | } 215 | 216 | void Spiner::on_value_changed() { 217 | *dest = get_value(); 218 | } 219 | 220 | /**********************************************************************/ 221 | 222 | class ToolBar : public Gtk::Toolbar { 223 | public: 224 | ToolBar(); 225 | private: 226 | bool is_active; 227 | Gtk::ToolButton * toolbutton; 228 | Gtk::Menu *menumono; 229 | Gtk::Menu *menustereo; 230 | Gtk::Menu *menumisc; 231 | Gtk::MenuItem* menuitem; 232 | void on_browser(); 233 | void on_close(); 234 | void on_stop(); 235 | void on_play(); 236 | void on_pause(); 237 | void on_menu_popup(Gtk::Menu *menu); 238 | void load_plug(Glib::ustring label); 239 | typedef std::list::iterator _iterator; 240 | }; 241 | 242 | ToolBar::ToolBar() { 243 | 244 | toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::NEW)); 245 | toolbutton->set_label("NEW"); 246 | append(*toolbutton, sigc::mem_fun( *this, &ToolBar::on_browser)); 247 | 248 | menumono = Gtk::manage (new Gtk::Menu); 249 | toolbutton = Gtk::manage(new Gtk::ToolButton()); 250 | toolbutton->set_label("Mono"); 251 | append(*toolbutton, sigc::bind( 252 | sigc::mem_fun(*this, &ToolBar::on_menu_popup), menumono)); 253 | 254 | menustereo = Gtk::manage (new Gtk::Menu); 255 | toolbutton = Gtk::manage(new Gtk::ToolButton()); 256 | toolbutton->set_label("Stereo"); 257 | append(*toolbutton, sigc::bind( 258 | sigc::mem_fun( *this, &ToolBar::on_menu_popup), menustereo)); 259 | 260 | menumisc = Gtk::manage (new Gtk::Menu); 261 | toolbutton = Gtk::manage(new Gtk::ToolButton()); 262 | toolbutton->set_label("Misc"); 263 | append(*toolbutton, sigc::bind( 264 | sigc::mem_fun( *this, &ToolBar::on_menu_popup), menumisc)); 265 | 266 | toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::MEDIA_PLAY)); 267 | toolbutton->set_label("PLAY"); 268 | append(*toolbutton, sigc::mem_fun( *this, &ToolBar::on_play)); 269 | 270 | toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::MEDIA_PAUSE)); 271 | toolbutton->set_label("PAUSE"); 272 | append(*toolbutton, sigc::mem_fun( *this, &ToolBar::on_pause)); 273 | 274 | toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::MEDIA_STOP)); 275 | toolbutton->set_label("STOP"); 276 | append(*toolbutton, sigc::mem_fun( *this, &ToolBar::on_stop)); 277 | 278 | toolbutton = Gtk::manage(new Gtk::ToolButton(Gtk::Stock::CLOSE)); 279 | toolbutton->set_label("CLOSE"); 280 | append(*toolbutton, sigc::mem_fun( *this, &ToolBar::on_close)); 281 | 282 | 283 | for (_iterator its = l_h.plug_mono_list.begin(); 284 | its != l_h.plug_mono_list.end(); its++) { 285 | std::string entry = *its; 286 | menuitem = Gtk::manage(new Gtk::MenuItem(entry, true)); 287 | menuitem->set_use_underline(false); 288 | menumono->add(*menuitem); 289 | menuitem->show(); 290 | menuitem->signal_activate().connect( 291 | sigc::bind(sigc::mem_fun(*this, &ToolBar::load_plug), 292 | menuitem->get_label())); 293 | } 294 | 295 | for (_iterator its = l_h.plug_stereo_list.begin(); 296 | its != l_h.plug_stereo_list.end(); its++) { 297 | std::string entry = *its; 298 | menuitem = Gtk::manage(new Gtk::MenuItem(entry, true)); 299 | menuitem->set_use_underline(false); 300 | menustereo->add(*menuitem); 301 | menuitem->show(); 302 | menuitem->signal_activate().connect( 303 | sigc::bind(sigc::mem_fun(*this, &ToolBar::load_plug), 304 | menuitem->get_label())); 305 | } 306 | 307 | 308 | for (_iterator its = l_h.plug_misc_list.begin(); 309 | its != l_h.plug_misc_list.end(); its++) { 310 | std::string entry = *its; 311 | menuitem = Gtk::manage(new Gtk::MenuItem(entry, true)); 312 | menuitem->set_use_underline(false); 313 | menumisc->add(*menuitem); 314 | menuitem->show(); 315 | menuitem->signal_activate().connect( 316 | sigc::bind(sigc::mem_fun(*this, &ToolBar::load_plug), 317 | menuitem->get_label())); 318 | } 319 | 320 | is_active = true; 321 | } 322 | 323 | void ToolBar::load_plug(Glib::ustring label) { 324 | 325 | int mark = label.find_first_of(' '); 326 | Glib::ustring lib = label.substr(1, mark-2); 327 | mark = label.find_last_of(' '); 328 | Glib::ustring ID = label.substr(mark, label.length() - mark); 329 | char** argv; 330 | argv = new char*[3]; 331 | argv[0] = const_cast(lib.c_str()); 332 | argv[1] = const_cast(lib.c_str()); 333 | argv[2] = const_cast(ID.c_str()); 334 | l_h.set_ex(false); 335 | l_h.set_go_on(true); 336 | l_h.jack_cleanup(); 337 | delete l_h.m_window; 338 | l_h.m_window = NULL; 339 | l_h.sila_start(3,argv); 340 | delete []argv; 341 | } 342 | 343 | void ToolBar::on_browser() { 344 | l_h.set_ex(false); 345 | l_h.set_go_on(true); 346 | l_h.jack_cleanup(); 347 | delete l_h.m_window; 348 | l_h.m_window = NULL; 349 | l_h.sila_start(0,NULL); 350 | } 351 | 352 | void ToolBar::on_close() { 353 | l_h.set_ex(true); 354 | l_h.set_go_on(false); 355 | delete l_h.m_window; 356 | l_h.m_window = NULL; 357 | l_h.jack_cleanup(); 358 | } 359 | 360 | void ToolBar::on_stop() { 361 | l_h.deactivate_jack(); 362 | is_active = false; 363 | } 364 | 365 | void ToolBar::on_play() { 366 | l_h.set_pause(false); 367 | if(!is_active) { 368 | l_h.activate_jack(); 369 | is_active = true; 370 | } 371 | } 372 | 373 | void ToolBar::on_pause() { 374 | if(l_h.get_pause()) { 375 | l_h.set_pause(false); 376 | } else { 377 | l_h.set_pause(true); 378 | } 379 | } 380 | 381 | void ToolBar::on_menu_popup(Gtk::Menu *menu) { 382 | guint32 tim = gtk_get_current_event_time(); 383 | menu->popup(2, tim); 384 | } 385 | 386 | /**********************************************************************/ 387 | 388 | Gtk::Window *LadspaHost::create_widgets() { 389 | Gtk::VBox *main_box = Gtk::manage(new Gtk::VBox); 390 | main_box->pack_start(*Gtk::manage(new ToolBar()),Gtk::PACK_SHRINK); 391 | Gtk::VBox *controller_box = Gtk::manage(new Gtk::VBox); 392 | 393 | /* controller_box->pack_start(*Gtk::manage(new Gtk::Label(l_h.l_p.descriptor->Label, 394 | Pango::ALIGN_CENTER, Pango::ALIGN_CENTER,false)), 395 | Gtk::PACK_EXPAND_WIDGET);*/ 396 | for (int i = 0; i < (int)l_h.l_p.descriptor->PortCount; i++) { 397 | LADSPA_PortRangeHint hint = l_h.l_p.descriptor->PortRangeHints[i]; 398 | LADSPA_PortRangeHintDescriptor hdescriptor = hint.HintDescriptor; 399 | if (LADSPA_IS_PORT_INPUT(l_h.l_p.descriptor->PortDescriptors[i]) && 400 | LADSPA_IS_PORT_CONTROL(l_h.l_p.descriptor->PortDescriptors[i])) { 401 | if (LADSPA_IS_HINT_TOGGLED(hdescriptor)) { 402 | Gtk::HBox *box = Gtk::manage(new Gtk::HBox); 403 | box->set_spacing(10); 404 | box->pack_start(*Gtk::manage(new TButton( i)), 405 | Gtk::PACK_SHRINK); 406 | box->pack_start(*Gtk::manage(new Gtk::HBox), Gtk::PACK_EXPAND_WIDGET); 407 | controller_box->pack_start(*box, Gtk::PACK_SHRINK); 408 | } else if (LADSPA_IS_HINT_INTEGER(hdescriptor)) { 409 | Gtk::HBox *box = Gtk::manage(new Gtk::HBox); 410 | box->set_spacing(10); 411 | box->pack_start(*Gtk::manage(new Gtk::Label(l_h.l_p.descriptor->PortNames[i], 412 | Pango::ALIGN_RIGHT)), Gtk::PACK_SHRINK); 413 | box->pack_start(*Gtk::manage(new Spiner(i)), 414 | Gtk::PACK_SHRINK); 415 | box->pack_start(*Gtk::manage(new Gtk::HBox), Gtk::PACK_EXPAND_WIDGET); 416 | controller_box->pack_start(*box, Gtk::PACK_SHRINK); 417 | } else { 418 | Gtk::HBox *box = Gtk::manage(new Gtk::HBox); 419 | box->set_spacing(10); 420 | controller_box->pack_start(*Gtk::manage(new Gtk::Label(l_h.l_p.descriptor->PortNames[i], 421 | Pango::ALIGN_RIGHT)), Gtk::PACK_EXPAND_WIDGET); 422 | box->pack_start(*Gtk::manage(new Slider(i)), 423 | Gtk::PACK_EXPAND_WIDGET); 424 | controller_box->pack_start(*box, Gtk::PACK_SHRINK); 425 | } 426 | Gtk::HSeparator *sep = Gtk::manage(new Gtk::HSeparator); 427 | controller_box->pack_start(*sep, Gtk::PACK_EXPAND_WIDGET); 428 | } 429 | } 430 | if (l_p.cnp > 12) { 431 | Gtk::ScrolledWindow *scrolled_window = Gtk::manage(new Gtk::ScrolledWindow); 432 | scrolled_window->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS); 433 | scrolled_window->set_shadow_type(Gtk::SHADOW_NONE); 434 | scrolled_window->add(*controller_box); 435 | scrolled_window->set_size_request(400, 300); 436 | main_box->pack_start(*scrolled_window, Gtk::PACK_EXPAND_WIDGET); 437 | } else { 438 | main_box->pack_start(*controller_box, Gtk::PACK_EXPAND_WIDGET); 439 | } 440 | return((Gtk::Window*)main_box); 441 | } 442 | 443 | /*****************************************************************************/ 444 | -------------------------------------------------------------------------------- /loopedSample/README: -------------------------------------------------------------------------------- 1 | Looped Sample 2 | 3 | This tutorial will show you how to load a sample into memory, and then 4 | play it back trough a JACK port, continually looping. There's not really 5 | that much to it, just a bit of thinking of the order things happen, and 6 | arrays. If you've read the "Writing a sample" tutorial, you'll already 7 | be familiar with LibSndFile, the library we use to read / write samples, 8 | so there's nothing too hard to handle :) 9 | -------------------------------------------------------------------------------- /loopedSample/loopedSample.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Contact: Harry van Haaren 3 | // Compile: g++ loopedSample.cpp `pkg-config --cflags --libs jack sndfile` 4 | 5 | // If this tutorial seems a little advanced, please look back over the 6 | // Simple JACK client tutorial. https://github.com/harryhaaren/Linux-Audio-Programming-Documentation/ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | // include sndfile so we have access to the sample loading library. 14 | // Note we've included a .hh file, that's the C++ wrapper for sndfile. 15 | #include 16 | 17 | jack_port_t* outputPort = 0; 18 | 19 | // get a vector, that's an Array that we can dynamically resize. It contains 20 | // floats in this case, so we can store audio samples in it. 21 | std::vector sampleVector; 22 | 23 | // create an "index", variable that keeps track of where we are currently 24 | // playing back in the file. 25 | int playbackIndex = 0; 26 | 27 | int process(jack_nframes_t nframes, void* ) 28 | { 29 | float* outputBuffer= (float*)jack_port_get_buffer ( outputPort, nframes); 30 | 31 | // this is the intresting part, we work with each sample of audio data 32 | // one by one, copying them across from the vector that has the sample 33 | // in it, to the output buffer of JACK. 34 | for ( int i = 0; i < (int) nframes; i++) 35 | { 36 | // here we check if index has gone played the last sample, and if it 37 | // has, then we reset it to 0 (the start of the sample). 38 | if ( playbackIndex >= sampleVector.size() ) { 39 | playbackIndex = 0; 40 | } 41 | 42 | // because we have made sure that index is always between 0 -> sample.size(), 43 | // its safe to access the array .at(index) The program would crash it furthur 44 | outputBuffer[i] = sampleVector.at(playbackIndex); 45 | 46 | // increase the index, so that we play the next sample 47 | playbackIndex++; 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | int loadSample() 54 | { 55 | // create a "Sndfile" handle, it's part of the sndfile library we 56 | // use to load samples 57 | SndfileHandle fileHandle( "sample.wav" , SFM_READ, SF_FORMAT_WAV | SF_FORMAT_FLOAT , 1 , 44100); 58 | 59 | // get the number of frames in the sample 60 | int size = fileHandle.frames(); 61 | 62 | if ( size == 0 ) 63 | { 64 | // file doesn't exist or is of incompatible type, main handles the -1 65 | return -1; 66 | } 67 | 68 | // get some more info of the sample 69 | int channels = fileHandle.channels(); 70 | int samplerate = fileHandle.samplerate(); 71 | 72 | // we declared sampleVector earlier, now we resize it 73 | sampleVector.resize(size); 74 | 75 | // this tells sndfile to 76 | fileHandle.read( &sampleVector.at(0) , size ); 77 | 78 | std::cout << "Loaded a file with " << channels << " channels, and a samplerate of " << 79 | samplerate << " with " << size << " samples, so its duration is " << 80 | size / samplerate << " seconds long." << std::endl; 81 | 82 | return 0; 83 | } 84 | 85 | 86 | int main() 87 | { 88 | std::cout << "Looped Sample Playback tutorial" << std::endl; 89 | 90 | // call the loadSample function above, just to separte the sample 91 | // loading code from the jack client creation code below. 92 | int loadNotOK = loadSample(); 93 | 94 | if ( loadNotOK ) 95 | { 96 | std::cout << "Error in sample loading function, check the sample\ 97 | exists in the directory the program is running!" << std::endl; 98 | return 0; // quit, as we didn't setup correctly 99 | } 100 | 101 | jack_client_t* client = jack_client_open ("LoopedSample", 102 | JackNullOption, 103 | 0, 104 | 0 ); 105 | 106 | jack_set_process_callback (client, process , 0); 107 | 108 | // register an audio output port 109 | outputPort = jack_port_register ( client, 110 | "output", 111 | JACK_DEFAULT_AUDIO_TYPE, 112 | JackPortIsOutput, 113 | 0 ); 114 | 115 | jack_activate(client); 116 | 117 | sleep(60); 118 | 119 | std::cout << "Quitting, 60 seconds sleep is over :)" << std::endl; 120 | 121 | jack_deactivate(client); 122 | 123 | jack_client_close(client); 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /loopedSample/sample.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harryhaaren/openAudioProgrammingTutorials/56c970ef994c97adbea8b05a6fa3b765dd70613d/loopedSample/sample.wav -------------------------------------------------------------------------------- /pureDataHost/README: -------------------------------------------------------------------------------- 1 | 2 | Pure Data Host class 3 | 4 | 5 | This is a relatively advanced tutorial, if you're not comfortable with 6 | classes and audio processing perhaps start with the jackClient tutorial. 7 | 8 | To use the libpd library we need to install it, which is going to take 9 | some effort as its not yet packaged by most distrobutions. So we need to 10 | get it's source, copy the files to the right places manually, and then 11 | link our executable against it. That is not easy, and can be frustrating 12 | if its not working... 13 | 14 | Getting libPD: 15 | https://github.com/libpd/libpd 16 | or 17 | $ cd /path/to/programming/folder 18 | $ git clone https://github.com/libpd/libpd.git 19 | 20 | ___________________OPTIONAL____________________ 21 | 22 | If you don't want the JAVA bindings because you're using C or C++ then 23 | just change this line 67 in the makefile: 24 | 25 | all: $(LIBPD) javalib 26 | 27 | all: $(LIBPD) 28 | 29 | _______________________________________________ 30 | 31 | Now we build the library: 32 | $ make 33 | 34 | Finally we need to install the right files in the right places. There's 35 | three steps here: 36 | -Copy the .pc file, so pkg-config will find the library's includes etc 37 | -Copy the include headers 38 | -Copy the compiled binary .so 39 | 40 | 1. In this tutorials folder you'll find a file called libpd.pc 41 | That needs to go to /usr/lib/pkgconfig/ 42 | $ cp libpd.pc /usr/lib/pkgconfig 43 | 44 | 2. The headers are in the "libpd_wrapper" folder in the libpd source. 45 | They need to be copied to /usr/local/include/ 46 | $ cd /path/to/libpd/ 47 | $ cp libpd_wrapper/* /usr/local/include/ 48 | 49 | 3. The binary file in the libpd source "libs" folder needs to move to 50 | somewhere where LD will find it: 51 | $ cp libs/libpd.so /usr/local/libs/ 52 | 53 | # Note: I've linked from /usr/lib/libpd.so to /usr/local/lib/libpd.so so 54 | my linker can find the shared object... 55 | 56 | Now the compile command in main.cpp should work!! Good luck! 57 | 58 | -------------------------------------------------------------------------------- /pureDataHost/libpd.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/lib 4 | includedir=${prefix}/include 5 | 6 | Name: libPD 7 | Description: LibPureData is a DSP library allowing the use of Pure Data patches in programs 8 | Version: 0.1 9 | Libs: -L${libdir} -lpd 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /pureDataHost/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Contact: Harry van Haaren 3 | // Compile: g++ -o pureDataHost puredatahost.cpp main.cpp `pkg-config --cflags --libs libpd` 4 | 5 | // this is a simple class that demonstrates how to create an instance of 6 | // the PureDataHost class, which allows simple use of PD patches. 7 | 8 | // Note: No audio or MIDI input / output exists in the example, it is purely 9 | // a PureDataHost tutorial. 10 | 11 | #include 12 | 13 | #include "puredatahost.hpp" 14 | 15 | using namespace std; 16 | 17 | int main() 18 | { 19 | int nframes = 256; 20 | int samplerate = 44100; 21 | 22 | cout << "Main, creating PureDataHost now... " << endl; 23 | 24 | PureDataHost pdHost(nframes, samplerate); 25 | 26 | cout << "Successfully created PureDataHost instance! Quitting now" << endl; 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /pureDataHost/puredatahost.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "puredatahost.hpp" 3 | 4 | extern "C" 5 | { 6 | #include "z_libpd.h" 7 | } 8 | 9 | using namespace std; 10 | 11 | PureDataHost::PureDataHost(int inNframes, int inSamplerate) 12 | { 13 | nframes = inNframes; 14 | samplerate = inSamplerate; 15 | 16 | fileOpen = false; 17 | filename = ""; 18 | 19 | cout<< "Initializing PureDataHost..." << flush; 20 | cout<< "\tsetting up hooks & audio IO..." << flush; 21 | 22 | libpd_printhook = (t_libpd_printhook) pdprint; 23 | libpd_noteonhook = (t_libpd_noteonhook) pdnoteon; 24 | libpd_init(); 25 | 26 | // init( in chans, out chans, samplerate ) 27 | libpd_init_audio(1, 1, samplerate); 28 | 29 | 30 | // compute audio message [; pd dsp 1( 31 | 32 | // start_message( numEntriesInMessage ) 33 | libpd_start_message(1); 34 | 35 | libpd_add_float(1.0f); 36 | libpd_finish_message("pd", "dsp"); 37 | 38 | 39 | libpd_start_message(1); 40 | libpd_finish_message("loadbang", "1"); 41 | 42 | cout << "Done!" << endl; 43 | } 44 | 45 | // output from PD 46 | void PureDataHost::pdnoteon(int ch, int pitch, int vel) 47 | { 48 | cout << "NoteOn: " << ch << " " << pitch << " " << vel << endl; 49 | } 50 | 51 | // input to PD 52 | void PureDataHost::noteOn(int c, int p, int v) 53 | { 54 | if ( c == 144 ) 55 | { 56 | cout << "PureDataHost::noteOn("< 7 | #include 8 | #include 9 | #include 10 | 11 | class PureDataHost 12 | { 13 | public: 14 | PureDataHost(int nframes, int samplerate); 15 | 16 | void printInfo(); 17 | void openFile(std::string openFile); 18 | 19 | void noteOn(int c, int p, int v); 20 | 21 | void process(float *L, float *R, int nframes); 22 | 23 | protected: 24 | int nframes; 25 | int samplerate; 26 | 27 | bool fileOpen; 28 | std::string filename; 29 | 30 | // static methods for PD output 31 | static void pdprint(const char *s); 32 | static void pdnoteon(int ch, int pitch, int vel); 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /writingSoundfileToDisk/README: -------------------------------------------------------------------------------- 1 | Writing Soundfile To Disk 2 | 3 | This short tutorial will show you how to write a buffer of audio that's 4 | in memory to disk. We use a library called Libsndfile for this purpose, 5 | its a great library to read and write a variety of soundfile formats. 6 | 7 | So if you haven't done so yet, you'll need to install libsndfile-dev or 8 | similar to allow you link your C++ program against that library. Note 9 | that we're going to use the C++ wrapper around the C library, and hence 10 | we're including 11 | -------------------------------------------------------------------------------- /writingSoundfileToDisk/writingSoundfileToDisk.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Contact: Harry van Haaren 3 | // Compile: g++ writingSoundfileToDisk.cpp `pkg-config --cflags --libs sndfile` 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | const int format=SF_FORMAT_WAV | SF_FORMAT_FLOAT; 12 | const int channels=1; 13 | const int sampleRate=44100; 14 | const char* outfilename="foo.wav"; 15 | 16 | // open a handle to a file, using the parameters defined above 17 | SndfileHandle outfile(outfilename, SFM_WRITE, format, channels, sampleRate); 18 | 19 | // prepare an array of 3 seconds long, based on the samplerate 20 | int size = sampleRate*3; 21 | float sample[size]; 22 | 23 | int frequency = 1500; // <-- frequency of the wave generated 24 | 25 | for (int i=0; i < size; i++) 26 | { 27 | // write each sample, the math here is generating a sin wave 28 | sample[i]=sin( float(i) / size * M_PI * frequency); 29 | } 30 | 31 | // tell our file handle that we want to write "size" amount of data 32 | // from "the-address" of the "sample" array, starting at "0" 33 | outfile.write(&sample[0], size); 34 | 35 | return 0; 36 | } 37 | --------------------------------------------------------------------------------