From f163832736b9d2007a16a4476cbb490ae7a34547 Mon Sep 17 00:00:00 2001 From: terminatorX <> Date: Fri, 18 Jul 2003 17:17:21 +0000 Subject: [PATCH] ALSA fixes, MIDI fixes and some new features, misc fixes and a preliminary JACK backend - Alex --- configure.in | 60 ++++++++--- src/main.cc | 27 ++++- src/tX_audiodevice.cc | 208 ++++++++++++++++++++++++++++++++++++-- src/tX_audiodevice.h | 64 ++++++++++-- src/tX_dialog.cc | 87 +++++----------- src/tX_engine.cc | 14 ++- src/tX_glade_interface.cc | 21 +++- src/tX_global.c | 23 ++++- src/tX_global.h | 1 + src/tX_midiin.cc | 103 ++++++++++++++++++- src/tX_midiin.h | 6 ++ src/tX_seqpar.cc | 122 +++++++++++++++++++--- src/tX_seqpar.h | 28 ++++- src/tX_vtt.cc | 66 +++++------- src/tX_vtt.h | 5 +- src/tX_vttfx.cc | 18 ++-- terminatorX.glade | 48 ++++++++- 17 files changed, 732 insertions(+), 169 deletions(-) diff --git a/configure.in b/configure.in index c06885c..95650cb 100644 --- a/configure.in +++ b/configure.in @@ -13,6 +13,7 @@ dnl AC_ARG_ENABLE(benchmark,[ --enable-benchmark creates a non-functional AC_ARG_ENABLE(wav, [ --enable-wav enables the builtin wav-loader (default=yes) ]) AC_ARG_ENABLE(xsetpointer, [ --enable-xsetpointer enables executing of xsetpointer (default=auto) ]) AC_ARG_ENABLE(alsa, [ --enable-alsa use ALSA for sound output (default=auto) ]) +AC_ARG_ENABLE(jack, [ --enable-jack use JACK for sound output (default=auto) ]) AC_ARG_ENABLE(oss, [ --enable-oss use OSS for sound output (default=auto) ]) dnl AC_ARG_ENABLE(dga2, [ --enable-dga2 use DGA2 instead of DGA1. (experimental) (default=no) ]) AC_ARG_ENABLE(libxml2, [ --disable-libxml2 use libxml even if verion 2 detected (default=auto) ]) @@ -33,6 +34,9 @@ AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_RANLIB +OPTION_OSS="no" +OPTION_ALSA="no" +OPTION_JACK="no" OPTION_SOX="no" OPTION_MPG123="no" OPTION_MAD="no" @@ -275,6 +279,7 @@ fi using_alsa=no using_oss=no +using_jack=no if test "$enable_alsa" != "no"; then AC_CHECK_LIB(asound, snd_pcm_writei,alsalib=yes,alsalib=no) @@ -282,9 +287,10 @@ if test "$enable_alsa" != "no"; then if test "$alsalib" = "yes"; then if test "$alsaheader" = "yes"; then - AC_DEFINE_UNQUOTED([USE_ALSA], 1, [ Define to use ALSA audio backend instead of OSS.]) + AC_DEFINE_UNQUOTED([USE_ALSA], 1, [ Define to enable ALSA audio backend. ]) LIBS="$LIBS -lasound" - using_alsa=yes; + using_alsa=yes + OPTION_ALSA=yes else if test "$enable_alsa" = "yes"; then AC_MSG_ERROR([** Coulnd't find ALSA header file sys/asoundlib.h **]) @@ -297,11 +303,35 @@ if test "$enable_alsa" != "no"; then fi fi +if test "$enable_jack" != "no"; then + AC_CHECK_LIB(jack,jack_activate,jacklib=yes,jacklib=no) + AC_CHECK_HEADERS(jack/jack.h,jackheader=yes,jackheader=no) + + if test "$jacklib" = "yes"; then + if test "$jackheader" = "yes"; then + AC_DEFINE_UNQUOTED([USE_JACK], 1, [ Define to enable JACK audio backend.]) + LIBS="$LIBS -ljack" + using_jack=yes + OPTION_JACK=yes + else + if test "$enable_jack" = "yes"; then + AC_MSG_ERROR([** Coulnd't find JACK header file jack/jack.h **]) + fi + fi + else + if test "$enable_jack" = "yes"; then + AC_MSG_ERROR([** Coulnd'f find JACK library libjack. **]) + fi + fi +fi + + if test "$enable_oss" != "no"; then AC_CHECK_HEADERS(sys/ioctl.h sys/soundcard.h,oss=yes,oss=no) if test "$oss" = "yes"; then AC_DEFINE_UNQUOTED([USE_OSS], 1, [Use OSS]) + OPTION_OSS=yes using_oss=yes; else if test "$enable_oss" = "yes"; then @@ -336,18 +366,11 @@ if test "$enable_alsamidi" != "no"; then fi fi -if test "$using_alsa" = "yes"; then - AC_MSG_RESULT([termnatorX audiodevice: using ALSA.]) - AC_DEFINE_UNQUOTED([USE_ALSA], 1, [Define for ALSA pcm output]) -else - if test "$using_oss" = "yes"; then - AC_MSG_RESULT([termnatorX audiodevice: using OSS.]) - fi -fi - if test "$using_alsa" != "yes"; then if test "$using_oss" != "yes"; then - AC_MSG_ERROR([** Found neither OSS nor ALSA! **]) + if test "$using_jack" != "yes"; then + AC_MSG_ERROR([** Found neither OSS, ALSA nor JACK - no output device! **]) + fi fi fi @@ -564,6 +587,16 @@ echo " - http://www.mpg123.de/" echo " - and reconfigure terminatorX" fi + +option=oss; option_val=$OPTION_OSS; option_url=http://www.kernel.org +option_info; + +option=alsa; option_val=$OPTION_ALSA; option_url=http://www.alsa-project.org +option_info; + +option=jack; option_val=$OPTION_JACK; option_url=http://jackit.sourceforge.net +option_info; + option=mad; option_val=$OPTION_MAD; option_url=http://www.mars.org/home/rob/proj/mpeg/ option_info; @@ -587,9 +620,10 @@ if test "$OPTION_SUIDROOT" = "yes"; then option_info; fi -echo "legacy files supprt: $OPTION_LEGACY" +echo "legacy files support: $OPTION_LEGACY" echo "builtin-wav support: $OPTION_WAV" echo "enhanced scheduling support: $OPTION_SCHEDULER" +echo "suid-root support: $OPTION_SUIDROOT" echo "GNOME support: $OPTION_GNOME" echo echo You can now run \'make\' to compile terminatorX diff --git a/src/main.cc b/src/main.cc index 4da4f7c..b2fae4f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -53,6 +53,7 @@ #include "tX_endian.h" #include "tX_types.h" #include "tX_global.h" +#include "tX_audiodevice.h" #include "version.h" #include "tX_dialog.h" #include @@ -68,6 +69,14 @@ GTimer *my_time; gint idle_tag; +#ifdef USE_JACK +void jack_check() +{ + if (!tX_jack_client::get_instance()) { + tx_note("Couldn't connect to JACK server - JACK output not available.\n\nIf you want to use JACK, ensure the JACK daemon is running before you start terminatorX.", true); + } +} +#endif int idle() { @@ -81,6 +90,9 @@ int idle() g_timer_destroy(my_time); destroy_about(); display_mastergui(); +#ifdef USE_JACK + jack_check(); +#endif } return TRUE; @@ -225,10 +237,18 @@ int main(int argc, char **argv) LADSPA_Class :: init(); LADSPA_Plugin :: init(); +#ifdef USE_JACK + tX_jack_client :: init(); +#endif create_mastergui(globals.width, globals.height); - if (!globals.show_nag) display_mastergui(); + if (!globals.show_nag) { + display_mastergui(); +#ifdef USE_JACK + jack_check(); +#endif + } if (globals.startup_set) { @@ -245,6 +265,11 @@ int main(int argc, char **argv) store_globals(); delete engine; +#ifdef USE_JACK + if (tX_jack_client::get_instance()) { + delete tX_jack_client::get_instance(); + } +#endif fprintf(stderr, "Have a nice life.\n"); #else diff --git a/src/tX_audiodevice.cc b/src/tX_audiodevice.cc index cd96657..60958f5 100644 --- a/src/tX_audiodevice.cc +++ b/src/tX_audiodevice.cc @@ -176,11 +176,6 @@ int tX_audiodevice_oss :: close() tX_audiodevice_oss :: tX_audiodevice_oss() : tX_audiodevice(), fd(0), blocksize(0) {} -double tX_audiodevice_oss :: get_latency() -{ - return 0; -} - void tX_audiodevice_oss :: play(int16_t *buffer) { #ifdef BIG_ENDIAN_MACHINE @@ -204,7 +199,15 @@ int tX_audiodevice_alsa :: open() char pcm_name[64]; char foo[PATH_MAX]; - strcpy(pcm_name, globals.alsa_device_id); + /* Removing the device ID comment... */ + for (int i=0; i failed to connect to jackd."); + instance=NULL; + } else { + instance=this; + const char **ports; + + /* Setting up jack callbacks... */ + jack_set_process_callback(client, tX_jack_client::process, NULL); + jack_set_sample_rate_callback(client, tX_jack_client::srate, NULL); + jack_on_shutdown (client, tX_jack_client::shutdown, NULL); + + /* Creating the port... */ + left_port = jack_port_register (client, "output_1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + right_port = jack_port_register (client, "output_2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + /* Action... */ + jack_activate(client); + + /* Connect some ports... */ + if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) { + tX_error("tX_jack_client() no ports to connect to found. Connect manually."); + } else if (ports[0] && ports[1]) { + if (jack_connect (client, jack_port_name(left_port), ports[0])) { + tX_error("tX_jack_client() failed to connect left port."); + } + if (jack_connect (client, jack_port_name(right_port), ports[1])) { + tX_error("tX_jack_client() failed to connect right port."); + } + free (ports); + } + } +} + +tX_jack_client::~tX_jack_client() +{ + instance=NULL; + if (client) jack_client_close(client); +} + +void tX_jack_client::error(const char *desc) +{ + tX_error("tX_jack_client::error() jack error: %s.", desc); +} + +int tX_jack_client::srate(jack_nframes_t nframes, void *arg) +{ + tX_error("tX_jack_client::srate() jack changed samplerate -> this is bad, stopping audio."); + return 0; +} + +void tX_jack_client::shutdown(void *arg) +{ + /***AARGH how to handle this?***/ +} + +int tX_jack_client::process(jack_nframes_t nframes, void *arg) +{ + if (instance) { + return instance->play(nframes); + } + + /* Hmm, what to do in such a case? */ + return 0; +} + +int tX_jack_client::play(jack_nframes_t nframes) +{ + jack_default_audio_sample_t *left = (jack_default_audio_sample_t *) jack_port_get_buffer (left_port, nframes); + jack_default_audio_sample_t *right = (jack_default_audio_sample_t *) jack_port_get_buffer (right_port, nframes); + + if (device) { + device->fill_frames(left, right, nframes); + } else { + memset(left, 0, sizeof (jack_default_audio_sample_t) * nframes); + memset(right, 0, sizeof (jack_default_audio_sample_t) * nframes); + } + + return 0; +} + +int tX_jack_client::get_sample_rate() +{ + return jack_get_sample_rate(client); +} + +/* tX_audiodevice_jack */ + +tX_audiodevice_jack::tX_audiodevice_jack():tX_audiodevice() +{ + client=NULL; +} + +int tX_audiodevice_jack::open() +{ + tX_jack_client *jack_client=tX_jack_client::get_instance(); + sample_rate=jack_client->get_sample_rate(); + + if (jack_client) { + client=jack_client; + is_open=true; + + return 0; + } + + return 1; +} + +int tX_audiodevice_jack::close() +{ + if (client) { + client->set_device(NULL); + } + + is_open=false; + + return 0; +} + +void tX_audiodevice_jack::play(int16_t *buffer) +{ + tX_error("tX_audiodevice_jack::play()"); +} + +void tX_audiodevice_jack::start() +{ + tX_debug("activating jack playback"); + overrun_buffer=new f_prec[vtt_class::samples_in_mix_buffer]; + + client->set_device(this); + while (!engine->is_stopped()) { + usleep(100); + } + tX_debug("stopping jack playback"); + client->set_device(NULL); + + delete [] overrun_buffer; +} + +void tX_audiodevice_jack::fill_frames(jack_default_audio_sample_t *left, jack_default_audio_sample_t *right, jack_nframes_t nframes) +{ + int outbuffer_pos=0; + int sample; + + if (samples_in_overrun_buffer) { + for (sample=0; ((samplerender_cycle(); + + for (sample=0; ((sample #endif +#ifdef USE_JACK +#include +#endif + class tX_engine; class tX_audiodevice @@ -54,7 +58,6 @@ class tX_audiodevice tX_audiodevice(); public: - virtual double get_latency()=0; /* call only valid *after* open() */ int get_buffersize() { return samples_per_buffer; } /* call only valid *after* open() */ int get_sample_rate() { return sample_rate; } @@ -79,10 +82,7 @@ class tX_audiodevice_oss : public tX_audiodevice public: virtual int open(); virtual int close(); - - virtual double get_latency(); /* call only valid *after* open() */ - - virtual void play(int16_t*); /* play blocked */ + virtual void play(int16_t*); tX_audiodevice_oss(); }; @@ -100,14 +100,60 @@ class tX_audiodevice_alsa : public tX_audiodevice public: virtual int open(); virtual int close(); - - virtual double get_latency(); /* call only valid *after* open() */ - - virtual void play(int16_t*); /* play blocked */ + virtual void play(int16_t*); tX_audiodevice_alsa(); }; #endif +#ifdef USE_JACK + +class tX_jack_client; + +class tX_audiodevice_jack : public tX_audiodevice +{ + private: + tX_jack_client *client; + jack_default_audio_sample_t *overrun_buffer; + int samples_in_overrun_buffer; + + public: + virtual int open(); + virtual int close(); + virtual void play(int16_t*); + virtual void start(); + void fill_frames(jack_default_audio_sample_t *left, jack_default_audio_sample_t *right, jack_nframes_t nframes); + + tX_audiodevice_jack(); +}; + +class tX_jack_client +{ + public: + static void init(); + static tX_jack_client *get_instance() { return instance; }; + ~tX_jack_client(); + + private: + tX_jack_client(); + static tX_jack_client *instance; + static void error(const char *desc); + static int srate(jack_nframes_t nframes, void *arg); + static void shutdown(void *arg); + static int process(jack_nframes_t nframes, void *arg); + + jack_client_t *client; + tX_audiodevice_jack *device; + jack_port_t *left_port; + jack_port_t *right_port; + int play(jack_nframes_t nframes); + + public: + int get_sample_rate(); + void set_device(tX_audiodevice_jack *dev) { device=dev; } +}; + +#endif + #endif diff --git a/src/tX_dialog.cc b/src/tX_dialog.cc index f2cc72a..b3f891f 100644 --- a/src/tX_dialog.cc +++ b/src/tX_dialog.cc @@ -16,7 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - File: tX_dialog.c + File: tX_dialog.cc Description: Contains the implementation of the Options and About Dialogs. (And some really ugly "WE WANT TO @@ -55,27 +55,6 @@ int opt_hidden=0; static GtkWidget *last_alsa_device_widget=NULL; static GtkWidget *alsa_device_entry=NULL; -static void alsa_device_changed(GtkList *list, GtkWidget *widget, gpointer user_data) { - if (widget) { - if (widget != last_alsa_device_widget) { - last_alsa_device_widget = widget; - GtkWidget *label=gtk_bin_get_child(GTK_BIN(widget)); - - if (label) { - char foo[PATH_MAX]; - char tmp[PATH_MAX]; - int card; - int device; - - sscanf(gtk_label_get_text(GTK_LABEL(label)), "%i-%i: %s", &card, &device, foo); - sprintf(tmp, "hw:%i,%i", card, device); - - gtk_entry_set_text(GTK_ENTRY(alsa_device_entry), tmp); - } - } - } -} - void apply_options(GtkWidget *dialog) { /* Audio */ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(dialog, "alsa_driver")))) { @@ -135,6 +114,7 @@ void apply_options(GtkWidget *dialog) { strcpy(globals.lrdf_path, gtk_entry_get_text(GTK_ENTRY(lookup_widget(dialog, "ladspa_rdf_path")))); globals.compress_set_files=(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(dialog, "compress_set_files")))==TRUE); globals.prelis=(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(dialog, "prelisten_enabled")))==TRUE); + globals.restore_midi_connections=(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(dialog, "reconnect_enabled")))==TRUE); } @@ -194,7 +174,16 @@ GList *get_alsa_device_list() { buffer[PATH_MAX]=0; if (strlen(buffer)) buffer[strlen(buffer)-1]=0; if(strstr(buffer, "playback")) { - alsa_devices=g_list_append (alsa_devices, strdup(buffer)); + char foo[PATH_MAX]; + char tmp[PATH_MAX]; + memset(foo, 0, PATH_MAX); + int card; + int device; + + sscanf(buffer, "%i-%i: %1024c", &card, &device, foo); + sprintf(tmp, "hw:%i,%i# %s", card, device, foo); + + alsa_devices=g_list_append (alsa_devices, strdup(tmp)); } } fclose(file); @@ -336,8 +325,6 @@ void init_tx_options(GtkWidget *dialog) { } gtk_entry_set_text(GTK_ENTRY(combo->entry), globals.alsa_device_id); - g_signal_connect(G_OBJECT(combo->list), "select_child", G_CALLBACK(alsa_device_changed), NULL); - gtk_range_set_value(GTK_RANGE(lookup_widget(dialog, "alsa_buffer_time")), globals.alsa_buffer_time/1000); gtk_tooltips_set_tip(tooltips, lookup_widget(dialog, "alsa_buffer_time"), "Sets the size of the ALSA ring buffer. On slower systems you might have to increase this value (if you hear \"clicks\" or drop-outs). Lower values mean lower latency though.", NULL); gtk_range_set_value(GTK_RANGE(lookup_widget(dialog, "alsa_period_time")), globals.alsa_period_time/1000); @@ -400,12 +387,13 @@ void init_tx_options(GtkWidget *dialog) { gtk_entry_set_text(GTK_ENTRY(lookup_widget(dialog, "ladspa_rdf_path")), globals.lrdf_path); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(dialog, "compress_set_files")), globals.compress_set_files); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(dialog, "prelisten_enabled")), globals.prelis); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lookup_widget(dialog, "reconnect_enabled")), globals.restore_midi_connections); } void create_options() { opt_dialog=create_tx_options(); - gtk_widget_hide(lookup_widget(opt_dialog, "jack_driver")); + //gtk_widget_hide(lookup_widget(opt_dialog, "jack_driver")); init_tx_options(opt_dialog); gtk_widget_show(opt_dialog); } @@ -423,22 +411,18 @@ GtkWidget *about=NULL; void raise_about() { - if (about) - gdk_window_raise(about->window); + if (about) gdk_window_raise(about->window); } void destroy_about() { - if (about) - { + if (about) { gtk_widget_destroy(about); about=NULL; } } - - #define add_about_wid(wid); gtk_box_pack_start(GTK_BOX(box), wid, WID_DYN); \ gtk_widget_show(wid); @@ -461,32 +445,25 @@ void show_about(int nag) GtkWidget *scroll; GdkPixmap *pmap=NULL; - if (about) - { + /* Only raise the window if it's already open... */ + if (about) { gdk_window_raise(about->window); return; } + /* Create the window... */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_wmclass(GTK_WINDOW(window), "terminatorX", "tX_about"); - gtk_container_set_border_width(GTK_CONTAINER(window), 5); - -// GTK_WINDOW(window)->use_uposition=TRUE; - g_object_set (G_OBJECT (window), "type", GTK_WINDOW_TOPLEVEL, NULL); - if (nag) { gtk_window_set_decorated(GTK_WINDOW(window), FALSE); } + gtk_window_set_decorated(GTK_WINDOW(window), nag ? TRUE : FALSE); gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); - //gtk_window_set_resizable (GTK_WINDOW (window), FALSE); + gtk_window_set_resizable (GTK_WINDOW (window), FALSE); gtk_window_set_title(GTK_WINDOW(window), "terminatorX - About"); - //gtk_widget_set_size_request(window, 640, 210); - gtk_widget_realize(window); + GdkPixbuf *image=gdk_pixbuf_new_from_xpm_data((const char **)logo_xpm); + gdk_pixbuf_render_pixmap_and_mask(image, &pmap, &mask, 0); - style = gtk_widget_get_style( window ); - - pmap=gdk_pixmap_create_from_xpm_d(window->window, &mask, &style->bg[GTK_STATE_NORMAL], (gchar **)logo_xpm); - pwid = gtk_pixmap_new( pmap, mask ); if (nag) { @@ -510,13 +487,10 @@ void show_about(int nag) gtk_widget_show(box2); gtk_widget_show(box); - gtk_widget_show(window); gtk_widget_show(pwid); - while (gtk_events_pending()) gtk_main_iteration(); - } - else - { + gtk_widget_show(window); + } else { box=gtk_vbox_new(FALSE, 5); add_about_wid_fix(pwid); @@ -588,7 +562,7 @@ void show_about(int nag) gtk_text_buffer_get_iter_at_offset (tbuffer, &iter, 0); scroll=gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (scroll), text); gtk_text_buffer_create_tag (tbuffer, "courier", "family", "courier", NULL); @@ -629,15 +603,10 @@ GtkWidget *tX_icon_widget=NULL; void tX_set_icon(GtkWidget *widget, char *name) { - GtkStyle *style; - - style = gtk_widget_get_style( widget ); + GtkStyle *style = gtk_widget_get_style(widget); - if (!tX_icon_pmap) - { + if (!tX_icon_pmap) { tX_icon_pmap=gdk_pixmap_create_from_xpm_d(widget->window, &tX_icon_mask, &style->bg[GTK_STATE_NORMAL], (gchar **) tX_icon_xpm ); - //tX_icon_widget = gtk_pixmap_new( tX_icon_pmap, tX_icon_mask ); - //gtk_widget_realize(tX_icon_widget); } gdk_window_set_icon(widget->window, NULL, tX_icon_pmap, tX_icon_mask); diff --git a/src/tX_engine.cc b/src/tX_engine.cc index 165646d..c439c18 100644 --- a/src/tX_engine.cc +++ b/src/tX_engine.cc @@ -126,7 +126,6 @@ void tX_engine :: loop() { } } - void *engine_thread_entry(void *engine_void) { tX_engine *engine=(tX_engine*) engine_void; int result; @@ -150,6 +149,11 @@ void *engine_thread_entry(void *engine_void) { } } +#ifdef USE_JACK + /* Create the client now, so the user has something to connect to. */ + tX_jack_client *jack_client=tX_jack_client::get_instance(); +#endif + engine->loop(); tX_debug("engine_thread_entry() - Engine thread terminating."); @@ -185,7 +189,7 @@ tX_engine :: tX_engine() { result=pthread_create(&thread, &pattr, engine_thread_entry, (void *) this); } else { - tX_debug("tX_engine() - Lacking root privileges - no realtime scheduling!"); + tX_debug("tX_engine() - Lacking root privileges - no realtime scheduling."); #endif result=pthread_create(&thread, NULL, engine_thread_entry, (void *) this); #ifdef USE_SCHEDULER @@ -245,6 +249,12 @@ tX_engine_error tX_engine :: run() { device=new tX_audiodevice_alsa(); break; #endif + +#ifdef USE_JACK + case JACK: + device=new tX_audiodevice_jack(); + break; +#endif default: device=NULL; return ERROR_AUDIO; diff --git a/src/tX_glade_interface.cc b/src/tX_glade_interface.cc index 8d49e40..9e232f2 100644 --- a/src/tX_glade_interface.cc +++ b/src/tX_glade_interface.cc @@ -222,6 +222,8 @@ create_tx_options (void) GtkWidget *ladspa_rdf_path; GtkWidget *label33; GtkWidget *compress_set_files; + GtkWidget *label40; + GtkWidget *reconnect_enabled; GtkWidget *label3; GtkWidget *dialog_action_area3; GtkWidget *pref_reset; @@ -690,7 +692,7 @@ create_tx_options (void) gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook1), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook1), 4), label2); gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT); - table3 = gtk_table_new (4, 2, FALSE); + table3 = gtk_table_new (5, 2, FALSE); gtk_widget_show (table3); gtk_container_add (GTK_CONTAINER (notebook1), table3); gtk_container_set_border_width (GTK_CONTAINER (table3), 4); @@ -755,6 +757,21 @@ create_tx_options (void) (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); + label40 = gtk_label_new ("Restore MIDI connections:"); + gtk_widget_show (label40); + gtk_table_attach (GTK_TABLE (table3), label40, 0, 1, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_label_set_justify (GTK_LABEL (label40), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (label40), 0, 0.5); + + reconnect_enabled = gtk_check_button_new_with_mnemonic ("Enabled"); + gtk_widget_show (reconnect_enabled); + gtk_table_attach (GTK_TABLE (table3), reconnect_enabled, 1, 2, 4, 5, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, reconnect_enabled, "When enabled soundfiles will be playedback when selected in a file dialog (before loading them).", NULL); + label3 = gtk_label_new ("Misc"); gtk_widget_show (label3); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook1), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook1), 5), label3); @@ -881,6 +898,8 @@ create_tx_options (void) GLADE_HOOKUP_OBJECT (tx_options, ladspa_rdf_path, "ladspa_rdf_path"); GLADE_HOOKUP_OBJECT (tx_options, label33, "label33"); GLADE_HOOKUP_OBJECT (tx_options, compress_set_files, "compress_set_files"); + GLADE_HOOKUP_OBJECT (tx_options, label40, "label40"); + GLADE_HOOKUP_OBJECT (tx_options, reconnect_enabled, "reconnect_enabled"); GLADE_HOOKUP_OBJECT (tx_options, label3, "label3"); GLADE_HOOKUP_OBJECT_NO_REF (tx_options, dialog_action_area3, "dialog_action_area3"); GLADE_HOOKUP_OBJECT (tx_options, pref_reset, "pref_reset"); diff --git a/src/tX_global.c b/src/tX_global.c index 6281e59..8890b4e 100644 --- a/src/tX_global.c +++ b/src/tX_global.c @@ -45,6 +45,11 @@ tx_global globals; int _store_compress_xml=0; +#ifdef USE_ALSA_MIDI_IN +extern void tX_midiin_store_connections(FILE *rc, char *indent); +extern void tX_midiin_restore_connections(xmlNodePtr node); +#endif + void get_rc_name(char *buffer) { strcpy(buffer,""); @@ -138,6 +143,7 @@ void set_global_defaults() { globals.alsa_free_hwstats=1; globals.filename_length=20; + globals.restore_midi_connections=1; } int load_globals_xml() { @@ -219,6 +225,16 @@ int load_globals_xml() { restore_int("fullscreen_enabled", globals.fullscreen_enabled); restore_int("confirm_events", globals.confirm_events); restore_float("vtt_inertia", globals.vtt_inertia); + restore_int("restore_midi_connections", globals.restore_midi_connections); + +#ifdef USE_ALSA_MIDI_IN + if (!elementFound && (xmlStrcmp(cur->name, (xmlChar *) "midi_connections")==0)) { + if (globals.restore_midi_connections) { + tX_midiin_restore_connections(cur); + } + elementFound=1; + } +#endif if (!elementFound) { fprintf(stderr, "tX: Unhandled XML element: \"%s\"\n", cur->name); @@ -238,7 +254,7 @@ int load_globals_xml() { void store_globals() { char rc_name[PATH_MAX+256]=""; char device_type[16]; - char indent[]="\t"; + char indent[32]="\t"; FILE *rc=NULL; gzFile rz=NULL; char tmp_xml_buffer[4096]; @@ -310,6 +326,11 @@ void store_globals() { store_float("vtt_inertia", globals.vtt_inertia); store_string("last_path", globals.current_path); + store_int("restore_midi_connections", globals.restore_midi_connections); + +#ifdef USE_ALSA_MIDI_IN + tX_midiin_store_connections(rc, indent); +#endif fprintf(rc,"\n"); } diff --git a/src/tX_global.h b/src/tX_global.h index 816507f..a0b90f7 100644 --- a/src/tX_global.h +++ b/src/tX_global.h @@ -145,6 +145,7 @@ typedef struct { int alsa_free_hwstats; int filename_length; + int restore_midi_connections; } tx_global; extern tx_global globals; diff --git a/src/tX_midiin.cc b/src/tX_midiin.cc index df977e5..3cd565d 100644 --- a/src/tX_midiin.cc +++ b/src/tX_midiin.cc @@ -37,6 +37,7 @@ #ifdef USE_ALSA_MIDI_IN #include "tX_global.h" #include +#include "tX_engine.h" using namespace std; @@ -49,8 +50,6 @@ static gboolean midi_callback(GIOChannel *source, GIOCondition condition, gpoint tX_midiin::tX_midiin() { - - int portid; is_open=false; sp_to_learn=NULL; learn_dialog=NULL; @@ -110,9 +109,10 @@ int tX_midiin::check_event() tX_midievent event; event.is_noteon = false; bool event_usable = true; - + printf("type: %i\n", ev->type); switch (ev->type) { case SND_SEQ_EVENT_CONTROLLER: + printf("ctrl: p: %i, v: %i, c: %i\n", ev->data.control.param, ev->data.control.value, ev->data.control.channel); event.type = tX_midievent::CC; event.number = ev->data.control.param; event.value = ev->data.control.value / 127.0; @@ -131,12 +131,14 @@ int tX_midiin::check_event() event.channel = ev->data.control.channel; break; case SND_SEQ_EVENT_REGPARAM: + printf("rpn: p: %i, v: %i, c: %i\n", ev->data.control.param, ev->data.control.value, ev->data.control.channel); event.type = tX_midievent::RPN; event.number = ev->data.control.param; event.value = ev->data.control.value / 16383.0; event.channel = ev->data.control.channel; break; case SND_SEQ_EVENT_NONREGPARAM: + printf("nrpn: p: %i, v: %i, c: %i\n", ev->data.control.param, ev->data.control.value, ev->data.control.channel); event.type = tX_midievent::NRPN; event.number = ev->data.control.param; event.value = ev->data.control.value / 16383.0; @@ -436,5 +438,100 @@ gboolean tX_midiin::midi_learn_destroy(GtkWidget *widget, tX_midiin *midi) midi->cancel_midi_learn(); } +void tX_midiin::store_connections(FILE *rc, char *indent) +{ + gzFile *rz; + + tX_store("%s\n", indent); + strcat(indent, "\t"); + + snd_seq_addr_t my_addr; + my_addr.client=snd_seq_client_id(ALSASeqHandle); + my_addr.port=portid; + + snd_seq_query_subscribe_t *subs; + snd_seq_query_subscribe_alloca(&subs); + snd_seq_query_subscribe_set_root(subs, &my_addr); + snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_WRITE); + snd_seq_query_subscribe_set_index(subs, 0); + + while (snd_seq_query_port_subscribers(ALSASeqHandle, subs) >= 0) { + const snd_seq_addr_t *addr; + addr = snd_seq_query_subscribe_get_addr(subs); + + tX_store("%s\n", indent, addr->client, addr->port); + snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1); + } + + indent[strlen(indent)-1]=0; + tX_store("%s\n", indent); +} + +void tX_midiin::restore_connections(xmlNodePtr node) +{ + snd_seq_addr_t my_addr; + my_addr.client=snd_seq_client_id(ALSASeqHandle); + my_addr.port=portid; + + if (xmlStrcmp(node->name, (xmlChar *) "midi_connections")==0) { + for (xmlNodePtr cur=node->xmlChildrenNode; cur != NULL; cur = cur->next) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrcmp(cur->name, (xmlChar *) "link")==0) { + char *buffer; + int client=-1; + int port=-1; + + buffer=(char *) xmlGetProp(cur, (xmlChar *) "client"); + if (buffer) { + sscanf(buffer, "%i", &client); + } + + buffer=(char *) xmlGetProp(cur, (xmlChar *) "port"); + if (buffer) { + sscanf(buffer, "%i", &port); + } + + if ((port>=0) && (client>=0)) { + snd_seq_addr_t sender_addr; + sender_addr.client=client; + sender_addr.port=port; + + snd_seq_port_subscribe_t *subs; + snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_subscribe_set_sender(subs, &sender_addr); + snd_seq_port_subscribe_set_dest(subs, &my_addr); + + if (snd_seq_subscribe_port(ALSASeqHandle, subs) < 0) { + tX_error("tX_midiin::restore_connections() -> failed to connect to: %d:%d.", port, client); + } + } else { + tX_error("tX_midiin::restore_connections() -> invalid port: %d:%d.", port, client); + } + + } else { + tX_error("tX_midiin::restore_connections() -> invalid element: %s.", cur->name); + } + } + } + } else { + tX_error("tX_midiin::restore_connections() -> invalid XML element."); + } +} + + +extern "C" { + void tX_midiin_store_connections(FILE *rc, char *indent); + void tX_midiin_restore_connections(xmlNodePtr node); +}; + +void tX_midiin_store_connections(FILE *rc, char *indent) +{ + tX_engine::get_instance()->get_midi()->store_connections(rc, indent); +} + +void tX_midiin_restore_connections(xmlNodePtr node) +{ + tX_engine::get_instance()->get_midi()->restore_connections(node); +} #endif // USE_ALSA_MIDI_IN diff --git a/src/tX_midiin.h b/src/tX_midiin.h index 60770c5..71013be 100644 --- a/src/tX_midiin.h +++ b/src/tX_midiin.h @@ -29,6 +29,9 @@ #define _tx_midiin_h 1 #include +#include +#include +#include class vtt_class; class tX_seqpar; @@ -79,6 +82,7 @@ class tX_midiin bool is_open; tX_seqpar *sp_to_learn; GtkWidget *learn_dialog; + int portid; public: tX_midiin(); @@ -99,6 +103,8 @@ class tX_midiin void set_midi_learn_sp(tX_seqpar *); void cancel_midi_learn(); + void store_connections(FILE *rc, char *indent); + void restore_connections(xmlNodePtr node); static gboolean midi_learn_cancel(GtkWidget *, tX_midiin *); static gboolean midi_learn_destroy(GtkWidget *, tX_midiin *); diff --git a/src/tX_seqpar.cc b/src/tX_seqpar.cc index ebc3b3f..242c32a 100644 --- a/src/tX_seqpar.cc +++ b/src/tX_seqpar.cc @@ -51,6 +51,9 @@ tX_seqpar :: tX_seqpar () : bound_midi_event() is_mappable=1; all.push_back(this); last_event_recorded=NULL; + + midi_lower_bound_set=false; + midi_upper_bound_set=false; } void tX_seqpar :: set_mapping_parameters(float max, float min, float scale, int mappable) @@ -77,19 +80,26 @@ void tX_seqpar :: handle_mouse_input(float adjustment) #ifdef USE_ALSA_MIDI_IN void tX_seqpar :: handle_midi_input( const tX_midievent& event ) { - float tmpvalue = -1000; - - //event.print( (string(__FUNCTION__) + " - " + get_name()).c_str() ); + double tmpvalue = -1000; + double max=max_value; + double min=min_value; - if( !is_boolean ) - { + if (midi_upper_bound_set) { + max=midi_upper_bound; + } + + if (midi_lower_bound_set) { + min=midi_lower_bound; + } + + if( !is_boolean ) { switch (event.type) { case tX_midievent::CC: case tX_midievent::CC14: case tX_midievent::PITCHBEND: case tX_midievent::RPN: case tX_midievent::NRPN: - tmpvalue = event.value * (max_value-min_value) + min_value; + tmpvalue = event.value * (max-min) + min; break; case tX_midievent::NOTE: tmpvalue = event.is_noteon; @@ -98,11 +108,9 @@ void tX_seqpar :: handle_midi_input( const tX_midievent& event ) return; } - if (tmpvalue>max_value) tmpvalue=max_value; - else if (tmpvaluemax) tmpvalue=max; + else if (tmpvalueget_name()); } + buffer=(char *) xmlGetProp(node, (xmlChar *) "midiUpperBound"); + if (buffer) { + sscanf(buffer, "%lf", &temp); + set_upper_midi_bound(temp); + } + + buffer=(char *) xmlGetProp(node, (xmlChar *) "midiLowerBound"); + if (buffer) { + sscanf(buffer, "%lf", &temp); + set_lower_midi_bound(temp); + } + buffer=(char *) xmlGetProp(node, (xmlChar *) "midiType"); if (buffer) { if (strcmp("cc", buffer)==0) { @@ -266,7 +287,8 @@ void tX_seqpar :: restore_meta(xmlNodePtr node) { } void tX_seqpar :: store_meta(FILE *rc, gzFile rz) { - char buffer[256]; + char buffer[512]; + char buffer2[256]; if (bound_midi_event.type!=tX_midievent::NONE) { char *type; @@ -284,6 +306,17 @@ void tX_seqpar :: store_meta(FILE *rc, gzFile rz) { } else { sprintf(buffer, "id=\"%i\"", persistence_id); } + + if (midi_upper_bound_set) { + sprintf(buffer2, " midiUpperBound=\"%lf\"", midi_upper_bound); + strcat(buffer, buffer2); + } + + if (midi_lower_bound_set) { + sprintf(buffer2, " midiLowerBound=\"%lf\"", midi_lower_bound); + strcat(buffer, buffer2); + } + tX_store(buffer); } @@ -1036,6 +1069,41 @@ gboolean tX_seqpar::tX_seqpar_press(GtkWidget *widget, GdkEventButton *event, gp gtk_widget_set_sensitive(item, FALSE); } g_signal_connect(item, "activate", (GCallback) tX_seqpar::remove_midi_binding, sp); + + if (!sp->is_boolean) { + item = gtk_menu_item_new(); + gtk_menu_append(menu, item); + gtk_widget_set_sensitive(item, FALSE); + gtk_widget_show(item); + + item = gtk_menu_item_new_with_label("Set Upper MIDI Bound"); + gtk_menu_append(menu, item); + gtk_widget_show(item); + g_signal_connect(item, "activate", (GCallback) tX_seqpar::set_midi_upper_bound, sp); + + item = gtk_menu_item_new_with_label("Reset Upper MIDI Bound"); + gtk_menu_append(menu, item); + gtk_widget_show(item); + g_signal_connect(item, "activate", (GCallback) tX_seqpar::reset_midi_upper_bound, sp); + + if (!sp->midi_upper_bound_set) { + gtk_widget_set_sensitive(item, FALSE); + } + + item = gtk_menu_item_new_with_label("Set Lower MIDI Bound"); + gtk_menu_append(menu, item); + gtk_widget_show(item); + g_signal_connect(item, "activate", (GCallback) tX_seqpar::set_midi_lower_bound, sp); + + item = gtk_menu_item_new_with_label("Reset Lower MIDI Bound"); + gtk_menu_append(menu, item); + gtk_widget_show(item); + g_signal_connect(item, "activate", (GCallback) tX_seqpar::reset_midi_lower_bound, sp); + + if (!sp->midi_lower_bound_set) { + gtk_widget_set_sensitive(item, FALSE); + } + } item = gtk_menu_item_new(); gtk_menu_append(menu, item); @@ -1050,7 +1118,7 @@ gboolean tX_seqpar::tX_seqpar_press(GtkWidget *widget, GdkEventButton *event, gp gtk_widget_show(menu); gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time); - gtk_grab_remove(gtk_grab_get_current()); + //gtk_grab_remove(gtk_grab_get_current()); return TRUE; } @@ -1075,3 +1143,31 @@ gboolean tX_seqpar::learn_midi_binding(GtkWidget *widget, gpointer data) { return TRUE; } + +gboolean tX_seqpar::set_midi_upper_bound(GtkWidget *widget, gpointer data) { + tX_seqpar *sp=(tX_seqpar *) data; + sp->set_upper_midi_bound(sp->get_value()); + + return TRUE; +} + +gboolean tX_seqpar::reset_midi_upper_bound(GtkWidget *widget, gpointer data) { + tX_seqpar *sp=(tX_seqpar *) data; + sp->reset_upper_midi_bound(); + + return TRUE; +} + +gboolean tX_seqpar::set_midi_lower_bound(GtkWidget *widget, gpointer data) { + tX_seqpar *sp=(tX_seqpar *) data; + sp->set_lower_midi_bound(sp->get_value()); + + return TRUE; +} + +gboolean tX_seqpar::reset_midi_lower_bound(GtkWidget *widget, gpointer data) { + tX_seqpar *sp=(tX_seqpar *) data; + sp->reset_lower_midi_bound(); + + return TRUE; +} diff --git a/src/tX_seqpar.h b/src/tX_seqpar.h index 1b7ef14..2e8d02e 100644 --- a/src/tX_seqpar.h +++ b/src/tX_seqpar.h @@ -127,6 +127,12 @@ class tX_seqpar float min_value; float scale_value; + bool midi_upper_bound_set; + double midi_upper_bound; + + bool midi_lower_bound_set; + double midi_lower_bound; + bool is_boolean; public: @@ -134,9 +140,29 @@ class tX_seqpar void restore_meta(xmlNodePtr node); void store_meta(FILE *rc, gzFile rz); + void set_upper_midi_bound(double val) { + midi_upper_bound=val; + midi_upper_bound_set=true; + } + + void reset_upper_midi_bound() { midi_upper_bound_set=false; } + + void set_lower_midi_bound(double val) { + midi_lower_bound=val; + midi_lower_bound_set=true; + } + + void reset_lower_midi_bound() { midi_lower_bound_set=false; } + static gboolean tX_seqpar_press(GtkWidget *widget, GdkEventButton *event, gpointer data); static gboolean remove_midi_binding(GtkWidget *widget, gpointer data); - static gboolean learn_midi_binding(GtkWidget *widget, gpointer data); + static gboolean learn_midi_binding(GtkWidget *widget, gpointer data); + + static gboolean set_midi_upper_bound(GtkWidget *widget, gpointer data); + static gboolean reset_midi_upper_bound(GtkWidget *widget, gpointer data); + + static gboolean set_midi_lower_bound(GtkWidget *widget, gpointer data); + static gboolean reset_midi_lower_bound(GtkWidget *widget, gpointer data); }; class tX_seqpar_update : public tX_seqpar diff --git a/src/tX_vtt.cc b/src/tX_vtt.cc index c5000ae..ca40c47 100644 --- a/src/tX_vtt.cc +++ b/src/tX_vtt.cc @@ -813,8 +813,7 @@ int vtt_class :: set_mix_buffer_size(int no_samples) // printf("mix_out_buffer: %12x\n", mix_out_buffer); - for (vtt=main_list.begin(); vtt!=main_list.end(); vtt++) - { + for (vtt=main_list.begin(); vtt!=main_list.end(); vtt++) { res|=(*vtt)->set_output_buffer_size(no_samples); } @@ -836,22 +835,17 @@ int16_t * vtt_class :: render_all_turntables() pthread_mutex_lock(&render_lock); - if (render_list.size()==0) - { - for (sample=0; samplerender(); max=(*vtt)->max_value; min=max; - for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) - { + for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) { temp=(*vtt)->output_buffer[sample]; mix_buffer[mix_sample]=temp*(*vtt)->res_volume_left; mix_sample++; @@ -865,10 +859,8 @@ int16_t * vtt_class :: render_all_turntables() min*=-1.0; if (min>max) (*vtt)->max_value=min; else (*vtt)->max_value=max; - if ((*vtt)->ec_enable) - { - for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) - { + if ((*vtt)->ec_enable) { + for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) { temp=(*vtt)->ec_output_buffer[sample]; mix_buffer[mix_sample]+=temp*(*vtt)->ec_volume_left; @@ -878,19 +870,13 @@ int16_t * vtt_class :: render_all_turntables() } } - if (master_triggered) - { + if (master_triggered) { pthread_mutex_unlock(&render_lock); - for (vtt=main_list.begin(); vtt!=main_list.end(); vtt++) - { - if ((*vtt)->is_sync_client) - { - if ((*vtt)->sync_countdown) - { + for (vtt=main_list.begin(); vtt!=main_list.end(); vtt++) { + if ((*vtt)->is_sync_client) { + if ((*vtt)->sync_countdown) { (*vtt)->sync_countdown--; - } - else - { + } else { (*vtt)->sync_countdown=(*vtt)->sync_cycles; (*vtt)->trigger(); } @@ -900,14 +886,12 @@ int16_t * vtt_class :: render_all_turntables() } vtt=render_list.begin(); - for (vtt++; vtt!=render_list.end(); vtt++) - { + for (vtt++; vtt!=render_list.end(); vtt++) { (*vtt)->render(); max=(*vtt)->max_value; min=max; - for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) - { + for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) { temp=(*vtt)->output_buffer[sample]; mix_buffer[mix_sample]+=temp*(*vtt)->res_volume_left; mix_sample++; @@ -921,10 +905,8 @@ int16_t * vtt_class :: render_all_turntables() min*=-1.0; if (min>max) (*vtt)->max_value=min; else (*vtt)->max_value=max; - if ((*vtt)->ec_enable) - { - for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) - { + if ((*vtt)->ec_enable) { + for (sample=0, mix_sample=0; sample<(*vtt)->samples_in_outputbuffer; sample++) { temp=(*vtt)->ec_output_buffer[sample]; mix_buffer[mix_sample]+=temp*(*vtt)->ec_volume_left; @@ -940,8 +922,7 @@ int16_t * vtt_class :: render_all_turntables() max=mix_max_l; min=max; - for (sample=0; sample0)&&(!mix_solo))); } + + static f_prec *get_mix_buffer() { return mix_buffer; } }; #endif diff --git a/src/tX_vttfx.cc b/src/tX_vttfx.cc index 34ab35e..70f9481 100644 --- a/src/tX_vttfx.cc +++ b/src/tX_vttfx.cc @@ -279,7 +279,7 @@ void vtt_fx_ladspa :: save (FILE *rc, gzFile rz, char *indent) { store_int("ladspa_id", ID); for (sp=controls.begin(); sp!=controls.end(); sp++) { - store_float_id("param", (*sp)->get_value(), (*sp)->get_persistence_id()); + store_float_sp("param", (*sp)->get_value(), (*(*sp))); } store_bool("panel_hidden", panel->is_hidden()); @@ -293,7 +293,6 @@ void vtt_fx_ladspa :: load(xmlDocPtr doc, xmlNodePtr node) { bool hidden=false; list :: iterator sp=controls.begin(); int elementFound; - guint32 pid=0; double val; for (xmlNodePtr cur=node->xmlChildrenNode; cur!=NULL; cur=cur->next) { @@ -302,21 +301,16 @@ void vtt_fx_ladspa :: load(xmlDocPtr doc, xmlNodePtr node) { restore_int("ladspa_id", dummy); restore_bool("panel_hidden", hidden); + if ((!elementFound) && (xmlStrcmp(cur->name, (xmlChar *) "param")==0)) { val=0; - elementFound=1; + elementFound=0; + double dvalue; if (sp==controls.end()) { tX_warning("found unexpected parameters for ladspa plugin [%i].", dummy); - } else { - char *buff=(char *) xmlGetProp(cur, (xmlChar *) "id"); - sscanf(buff, "%i", &pid); - - if (xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)) { - sscanf((char *) xmlNodeListGetString(doc, cur->xmlChildrenNode, 1), "%lf", &val); - } - (*sp)->set_persistence_id(pid); - (*sp)->do_exec(val); + } else { + restore_float_id("param", val, (*(*sp)), (*sp)->do_exec(val)); (*sp)->do_update_graphics(); sp++; } diff --git a/terminatorX.glade b/terminatorX.glade index 73b4792..b5412e9 100644 --- a/terminatorX.glade +++ b/terminatorX.glade @@ -1682,7 +1682,7 @@ 4 True - 4 + 5 2 False 2 @@ -1869,6 +1869,52 @@ + + + + True + Restore MIDI connections: + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + 1 + 4 + 5 + fill + + + + + + + True + When enabled soundfiles will be playedback when selected in a file dialog (before loading them). + True + Enabled + True + GTK_RELIEF_NORMAL + False + False + True + + + 1 + 2 + 4 + 5 + fill + + + False -- 2.25.4