/* * Test app to load & run all LADSPA plugins for one cycle. * * Compile with: * gcc `pkg-config --cflags --libs gmodule-2.0` -lm -o test-ladspa test-ladspa.c * * Simply run it with './test-ladspa' to test all plugins. * Run it with './test-ladspa -p PACKAGE' to test a particular package. * (Run ./test-ladspa --help to see the list of known packages.) * * Run it with 'valgrind --tool=memcheck ./test-ladspa' to spot memory errors. */ #include #include #include #include #include /* The default places to look for LADSPA plugins, if LADSPA_PATH isn't set. */ #define DEFAULT_LADSPA_PATH "/usr/lib/ladspa:/usr/local/lib/ladspa" /* The sample rate to pass to the plugins. We don't output audio so it's not too important. */ #define SAMPLE_RATE 48000 /* The number of samples to use for the run. Again, not too important. */ #define NUM_SAMPLES 256 static char *package = NULL; static char **file_list = NULL; /* These are the lists of files containing the plugins in the various packages. I got most of these using 'rpm -ql '. There may be more packages around that I haven't got installed at present. */ static char *amb_file_list[] = { "ambisonic1.so", "ambisonic2.so", NULL }; static char *blop_file_list[] = { "adsr_1653.so", "adsr_1680.so", "amp_1654.so", "branch_1673.so", "dahdsr_2021.so", "difference_2030.so", "fmod_1656.so", "interpolator_1660.so", "lp4pole_1671.so", "parabola_1649_data.so", "product_1668.so", "pulse_1645.so", "quantiser100_2029.so", "quantiser20_2027.so", "quantiser50_2028.so", "random_1661.so", "ratio_2034.so", "sawtooth_1641_data.so", "sawtooth_1641.so", "sequencer16_1677.so", "sequencer32_1676.so", "sequencer64_1675.so", "square_1643_data.so", "square_1643.so", "sum_1665.so", "sync_pulse_2023.so", "sync_square_1678.so", "tracker_2025.so", "triangle_1649.so", NULL }; static char *calf_file_list[] = { "calf.so", NULL }; static char *caps_file_list[] = { "caps.so", NULL }; static char *cmt_file_list[] = { "cmt.so", NULL }; static char *fil_file_list[] = { "filters.so", NULL }; static char *ladspa_file_list[] = { "amp.so", "delay.so", "filter.so", "noise.so", "sine.so", NULL }; static char *mcp_file_list[] = { "cs_chorus.so", "cs_phaser.so", "mvchpf24.so", "mvclpf24.so", NULL }; static char *rev_file_list[] = { "g2reverb.so", NULL }; static char *swh_file_list[] = { "alias_1407.so", "allpass_1895.so", "am_pitchshift_1433.so", "amp_1181.so", "analogue_osc_1416.so", "bandpass_a_iir_1893.so", "bandpass_iir_1892.so", "bode_shifter_1431.so", "bode_shifter_cv_1432.so", "butterworth_1902.so", "chebstortion_1430.so", "comb_1190.so", "comb_1887.so", "comb_splitter_1411.so", "const_1909.so", "crossover_dist_1404.so", "dc_remove_1207.so", "decay_1886.so", "decimator_1202.so", "declip_1195.so", "delay_1898.so", "delayorama_1402.so", "diode_1185.so", "divider_1186.so", "dj_eq_1901.so", "dj_flanger_1438.so", "dyson_compress_1403.so", "fad_delay_1192.so", "fast_lookahead_limiter_1913.so", "flanger_1191.so", "fm_osc_1415.so", "foldover_1213.so", "foverdrive_1196.so", "freq_tracker_1418.so", "gate_1410.so", "giant_flange_1437.so", "gong_1424.so", "gong_beater_1439.so", "gsm_1215.so", "gverb_1216.so", "hard_limiter_1413.so", "harmonic_gen_1220.so", "hermes_filter_1200.so", "highpass_iir_1890.so", "hilbert_1440.so", "imp_1199.so", "impulse_1885.so", "inv_1429.so", "karaoke_1409.so", "latency_1914.so", "lcr_delay_1436.so", "lowpass_iir_1891.so", "ls_filter_1908.so", "matrix_ms_st_1421.so", "matrix_spatialiser_1422.so", "matrix_st_ms_1420.so", "mbeq_1197.so", "mod_delay_1419.so", "multivoice_chorus_1201.so", "notch_iir_1894.so", "phasers_1217.so", "pitch_scale_1193.so", "pitch_scale_1194.so", "plate_1423.so", "pointer_cast_1910.so", "rate_shifter_1417.so", "retro_flange_1208.so", "revdelay_1605.so", "ringmod_1188.so", "satan_maximiser_1408.so", "sc1_1425.so", "sc2_1426.so", "sc3_1427.so", "sc4_1882.so", "sc4m_1916.so", "se4_1883.so", "shaper_1187.so", "sifter_1210.so", "sin_cos_1881.so", "single_para_1203.so", "sinus_wavewrapper_1198.so", "smooth_decimate_1414.so", "split_1406.so", "step_muxer_1212.so", "surround_encoder_1401.so", "svf_1214.so", "tape_delay_1211.so", "transient_1206.so", "triple_para_1204.so", "valve_1209.so", "valve_rect_1405.so", "vynil_1905.so", "wave_terrain_1412.so", "xfade_1915.so", "zm1_1428.so", NULL }; static char *tap_file_list[] = { "tap_autopan.so", "tap_chorusflanger.so", "tap_deesser.so", "tap_doubler.so", "tap_dynamics_m.so", "tap_dynamics_st.so", "tap_echo.so", "tap_eq.so", "tap_eqbw.so", "tap_limiter.so", "tap_pinknoise.so", "tap_pitch.so", "tap_reflector.so", "tap_reverb.so", "tap_rotspeak.so", "tap_sigmoid.so", "tap_tremolo.so", "tap_tubewarmth.so", "tap_vibrato.so", NULL }; static char *vco_file_list[] = { "blvco.so", "vco_sawpulse.so", NULL }; static gpointer create_buffer_for_port (LADSPA_Descriptor *descriptor, gint port_num) { LADSPA_PortDescriptor port_descriptor; const LADSPA_PortRangeHint *port_range_hint; LADSPA_PortRangeHintDescriptor hint_descriptor; gfloat *buffer, minimum, maximum, value = 0.0F; gint i; port_descriptor = descriptor->PortDescriptors[port_num]; port_range_hint = &descriptor->PortRangeHints[port_num]; hint_descriptor = port_range_hint->HintDescriptor; if (port_descriptor & LADSPA_PORT_AUDIO) { buffer = g_new (float, NUM_SAMPLES); for (i = 0; i < NUM_SAMPLES; i++) buffer[i] = 0.0F; } else { buffer = g_new (float, 1); minimum = port_range_hint->LowerBound; if (LADSPA_IS_HINT_SAMPLE_RATE (hint_descriptor)) minimum *= SAMPLE_RATE; maximum = port_range_hint->UpperBound; if (LADSPA_IS_HINT_SAMPLE_RATE (hint_descriptor)) maximum *= SAMPLE_RATE; /* Calculate the default value. See ladspa.h for the formulas. We could try testing plugins with all kinds of input data later. */ if (LADSPA_IS_HINT_HAS_DEFAULT (hint_descriptor)) { if (LADSPA_IS_HINT_DEFAULT_MINIMUM (hint_descriptor)) value = minimum; else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM (hint_descriptor)) value = maximum; else if (LADSPA_IS_HINT_DEFAULT_0 (hint_descriptor)) value = 0; else if (LADSPA_IS_HINT_DEFAULT_1 (hint_descriptor)) value = 1; else if (LADSPA_IS_HINT_DEFAULT_100 (hint_descriptor)) value = 100; else if (LADSPA_IS_HINT_DEFAULT_440 (hint_descriptor)) value = 440; else if (LADSPA_IS_HINT_DEFAULT_LOW (hint_descriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC (hint_descriptor)) value = exp (log (minimum) * 0.75 + log (maximum) * 0.25); else value = (minimum * 0.75 + maximum * 0.25); } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE (hint_descriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC (hint_descriptor)) value = exp (log (minimum) * 0.5 + log (maximum) * 0.5); else value = (minimum * 0.5 + maximum * 0.5); } else if (LADSPA_IS_HINT_DEFAULT_HIGH (hint_descriptor)) { if (LADSPA_IS_HINT_LOGARITHMIC (hint_descriptor)) value = exp (log (minimum) * 0.25 + log (maximum) * 0.75); else value = (minimum * 0.25 + maximum * 0.75); } } *buffer = value; } return buffer; } /* Returns TRUE if a plugin should be skipped. */ static gboolean skip_file (const gchar *filename) { gint i; if (file_list) { for (i = 0; file_list[i]; i++) { if (!strcmp (file_list[i], filename)) return FALSE; } return TRUE; } return FALSE; } static void test_ladspa_plugin (const gchar *filename, LADSPA_Descriptor *descriptor) { LADSPA_Handle ladspa_instance; gint num_ports, port_num; GList *buffers = NULL, *elem; gpointer buffer; #if 1 g_print ("###############################################################################\n"); #endif #if 1 g_print ("Testing %i: %s (%s)\n", descriptor->UniqueID, descriptor->Name ? descriptor->Name : "Unnamed", filename); #endif /* Instantiate the plugin. */ ladspa_instance = descriptor->instantiate (descriptor, SAMPLE_RATE); /* Activate it, if necessary. */ if (descriptor->activate) descriptor->activate (ladspa_instance); /* Connect up the ports. */ num_ports = descriptor->PortCount; for (port_num = 0; port_num < num_ports; port_num++) { buffer = create_buffer_for_port (descriptor, port_num); descriptor->connect_port (ladspa_instance, port_num, buffer); buffers = g_list_prepend (buffers, buffer); } /* Do one run of the plugin. */ descriptor->run (ladspa_instance, NUM_SAMPLES); /* Free the buffers. */ for (elem = buffers; elem; elem = elem->next) g_free (elem->data); g_list_free (buffers); /* Deactivate it, if necessary. */ if (descriptor->deactivate) descriptor->deactivate (ladspa_instance); /* Free any resources. */ descriptor->cleanup (ladspa_instance); } static void scan_ladspa_plugin (gchar *pathname, const gchar *filename) { GModule *module; LADSPA_Descriptor_Function ladspa_func; LADSPA_Descriptor *descriptor; gpointer symbol; gint i; module = g_module_open (pathname, G_MODULE_BIND_LAZY); if (!module) return; /* Lookup the special "ladspa_descriptor" symbol. */ if (g_module_symbol (module, "ladspa_descriptor", &symbol)) { ladspa_func = (LADSPA_Descriptor_Function) symbol; /* Get the LADSPA descriptor for each module in the library. */ for (i = 0; ; i++) { descriptor = (LADSPA_Descriptor*) ladspa_func (i); if (!descriptor) break; test_ladspa_plugin (filename, descriptor); } } g_module_close (module); } static void scan_ladspa_directory (gchar *directory) { GDir *dir; const char *filename; char *pathname; dir = g_dir_open (directory, 0, NULL); if (!dir) return; /* Step through the files in the LADSPA plugin directory. */ while ((filename = g_dir_read_name (dir))) { if (skip_file (filename)) { #if 0 g_print ("Skipping file: %s\n", filename); #endif continue; } pathname = g_strdup_printf ("%s%c%s", directory, G_DIR_SEPARATOR, filename); scan_ladspa_plugin (pathname, filename); g_free (pathname); } g_dir_close (dir); } static void test_ladspa_plugins () { const gchar *path; gchar **directories; gint directory_num; /* Use the $LADSPA_PATH environment variable, or the default path if that hasn't been set. */ path = g_getenv ("LADSPA_PATH"); if (!path) path = DEFAULT_LADSPA_PATH; /* Split the path into an array of directories. */ directories = g_strsplit (path, ":", -1); /* Scan each directory in turn. */ for (directory_num = 0; directories[directory_num]; directory_num++) scan_ladspa_directory (directories[directory_num]); /* Free the array. */ g_strfreev (directories); } /* Command-line options. */ static GOptionEntry entries[] = { { "package", 'p', 0, G_OPTION_ARG_STRING, &package, "Package to test (amb, blop, calf, caps, cmt, fil, ladspa, mcp, rev, swh, tap or vco)", "PACKAGE" }, { NULL } }; int main (int argc, char *argv[]) { GOptionContext *context; GError *error = NULL; context = g_option_context_new ("- test LADSPA plugins"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print ("option parsing failed: %s\n", error->message); exit (1); } /* If a package is specified, set the appropriate file list to scan. */ if (package) { if (!strcmp (package, "amb")) file_list = amb_file_list; else if (!strcmp (package, "blop")) file_list = blop_file_list; else if (!strcmp (package, "calf")) file_list = calf_file_list; else if (!strcmp (package, "caps")) file_list = caps_file_list; else if (!strcmp (package, "cmt")) file_list = cmt_file_list; else if (!strcmp (package, "fil")) file_list = fil_file_list; else if (!strcmp (package, "ladspa")) file_list = ladspa_file_list; else if (!strcmp (package, "mcp")) file_list = mcp_file_list; else if (!strcmp (package, "rev")) file_list = rev_file_list; else if (!strcmp (package, "swh")) file_list = swh_file_list; else if (!strcmp (package, "tap")) file_list = tap_file_list; else if (!strcmp (package, "vco")) file_list = vco_file_list; } test_ladspa_plugins (); return 0; }