From 180843f9bd28b191c7404245ba0a121107992511 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 23 Feb 2020 20:48:02 +0100 Subject: Also move Lua scripts to share subfolder --- scripts/HiAndLowPass.lua | 396 ---------------- scripts/README | 29 -- scripts/__convolv.lua | 59 --- scripts/__fluidsynth1.lua | 51 --- scripts/__plugin_modulation.lua | 46 -- scripts/__readable.lua | 47 -- scripts/_amp1.lua | 47 -- scripts/_amp2.lua | 51 --- scripts/_amp3.lua | 44 -- scripts/_biquad_filter.lua | 280 ------------ scripts/_calc_dsp_statistics.lua | 27 -- scripts/_cron.lua | 37 -- scripts/_dialog_test.lua | 95 ---- scripts/_dsp_plugin_communication.lua | 78 ---- scripts/_dump_latency.lua | 77 ---- scripts/_dump_midiregion.lua | 20 - scripts/_dump_playlists.lua | 31 -- scripts/_export_plugins_on_save.lua | 44 -- scripts/_export_tracks.lua | 10 - scripts/_fan_out_instrument.lua | 69 --- scripts/_find_nonzero_sample.lua | 74 --- scripts/_fir.lua | 36 -- scripts/_hook_test.lua | 41 -- scripts/_insert_region_gaps.lua | 62 --- scripts/_midi_lfo.lua | 74 --- scripts/_midi_rec_start.lua | 37 -- scripts/_midi_rewrite.lua | 34 -- scripts/_midifilter.lua | 39 -- scripts/_midigenerator.lua | 48 -- scripts/_midigenerator2.lua | 34 -- scripts/_osc_hook_example.lua | 52 --- scripts/_plot_graph.lua | 24 - scripts/_pong.lua | 260 ----------- scripts/_rawmidi.lua | 110 ----- scripts/_region_transients.lua | 16 - scripts/_remember_file.lua | 37 -- scripts/_rewind.lua | 12 - scripts/_rgh_midi_track_trick.lua | 81 ---- scripts/_route_template_generic_audio.lua | 119 ----- scripts/_route_template_generic_midi.lua | 78 ---- scripts/_rubberband_swing.lua | 175 -------- scripts/_session_load_hook.lua | 32 -- scripts/_session_test.lua | 34 -- scripts/_smash.lua | 31 -- scripts/_sort_tracks_by_name.lua | 36 -- scripts/_spike_synth.lua | 37 -- scripts/_split_benchmark.lua | 58 --- scripts/_stereo_to_mono.lua | 57 --- scripts/_system_exec.lua | 20 - scripts/_tempo_map_dump.lua | 14 - scripts/_toggle_monitor_section.lua | 10 - scripts/_tx_raw_midi_from_file.lua | 128 ------ scripts/_vamp_example.lua | 63 --- scripts/_vamp_note_example.lua | 68 --- scripts/_vamp_onset_example.lua | 83 ---- scripts/_vamp_tempomap_example.lua | 85 ---- scripts/_varispeed_callback.lua | 32 -- scripts/_vca_slave_assign.lua | 73 --- scripts/a_slow_mute.lua | 66 --- scripts/access_action.lua | 37 -- scripts/add_filters.lua | 54 --- scripts/addscopes.lua | 75 ---- scripts/amp4.lua | 119 ----- scripts/bounce_replace.lua | 101 ----- scripts/bypass_all_plugins.lua | 22 - scripts/create_drum_tracks.lua | 73 --- scripts/delete_xrun_markers.lua | 40 -- scripts/export_mp4chaps.lua | 78 ---- scripts/faders_to_trims.lua | 72 --- scripts/jump_to_marker.lua | 67 --- scripts/lfo_automation.lua | 189 -------- scripts/list_plugins.lua | 50 --- scripts/ltc_reader.lua | 82 ---- scripts/meter_tap.lua | 42 -- scripts/midi_cc_to_automation.lua | 130 ------ scripts/midi_remap.lua | 97 ---- scripts/midimon.lua | 213 --------- scripts/mixer_screenshot.lua | 46 -- scripts/mixer_settings_recall.lua | 470 ------------------- scripts/mixer_settings_store.lua | 383 ---------------- scripts/mute_all_tracks.lua | 21 - scripts/new_playlist.lua | 17 - scripts/noisegen.lua | 111 ----- scripts/normalize_all_tracks.lua | 58 --- scripts/notch_bank.lua | 125 ------ scripts/periodic_backup.lua | 47 -- scripts/post_export_save_hook.lua | 26 -- scripts/preare_record_example.lua | 82 ---- scripts/remove_unknown_procs.lua | 44 -- scripts/reset_mixer.lua | 249 ---------- scripts/s_chanmap.lua | 34 -- scripts/s_fader_automation.lua | 58 --- scripts/s_foreach_track.lua | 10 - scripts/s_group_color.lua | 11 - scripts/s_import_files.lua | 14 - scripts/s_plugin_automation.lua | 43 -- scripts/s_plugin_reorder.lua | 23 - scripts/s_pluginutils.lua | 57 --- scripts/s_portengine.lua | 35 -- scripts/s_region_gain.lua | 82 ---- scripts/s_region_gain2.lua | 63 --- scripts/s_replaceplugin.lua | 11 - scripts/s_selection.lua | 60 --- scripts/s_showhide_track.lua | 23 - scripts/s_thin_automation.lua | 47 -- scripts/s_timecode.lua | 21 - scripts/s_track_props.lua | 48 -- scripts/s_vamp_plugin_index.lua | 45 -- scripts/s_whoami.lua | 22 - scripts/scope.lua | 229 ---------- scripts/select_every_2nd_region.lua | 61 --- scripts/send_to_bus.lua | 41 -- scripts/session_template_advanced.lua | 59 --- scripts/session_template_record.lua | 60 --- scripts/set_automation_mode.lua | 83 ---- scripts/singen.lua | 94 ---- scripts/spectrogram.lua | 363 --------------- scripts/split_all_markers.lua | 122 ----- scripts/stop_at_marker.lua | 49 -- scripts/store_recall_mixer.lua | 575 ------------------------ scripts/synth1.lua | 100 ----- scripts/tomsloop.lua | 315 ------------- scripts/track_organizer.lua | 100 ----- scripts/transparent_regions.lua | 28 -- scripts/vamp_audio_to_midi.lua | 112 ----- scripts/voice_activate.lua | 53 --- scripts/wscript | 16 - share/scripts/HiAndLowPass.lua | 396 ++++++++++++++++ share/scripts/README | 29 ++ share/scripts/__convolv.lua | 59 +++ share/scripts/__fluidsynth1.lua | 51 +++ share/scripts/__plugin_modulation.lua | 46 ++ share/scripts/__readable.lua | 47 ++ share/scripts/_amp1.lua | 47 ++ share/scripts/_amp2.lua | 51 +++ share/scripts/_amp3.lua | 44 ++ share/scripts/_biquad_filter.lua | 280 ++++++++++++ share/scripts/_calc_dsp_statistics.lua | 27 ++ share/scripts/_cron.lua | 37 ++ share/scripts/_dialog_test.lua | 95 ++++ share/scripts/_dsp_plugin_communication.lua | 78 ++++ share/scripts/_dump_latency.lua | 77 ++++ share/scripts/_dump_midiregion.lua | 20 + share/scripts/_dump_playlists.lua | 31 ++ share/scripts/_export_plugins_on_save.lua | 44 ++ share/scripts/_export_tracks.lua | 10 + share/scripts/_fan_out_instrument.lua | 69 +++ share/scripts/_find_nonzero_sample.lua | 74 +++ share/scripts/_fir.lua | 36 ++ share/scripts/_hook_test.lua | 41 ++ share/scripts/_insert_region_gaps.lua | 62 +++ share/scripts/_midi_lfo.lua | 74 +++ share/scripts/_midi_rec_start.lua | 37 ++ share/scripts/_midi_rewrite.lua | 34 ++ share/scripts/_midifilter.lua | 39 ++ share/scripts/_midigenerator.lua | 48 ++ share/scripts/_midigenerator2.lua | 34 ++ share/scripts/_osc_hook_example.lua | 52 +++ share/scripts/_plot_graph.lua | 24 + share/scripts/_pong.lua | 260 +++++++++++ share/scripts/_rawmidi.lua | 110 +++++ share/scripts/_region_transients.lua | 16 + share/scripts/_remember_file.lua | 37 ++ share/scripts/_rewind.lua | 12 + share/scripts/_rgh_midi_track_trick.lua | 81 ++++ share/scripts/_route_template_generic_audio.lua | 119 +++++ share/scripts/_route_template_generic_midi.lua | 78 ++++ share/scripts/_rubberband_swing.lua | 175 ++++++++ share/scripts/_session_load_hook.lua | 32 ++ share/scripts/_session_test.lua | 34 ++ share/scripts/_smash.lua | 31 ++ share/scripts/_sort_tracks_by_name.lua | 36 ++ share/scripts/_spike_synth.lua | 37 ++ share/scripts/_split_benchmark.lua | 58 +++ share/scripts/_stereo_to_mono.lua | 57 +++ share/scripts/_system_exec.lua | 20 + share/scripts/_tempo_map_dump.lua | 14 + share/scripts/_toggle_monitor_section.lua | 10 + share/scripts/_tx_raw_midi_from_file.lua | 128 ++++++ share/scripts/_vamp_example.lua | 63 +++ share/scripts/_vamp_note_example.lua | 68 +++ share/scripts/_vamp_onset_example.lua | 83 ++++ share/scripts/_vamp_tempomap_example.lua | 85 ++++ share/scripts/_varispeed_callback.lua | 32 ++ share/scripts/_vca_slave_assign.lua | 73 +++ share/scripts/a_slow_mute.lua | 66 +++ share/scripts/access_action.lua | 37 ++ share/scripts/add_filters.lua | 54 +++ share/scripts/addscopes.lua | 75 ++++ share/scripts/amp4.lua | 119 +++++ share/scripts/bounce_replace.lua | 101 +++++ share/scripts/bypass_all_plugins.lua | 22 + share/scripts/create_drum_tracks.lua | 73 +++ share/scripts/delete_xrun_markers.lua | 40 ++ share/scripts/export_mp4chaps.lua | 78 ++++ share/scripts/faders_to_trims.lua | 72 +++ share/scripts/jump_to_marker.lua | 67 +++ share/scripts/lfo_automation.lua | 189 ++++++++ share/scripts/list_plugins.lua | 50 +++ share/scripts/ltc_reader.lua | 82 ++++ share/scripts/meter_tap.lua | 42 ++ share/scripts/midi_cc_to_automation.lua | 130 ++++++ share/scripts/midi_remap.lua | 97 ++++ share/scripts/midimon.lua | 213 +++++++++ share/scripts/mixer_screenshot.lua | 46 ++ share/scripts/mixer_settings_recall.lua | 470 +++++++++++++++++++ share/scripts/mixer_settings_store.lua | 383 ++++++++++++++++ share/scripts/mute_all_tracks.lua | 21 + share/scripts/new_playlist.lua | 17 + share/scripts/noisegen.lua | 111 +++++ share/scripts/normalize_all_tracks.lua | 58 +++ share/scripts/notch_bank.lua | 125 ++++++ share/scripts/periodic_backup.lua | 47 ++ share/scripts/post_export_save_hook.lua | 26 ++ share/scripts/preare_record_example.lua | 82 ++++ share/scripts/remove_unknown_procs.lua | 44 ++ share/scripts/reset_mixer.lua | 249 ++++++++++ share/scripts/s_chanmap.lua | 34 ++ share/scripts/s_fader_automation.lua | 58 +++ share/scripts/s_foreach_track.lua | 10 + share/scripts/s_group_color.lua | 11 + share/scripts/s_import_files.lua | 14 + share/scripts/s_plugin_automation.lua | 43 ++ share/scripts/s_plugin_reorder.lua | 23 + share/scripts/s_pluginutils.lua | 57 +++ share/scripts/s_portengine.lua | 35 ++ share/scripts/s_region_gain.lua | 82 ++++ share/scripts/s_region_gain2.lua | 63 +++ share/scripts/s_replaceplugin.lua | 11 + share/scripts/s_selection.lua | 60 +++ share/scripts/s_showhide_track.lua | 23 + share/scripts/s_thin_automation.lua | 47 ++ share/scripts/s_timecode.lua | 21 + share/scripts/s_track_props.lua | 48 ++ share/scripts/s_vamp_plugin_index.lua | 45 ++ share/scripts/s_whoami.lua | 22 + share/scripts/scope.lua | 229 ++++++++++ share/scripts/select_every_2nd_region.lua | 61 +++ share/scripts/send_to_bus.lua | 41 ++ share/scripts/session_template_advanced.lua | 59 +++ share/scripts/session_template_record.lua | 60 +++ share/scripts/set_automation_mode.lua | 83 ++++ share/scripts/singen.lua | 94 ++++ share/scripts/spectrogram.lua | 363 +++++++++++++++ share/scripts/split_all_markers.lua | 122 +++++ share/scripts/stop_at_marker.lua | 49 ++ share/scripts/store_recall_mixer.lua | 575 ++++++++++++++++++++++++ share/scripts/synth1.lua | 100 +++++ share/scripts/tomsloop.lua | 315 +++++++++++++ share/scripts/track_organizer.lua | 100 +++++ share/scripts/transparent_regions.lua | 28 ++ share/scripts/vamp_audio_to_midi.lua | 112 +++++ share/scripts/voice_activate.lua | 53 +++ share/scripts/wscript | 16 + tools/linux_packaging/build | 2 +- tools/osx_packaging/osx_build | 2 +- 256 files changed, 10297 insertions(+), 10297 deletions(-) delete mode 100644 scripts/HiAndLowPass.lua delete mode 100644 scripts/README delete mode 100644 scripts/__convolv.lua delete mode 100644 scripts/__fluidsynth1.lua delete mode 100644 scripts/__plugin_modulation.lua delete mode 100644 scripts/__readable.lua delete mode 100644 scripts/_amp1.lua delete mode 100644 scripts/_amp2.lua delete mode 100644 scripts/_amp3.lua delete mode 100644 scripts/_biquad_filter.lua delete mode 100644 scripts/_calc_dsp_statistics.lua delete mode 100644 scripts/_cron.lua delete mode 100644 scripts/_dialog_test.lua delete mode 100644 scripts/_dsp_plugin_communication.lua delete mode 100644 scripts/_dump_latency.lua delete mode 100644 scripts/_dump_midiregion.lua delete mode 100644 scripts/_dump_playlists.lua delete mode 100644 scripts/_export_plugins_on_save.lua delete mode 100644 scripts/_export_tracks.lua delete mode 100644 scripts/_fan_out_instrument.lua delete mode 100644 scripts/_find_nonzero_sample.lua delete mode 100644 scripts/_fir.lua delete mode 100644 scripts/_hook_test.lua delete mode 100644 scripts/_insert_region_gaps.lua delete mode 100644 scripts/_midi_lfo.lua delete mode 100644 scripts/_midi_rec_start.lua delete mode 100644 scripts/_midi_rewrite.lua delete mode 100644 scripts/_midifilter.lua delete mode 100644 scripts/_midigenerator.lua delete mode 100644 scripts/_midigenerator2.lua delete mode 100644 scripts/_osc_hook_example.lua delete mode 100644 scripts/_plot_graph.lua delete mode 100644 scripts/_pong.lua delete mode 100644 scripts/_rawmidi.lua delete mode 100644 scripts/_region_transients.lua delete mode 100644 scripts/_remember_file.lua delete mode 100644 scripts/_rewind.lua delete mode 100644 scripts/_rgh_midi_track_trick.lua delete mode 100644 scripts/_route_template_generic_audio.lua delete mode 100644 scripts/_route_template_generic_midi.lua delete mode 100644 scripts/_rubberband_swing.lua delete mode 100644 scripts/_session_load_hook.lua delete mode 100644 scripts/_session_test.lua delete mode 100644 scripts/_smash.lua delete mode 100644 scripts/_sort_tracks_by_name.lua delete mode 100644 scripts/_spike_synth.lua delete mode 100644 scripts/_split_benchmark.lua delete mode 100644 scripts/_stereo_to_mono.lua delete mode 100644 scripts/_system_exec.lua delete mode 100644 scripts/_tempo_map_dump.lua delete mode 100644 scripts/_toggle_monitor_section.lua delete mode 100644 scripts/_tx_raw_midi_from_file.lua delete mode 100644 scripts/_vamp_example.lua delete mode 100644 scripts/_vamp_note_example.lua delete mode 100644 scripts/_vamp_onset_example.lua delete mode 100644 scripts/_vamp_tempomap_example.lua delete mode 100644 scripts/_varispeed_callback.lua delete mode 100644 scripts/_vca_slave_assign.lua delete mode 100644 scripts/a_slow_mute.lua delete mode 100644 scripts/access_action.lua delete mode 100644 scripts/add_filters.lua delete mode 100644 scripts/addscopes.lua delete mode 100644 scripts/amp4.lua delete mode 100644 scripts/bounce_replace.lua delete mode 100644 scripts/bypass_all_plugins.lua delete mode 100644 scripts/create_drum_tracks.lua delete mode 100644 scripts/delete_xrun_markers.lua delete mode 100644 scripts/export_mp4chaps.lua delete mode 100644 scripts/faders_to_trims.lua delete mode 100644 scripts/jump_to_marker.lua delete mode 100644 scripts/lfo_automation.lua delete mode 100644 scripts/list_plugins.lua delete mode 100644 scripts/ltc_reader.lua delete mode 100644 scripts/meter_tap.lua delete mode 100644 scripts/midi_cc_to_automation.lua delete mode 100644 scripts/midi_remap.lua delete mode 100644 scripts/midimon.lua delete mode 100644 scripts/mixer_screenshot.lua delete mode 100644 scripts/mixer_settings_recall.lua delete mode 100644 scripts/mixer_settings_store.lua delete mode 100644 scripts/mute_all_tracks.lua delete mode 100644 scripts/new_playlist.lua delete mode 100644 scripts/noisegen.lua delete mode 100644 scripts/normalize_all_tracks.lua delete mode 100644 scripts/notch_bank.lua delete mode 100644 scripts/periodic_backup.lua delete mode 100644 scripts/post_export_save_hook.lua delete mode 100644 scripts/preare_record_example.lua delete mode 100644 scripts/remove_unknown_procs.lua delete mode 100644 scripts/reset_mixer.lua delete mode 100644 scripts/s_chanmap.lua delete mode 100644 scripts/s_fader_automation.lua delete mode 100644 scripts/s_foreach_track.lua delete mode 100644 scripts/s_group_color.lua delete mode 100644 scripts/s_import_files.lua delete mode 100644 scripts/s_plugin_automation.lua delete mode 100644 scripts/s_plugin_reorder.lua delete mode 100644 scripts/s_pluginutils.lua delete mode 100644 scripts/s_portengine.lua delete mode 100644 scripts/s_region_gain.lua delete mode 100644 scripts/s_region_gain2.lua delete mode 100644 scripts/s_replaceplugin.lua delete mode 100644 scripts/s_selection.lua delete mode 100644 scripts/s_showhide_track.lua delete mode 100644 scripts/s_thin_automation.lua delete mode 100644 scripts/s_timecode.lua delete mode 100644 scripts/s_track_props.lua delete mode 100644 scripts/s_vamp_plugin_index.lua delete mode 100644 scripts/s_whoami.lua delete mode 100644 scripts/scope.lua delete mode 100644 scripts/select_every_2nd_region.lua delete mode 100644 scripts/send_to_bus.lua delete mode 100644 scripts/session_template_advanced.lua delete mode 100644 scripts/session_template_record.lua delete mode 100644 scripts/set_automation_mode.lua delete mode 100644 scripts/singen.lua delete mode 100644 scripts/spectrogram.lua delete mode 100644 scripts/split_all_markers.lua delete mode 100644 scripts/stop_at_marker.lua delete mode 100644 scripts/store_recall_mixer.lua delete mode 100644 scripts/synth1.lua delete mode 100644 scripts/tomsloop.lua delete mode 100644 scripts/track_organizer.lua delete mode 100644 scripts/transparent_regions.lua delete mode 100644 scripts/vamp_audio_to_midi.lua delete mode 100644 scripts/voice_activate.lua delete mode 100644 scripts/wscript create mode 100644 share/scripts/HiAndLowPass.lua create mode 100644 share/scripts/README create mode 100644 share/scripts/__convolv.lua create mode 100644 share/scripts/__fluidsynth1.lua create mode 100644 share/scripts/__plugin_modulation.lua create mode 100644 share/scripts/__readable.lua create mode 100644 share/scripts/_amp1.lua create mode 100644 share/scripts/_amp2.lua create mode 100644 share/scripts/_amp3.lua create mode 100644 share/scripts/_biquad_filter.lua create mode 100644 share/scripts/_calc_dsp_statistics.lua create mode 100644 share/scripts/_cron.lua create mode 100644 share/scripts/_dialog_test.lua create mode 100644 share/scripts/_dsp_plugin_communication.lua create mode 100644 share/scripts/_dump_latency.lua create mode 100644 share/scripts/_dump_midiregion.lua create mode 100644 share/scripts/_dump_playlists.lua create mode 100644 share/scripts/_export_plugins_on_save.lua create mode 100644 share/scripts/_export_tracks.lua create mode 100644 share/scripts/_fan_out_instrument.lua create mode 100644 share/scripts/_find_nonzero_sample.lua create mode 100644 share/scripts/_fir.lua create mode 100644 share/scripts/_hook_test.lua create mode 100644 share/scripts/_insert_region_gaps.lua create mode 100644 share/scripts/_midi_lfo.lua create mode 100644 share/scripts/_midi_rec_start.lua create mode 100644 share/scripts/_midi_rewrite.lua create mode 100644 share/scripts/_midifilter.lua create mode 100644 share/scripts/_midigenerator.lua create mode 100644 share/scripts/_midigenerator2.lua create mode 100644 share/scripts/_osc_hook_example.lua create mode 100644 share/scripts/_plot_graph.lua create mode 100644 share/scripts/_pong.lua create mode 100644 share/scripts/_rawmidi.lua create mode 100644 share/scripts/_region_transients.lua create mode 100644 share/scripts/_remember_file.lua create mode 100644 share/scripts/_rewind.lua create mode 100644 share/scripts/_rgh_midi_track_trick.lua create mode 100644 share/scripts/_route_template_generic_audio.lua create mode 100644 share/scripts/_route_template_generic_midi.lua create mode 100644 share/scripts/_rubberband_swing.lua create mode 100644 share/scripts/_session_load_hook.lua create mode 100644 share/scripts/_session_test.lua create mode 100644 share/scripts/_smash.lua create mode 100644 share/scripts/_sort_tracks_by_name.lua create mode 100644 share/scripts/_spike_synth.lua create mode 100644 share/scripts/_split_benchmark.lua create mode 100644 share/scripts/_stereo_to_mono.lua create mode 100644 share/scripts/_system_exec.lua create mode 100644 share/scripts/_tempo_map_dump.lua create mode 100644 share/scripts/_toggle_monitor_section.lua create mode 100644 share/scripts/_tx_raw_midi_from_file.lua create mode 100644 share/scripts/_vamp_example.lua create mode 100644 share/scripts/_vamp_note_example.lua create mode 100644 share/scripts/_vamp_onset_example.lua create mode 100644 share/scripts/_vamp_tempomap_example.lua create mode 100644 share/scripts/_varispeed_callback.lua create mode 100644 share/scripts/_vca_slave_assign.lua create mode 100644 share/scripts/a_slow_mute.lua create mode 100644 share/scripts/access_action.lua create mode 100644 share/scripts/add_filters.lua create mode 100644 share/scripts/addscopes.lua create mode 100644 share/scripts/amp4.lua create mode 100644 share/scripts/bounce_replace.lua create mode 100644 share/scripts/bypass_all_plugins.lua create mode 100644 share/scripts/create_drum_tracks.lua create mode 100644 share/scripts/delete_xrun_markers.lua create mode 100644 share/scripts/export_mp4chaps.lua create mode 100644 share/scripts/faders_to_trims.lua create mode 100644 share/scripts/jump_to_marker.lua create mode 100644 share/scripts/lfo_automation.lua create mode 100644 share/scripts/list_plugins.lua create mode 100644 share/scripts/ltc_reader.lua create mode 100644 share/scripts/meter_tap.lua create mode 100644 share/scripts/midi_cc_to_automation.lua create mode 100644 share/scripts/midi_remap.lua create mode 100644 share/scripts/midimon.lua create mode 100644 share/scripts/mixer_screenshot.lua create mode 100644 share/scripts/mixer_settings_recall.lua create mode 100644 share/scripts/mixer_settings_store.lua create mode 100644 share/scripts/mute_all_tracks.lua create mode 100644 share/scripts/new_playlist.lua create mode 100644 share/scripts/noisegen.lua create mode 100644 share/scripts/normalize_all_tracks.lua create mode 100644 share/scripts/notch_bank.lua create mode 100644 share/scripts/periodic_backup.lua create mode 100644 share/scripts/post_export_save_hook.lua create mode 100644 share/scripts/preare_record_example.lua create mode 100644 share/scripts/remove_unknown_procs.lua create mode 100644 share/scripts/reset_mixer.lua create mode 100644 share/scripts/s_chanmap.lua create mode 100644 share/scripts/s_fader_automation.lua create mode 100644 share/scripts/s_foreach_track.lua create mode 100644 share/scripts/s_group_color.lua create mode 100644 share/scripts/s_import_files.lua create mode 100644 share/scripts/s_plugin_automation.lua create mode 100644 share/scripts/s_plugin_reorder.lua create mode 100644 share/scripts/s_pluginutils.lua create mode 100644 share/scripts/s_portengine.lua create mode 100644 share/scripts/s_region_gain.lua create mode 100644 share/scripts/s_region_gain2.lua create mode 100644 share/scripts/s_replaceplugin.lua create mode 100644 share/scripts/s_selection.lua create mode 100644 share/scripts/s_showhide_track.lua create mode 100644 share/scripts/s_thin_automation.lua create mode 100644 share/scripts/s_timecode.lua create mode 100644 share/scripts/s_track_props.lua create mode 100644 share/scripts/s_vamp_plugin_index.lua create mode 100644 share/scripts/s_whoami.lua create mode 100644 share/scripts/scope.lua create mode 100644 share/scripts/select_every_2nd_region.lua create mode 100644 share/scripts/send_to_bus.lua create mode 100644 share/scripts/session_template_advanced.lua create mode 100644 share/scripts/session_template_record.lua create mode 100644 share/scripts/set_automation_mode.lua create mode 100644 share/scripts/singen.lua create mode 100644 share/scripts/spectrogram.lua create mode 100644 share/scripts/split_all_markers.lua create mode 100644 share/scripts/stop_at_marker.lua create mode 100644 share/scripts/store_recall_mixer.lua create mode 100644 share/scripts/synth1.lua create mode 100644 share/scripts/tomsloop.lua create mode 100644 share/scripts/track_organizer.lua create mode 100644 share/scripts/transparent_regions.lua create mode 100644 share/scripts/vamp_audio_to_midi.lua create mode 100644 share/scripts/voice_activate.lua create mode 100644 share/scripts/wscript diff --git a/scripts/HiAndLowPass.lua b/scripts/HiAndLowPass.lua deleted file mode 100644 index 75704d9701..0000000000 --- a/scripts/HiAndLowPass.lua +++ /dev/null @@ -1,396 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-High/Low Pass Filter", - category = "Filter", - license = "GPLv2", - author = "Ardour Team", - description = [[High and Low Pass Filter with de-zipped controls, written in Ardour-Lua]] -} - -function dsp_ioconfig () - return - { - -- allow any number of I/O as long as port-count matches - { audio_in = -1, audio_out = -1}, - } -end - - -function dsp_params () - return - { - { ["type"] = "input", name = "High Pass Steepness", min = 0, max = 4, default = 1, enum = true, scalepoints = - { - ["Off"] = 0, - ["12dB/oct"] = 1, - ["24dB/oct"] = 2, - ["36dB/oct"] = 3, - ["48dB/oct"] = 4, - } - }, - { ["type"] = "input", name = "High Pass Cut off frequency", min = 5, max = 20000, default = 100, unit="Hz", logarithmic = true }, - { ["type"] = "input", name = "High Pass Resonance", min = 0.1, max = 6, default = .707, logarithmic = true }, - - { ["type"] = "input", name = "Low Pass Steepness", min = 0, max = 4, default = 1, enum = true, scalepoints = - { - ["Off"] = 0, - ["12dB/oct"] = 1, - ["24dB/oct"] = 2, - ["36dB/oct"] = 3, - ["48dB/oct"] = 4, - } - }, - { ["type"] = "input", name = "Low Pass Cut off frequency", min = 20, max = 20000, default = 18000, unit="Hz", logarithmic = true }, - { ["type"] = "input", name = "Low Pass Resonance", min = 0.1, max = 6, default = .707, logarithmic = true }, - } -end - --- these globals are *not* shared between DSP and UI -local hp = {} -- the biquad high-pass filter instances (DSP) -local lp = {} -- the biquad high-pass filter instances (DSP) -local filt = nil -- the biquad filter instance (GUI, response) -local cur = {0, 0, 0, 0, 0, 0} -- current parameters -local lpf = 0.03 -- parameter low-pass filter time-constant -local chn = 0 -- channel/filter count -local lpf_chunk = 0 -- chunk size for audio processing when interpolating parameters -local max_freq = 20000 - -local mem = nil -- memory x-fade buffer - -function dsp_init (rate) - -- allocate some mix-buffer - mem = ARDOUR.DSP.DspShm (8192) - - -- max allowed cut-off frequency - max_freq = .499 * rate - - -- create a table of objects to share with the GUI - local tbl = {} - tbl['samplerate'] = rate - tbl['max_freq'] = max_freq - self:table ():set (tbl) - - - -- Parameter smoothing: we want to filter out parameter changes that are - -- faster than 15Hz, and interpolate between parameter values. - -- For performance reasons, we want to ensure that two consecutive values - -- of the interpolated "steepness" are less that 1 apart. By choosing the - -- interpolation chunk size to be 64 in most cases, but 32 if the rate is - -- strictly less than 22kHz (there's only 8kHz in standard rates), we can - -- ensure that steepness interpolation will never change the parameter by - -- more than ~0.86. - lpf_chunk = 64 - if rate < 22000 then lpf_chunk = 32 end - -- We apply a discrete version of the standard RC low-pass, with a cutoff - -- frequency of 15Hz. For more information about the underlying math, see - -- https://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization - -- (here Δt is lpf_chunk / rate) - local R = 2 * math.pi * lpf_chunk * 15 -- Hz - lpf = R / (R + rate) -end - -function dsp_configure (ins, outs) - assert (ins:n_audio () == outs:n_audio ()) - local tbl = self:table ():get () -- get shared memory table - - chn = ins:n_audio () - cur = {0, 0, 0, 0, 0, 0} - - hp = {} - lp = {} - - collectgarbage () - - for c = 1, chn do - hp[c] = {} - lp[c] = {} - -- initialize filters - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad - - -- A different Biquad is needed for each pass and channel because they - -- remember the last two samples seen during the last call of Biquad:run(). - -- For continuity these have to come from the previous audio chunk of the - -- same channel and pass and would be clobbered if the same Biquad was - -- called several times by cycle. - for k = 1,4 do - hp[c][k] = ARDOUR.DSP.Biquad (tbl['samplerate']) - lp[c][k] = ARDOUR.DSP.Biquad (tbl['samplerate']) - end - end -end - -function santize_params (ctrl) - -- don't allow manual cross-fades. enforce enums - ctrl[1] = math.floor(ctrl[1] + .5) - ctrl[4] = math.floor(ctrl[4] + .5) - - -- high pass, clamp range - ctrl[2] = math.min (max_freq, math.max (5, ctrl[2])) - ctrl[3] = math.min (6, math.max (0.1, ctrl[3])) - - -- low pass, clamp range - ctrl[5] = math.min (max_freq, math.max (20, ctrl[5])) - ctrl[6] = math.min (6, math.max (0.1, ctrl[6])) - return ctrl -end - --- helper functions for parameter interpolation -function param_changed (ctrl) - for p = 1,6 do - if ctrl[p] ~= cur[p] then - return true - end - end - return false -end - -function low_pass_filter_param (old, new, limit) - if math.abs (old - new) < limit then - return new - else - return old + lpf * (new - old) - end -end - --- apply parameters, re-compute filter coefficients if needed -function apply_params (ctrl) - if not param_changed (ctrl) then - return - end - - -- low-pass filter ctrl parameter values, smooth transition - cur[1] = low_pass_filter_param (cur[1], ctrl[1], 0.05) -- HP order x-fade - cur[2] = low_pass_filter_param (cur[2], ctrl[2], 1.0) -- HP freq/Hz - cur[3] = low_pass_filter_param (cur[3], ctrl[3], 0.01) -- HP quality - cur[4] = low_pass_filter_param (cur[4], ctrl[4], 0.05) -- LP order x-fade - cur[5] = low_pass_filter_param (cur[5], ctrl[5], 1.0) -- LP freq/Hz - cur[6] = low_pass_filter_param (cur[6], ctrl[6], 0.01) -- LP quality - - for c = 1, chn do - for k = 1,4 do - hp[c][k]:compute (ARDOUR.DSP.BiquadType.HighPass, cur[2], cur[3], 0) - lp[c][k]:compute (ARDOUR.DSP.BiquadType.LowPass, cur[5], cur[6], 0) - end - end -end - - --- the actual DSP callback -function dsp_run (ins, outs, n_samples) - assert (n_samples <= 8192) - assert (#ins == chn) - local ctrl = santize_params (CtrlPorts:array ()) - - local changed = false - local siz = n_samples - local off = 0 - - -- if a parameter was changed, process at most lpf_chunk samples - -- at a time and interpolate parameters until the current settings - -- match the target values - if param_changed (ctrl) then - changed = true - siz = lpf_chunk - end - - while n_samples > 0 do - if changed then apply_params (ctrl) end - if siz > n_samples then siz = n_samples end - - local ho = math.floor(cur[1]) - local lo = math.floor(cur[4]) - - -- process all channels - for c = 1, #ins do - - -- High Pass - local xfade = cur[1] - ho - - -- prepare scratch memory - ARDOUR.DSP.copy_vector (mem:to_float (off), ins[c]:offset (off), siz) - - -- run at least |ho| biquads... - for k = 1,ho do - hp[c][k]:run (mem:to_float (off), siz) - end - ARDOUR.DSP.copy_vector (outs[c]:offset (off), mem:to_float (off), siz) - - -- mix the output of |ho| biquads (with weight |1-xfade|) - -- with the output of |ho+1| biquads (with weight |xfade|) - if xfade > 0 then - ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, 1 - xfade) - hp[c][ho+1]:run (mem:to_float (off), siz) - ARDOUR.DSP.mix_buffers_with_gain (outs[c]:offset (off), mem:to_float (off), siz, xfade) - -- also run the next biquad because it needs to have the correct state - -- in case it start affecting the next chunck of output. Higher order - -- ones are guaranteed not to be needed for the next run because the - -- interpolated order won't increase more than 0.86 in one step thanks - -- to the choice of the value of |lpf|. - if ho + 2 <= 4 then hp[c][ho+2]:run (mem:to_float (off), siz) end - elseif ho + 1 <= 4 then - -- run the next biquad in case it is used next chunk - hp[c][ho+1]:run (mem:to_float (off), siz) - end - - -- Low Pass - xfade = cur[4] - lo - - -- prepare scratch memory (from high pass output) - ARDOUR.DSP.copy_vector (mem:to_float (off), outs[c]:offset (off), siz) - - -- run at least |lo| biquads... - for k = 1,lo do - lp[c][k]:run (mem:to_float (off), siz) - end - ARDOUR.DSP.copy_vector (outs[c]:offset (off), mem:to_float (off), siz) - - -- mix the output of |lo| biquads (with weight |1-xfade|) - -- with the output of |lo+1| biquads (with weight |xfade|) - if xfade > 0 then - ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, 1 - xfade) - lp[c][lo+1]:run (mem:to_float (off), siz) - ARDOUR.DSP.mix_buffers_with_gain (outs[c]:offset (off), mem:to_float (off), siz, xfade) - -- also run the next biquad in case it start affecting the next - -- chunck of output. - if lo + 2 <= 4 then lp[c][lo+2]:run (mem:to_float (off), siz) end - elseif lo + 1 <= 4 then - -- run the next biquad in case it is used next chunk - lp[c][lo+1]:run (mem:to_float (off), siz) - end - - end - - n_samples = n_samples - siz - off = off + siz - end - - if changed then - -- notify display - self:queue_draw () - end -end - - -------------------------------------------------------------------------------- ---- inline display - -function round (n) - return math.floor (n + .5) -end - -function freq_at_x (x, w) - -- frequency in Hz at given x-axis pixel - return 20 * 1000 ^ (x / w) -end - -function x_at_freq (f, w) - -- x-axis pixel for given frequency, power-scale - return w * math.log (f / 20.0) / math.log (1000.0) -end - -function db_to_y (db, h) - -- y-axis gain mapping - if db < -60 then db = -60 end - if db > 12 then db = 12 end - return -.5 + round (0.2 * h) - h * db / 60 -end - -function grid_db (ctx, w, h, db) - -- draw horizontal grid line - -- note that a cairo pixel at Y spans [Y - 0.5 to Y + 0.5] - local y = -.5 + round (db_to_y (db, h)) - ctx:move_to (0, y) - ctx:line_to (w, y) - ctx:stroke () -end - -function grid_freq (ctx, w, h, f) - -- draw vertical grid line - local x = -.5 + round (x_at_freq (f, w)) - ctx:move_to (x, 0) - ctx:line_to (x, h) - ctx:stroke () -end - -function response (ho, lo, f) - -- calculate transfer function response for given - -- hi/po pass order at given frequency [Hz] - local db = ho * filt['hp']:dB_at_freq (f) - return db + lo * filt['lp']:dB_at_freq (f) -end - -function render_inline (ctx, w, max_h) - if not filt then - local tbl = self:table ():get () -- get shared memory table - -- instantiate filter (to calculate the transfer function's response) - filt = {} - filt['hp'] = ARDOUR.DSP.Biquad (tbl['samplerate']) - filt['lp'] = ARDOUR.DSP.Biquad (tbl['samplerate']) - max_freq = tbl['max_freq'] - end - - local ctrl = santize_params (CtrlPorts:array ()) - -- set filter coefficients if they have changed - if param_changed (ctrl) then - for k = 1,6 do cur[k] = ctrl[k] end - filt['hp']:compute (ARDOUR.DSP.BiquadType.HighPass, cur[2], cur[3], 0) - filt['lp']:compute (ARDOUR.DSP.BiquadType.LowPass, cur[5], cur[6], 0) - end - - -- calc height of inline display - local h = 1 | math.ceil (w * 9 / 16) -- use 16:9 aspect, odd number of y pixels - if (h > max_h) then h = max_h end -- but at most max-height - - -- ctx is a http://cairographics.org/ context - -- http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context - - -- clear background - ctx:rectangle (0, 0, w, h) - ctx:set_source_rgba (.2, .2, .2, 1.0) - ctx:fill () - ctx:rectangle (0, 0, w, h) - ctx:clip () - - -- set line width: 1px - ctx:set_line_width (1.0) - - -- draw grid - local dash3 = C.DoubleVector () - local dash2 = C.DoubleVector () - dash2:add ({1, 2}) - dash3:add ({1, 3}) - ctx:set_dash (dash2, 2) -- dotted line: 1 pixel 2 space - ctx:set_source_rgba (.5, .5, .5, .8) - grid_db (ctx, w, h, 0) - ctx:set_dash (dash3, 2) -- dashed line: 1 pixel 3 space - ctx:set_source_rgba (.5, .5, .5, .5) - grid_db (ctx, w, h, -12) - grid_db (ctx, w, h, -24) - grid_db (ctx, w, h, -36) - grid_freq (ctx, w, h, 100) - grid_freq (ctx, w, h, 1000) - grid_freq (ctx, w, h, 10000) - ctx:unset_dash () - - -- draw transfer function line - local ho = math.floor(cur[1]) - local lo = math.floor(cur[4]) - - ctx:set_source_rgba (.8, .8, .8, 1.0) - ctx:move_to (-.5, db_to_y (response(ho, lo, freq_at_x (0, w)), h)) - for x = 1,w do - local db = response(ho, lo, freq_at_x (x, w)) - ctx:line_to (-.5 + x, db_to_y (db, h)) - end - -- stoke a line, keep the path - ctx:stroke_preserve () - - -- fill area to zero under the curve - ctx:line_to (w, -.5 + round (db_to_y (0, h))) - ctx:line_to (0, -.5 + round (db_to_y (0, h))) - ctx:close_path () - ctx:set_source_rgba (.5, .5, .5, .5) - ctx:fill () - - return {w, h} -end diff --git a/scripts/README b/scripts/README deleted file mode 100644 index 2527498ee3..0000000000 --- a/scripts/README +++ /dev/null @@ -1,29 +0,0 @@ -Ardour Lua Scripts -================== - -https://manual.ardour.org/lua-scripting/ - -For upstream script addition, please file a pull-request at -https://github.com/Ardour/ardour/tree/master/scripts - -Script Naming conventions: - -^_ - A script filename with a leading underscore indicates an example script. - These scripts are only available from ardour's git repository and not - installed nor included with binary bundles. - -^__ - Scripts with a filename starting with two underscores are excluded from - unit-tests. This is currently the case for convolver, fluidsynth and - plugin-modulation. - They depend on external files (soundfont, impulse-response) or a specific - session-setup (plugin-modulation needs an automatable plugin). - -^s_ - A filename beginning with "s_" indicates a code snippet. - These scripts can only be used in the interactive interpreter - (Window > Scripting). They may be useful by themselves or handy for copy/edit - to create EditorActions. - The filename prefix is only for convenience, "type" = "Snippet" is used when - scripts are listed at runtime. diff --git a/scripts/__convolv.lua b/scripts/__convolv.lua deleted file mode 100644 index 23ad9034ed..0000000000 --- a/scripts/__convolv.lua +++ /dev/null @@ -1,59 +0,0 @@ -ardour { ["type"] = "dsp", name = "Lua Convolver", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] } - -function dsp_ioconfig () return - { - { audio_in = 1, audio_out = 1}, - { audio_in = 1, audio_out = 2}, - { audio_in = 2, audio_out = 2}, - } -end - -local conv, mode, ir_file - -ir_file = "/tmp/reverbs/St Nicolaes Church.wav" -ir_file = "/tmp/reverbs/Large Wide Echo Hall.wav" - -function dsp_configure (ins, outs) - if outs:n_audio() == 1 then - assert (ins:n_audio() == 1) - mode = ARDOUR.DSP.IRChannelConfig.Mono - elseif ins:n_audio() == 1 then - assert (outs:n_audio() == 2) - mode = ARDOUR.DSP.IRChannelConfig.MonoToStereo - else - assert (ins:n_audio() == 2) - assert (outs:n_audio() == 2) - mode = ARDOUR.DSP.IRChannelConfig.Stereo - end - - local irs = ARDOUR.DSP.IRSettings() - irs.gain = 0.5 - conv = ARDOUR.DSP.Convolver (Session, ir_file, mode, irs) - collectgarbage () -end - -function dsp_latency () - if conv then - return conv:latency() - else - return 0 - end -end - --- the DSP callback function to process audio audio --- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray -function dsp_run (ins, outs, n_samples) - assert (#ins <= #outs) - - for c = 1, #ins do - if ins[c] ~= outs[c] then -- if processing is not in-place.. - ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) -- ..copy data from input to output. - end - end - - if #outs == 1 then - conv:run_mono (outs[1], n_samples) - else - conv:run_stereo (outs[1], outs[2], n_samples) - end -end diff --git a/scripts/__fluidsynth1.lua b/scripts/__fluidsynth1.lua deleted file mode 100644 index f2e0ea2b52..0000000000 --- a/scripts/__fluidsynth1.lua +++ /dev/null @@ -1,51 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Lua Fluid Synth", - category = "Instrument", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An Example Synth for Prototyping.]] -} - -function dsp_ioconfig () - return - { - { midi_in = 1, audio_in = 0, audio_out = 2}, - } -end - -fluidsynth = nil - -function dsp_init (rate) - fluidsynth = ARDOUR.FluidSynth (rate, 32) - assert (fluidsynth:load_sf2 ("/usr/share/sounds/sf2/FluidR3_GM.sf2")) -end - -function dsp_run (ins, outs, n_samples) - local tme = 1 - assert (#outs == 2) - - -- parse midi messages - assert (type(midiin) == "table") -- global table of midi events (for now) - for _, e in pairs (midiin) do - local t = e["time"] -- t = [ 1 .. n_samples ] - - -- synth sound until event - if t > tme then - local off = tme - 1 - local len = t - tme - fluidsynth:synth (outs[1]:offset (off), outs[2]:offset (off), len) - end - - tme = t + 1 - - fluidsynth:midi_event (e["bytes"], e["size"]) -- parse midi event - end - - -- synth rest of cycle - if tme <= n_samples then - local off = tme - 1 - local len = 1 + n_samples - tme - fluidsynth:synth (outs[1]:offset (off), outs[2]:offset (off), len) - end -end diff --git a/scripts/__plugin_modulation.lua b/scripts/__plugin_modulation.lua deleted file mode 100644 index bb94b64a00..0000000000 --- a/scripts/__plugin_modulation.lua +++ /dev/null @@ -1,46 +0,0 @@ ---- session-script example to modulate plugin parameter(s) globally --- --- Ardour > Menu > Session > Scripting > Add Lua Script --- "Add" , select "Modulate Plugin Parameter", click "Add" + OK. --- ------------------------------------------------------------------------------ --- This script currently assumes you have a track named "Audio" --- which as a plugin at the top, where the first parameter has a range > 200 --- e.g. "No Delay Line" --- --- edit below.. - - --- plugin descriptor -ardour { - ["type"] = "session", - name = "Modulate Plugin Parameter", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An example session to modulate a plugin parameter.]] -} - -function factory () -- generate a new script instance - - local count = 0 -- script-instance "global" variable - - -- the "run" function called at the beginning of every process cycle - return function (n_samples) - count = (count + 1) % 200; -- count process cycles - local tri = math.abs (100 - count) -- triangle wave 0..100 - - -- get the track named "Audio" - -- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session - -- and http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route - local route = Session:route_by_name ("Audio") - assert (not route:isnil ()) -- make sure it exists - - -- the 1st plugin (from top) on that track, ardour starts counting at zero - -- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Processor - local plugin = route:nth_plugin (0) - assert (not plugin:isnil ()) -- make sure it exists - - -- modulate the plugin's first parameter (0) from 200 .. 300 - ARDOUR.LuaAPI.set_processor_param (plugin, 0, 200.0 + tri) - end -end diff --git a/scripts/__readable.lua b/scripts/__readable.lua deleted file mode 100644 index b9a991a782..0000000000 --- a/scripts/__readable.lua +++ /dev/null @@ -1,47 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Readable Test" } - -function factory () return function () - - local file = "/tmp/reverbs/Large Wide Echo Hall.wav" - local rl = ARDOUR.Readable.load (Session, file) - local cmem = ARDOUR.DSP.DspShm (8192) - - local d = cmem:to_float (0):array() - for i = 1, 8192 do d[i] = 0 end - d[1] = .5 - - local ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 8192) - - rl:push_back (ar) - - local c = 1 - for rd in rl:iter () do - local n_samples = rd:readable_length () - assert (rd:n_channels () == 1) - - local peak = 0 - local pos = 0 - repeat - -- read at most 8K samples starting at 'pos' - local s = rd:read (cmem:to_float (0), pos, 8192, 0) - pos = pos + s - -- access the raw audio data - -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray - local d = cmem:to_float (0):array() - -- iterate over the audio sample data - for i = 0, s do - if math.abs (d[i]) > peak then - peak = math.abs (d[i]) - end - end - until s < 8192 - assert (pos == n_samples) - - if (peak > 0) then - print ("File:", file, "channel", c, "peak:", 20 * math.log (peak) / math.log(10), "dBFS") - else - print ("File:", file, "channel", c, " is silent") - end - c = c + 1; - end -end end diff --git a/scripts/_amp1.lua b/scripts/_amp1.lua deleted file mode 100644 index 03be3961b6..0000000000 --- a/scripts/_amp1.lua +++ /dev/null @@ -1,47 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Simple Amp", - category = "Example", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[ - An Example DSP Plugin for processing audio, to - be used with Ardour's Lua scripting facility.]] -} - - --- return possible i/o configurations -function dsp_ioconfig () - -- -1, -1 = any number of channels as long as input and output count matches - return { [1] = { audio_in = -1, audio_out = -1}, } -end - --- optional function, called when configuring the plugin -function dsp_configure (ins, outs) - -- store configuration in global variable - audio_ins = ins:n_audio(); - local audio_outs = outs:n_audio() - assert (audio_ins == audio_outs) -end - --- this variant asks for a complete *copy* of the --- audio data in a lua-table. --- after processing the data is copied back. --- --- this also exemplifies the direct "connect and run" process function, --- where the channel-mapping needs to be done in lua. - -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - for c = 1,audio_ins do - -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0 - local ib = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped input buffer for given channel - local ob = out_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped output buffer for given channel - assert (ib ~= ARDOUR.ChanMapping.Invalid); - assert (ob ~= ARDOUR.ChanMapping.Invalid); - local a = bufs:get_audio (ib):data (offset):get_table(n_samples) -- copy audio-data from input buffer - for s = 1,n_samples do - a[s] = a[s] * 2; -- amplify data in lua table - end - bufs:get_audio(ob):data(offset):set_table(a, n_samples) -- copy back - end -end diff --git a/scripts/_amp2.lua b/scripts/_amp2.lua deleted file mode 100644 index 5a594d57e3..0000000000 --- a/scripts/_amp2.lua +++ /dev/null @@ -1,51 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Simple Amp II", - category = "Example", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[ - An Example DSP Plugin for processing audio, to - be used with Ardour's Lua scripting facility.]] -} - --- see amp1.lua -function dsp_ioconfig () - return { [1] = { audio_in = -1, audio_out = -1}, } -end - -function dsp_configure (ins, outs) - audio_ins = ins:n_audio(); - local audio_outs = outs:n_audio() - assert (audio_ins == audio_outs) -end - - --- this variant modifies the audio data in-place --- in Ardour's buffer. --- --- It relies on the fact that by default Ardour requires --- plugins to process data in-place (zero copy). --- --- Every assignment directly calls a c-function behind --- the scenes to get/set the value. --- It's a bit more efficient than "Amp I" on most systems. - -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - for c = 1,audio_ins do - -- ensure that processing does happen in-place - local ib = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped input buffer for given cannel - local ob = out_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped output buffer for given cannel - assert (ib ~= ARDOUR.ChanMapping.Invalid); - assert (ob ~= ARDOUR.ChanMapping.Invalid); - - local bi = bufs:get_audio(ib) - local bo = bufs:get_audio(ob) - assert (bi == bo) - - local a = bufs:get_audio(ib):data(offset):array() -- get a reference (pointer to array) - for s = 1,n_samples do - a[s] = a[s] * 2; -- modify data in-place (shared with ardour) - end - end -end diff --git a/scripts/_amp3.lua b/scripts/_amp3.lua deleted file mode 100644 index 006a22afbb..0000000000 --- a/scripts/_amp3.lua +++ /dev/null @@ -1,44 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Simple Amp III", - category = "Example", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[ - An Example DSP Plugin for processing audio, to - be used with Ardour's Lua scripting facility.]] -} - -function dsp_ioconfig () - return - { - { audio_in = -1, audio_out = -1}, - } -end - - -function dsp_params () - return - { - { ["type"] = "input", name = "Gain", min = -20, max = 20, default = 6, unit="dB", scalepoints = { ["0"] = 0, ["twice as loud"] = 6 , ["half as loud"] = -6 } }, - } -end - - --- use ardour's vectorized functions --- --- This is as efficient as Ardour doing it itself in C++ --- Lua function overhead is negligible --- --- this also exemplifies the /simpler/ way of delegating the --- channel-mapping to ardour. - -function dsp_run (ins, outs, n_samples) - local ctrl = CtrlPorts:array() -- get control port array (read/write) - local gain = ARDOUR.DSP.dB_to_coefficient (ctrl[1]) - assert (#ins == #outs) -- ensure that we can run in-place (channel count matches) - for c = 1,#ins do - assert (ins[c] == outs[c]) -- check in-place - ARDOUR.DSP.apply_gain_to_buffer (ins[c], n_samples, gain); -- process in-place - end -end diff --git a/scripts/_biquad_filter.lua b/scripts/_biquad_filter.lua deleted file mode 100644 index 5dc6e3be1b..0000000000 --- a/scripts/_biquad_filter.lua +++ /dev/null @@ -1,280 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Biquad Filter", - category = "Filter", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[A Versatile Filter Plugin]] -} - -function dsp_ioconfig () - return - { - -- allow any number of I/O as long as port-count matches - { audio_in = -1, audio_out = -1}, - } -end - - -function dsp_params () - return - { - { ["type"] = "input", name = "Enable", min = 0, max = 1, default = 1, bypass = true, toggled = true }, - { ["type"] = "input", name = "Type", min = 0, max = 4, default = 0, enum = true, scalepoints = - { - ["Peaking"] = 0, - ["Low Shelf"] = 1, - ["High Shelf"] = 2, - ["Low Pass"] = 3, - ["High Pass"] = 4, - } - }, - { ["type"] = "input", name = "Gain", min = -20, max = 20, default = 0, unit="dB" }, - { ["type"] = "input", name = "Freq", min = 20, max = 20000, default = 1000, unit="Hz", logarithmic = true }, - { ["type"] = "input", name = "Q", min = 0.1, max = 8, default = .707, logarithmic = true }, - } -end - --- translate type parameter to DSP enum --- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.DSP.Biquad.Type -function map_type (t) - if t == 1 then - return ARDOUR.DSP.BiquadType.LowShelf - elseif t == 2 then - return ARDOUR.DSP.BiquadType.HighShelf - elseif t == 3 then - return ARDOUR.DSP.BiquadType.LowPass - elseif t == 4 then - return ARDOUR.DSP.BiquadType.HighPass - else - return ARDOUR.DSP.BiquadType.Peaking - end -end - -function ctrl_data () - local ctrl = CtrlPorts:array () - if ctrl[1] <= 0 then -- when disabled - ctrl[3] = 0; -- force gain to 0dB - end - return ctrl -end - --- these globals are *not* shared between DSP and UI -local filters = {} -- the biquad filter instances (DSP) -local filt -- the biquad filter instance (GUI, response) -local cur = {0, 0, 0, 0, 0} -- current parameters -local lpf = 0.03 -- parameter low-pass filter time-constant -local chn = 0 -- channel/filter count - -function dsp_init (rate) - self:shmem ():allocate (1) -- shared mem to tell the GUI the samplerate - local cfg = self:shmem ():to_int (0):array () - cfg[1] = rate - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad - filt = ARDOUR.DSP.Biquad (rate) -- initialize filter - lpf = 13000 / rate -- interpolation time constant -end - -function dsp_configure (ins, outs) - assert (ins:n_audio () == outs:n_audio ()) - local cfg = self:shmem ():to_int (0):array () - local rate = cfg[1] - chn = ins:n_audio () - for c = 1, chn do - filters[c] = ARDOUR.DSP.Biquad (rate) -- initialize filters - end - cur = {0, 0, 0, 0, 0} -end - --- helper functions for parameter interpolation -function param_changed (ctrl) - if ctrl[2] == cur[2] and ctrl[3] == cur[3] and ctrl[4] == cur[4] and ctrl[5] == cur[5] then - return false - end - return true -end - -function low_pass_filter_param (old, new, limit) - if math.abs (old - new) < limit then - return new - else - return old + lpf * (new - old) - end -end - --- apply parameters, re-compute filter coefficients if needed -function apply_params (ctrl) - if not param_changed (ctrl) then - return - end - - if cur[2] ~= ctrl[2] then - -- reset filter state when type changes - filt:reset () - for k = 2,5 do cur[k] = ctrl[k] end - else - -- low-pass filter ctrl parameter values, smooth transition - cur[3] = low_pass_filter_param (cur[3], ctrl[3], 0.1) -- gain/dB - cur[4] = low_pass_filter_param (cur[4], ctrl[4], 1.0) -- freq/Hz - cur[5] = low_pass_filter_param (cur[5], ctrl[5], 0.01) -- quality - end - - for c = 1, chn do - filters[c]:compute (map_type (cur[2]), cur[4], cur[5], cur[3]) - end -end - - --- the actual DSP callback -function dsp_run (ins, outs, n_samples) - local changed = false - local siz = n_samples - local off = 0 - local ctrl = ctrl_data () - - -- if a parameter was changed, process at most 64 samples at a time - -- and interpolate parameters until the current settings match - -- the target values - if param_changed (ctrl) then - changed = true - siz = 64 - end - - while n_samples > 0 do - if changed then apply_params (ctrl) end - if siz > n_samples then siz = n_samples end - - -- process all channels - for c = 1,#ins do - -- check if output and input buffers for this channel are identical - -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray - if ins[c] == outs[c] then - filters[c]:run (ins[c]:offset (off), siz) -- in-place processing - else - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP - ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz) - filters[c]:run (outs[c]:offset (off), siz) - end - end - - n_samples = n_samples - siz - off = off + siz - end - - if changed then - -- notify display - self:queue_draw () - end -end - - -------------------------------------------------------------------------------- ---- inline display - -function round (n) - return math.floor (n + .5) -end - -function freq_at_x (x, w) - -- x-axis pixel for given freq, power-scale - return 20 * 1000 ^ (x / w) -end - -function x_at_freq (f, w) - -- frequency at given x-axis pixel - return w * math.log (f / 20.0) / math.log (1000.0) -end - -function db_to_y (db, h) - -- y-axis gain mapping - if db < -20 then db = -20 end - if db > 20 then db = 20 end - return -.5 + 0.5 * h * (1 - db / 20) -end - -function grid_db (ctx, w, h, db) - -- draw horizontal grid line - local y = -.5 + round (db_to_y (db, h)) - ctx:move_to (0, y) - ctx:line_to (w, y) - ctx:stroke () -end - -function grid_freq (ctx, w, h, f) - -- draw vertical grid line - local x = -.5 + round (x_at_freq (f, w)) - ctx:move_to (x, 0) - ctx:line_to (x, h) - ctx:stroke () -end - -function render_inline (ctx, w, max_h) - if not filt then - -- the GUI is separate from the DSP, but the GUI needs to know - -- the sample-rate that the DSP is using. - local shmem = self:shmem () -- get shared memory region - local cfg = shmem:to_int (0):array () -- "cast" into lua-table - -- instantiate filter (to calculate the transfer function's response) - filt = ARDOUR.DSP.Biquad (cfg[1]) - end - - -- set filter coefficients if they have changed - if param_changed (CtrlPorts:array ()) then - local ctrl = ctrl_data () - for k = 2,5 do cur[k] = ctrl[k] end - filt:compute (map_type (cur[2]), cur[4], cur[5], cur[3]) - end - - -- calc height of inline display - local h = math.ceil (w * 10 / 16) -- use 16:10 aspect - h = 2 * round (h / 2) -- with an even number of vertical pixels - if (h > max_h) then h = max_h end -- but at most max-height - - -- ctx is a http://cairographics.org/ context - -- http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context - - -- clear background - ctx:rectangle (0, 0, w, h) - ctx:set_source_rgba (.2, .2, .2, 1.0) - ctx:fill () - - -- set line width: 1px - -- Note: a cairo pixel at [1,1] spans [0.5->1.5 , 0.5->1.5] - -- hence the offset -0.5 in various move_to(), line_to() calls - ctx:set_line_width (1.0) - - -- draw grid - local dash3 = C.DoubleVector () - dash3:add ({1, 3}) - ctx:set_dash (dash3, 2) -- dotted line - ctx:set_source_rgba (.5, .5, .5, .5) - grid_db (ctx, w, h, 0) - grid_db (ctx, w, h, 6) - grid_db (ctx, w, h, 12) - grid_db (ctx, w, h, 18) - grid_db (ctx, w, h, -6) - grid_db (ctx, w, h, -12) - grid_db (ctx, w, h, -18) - grid_freq (ctx, w, h, 100) - grid_freq (ctx, w, h, 1000) - grid_freq (ctx, w, h, 10000) - ctx:unset_dash () - - -- draw transfer function line - ctx:set_source_rgba (.8, .8, .8, 1.0) - ctx:move_to (-.5, db_to_y (filt:dB_at_freq (freq_at_x (0, w)), h)) - for x = 1,w do - local db = filt:dB_at_freq (freq_at_x (x, w)) - ctx:line_to (-.5 + x, db_to_y (db, h)) - end - ctx:stroke_preserve () - - -- fill area to zero under the curve - ctx:line_to (w, -.5 + h * .5) - ctx:line_to (0, -.5 + h * .5) - ctx:close_path () - ctx:set_source_rgba (.5, .5, .5, .5) - ctx:fill () - - return {w, h} -end diff --git a/scripts/_calc_dsp_statistics.lua b/scripts/_calc_dsp_statistics.lua deleted file mode 100644 index 2d676055a8..0000000000 --- a/scripts/_calc_dsp_statistics.lua +++ /dev/null @@ -1,27 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Calculate DSP stats", - license = "MIT", - author = "Ardour Team", -} - -function factory () return function () - - for t in Session:get_routes ():iter () do - local i = 0 - while true do - local rv, stats - local proc = t:nth_processor (i) - if proc:isnil () then break end - if proc:to_plugininsert():isnil() then goto continue end - - rv, stats = proc:to_plugininsert():get_stats (0, 0, 0, 0) - if not rv then goto continue end - - print (string.format (" * %-28s | min: %.2f max: %.2f avg: %.3f std-dev: %.3f [ms]", - string.sub (proc:name() .. ' (' .. t:name() .. ')', 0, 28), - stats[1] / 1000.0, stats[2] / 1000.0, stats[3] / 1000.0, stats[4] / 1000.0)) - - ::continue:: - i = i + 1 - end - end -end end diff --git a/scripts/_cron.lua b/scripts/_cron.lua deleted file mode 100644 index 3f412d9ddd..0000000000 --- a/scripts/_cron.lua +++ /dev/null @@ -1,37 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Timed Event Example", - author = "Ardour Lua Task Force", - description = "Perform actions at specific wallclock time, example record", -} - -function signals () - return LuaSignal.Set():add ({[LuaSignal.LuaTimerDS] = true}) -end - -function factory () - local _last_time = 0 - return function (signal, ref, ...) - - -- calculate seconds since midnight - function hhmmss (hh, mm, ss) return hh * 3600 + mm * 60 + ss end - - -- current seconds since midnight UTC - -- (unix-time is UTC, no leap seconds, a day always has 86400 sec) - local now = os.time () % 86400 - - -- event at 09:30:00 UTC (here: rec-arm + roll) - if (now >= hhmmss (09, 30, 00) and _last_time < hhmmss (09, 30, 00)) then - Session:maybe_enable_record (false) - Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI) - end - - -- event at 09:32:00 UTC (here: rec-stop) - if (now >= hhmmss (09, 32, 00) and _last_time < hhmmss (09, 32, 00)) then - Session:disable_record (false, false) - Session:request_transport_speed (0.0, true, ARDOUR.TransportRequestSource.TRS_UI) - end - - _last_time = now - end -end diff --git a/scripts/_dialog_test.lua b/scripts/_dialog_test.lua deleted file mode 100644 index cc02f82a46..0000000000 --- a/scripts/_dialog_test.lua +++ /dev/null @@ -1,95 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Dialog Test" } - -function factory () return function () - local md = LuaDialog.Message ("title", "hello", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close) - print (md:run()) - - md = nil - collectgarbage () - - ----------------------------------------------------------------- - function basic_serialize (o) - if type(o) == "number" then - return tostring(o) - else - return string.format("%q", o) - end - end - - function serialize (name, value) - local rv = name .. ' = ' - collectgarbage() - if type(value) == "number" or type(value) == "string" or type(value) == "nil" or type (value) == "boolean" then - return rv .. basic_serialize(value) .. ' ' - elseif type(value) == "table" then - rv = rv .. '{} ' - for k,v in pairs(value) do - local fieldname = string.format("%s[%s]", name, basic_serialize(k)) - rv = rv .. serialize(fieldname, v) .. ' ' - end - return rv - elseif type(value) == "function" then - --return rv .. string.format("%q", string.dump(value, true)) - return rv .. "(function)" - else - error('cannot serialize a ' .. type(value)) - end - end - ----------------------------------------------------------------- - - function func () print "Hello" end - - local dialog_options = { - { type = "checkbox", key = "onoff", default = true, title = "OnOff" }, - - { type = "entry", key = "text", default = "changeme", title = "Text Entry" }, - - { - type = "radio", key = "select", title = "RadioBtn", values = - { - ["Option 1"] = 1, ["Option 2"] = "2", ["Option A"] = 'A' - }, - default = "Option 1" - }, - - { - type = "dropdown", key = "dropdown", title = "Menu", values = - { - ["Option 1"] = 1, ["Option 2"] = "2", ["Callback"] = func, - ["Option 4"] = - { - ["Option 4a"] = "test", ["Option 4b"] = 4.2 - } - }, - default = "Option 2" - }, - - { type = "fader", key = "gain", title = "Level", default = -10 }, -- unit = 'dB" - - { - type = "slider", key = "freq", title = "Frequency", min = 20, max = 20000, scalepoints = - { - [20] = "20", [200] = "nice", [2000] = "2k", [10000] = "too much" - }, - default = 500 - }, - - { type = "heading", title = "Heading" }, - - { type = "number", key = "number", title = "Whatever", min = 0, max = 10, step = 1, digits = 2 }, - - { type = "file", key = "file", title = "Select a File", path = ARDOUR.LuaAPI.build_filename (Session:path (), Session:name () .. ".ardour") }, - - { type = "folder", key = "folder", title = "Select a Folder", path = Session:path() } - } - - local od = LuaDialog.Dialog ("title", dialog_options) - local rv = od:run() - if (rv) then - print (serialize ("dialog", rv)) - end - - od = nil - collectgarbage () - -end end diff --git a/scripts/_dsp_plugin_communication.lua b/scripts/_dsp_plugin_communication.lua deleted file mode 100644 index e422e08cca..0000000000 --- a/scripts/_dsp_plugin_communication.lua +++ /dev/null @@ -1,78 +0,0 @@ -ardour { ["type"] = "dsp", name = "DSP Plugin Communication" } -function dsp_ioconfig () return { { audio_in = -1, audio_out = -1} } end - -function dsp_init (rate) - self:shmem ():allocate (1) -end - -function dsp_configure (ins, outs) -end - -function dsp_params () - return - { - { ["type"] = "output", name = "self", min = 0, max = 8}, - { ["type"] = "output", name = "gain", min = 0, max = 2}, - } -end - -function dsp_run (ins, outs, n_samples) - local ctrl = CtrlPorts:array () - local route = self:route () - local shmem = self:shmem () - - -- count plugins - local i = 0; - local l = 0; - local s = -1; -- 'self' this plugin instance - - -- iterate overall plugins on this track, - -- find all LuaProc instances of this plugin (unique_id), - repeat - local proc = route:nth_plugin (i) - if not proc:isnil () - and not proc:to_insert():plugin (0):to_luaproc():isnil () - and proc:to_insert():plugin (0):unique_id () == self:unique_id () then - if (self:id ():to_s() == proc:to_insert():plugin (0):id ():to_s()) then - s = l; -- *this* plugin instance - end - if l == 0 then - -- use shared-memory are of the first plugin instance for all. - -- - -- (the first plugin writes there, all later plugins only read, - -- plugins on a track are executed in order, in the same thread) - shmem = proc:to_insert():plugin (0):to_luaproc():shmem () - end - l = l + 1 -- count total instances of this plugin-type - end - i = i + 1 - until proc:isnil () - - assert (s >= 0) - ctrl[1] = s; - - -- calculate digital peak of all channels - local peak = 0 - for c = 1,#ins do - if not ins[c]:sameinstance (outs[c]) then - ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) - end - peak = ARDOUR.DSP.compute_peak(outs[c], n_samples, peak) - end - - - -- actual inter-plugin communication - local a = shmem:to_float (0):array () - if s == 0 then - -- the first plugin saves the peak - a[0] = peak - ctrl[2] = -1 - else - -- all later plugins display the difference to the first. - if (a[0] == 0) then - ctrl[2] = 1 - else - ctrl[2] = peak / a[0] - end - end -end diff --git a/scripts/_dump_latency.lua b/scripts/_dump_latency.lua deleted file mode 100644 index 9303e29508..0000000000 --- a/scripts/_dump_latency.lua +++ /dev/null @@ -1,77 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Dump Latency", - license = "MIT", - author = "Ardour Team", -} - -function factory () return function () - local all_procs = true - local show_ports = true - - print (" -- Session --") - print ("Worst Output Latency: ", Session:worst_output_latency ()) - print ("Worst Input Latency: ", Session:worst_input_latency ()) - print ("Worst Track Latency: ", Session:worst_route_latency ()) - print ("Worst Latency Preroll: ", Session:worst_latency_preroll ()) - - print (" -- Routes --") - for t in Session:get_routes ():iter () do - print (string.format ("%-30s signal-latency: %4d align: %4d play: %4d || in: %4d out: %4d", - t:name(), - t:signal_latency (), t:playback_latency (false), t:playback_latency (true), - t:input():latency(), t:output():latency())) - local i = 0 - while true do - local proc = t:nth_processor (i) - if proc:isnil () then break end - if all_procs and not proc:to_send():isnil () then - print (string.format (" * %-27s L: %4d in: %4d out: %4d capt: %4d play %4d DLY-SRC: %4d DLY-DST: %4d", - string.sub (proc:name(), 0, 27) , proc:signal_latency(), - proc:input_latency(), proc:output_latency(), - proc:capture_offset(), proc:playback_offset(), - proc:to_send():get_delay_in(), proc:to_send():get_delay_out() - )) - elseif all_procs or not proc:to_diskioprocessor():isnil () then - print (string.format (" * %-27s L: %4d in: %4d out: %4d capt: %4d play %4d", - string.sub (proc:name(), 0, 27) , proc:signal_latency(), - proc:input_latency(), proc:output_latency(), - proc:capture_offset(), proc:playback_offset() - )) - end - i = i + 1 - end - end - - if show_ports then - print (" -- Ports -- (latencies: port, priv, pub)") - local a = Session:engine() - _, t = a:get_ports (ARDOUR.DataType("audio"), ARDOUR.PortList()) - -- table 't' holds argument references. t[2] is the PortList - for p in t[2]:iter() do - local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true) - local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false) - local ppl = p:private_latency_range (true) - local pcl = p:private_latency_range (false) - local bpl = p:public_latency_range (true) - local bcl = p:public_latency_range (false) - print (string.format ("%-30s play: (%4d, %4d) (%4d, %4d) (%4d, %4d) capt: (%4d, %4d) (%4d, %4d) (%4d, %4d)", - p:name(), - lp[1].min, lp[1].max, ppl.min, ppl.max, bpl.min, bpl.max, - lc[1].min, lc[1].max, pcl.min, pcl.max, bcl.min, bcl.max)) - end - _, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList()) - -- table 't' holds argument references. t[2] is the PortList - for p in t[2]:iter() do - local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true) - local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false) - local ppl = p:private_latency_range (true) - local pcl = p:private_latency_range (false) - local bpl = p:public_latency_range (true) - local bcl = p:public_latency_range (false) - print (string.format ("%-30s play: (%4d, %4d) (%4d, %4d) (%4d, %4d) capt: (%4d, %4d) (%4d, %4d) (%4d, %4d)", - p:name(), - lp[1].min, lp[1].max, ppl.min, ppl.max, bpl.min, bpl.max, - lc[1].min, lc[1].max, pcl.min, pcl.max, bcl.min, bcl.max)) - end - end - collectgarbage () -end end diff --git a/scripts/_dump_midiregion.lua b/scripts/_dump_midiregion.lua deleted file mode 100644 index 0c62a56479..0000000000 --- a/scripts/_dump_midiregion.lua +++ /dev/null @@ -1,20 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Dump MIDI Region" } - -function factory () return function () - local sel = Editor:get_selection () - for r in sel.regions:regionlist ():iter () do - local mr = r:to_midiregion () - if mr:isnil () then goto next end - - print (r:name (), "Pos:", r:position (), "Start:", r:start ()) - local bfc = ARDOUR.BeatsSamplesConverter (Session:tempo_map (), r:position ()) - local nl = ARDOUR.LuaAPI.note_list (mr:model ()) - for n in nl:iter () do - print (" Note @", bfc:to (n:time ()), - ARDOUR.ParameterDescriptor.midi_note_name (n:note ()), - "Vel:", n:velocity ()) - end - print ("----") - ::next:: - end -end end diff --git a/scripts/_dump_playlists.lua b/scripts/_dump_playlists.lua deleted file mode 100644 index 656a5ead97..0000000000 --- a/scripts/_dump_playlists.lua +++ /dev/null @@ -1,31 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Dump Playlists" } - -function factory () return function () - - print ("Number of playlists:", Session:playlists():n_playlists()) - - print () - print ("Used playlists:") - for p in Session:playlists():get_used():iter() do - print ("-", p:name(), p:n_regions()) - end - - print () - print ("Unused playlists:") - for p in Session:playlists():get_unused():iter() do - print ("-", p:name(), p:n_regions()) - end - - print () - print ("Playlists by Track:") - for r in Session:get_tracks():iter() do - print ("*", r:name()) - for p in Session:playlists():playlists_for_track (r:to_track()):iter() do - if (p == r:to_track():playlist()) then - print (" >-", p:name(), p:n_regions()) - else - print (" -", p:name(), p:n_regions()) - end - end - end -end end diff --git a/scripts/_export_plugins_on_save.lua b/scripts/_export_plugins_on_save.lua deleted file mode 100644 index c732717bd8..0000000000 --- a/scripts/_export_plugins_on_save.lua +++ /dev/null @@ -1,44 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Save Extra Data (instruments)", - author = "Ardour Lua Task Force", - description = "Export custom data when the session is saved", -} - --- subscribe to signals --- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal -function signals () - s = LuaSignal.Set() - s:add ({[LuaSignal.StateSaved] = true}) - return s -end - --- create callback functions -function factory () return function (signal, ...) - assert (signal == LuaSignal.StateSaved) - - local all_instruments = {} - - -- iterate over all routes - for r in Session:get_routes():iter() do - local proc = r:the_instrument() -- get instrument processor (if any) - if proc:isnil() then goto nextroute end -- skip tracks/busses without instrument - local pi = proc:to_insert() -- check if it's a plugin-insert - if pi:isnil() then goto nextroute end - - local pp = pi:plugin (0) -- get first instance - all_instruments[r:name()] = string.format ("%s (%s)", proc:name(), pp:unique_id()) - - ::nextroute:: - end - - if next (all_instruments) ~= nil then -- check if table is not empty - -- write to a file in the session-folder - file = io.open (ARDOUR.LuaAPI.build_filename (Session:path(), Session:name () .. ".instruments.txt"), "w") - for nme, nfo in pairs (all_instruments) do - file:write (nme .. ": " .. nfo) - end - file:close() - end - -end end diff --git a/scripts/_export_tracks.lua b/scripts/_export_tracks.lua deleted file mode 100644 index 7db8dabe84..0000000000 --- a/scripts/_export_tracks.lua +++ /dev/null @@ -1,10 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Export Track XML" } - -function factory () return function () - local rlp = ARDOUR.RouteListPtr () - local sel = Editor:get_selection () - for r in sel.tracks:routelist ():iter () do - rlp:push_back (r) - end - print (Session:export_track_state (rlp, "/tmp/rexport")) -end end diff --git a/scripts/_fan_out_instrument.lua b/scripts/_fan_out_instrument.lua deleted file mode 100644 index 3fdf4ef907..0000000000 --- a/scripts/_fan_out_instrument.lua +++ /dev/null @@ -1,69 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Fan Out Instrument", - license = "MIT", - author = "Ardour Team", - description = [[Create Busses for every Instrument Output on selected Tracks]] -} - -function factory () return function () - - local outputs = 2 - local mst = Session:master_out(); - if not mst:isnil() then - outputs = mst:n_inputs():n_audio() - end - mst = nil -- drop reference - - local sel = Editor:get_selection () - for r in sel.tracks:routelist ():iter () do - local proc = r:the_instrument ():to_insert() - if proc:isnil () then - print ("Track", r:name(), "does not have an instrument plugin") - goto next - end - local plugin = proc:plugin(0); - - if (r:n_outputs ():n_audio() ~= proc:output_streams():n_audio()) then - print ("Instrument ", proc:name(), "outputs", proc:output_streams():n_audio(), "do not match track outputs", r:n_outputs ():n_audio()) - goto next - end - - -- collect port-group information, count target bus width - local targets = {} - for i = 1, proc:output_streams():n_audio() do - local pd = plugin:describe_io_port (ARDOUR.DataType("Audio"), false, i - 1) - local nn = proc:name() .. " " .. pd.group_name; -- TODO use track-name prefix? - targets[nn] = targets[nn] or 0 - targets[nn] = targets[nn] + 1 - end - - if #targets < 2 then - print ("Instrument ", proc:name(), "has only 1 output bus. Nothing to fan out.") - goto next - end - - -- create busses ; TODO retain order - for t,c in pairs (targets) do - local rt = Session:route_by_name (t) - if rt:isnil () then - Session:new_audio_route (c, outputs, nil, 1, t, ARDOUR.PresentationInfo.Flag.AudioBus, ARDOUR.PresentationInfo.max_order) - end - end - - r:output():disconnect_all (nil) - r:panner_shell():set_bypassed (true) - - -- connect the busses - for i = 1, proc:output_streams():n_audio() do - local pd = plugin:describe_io_port (ARDOUR.DataType("Audio"), false, i - 1) - local nn = proc:name() .. " " .. pd.group_name; - local rt = Session:route_by_name (nn) - assert (rt) - - local op = r:output():audio (i - 1) - local ip = rt:input():audio (pd.group_channel) - op:connect (ip:name()) - end - - ::next:: - end -end end diff --git a/scripts/_find_nonzero_sample.lua b/scripts/_find_nonzero_sample.lua deleted file mode 100644 index c23d2b0b4d..0000000000 --- a/scripts/_find_nonzero_sample.lua +++ /dev/null @@ -1,74 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Find non zero audio sample", - author = "Ardour Team", - description = [[Find the position of first non-zero audio sample in selected regions.]] -} - -function factory () return function () - -- get Editor GUI Selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- allocate a buffer (float* in C) - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm - local cmem = ARDOUR.DSP.DspShm (8192) - local msg = "" - - -- iterate over selected regions - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- test if it's an audio region - if r:to_audioregion ():isnil () then - goto next - end - - -- to read the Region data, we use the Readable interface of the Region - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable - local rd = r:to_readable () - - local n_samples = rd:readable_length () - local n_channels = rd:n_channels () - - local nonzeropos = -1 - - -- iterate over all channels in Audio Region - for c = 0, n_channels -1 do - local pos = 0 - repeat - -- read at most 8K samples of channel 'c' starting at 'pos' - local s = rd:read (cmem:to_float (0), pos, 8192, c) - -- access the raw audio data - -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray - local d = cmem:to_float (0):array() - -- iterate over the audio sample data - for i = 0, s do - if math.abs (d[i]) > 0 then - if (nonzeropos < 0 or pos + i < nonzeropos) then - nonzeropos = pos + i - end - break - end - end - pos = pos + s - if (nonzeropos >= 0 and pos > nonzeropos) then - break - end - until s < 8192 - end - - if (nonzeropos >= 0) then - msg = msg .. string.format("%s: %d\n", r:name (), nonzeropos + r:position()) - else - msg = msg .. "Region: '%s' is silent\n" - end - - ::next:: - end - - if (msg ~= "") then - local md = LuaDialog.Message ("First Non Zero Sample", msg, LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close) - md:run() - end - -end end diff --git a/scripts/_fir.lua b/scripts/_fir.lua deleted file mode 100644 index 64448ef8f6..0000000000 --- a/scripts/_fir.lua +++ /dev/null @@ -1,36 +0,0 @@ -ardour { ["type"] = "dsp", name = "Lua FIR Convolver", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] } - -function dsp_ioconfig () return - { - { audio_in = 1, audio_out = 1}, - } -end - -local conv - -function dsp_configure (ins, outs) - conv = ARDOUR.DSP.Convolution (Session, ins:n_audio (), outs:n_audio ()) - - local cmem = ARDOUR.DSP.DspShm (4) - cmem:clear () - local d = cmem:to_float (0):array() - d[1] = .5 - d[2] = .5 - local ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 4) - conv:add_impdata (0, 0, ar, 1.0, 0, 0, 0, 0) - - cmem:to_float (0):set_table({1, -1, 0, 0}, 4) - ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 3) - conv:add_impdata (0, 0, ar, 1.0, 0, 0, 0, 0) - - conv:restart () - collectgarbage () -end - -function dsp_latency () - return conv:latency() -end - -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - conv:run (bufs, in_map, out_map, n_samples, offset) -end diff --git a/scripts/_hook_test.lua b/scripts/_hook_test.lua deleted file mode 100644 index c5b00445ce..0000000000 --- a/scripts/_hook_test.lua +++ /dev/null @@ -1,41 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Callback Example", - author = "Ardour Lua Task Force", - description = "Rewind On Solo Change, Write a file when regions are moved", -} - -function signals () - s = LuaSignal.Set() - --s:add ({[LuaSignal.SoloActive] = true, [LuaSignal.RegionPropertyChanged] = true}) - s:add ( - { - [LuaSignal.SoloActive] = true, - [LuaSignal.RegionPropertyChanged] = true - } - ) - --for k,v in pairs (s:table()) do print (k, v) end - return s -end - -function factory (params) - return function (signal, ref, ...) - print (signal, ref, ...) - - if (signal == LuaSignal.SoloActive) then - Session:goto_start() - end - - if (signal == LuaSignal.RegionPropertyChanged) then - obj,pch = ... - file = io.open ("/tmp/test" ,"a") - io.output (file) - io.write (string.format ("Region: '%s' pos-changed: %s, length-changed: %s\n", - obj:name (), - tostring (pch:containsSamplePos (ARDOUR.Properties.Start)), - tostring (pch:containsSamplePos (ARDOUR.Properties.Length)) - )) - io.close (file) - end - end -end diff --git a/scripts/_insert_region_gaps.lua b/scripts/_insert_region_gaps.lua deleted file mode 100644 index 3a7f5ed8e9..0000000000 --- a/scripts/_insert_region_gaps.lua +++ /dev/null @@ -1,62 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Insert Gaps", - license = "MIT", - author = "Ardour Team", - description = [[Insert gaps between all regions on selected tracks]] -} - -function action_params () - return - { - ["gap"] = { title = "Gap size (in sec)", default = "2" }, - } -end - -function factory () return function () - -- get configuration - local p = params or {} - local gap = p["gap"] or 2 - if gap <= 0 then gap = 2 end - - local sel = Editor:get_selection () -- get current selection - - local add_undo = false -- keep track of changes - Session:begin_reversible_command ("Insert Gaps") - - -- iterate over all selected tracks - for route in sel.tracks:routelist ():iter () do - local track = route:to_track () - if track:isnil () then goto continue end - - -- get track's playlist - local playlist = track:playlist () - local offset = 0 - - -- iterate over all regions in the playlist - for region in playlist:region_list():iter() do - - -- preare for undo operation - region:to_stateful ():clear_changes () - - -- move region - region:set_position (region:position() + offset, 0) - offset = offset + Session:nominal_sample_rate () * gap - - -- create a diff of the performed work, add it to the session's undo stack - -- and check if it is not empty - if not Session:add_stateful_diff_command (region:to_statefuldestructible ()):empty () then - add_undo = true - end - end - ::continue:: - end - - -- all done, commit the combined Undo Operation - if add_undo then - -- the 'nil' Command here mean to use the collected diffs added above - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end -end end diff --git a/scripts/_midi_lfo.lua b/scripts/_midi_lfo.lua deleted file mode 100644 index fd8b2b07ef..0000000000 --- a/scripts/_midi_lfo.lua +++ /dev/null @@ -1,74 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "MIDI LFO", - category = "Example", -- Utility - license = "MIT", - author = "Ardour Lua Task Force", - description = [[MIDI CC LFO Example -- Triangle full scale CC Parameter automation]] -} - -function dsp_ioconfig () - return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, } -end - -function dsp_params () - return - { - { ["type"] = "input", name = "BPM", min = 40, max = 200, default = 60, unit="BPM"}, - { ["type"] = "input", name = "CC", min = 0, max = 127, default = 1, integer = true }, - } -end - -local samplerate -local time = 0 -local step = 0 - -function dsp_init (rate) - samplerate = rate - local bpm = 120 - spb = rate * 60 / bpm -end - -function dsp_run (_, _, n_samples) - assert (type(midiin) == "table") - assert (type(midiout) == "table") - - local ctrl = CtrlPorts:array () - local bpm = ctrl[1] - local cc = ctrl[2] - - local spb = samplerate * 60 / bpm -- samples per beat - local sps = spb / 254 -- samples per step (0..127..1 = 254 steps) - - assert (sps > 1) - local i = 1 - local m = 1 - - for ts = 1, n_samples do - time = time + 1 - - -- forward incoming midi - if i <= #midiin then - while midiin[i]["time"] == ts do - midiout[m] = midiin[i] - i = i + 1 - m = m + 1 - if i > #midiin then break end - end - end - - -- inject LFO events - if time >= spb then - local val - if step > 127 then val = 254 - step else val = step end - - midiout[m] = {} - midiout[m]["time"] = ts - midiout[m]["data"] = { 0xb0, cc, val } - - m = m + 1 - time = time - sps - if step == 253 then step = 0 else step = step + 1 end - end - end -end diff --git a/scripts/_midi_rec_start.lua b/scripts/_midi_rec_start.lua deleted file mode 100644 index b2e03ba28b..0000000000 --- a/scripts/_midi_rec_start.lua +++ /dev/null @@ -1,37 +0,0 @@ -ardour { - ["type"] = "session", - name = "MIDI Record Enable", - category = "Example", -- "Utility" - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An example script to start recording on note-on.]] -} - -function factory () - return function (n_samples) - if Session:actively_recording() then return end -- when recording already, do nothing - -- iterate over all MIDI ports - _, t = Session:engine ():get_ports (ARDOUR.DataType.midi (), ARDOUR.PortList ()) - for p in t[2]:iter () do - -- skip output ports - if not p:receives_input () then goto next end - local midiport = p:to_midiport () - -- and skip async event ports - if midiport:isnil () then goto next end - local mb = midiport:get_midi_buffer (n_samples) -- get the midi-data buffers - local events = mb:table () -- copy event list into lua table - for _,e in pairs (events) do -- iterate over all events in the midi-buffer - if (e:buffer():array()[1] & 0xf0) == 0x90 then -- note on - Session:maybe_enable_record (true) -- global record-enable from rt-context - -- maybe-enable may fail if there are no tracks or step-entry is active - -- roll transport if record-enable suceeded: - if ARDOUR.Session.RecordState.Enabled == Session:record_status() then - Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI) -- ...and go. - end - return - end - end - ::next:: - end - end -end diff --git a/scripts/_midi_rewrite.lua b/scripts/_midi_rewrite.lua deleted file mode 100644 index 4dfc28a6c3..0000000000 --- a/scripts/_midi_rewrite.lua +++ /dev/null @@ -1,34 +0,0 @@ -ardour { - ["type"] = "session", - name = "Rewrite Midi", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An example session script preprocesses midi buffers.]] -} - -function factory () - -- this function is called in every process cycle, before processing - return function (n_samples) - _, t = Session:engine ():get_ports (ARDOUR.DataType.midi (), ARDOUR.PortList ()) - for p in t[2]:iter () do - if not p:receives_input () then goto next end - - if not p:name () == "MIDI/midi_in 1" then goto next end - - midiport = p:to_midiport () - assert (not midiport:isnil ()) - mb = midiport:get_midi_buffer (n_samples); - - events = mb:table() -- copy event list into lua table - mb:silence (n_samples, 0); -- clear existing buffer - - for _,e in pairs (events) do - -- e is-a http://manual.ardour.org/lua-scripting/class_reference/#Evoral:MidiEvent - e:set_channel (2) - mb:push_event (e) - end - - ::next:: - end - end -end diff --git a/scripts/_midifilter.lua b/scripts/_midifilter.lua deleted file mode 100644 index 48f61a8277..0000000000 --- a/scripts/_midifilter.lua +++ /dev/null @@ -1,39 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Midi Filter", - category = "Example", -- "Utility" - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An Example Midi Filter for prototyping.]] -} - -function dsp_ioconfig () - return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, } -end - -function dsp_run (_, _, n_samples) - assert (type(midiin) == "table") - assert (type(midiout) == "table") - local cnt = 1; - - function tx_midi (time, data) - midiout[cnt] = {} - midiout[cnt]["time"] = time; - midiout[cnt]["data"] = data; - cnt = cnt + 1; - end - - -- for each incoming midi event - for _,b in pairs (midiin) do - local t = b["time"] -- t = [ 1 .. n_samples ] - local d = b["data"] -- get midi-event - local event_type - if #d == 0 then event_type = -1 else event_type = d[1] >> 4 end - - if (#d == 3 and event_type == 9) then -- note on - tx_midi (t, d) - elseif (#d == 3 and event_type == 8) then -- note off - tx_midi (t, d) - end - end -end diff --git a/scripts/_midigenerator.lua b/scripts/_midigenerator.lua deleted file mode 100644 index 9c093946f9..0000000000 --- a/scripts/_midigenerator.lua +++ /dev/null @@ -1,48 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "MIDI Generator", - category = "Example", -- "Utility" - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An Example Midi Generator for prototyping.]] -} - -function dsp_ioconfig () - return { { midi_out = 1, audio_in = 0, audio_out = 0}, } -end - -local tme = 0 -- sample-counter -local seq = 1 -- sequence-step -local spb = 0 -- samples per beat - -local midi_sequence = { - { 0x90, 64, 127 }, - { 0x80, 64, 0 }, -} - -function dsp_init (rate) - local bpm = 120 - spb = rate * 60 / bpm - if spb < 2 then spb = 2 end -end - -function dsp_run (_, _, n_samples) - assert (type(midiout) == "table") - assert (spb > 1) - local m = 1 - - for time = 1,n_samples do -- not very efficient - -- TODO, timestamp the sequence in beats, calc/skip to next event - tme = tme + 1 - - if tme >= spb then - midiout[m] = {} - midiout[m]["time"] = time - midiout[m]["data"] = midi_sequence[seq] - - tme = 0 - m = m + 1 - if seq == #midi_sequence then seq = 1 else seq = seq + 1 end - end - end -end diff --git a/scripts/_midigenerator2.lua b/scripts/_midigenerator2.lua deleted file mode 100644 index 6bd904b097..0000000000 --- a/scripts/_midigenerator2.lua +++ /dev/null @@ -1,34 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "MIDI Generator II", - category = "Example", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An Example Midi Generator for prototyping.]] -} - -function dsp_ioconfig () - return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, } -end - -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - local ob = out_map:get (ARDOUR.DataType ("midi"), 0) - if ob ~= ARDOUR.ChanMapping.Invalid then - local mb = bufs:get_midi (ob) - - -- see _midigenerator.lua for - -- how to use a timed sequence - - local ba = C.ByteVector () -- construct a byte vector - ba:add ({0x90, 64, 127}) -- add some data to the vector - -- send a message at cycle-start - mb:push_back (offset, ba:size (), ba:to_array()); - - ba:clear () - ba:add ({0x80, 64, 127}) - mb:push_back (n_samples - 1 - offset, ba:size (), ba:to_array()); - end - - -- passthrough audio, apply pin/channel mapping - ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio")) -end diff --git a/scripts/_osc_hook_example.lua b/scripts/_osc_hook_example.lua deleted file mode 100644 index 2ef81cd5bb..0000000000 --- a/scripts/_osc_hook_example.lua +++ /dev/null @@ -1,52 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "OSC Callback Example", - author = "Ardour Lua Task Force", - description = "Send OSC messages", -} - -function action_params () - return - { - ["uri"] = { title = "OSC URI ", default = "osc.udp://localhost:7890"}, - } -end - - -function signals () - s = LuaSignal.Set() - s:add ( - { - [LuaSignal.SoloActive] = true, - [LuaSignal.RegionPropertyChanged] = true, - [LuaSignal.Exported] = true, - [LuaSignal.TransportStateChange] = true - } - ) - return s -end - -function factory (params) - return function (signal, ref, ...) - local uri = params["uri"] or "osc.udp://localhost:7890" - local tx = ARDOUR.LuaOSC.Address (uri) - -- debug print (stdout) - -- print (signal, ref, ...) - - if (signal == LuaSignal.Exported) then - tx:send ("/session/exported", "ss", ...) - elseif (signal == LuaSignal.SoloActive) then - tx:send ("/session/solo_changed", "") - elseif (signal == LuaSignal.TransportStateChange) then - tx:send ("/session/transport", "if", - Session:transport_sample(), Session:transport_speed()) - elseif (signal == LuaSignal.RegionPropertyChanged) then - obj,pch = ... - tx:send ("/region_property_changed", "sTTiii", - obj:name (), - (pch:containsSamplePos (ARDOUR.Properties.Start)), - (pch:containsSamplePos (ARDOUR.Properties.Length)), - obj:position (), obj:start (), obj:length ()) - end - end -end diff --git a/scripts/_plot_graph.lua b/scripts/_plot_graph.lua deleted file mode 100644 index 9c8876dbbb..0000000000 --- a/scripts/_plot_graph.lua +++ /dev/null @@ -1,24 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Plot Process Graph", - author = "Ardour Team", - description = [[Export process graph to a graphviz file, and launch xdot]] -} - -function factory () return function () - if Session:plot_process_graph ("/tmp/ardour_graph.gv") then - os.forkexec ("/bin/sh", "-c", "xdot /tmp/ardour_graph.gv") - end -end end - -function icon (params) return function (ctx, width, height, fg) - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - local txt = Cairo.PangoLayout (ctx, "Sans ".. math.ceil(height / 3) .. "px") - txt:set_alignment (Cairo.Alignment.Center); - txt:set_width (width); - txt:set_ellipsize (Cairo.EllipsizeMode.Middle); - txt:set_text ("plot\ngrph") - local tw, th = txt:get_pixel_size () - ctx:move_to (0, .5 * (height - th)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/_pong.lua b/scripts/_pong.lua deleted file mode 100644 index 8eef49f004..0000000000 --- a/scripts/_pong.lua +++ /dev/null @@ -1,260 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-Pong", - category = "Toy", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[A console classic for your console]] -} - --- return possible i/o configurations -function dsp_ioconfig () - -- -1, -1 = any number of channels as long as input and output count matches - return { [1] = { audio_in = -1, audio_out = -1}, } -end - --- control port(s) -function dsp_params () - return - { - { ["type"] = "input", name = "Bar", min = 0, max = 1, default = 0.5 }, - { ["type"] = "input", name = "Reset", min = 0, max = 1, default = 0, toggled = true }, - { ["type"] = "input", name = "Difficulty", min = 1, max = 10, default = 3}, - } -end - - --- Game State (for this instance) --- NOTE: these variables are for the DSP part (not shared with the GUI instance) -local sample_rate -- sample-rate -local fps -- audio samples per game-step -local game_time -- counts up to fps -local game_score -local ball_x, ball_y -- ball position [0..1] -local dx, dy -- current ball speed -local lost_sound -- audio-sample counter for game-over [0..3*fps] -local ping_sound -- audio-sample counter for ping-sound [0..fps] -local ping_phase -- ping note phase-difference per sample -local ping_pitch - -function dsp_init (rate) - -- allocate a "shared memory" area to transfer state to the GUI - self:shmem ():allocate (3) - self:shmem ():clear () - -- initialize some variables - sample_rate = rate - fps = rate / 25 - ping_pitch = 752 / rate - ball_x = 0.5 - ball_y = 0 - dx = 0.00367 - dy = 0.01063 - game_score = 0 - game_time = fps -- start the ball immediately (notify GUI) - ping_sound = fps -- set to end of synth cycle - lost_sound = 3 * fps -end - -function queue_beep () - -- queue 'ping' sound (unless one is already playing to prevent clicks) - if (ping_sound >= fps) then - -- major scale, 2 octaves - local scale = { 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24 } - local midi_note = 60 + scale[1 + math.floor (math.random () * 14)] - ping_pitch = (440 / 32) * 2^((midi_note - 10.0) / 12.0) / sample_rate - ping_sound = 0 - ping_phase = 0 - end -end - --- callback: process "n_samples" of audio --- ins, outs are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray --- pointers to the audio buffers -function dsp_run (ins, outs, n_samples) - local ctrl = CtrlPorts:array () -- get control port array (read/write) - - local changed = false -- flag to notify GUI on every game-step - game_time = game_time + n_samples - - -- reset (allow to write automation from a given start-point) - -- ctrl[2] corresponds to the "Reset" input control - if ctrl[2] > 0 then - game_time = 0 - ball_x = 0.5 - ball_y = 0 - dx = 0.00367 - dy = 0.01063 - game_score = 0 - end - - -- simple game engine - while game_time > fps and ctrl[2] <= 0 do - changed = true - game_time = game_time - fps - - -- move the ball - ball_x = ball_x + dx * ctrl[3] - ball_y = ball_y + dy * ctrl[3] - - -- reflect left/right - if ball_x >= 1 or ball_x <= 0 then - dx = -dx - queue_beep () - end - - -- single player (reflect top) -- TODO "stereo" version, 2 ctrls :) - if ball_y <= 0 then - dy = - dy y = 0 - queue_beep () - end - - -- keep the ball in the field at all times - if ball_x >= 1 then ball_x = 1 end - if ball_x <= 0 then ball_x = 0 end - - -- bottom edge - if ball_y > 1 then - local bar = ctrl[1] -- get bar position - if math.abs (bar - ball_x) < 0.1 then - -- reflect the ball - dy = - dy - ball_y = 1.0 - dx = dx - 0.04 * (bar - ball_x) - -- make sure it's moving (not stuck on borders) - if math.abs (dx) < 0.0001 then dx = 0.0001 end - game_score = game_score + 1 - queue_beep () - else - -- game over, reset game - lost_sound = 0 -- re-start noise - ball_y = 0 - game_score = 0 - dx = 0.00367 - end - end - end - - -- forward audio if processing is not in-place - for c = 1,#outs do - -- check if output and input buffers for this channel are identical - -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray - if ins[c] ~= outs[c] then - -- fast (accelerated) copy - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP - ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) - end - end - - -- simple synth -- TODO Optimize - if ping_sound < fps then - -- cache audio data buffers for direct access, later - local abufs = {} - for c = 1,#outs do - abufs[c] = outs[c]:array() - end - -- simple sine synth with a sine-envelope - for s = 1, n_samples do - ping_sound = ping_sound + 1 - ping_phase = ping_phase + ping_pitch - local snd = 0.7 * math.sin(6.283185307 * ping_phase) * math.sin (3.141592 * ping_sound / fps) - -- add synthesized sound to all channels - for c = 1,#outs do - abufs[c][s] = abufs[c][s] + snd - end - -- break out of the loop when the sound finished - if ping_sound >= fps then goto ping_end end - end - ::ping_end:: - end - - if lost_sound < 3 * fps then - local abufs = {} - for c = 1,#outs do - abufs[c] = outs[c]:array() - end - for s = 1, n_samples do - lost_sound = lost_sound + 1 - -- -12dBFS white noise - local snd = 0.5 * (math.random () - 0.5) - for c = 1,#outs do - abufs[c][s] = abufs[c][s] + snd - end - if lost_sound >= 3 * fps then goto noise_end end - end - ::noise_end:: - end - - if changed then - -- notify the GUI - local shmem = self:shmem () -- get the shared memory region - local state = shmem:to_float (0):array () -- "cast" into lua-table - -- update data.. - state[1] = ball_x - state[2] = ball_y - state[3] = game_score - -- ..and wake up the UI - self:queue_draw () - end -end - - -------------------------------------------------------------------------------- ---- inline display - -local txt = nil -- cache font description (in GUI context) - -function render_inline (ctx, w, max_h) - local ctrl = CtrlPorts:array () -- control port array - local shmem = self:shmem () -- shared memory region (game state from DSP) - local state = shmem:to_float (0):array () -- cast to lua-table - - if (w > max_h) then - h = max_h - else - h = w - end - - -- prepare text rendering - if not txt then - -- allocate PangoLayout and set font - --http://manual.ardour.org/lua-scripting/class_reference/#Cairo:PangoLayout - txt = Cairo.PangoLayout (ctx, "Mono 10px") - end - - -- ctx is-a http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context - -- 2D vector graphics http://cairographics.org/ - - -- clear background - ctx:rectangle (0, 0, w, h) - ctx:set_source_rgba (.2, .2, .2, 1.0) - ctx:fill () - - -- print the current score - if (state[3] > 0) then - txt:set_text (string.format ("%.0f", state[3])); - local tw, th = txt:get_pixel_size () - ctx:set_source_rgba (1, 1, 1, 1.0) - ctx:move_to (w - tw - 3, 3) - txt:show_in_cairo_context (ctx) - end - - -- prepare line and dot rendering - ctx:set_line_cap (Cairo.LineCap.Round) - ctx:set_line_width (3.0) - ctx:set_source_rgba (.8, .8, .8, 1.0) - - -- display bar - local bar_width = w * .1 - local bar_space = w - bar_width - - ctx:move_to (bar_space * ctrl[1], h - 3) - ctx:rel_line_to (bar_width, 0) - ctx:stroke () - - -- display ball - ctx:move_to (1 + state[1] * (w - 3), state[2] * (h - 5)) - ctx:close_path () - ctx:stroke () - - return {w, h} -end diff --git a/scripts/_rawmidi.lua b/scripts/_rawmidi.lua deleted file mode 100644 index dba45f5483..0000000000 --- a/scripts/_rawmidi.lua +++ /dev/null @@ -1,110 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Midi Passthru", - category = "Example", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An Example Audio/MIDI Passthrough Plugin using Buffer Pointers]] -} - --- return possible audio i/o configurations -function dsp_ioconfig () - -- -1, -1 = any number of channels as long as input and output count matches - -- require 1 MIDI in, 1 MIDI out. - return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, } -end - --- "dsp_runmap" uses Ardour's internal processor API, eqivalent to --- 'connect_and_run()". There is no overhead (mapping, translating buffers). --- The lua implementation is responsible to map all the buffers directly. -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:ChanMapping - - local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- get index of the 1st mapped midi input buffer - - if ib ~= ARDOUR.ChanMapping.Invalid then - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:MidiBuffer - local mb = bufs:get_midi (ib) -- get the mapped buffer - local events = mb:table () -- copy event list into a lua table - - -- iterate over all MIDI events - for _, e in pairs (events) do - -- e is-a http://manual.ardour.org/lua-scripting/class_reference/#Evoral:MidiEvent - - -- do something with the event e.g. - print (e:channel (), e:time (), e:size (), e:buffer ():array ()[1], e:buffer ():get_table (e:size ())[1]) - end - end - - ---- - -- The following code is needed with "dsp_runmap" to work for arbitrary pin connections - -- this passes though all audio/midi data unprocessed. - - ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio")) - ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi")) - - -- equivalent lua code. - -- NOTE: the lua implementation below is intended for io-config [-1,-1]. - -- It only works for actually mapped channels due to in_map:count() out_map:count() - -- being identical to the i/o pin count in this case. - -- - -- Plugins that have multiple possible configurations will need to implement - -- dsp_configure() and remember the actual channel count. - -- - -- ARDOUR.DSP.process_map() does iterate over the mapping itself and works generally. - -- Still the lua code below does lend itself as elaborate example. - -- - --[[ - - local audio_ins = in_map:count (): n_audio () -- number of mapped audio input buffers - local audio_outs = out_map:count (): n_audio () -- number of mapped audio output buffers - assert (audio_outs, audio_ins) -- ioconfig [-1, -1]: must match - - -- copy audio data if any - for c = 1, audio_ins do - local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped input buffer - local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped output buffer - if ib ~= ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob then - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioBuffer - ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples) - end - end - -- Clear unconnected output buffers. - -- In case we're processing in-place some buffers may be identical, - -- so this must be done *after* copying relvant data from that port. - for c = 1, audio_outs do - local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) - local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) - if ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid then - bufs:get_audio (ob):silence (n_samples, offset) - end - end - - -- copy midi data - local midi_ins = in_map:count (): n_midi () -- number of midi input buffers - local midi_outs = out_map:count (): n_midi () -- number of midi input buffers - - -- with midi_in=1, midi_out=1 in dsp_ioconfig - -- the following will always be true - assert (midi_ins == 1) - assert (midi_outs == 1) - - for c = 1, midi_ins do - local ib = in_map:get (ARDOUR.DataType ("midi"), c - 1) - local ob = out_map:get (ARDOUR.DataType ("midi"), c - 1) - if ib ~= ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob then - bufs:get_midi (ob):copy (bufs:get_midi (ib)) - end - end - -- silence unused midi outputs - for c = 1, midi_outs do - local ib = in_map:get (ARDOUR.DataType ("midi"), c - 1) - local ob = out_map:get (ARDOUR.DataType ("midi"), c - 1) - if ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid then - bufs:get_midi (ob):silence (n_samples, offset) - end - end - --]] -end diff --git a/scripts/_region_transients.lua b/scripts/_region_transients.lua deleted file mode 100644 index de42992dd1..0000000000 --- a/scripts/_region_transients.lua +++ /dev/null @@ -1,16 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Region Transient List" } - -function factory () return function () - local sel = Editor:get_selection () - for r in sel.regions:regionlist ():iter () do - local region_pos = r:position() - local region_off = r:start() - print (r:name(), r:position(), r:start()) - local trans = r:transients() - for t in trans:iter() do - -- print absolute timeline position of transients - print (t + region_pos - region_off) - end - print ("----") - end -end end diff --git a/scripts/_remember_file.lua b/scripts/_remember_file.lua deleted file mode 100644 index 7a79fc861b..0000000000 --- a/scripts/_remember_file.lua +++ /dev/null @@ -1,37 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "File Name Test", - author = "Ardour Lua Taskforce", - description = [[Example Plugin to show to to select a file and remember the most recently used file.]] -} - -function factory () - local file_name_testscript_last_filename -- this acts as "global" variable, use a unique name - return function () - print (file_name_testscript_last_filename) -- debug - - --set filename to most recently used, fall back to use a default - local fn = file_name_testscript_last_filename or ARDOUR.LuaAPI.build_filename (Session:path (), Session:name () .. ".ardour") - - -- prepare a dialog - local dialog_options = { - { type = "file", key = "file", title = "Select a File", path = fn } - } - - -- show dialog - local od = LuaDialog.Dialog ("title", dialog_options) - local rv = od:run() - - if rv then - -- remember most recently selected file - file_name_testscript_last_filename = rv['file'] - LuaDialog.Message ("title", "set path to " .. file_name_testscript_last_filename, LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() - else - -- unset most recently used filename on dialog "cancel" - file_name_testscript_last_filename = nil - end - - od = nil - collectgarbage () - end -end diff --git a/scripts/_rewind.lua b/scripts/_rewind.lua deleted file mode 100644 index 88e150612c..0000000000 --- a/scripts/_rewind.lua +++ /dev/null @@ -1,12 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Rewind", - author = "Ardour Lua Task Force", - description = [[An Example Ardour Editor Action Script.]] -} - -function factory (params) - return function () - Session:goto_start() - end -end diff --git a/scripts/_rgh_midi_track_trick.lua b/scripts/_rgh_midi_track_trick.lua deleted file mode 100644 index 85308be0b1..0000000000 --- a/scripts/_rgh_midi_track_trick.lua +++ /dev/null @@ -1,81 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Rob's 16 MIDI Trick Pony", - description = [[clearly broken approach to go about things]] -} - -function route_setup () - return { - ['Insert_at'] = ARDOUR.PresentationInfo.max_order, - ['name'] = 'Sweet16', - ['group'] = false, -- return value will be a RouteGroup* or nil - } -end - -function factory (p) return function () - local name = "Sweet16" - local insert_at = ARDOUR.PresentationInfo.max_order - local group = nil - - -- check for "MIDI Channel Map" LV2 from x42 midifilters.lv2 - if ARDOUR.LuaAPI.new_plugin_info ("http://gareus.org/oss/lv2/midifilter#channelmap", ARDOUR.PluginType.LV2):isnil () then - LuaDialog.Message ("16 MIDI Tracks", "Error: Plugin 'MIDI Simple Channel Map' was not found.", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - return - end - - if type (p) == 'table' and p['how_many'] ~= nil then - -- used from the AddRouteDialog (or w/action_params) - name = p["name"] or 'Sweet16' - insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order; - group = p["group"] or nil - else - -- used standalone, prompt for name and insert position - local dialog_options = { - { type = "entry", key = "name", default = 'Sweet16', title = "Name Prefix" }, - { type = "entry", key = "group", default = '', title = "Group (empty for none)" }, - { type = "dropdown", key = "insertpos", title = "Position", default = "Last", values = - { - ["First"] = ArdourUI.InsertAt.First, - ["Before Selection"] = ArdourUI.InsertAt.BeforeSelection, - ["After Selection"] = ArdourUI.InsertAt.AfterSelection, - ["Last"] = ArdourUI.InsertAt.Last - } - } - } - - local od = LuaDialog.Dialog ("16 MIDI Tracks", dialog_options) - local rv = od:run() - if (not rv) then return end - name = rv['name'] or 'Sweet16' - if rv['insertpos'] then - insert_at = ArdourUI.translate_order (rv['insertpos']) - end - if rv['group'] and rv['group'] ~= '' then - group = Session:new_route_group (rv['group']) - end - end - collectgarbage () - - -- all systems go - - local tl = Session:new_midi_track ( - ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1), - ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1), - true, -- strict i/o - ARDOUR.PluginInfo(), nil, -- no instrument, no instrument preset - group, - 16, -- how many - name, insert_at, ARDOUR.TrackMode.Normal) - - local i = 1 - for track in tl:iter() do - local p = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/midifilter#channelmap", ARDOUR.PluginType.LV2, "") - assert (not p:isnil ()) - track:add_processor_by_index(p, 0, nil, true) - for j = 1, 16 do - ARDOUR.LuaAPI.set_processor_param (p, j, i) - end - i = i + 1 - end - collectgarbage () -- drop references to tracks. -end end diff --git a/scripts/_route_template_generic_audio.lua b/scripts/_route_template_generic_audio.lua deleted file mode 100644 index 7dabce8aef..0000000000 --- a/scripts/_route_template_generic_audio.lua +++ /dev/null @@ -1,119 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Generic Audio Track", - description = [[Add Audio tracks, by default as many as there are physical inputs]] -} - --- If a route_setup function is present in an Editor Action Script --- the script is also listed in the "add track/bus" dialog as meta-template --- --- The function is expected to return a Lua table. The table may be empty. -function route_setup () - local e = Session:engine() - local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector()) - return - { - -- keys control which AddRouteDialog controls are made sensitive. - -- The following keys accept a default value to pre-seed the dialog. - ['how_many'] = t[4]:size(), - ['name'] = 'Audio', - ['channels'] = 2, - ['track_mode'] = ARDOUR.TrackMode.Normal, - ['strict_io'] = true, - -- these keys just need to be set (to something other than nil) - -- in order to set the control sensitives - ['insert_at'] = ARDOUR.PresentationInfo.max_order, - ['group'] = false, -- return value will be a RouteGroup* - ['instrument'] = nil, -- return value will be a PluginInfoPtr - } -end - --- The Script can be used as EditorAction in which case it *could* --- optionally provide instantiation parmaters.. ---[[ -function action_params () - return - { - ['how_many'] = { title = "How Many tracks to add", default = "1" }, - ["name"] = { title = "Track Name Prefix", default = "Audio" }, - } -end ---]] - - -function factory (p) - -- when used from the AddRouteDialog (or w/action_params) - if type (p) == 'table' and p['how_many'] ~= nil then - return function () - -- When called from the AddRouteDialog, 'p' will be a table with - -- keys as described in route_setup() above. - local name = p["name"] or 'Audio' - local how_many = p["how_many"] or 1 - local channels = p["channels"] or 1 - local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order; - local group = p["group"] or nil - local mode = p["track_mode"] or ARDOUR.TrackMode.Normal - local strict_io = p["strict_io"] or false - local chan_out = 0 - - if ARDOUR.config():get_output_auto_connect() == ARDOUR.AutoConnectOption.AutoConnectMaster then - if not Session:master_out():isnil() then - chan_out = Session:master_out():n_inputs ():n_audio () - end - end - - if chan_out == 0 then - chan_out = channels; - end - - local tl = Session:new_audio_track (channels, chan_out, group, how_many, name, insert_at, mode) - - if strict_io then - for t in tl:iter() do - t:set_strict_io (true) - end - end - end - end - -- when used as standalone (no action parameters): interactive - return function () - local e = Session:engine() - local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector()) - local tracks = t[4]:size(); - - local dialog_options = { - { type = "number", key = "tracks", title = "Create Tracks", min = 1, max = 128, step = 1, digits = 0, default = tracks }, - { type = "entry", key = "name", default = 'Audio', title = "Name Prefix" }, - { type = "checkbox", key = "stereo", default = false, title = "Stereo" }, - { type = "checkbox", key = "recarm", default = false, title = "Record Arm Tracks" }, - } - - local dlg = LuaDialog.Dialog ("Create Audio Tracks", dialog_options) - local rv = dlg:run() - if (not rv or rv['tracks'] == 0) then - return - end - - local chan_in = stereo and 2 or 1 - local chan_out = 0 - - if ARDOUR.config():get_output_auto_connect() == ARDOUR.AutoConnectOption.AutoConnectMaster then - if not Session:master_out():isnil() then - chan_out = Session:master_out():n_inputs ():n_audio () - end - end - - if chan_out == 0 then - chan_out = chan_in; - end - - -- create tracks - local tl = Session:new_audio_track (chan_in, chan_out, nil, rv['tracks'], "", ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal) - -- and optionally record-arm them - if rv['recarm'] then - for track in tl:iter() do - track:rec_enable_control ():set_value (1, PBD.GroupControlDisposition.NoGroup) - end - end - end -end diff --git a/scripts/_route_template_generic_midi.lua b/scripts/_route_template_generic_midi.lua deleted file mode 100644 index a62197d137..0000000000 --- a/scripts/_route_template_generic_midi.lua +++ /dev/null @@ -1,78 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Generic MIDI Track", - description = [[Example]] -} - --- If a route_setup function is present in an Editor Action Script --- the script is also listed in the "add track/bus" dialog as meta-template --- --- The function is expected to return a Lua table. The table may be empty. -function route_setup () - return - { - -- keys control which AddRouteDialog controls are made sensitive. - -- The following keys accept a default value to pre-seed the dialog. - ['how_many'] = 1, - ['name'] = 'MIDI', - ['channels'] = nil, - ['track_mode'] = nil, - ['strict_io'] = true, - -- these keys just need to be set (to something other than nil) - -- in order to set the control sensitives - ['insert_at'] = ARDOUR.PresentationInfo.max_order, - ['group'] = false, -- return value will be a RouteGroup* - ['instrument'] = true, -- return value will be a PluginInfoPtr - } -end - --- The Script can be used as EditorAction in which case it can --- optionally provide instantiation parmaters -function action_params () - return - { - ['how_many'] = { title = "How Many tracks to add", default = "1" }, - ["name"] = { title = "Track Name Prefix", default = "MIDI" }, - ["instrument"] = { title = "Add Instrument", default = "true" }, - } -end - - -function factory (params) return function () - -- When called from the AddRouteDialog, 'params' will be a table with - -- keys as described in route_setup() above. - - local p = params or route_setup () - local name = p["name"] or 'Audio' - local how_many = p["how_many"] or 1 - local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order; - local group = p["group"] or nil - local strict_io = p["strict_io"] or false - local instrument = p["instrument"] or nil - - -- used in 'action-script mode' - if instrument == "true" then - instrument = ARDOUR.LuaAPI.new_plugin_info ("http://gareus.org/oss/lv2/gmsynth", ARDOUR.PluginType.LV2) -- general midi synth - if instrument:isnil () then - instrument = ARDOUR.LuaAPI.new_plugin_info ("https://community.ardour.org/node/7596", ARDOUR.PluginType.LV2) -- reasonable synth - end - if instrument:isnil () then - LuaDialog.Message ("MIDI track add", "Cannot find instrument plugin", - LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - return - end - end - - -- add no instrument - if type (instrument) ~= "userdata" then - instrument = ARDOUR.PluginInfo () - end - - Session:new_midi_track( - ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1), - ARDOUR.ChanCount(ARDOUR.DataType ("audio"), 2), - strict_io, - instrument, nil, - group, how_many, name, insert_at, ARDOUR.TrackMode.Normal) - -end end diff --git a/scripts/_rubberband_swing.lua b/scripts/_rubberband_swing.lua deleted file mode 100644 index f9c703284d..0000000000 --- a/scripts/_rubberband_swing.lua +++ /dev/null @@ -1,175 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Swing It (Rubberband)", - license = "MIT", - author = "Ardour Team", -description = [[ -Create a 'swing feel' in selected regions. - -The beat position of selected audio regions is analyzed, -then the audio is time-stretched, moving 8th notes back in -time while keeping 1/4-note beats in place to produce -a rhythmic swing style. - -(This script also servers as example for both VAMP -analysis as well as Rubberband region stretching.) - -Kudos to Chris Cannam. -]] -} - -function factory () return function () - - -- helper function -- - -- there is currently no direct way to find the track - -- corresponding to a [selected] region - function find_track_for_region (region_id) - for route in Session:get_tracks ():iter () do - local track = route:to_track () - local pl = track:playlist () - if not pl:region_by_id (region_id):isnil () then - return track - end - end - assert (0) -- can't happen, region must be in a playlist - end - - -- get Editor selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- Instantiate the QM BarBeat Tracker - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp - -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-barbeattracker - local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-barbeattracker", Session:nominal_sample_rate ()) - - -- prepare undo operation - Session:begin_reversible_command ("Rubberband Regions") - local add_undo = false -- keep track if something has changed - - -- for each selected region - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region - - -- test if it's an audio region - local ar = r:to_audioregion () - if ar:isnil () then - goto next - end - - -- create Rubberband stretcher - local rb = ARDOUR.LuaAPI.Rubberband (ar, false) - - -- the rubberband-filter also implements the readable API. - -- https://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable - -- This allows to read from the master-source of the given audio-region. - -- Any prior time-stretch or pitch-shift are ignored when reading, however - -- processing retains the previous settings - local max_pos = rb:readable ():readable_length () - - -- prepare table to hold analysis results - -- the beat-map is a table holding audio-sample positions: - -- [from] = to - local beat_map = {} - local prev_beat = 0 - - -- construct a progress-dialog with cancle button - local pdialog = LuaDialog.ProgressWindow ("Rubberband", true) - -- progress dialog callbacks - function vamp_callback (_, pos) - return pdialog:progress (pos / max_pos, "Analyzing") - end - function rb_progress (_, pos) - return pdialog:progress (pos / max_pos, "Stretching") - end - - -- run VAMP plugin, analyze the first channel of the audio-region - vamp:analyze (rb:readable (), 0, vamp_callback) - - -- getRemainingFeatures returns a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet - -- get the first output. here: Beats, estimated beat locations & beat-number - -- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList - local fl = vamp:plugin ():getRemainingFeatures ():at (0) - local beatcount = 0 - -- iterate over returned features - for f in fl:iter () do - -- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature - local fn = Vamp.RealTime.realTime2Frame (f.timestamp, Session:nominal_sample_rate ()) - beat_map[fn] = fn -- keep beats (1/4 notes) unchanged - if prev_beat > 0 then - -- move the half beats (1/8th) back - local diff = (fn - prev_beat) / 2 - beat_map[fn - diff] = fn - diff + diff / 3 -- moderate swing 2:1 (triplet) - --beat_map[fn - diff] = fn - diff + diff / 2 -- hard swing 3:1 (dotted 8th) - beatcount = beatcount + 1 - end - prev_beat = fn - end - -- reset the plugin state (prepare for next iteration) - vamp:reset () - - if pdialog:canceled () then goto out end - - -- skip regions shorter than a bar - if beatcount < 8 then - pdialog:done () - goto next - end - - -- configure rubberband stretch tool - rb:set_strech_and_pitch (1, 1) -- no overall stretching, no pitch-shift - rb:set_mapping (beat_map) -- apply beat-map from/to - - -- now stretch the region - local nar = rb:process (rb_progress) - - if pdialog:canceled () then goto out end - - -- hide modal progress dialog and destroy it - pdialog:done () - pdialog = nil - - -- replace region - if not nar:isnil () then - print ("new audio region: ", nar:name (), nar:length ()) - local track = find_track_for_region (r:to_stateful ():id ()) - local playlist = track:playlist () - playlist:to_stateful ():clear_changes () -- prepare undo - playlist:remove_region (r) - playlist:add_region (nar, r:position (), 1, false, 0, 0, false) - -- create a diff of the performed work, add it to the session's undo stack - -- and check if it is not empty - if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then - add_undo = true - end - end - - ::next:: - end - - ::out:: - - -- all done, commit the combined Undo Operation - if add_undo then - -- the 'nil' Command here mean to use the collected diffs added above - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end - - -- clean up, unload vamp plugin - vamp = nil - collectgarbage () -end end - - -function icon (params) return function (ctx, width, height, fg) - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(width * .7) .. "px") - txt:set_text ("\u{266b}\u{266a}") -- 8th note symbols - local tw, th = txt:get_pixel_size () - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/_session_load_hook.lua b/scripts/_session_load_hook.lua deleted file mode 100644 index 82546814f8..0000000000 --- a/scripts/_session_load_hook.lua +++ /dev/null @@ -1,32 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Load Session Hook Example", - author = "Ardour Lua Task Force", - description = "Display some dialogs during session load and execute actions", -} - --- subscribe to signals --- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal -function signals () - s = LuaSignal.Set() - s:add ({[LuaSignal.SetSession] = true}) - return s -end - --- create callback functions -function factory () return function (signal, ...) - assert (signal == LuaSignal.SetSession) - local md = LuaDialog.Message ("Set Session", "Loading Session:" .. Session:name(), LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close) - md:run() - - local dialog_options = { - { type = "checkbox", key = "tempo", default = true, title = "Show Tempo Ruler" }, - { type = "checkbox", key = "meter", default = true, title = "Show Meter Ruler" }, - } - local dlg = LuaDialog.Dialog ("Tweak Rulers", dialog_options) - local rv = dlg:run() - if (rv) then - Editor:set_toggleaction ("Rulers", "toggle-tempo-ruler", rv['tempo']) - Editor:set_toggleaction ("Rulers", "toggle-meter-ruler", rv['meter']) - end -end end diff --git a/scripts/_session_test.lua b/scripts/_session_test.lua deleted file mode 100644 index f6adea38c7..0000000000 --- a/scripts/_session_test.lua +++ /dev/null @@ -1,34 +0,0 @@ -ardour { - ["type"] = "session", - name = "Good Night", - author = "Ardour Lua Task Force", - description = [[ - Example Ardour Session Script. - Session scripts are called at the beginning of every process-callback (before doing any audio processing). - This example stops the transport after rolling for a configurable time which can be set when instantiating the script.]] -} - -function sess_params () - return - { - ["print"] = { title = "Debug Print (yes/no)", default = "no", optional = true }, - ["time"] = { title = "Timeout (sec)", default = "90", optional = false }, - } -end - -function factory (params) - return function (n_samples) - local p = params["print"] or "no" - local timeout = params["time"] or 90 - a = a or 0 - if p ~= "no" then print (a, n_samples, Session:sample_rate (), Session:transport_rolling ()) end -- debug output (not rt safe) - if (not Session:transport_rolling()) then - a = 0 - return - end - a = a + n_samples - if (a > timeout * Session:sample_rate()) then - Session:request_transport_speed(0.0, true, ARDOUR.TransportRequestSource.TRS_Engine) - end - end -end diff --git a/scripts/_smash.lua b/scripts/_smash.lua deleted file mode 100644 index 2ebcac19aa..0000000000 --- a/scripts/_smash.lua +++ /dev/null @@ -1,31 +0,0 @@ -ardour { ["type"] = "dsp", name = "Sound Smasher", category = "Dynamics", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] } - -function dsp_ioconfig () return - -- -1, -1 = any number of channels as long as input and output count matches - { { audio_in = -1, audio_out = -1}, } -end - - --- the DSP callback function to process audio audio --- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray -function dsp_run (ins, outs, n_samples) - for c = 1, #outs do -- for each output channel (count from 1 to number of output channels) - - if ins[c] ~= outs[c] then -- if processing is not in-place.. - ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) -- ..copy data from input to output. - end - - -- direct audio data access, in-place processing of output buffer - local buf = outs[c]:array() -- get channel's 'c' data as lua array reference - - -- process all audio samples - for s = 1, n_samples do - buf[s] = math.atan (1.5707 * buf[s]) -- some non-linear gain. - - -- NOTE: doing the maths per sample in lua is not super-efficient - -- (vs C/C++ vectorized functions -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP) - -- but it is very convenient, especially for prototypes and quick solutions. - end - - end -end diff --git a/scripts/_sort_tracks_by_name.lua b/scripts/_sort_tracks_by_name.lua deleted file mode 100644 index 36dfdc970c..0000000000 --- a/scripts/_sort_tracks_by_name.lua +++ /dev/null @@ -1,36 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Track Sort", - author = "Ardour Lua Taskforce", - description = [[Sort tracks alphabetically by name]] -} - -function factory () return function () - - -- sort compare function - -- a,b here are http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route - -- return true if route "a" should be ordered before route "b" - function tsort (a, b) - return a:name() < b:name() - end - - -- create a sortable list of tracks - local tracklist = {} - for t in Session:get_tracks():iter() do - table.insert(tracklist, t) - end - - -- sort the list using the compare function - table.sort(tracklist, tsort) - - -- traverse the sorted list and assign "presentation-order" to each track - local pos = 1; - for _, t in ipairs(tracklist) do - t:set_presentation_order(pos) - pos = pos + 1 - end - - -- drop all track references - tracklist = nil - collectgarbage () -end end diff --git a/scripts/_spike_synth.lua b/scripts/_spike_synth.lua deleted file mode 100644 index 167e0e27d2..0000000000 --- a/scripts/_spike_synth.lua +++ /dev/null @@ -1,37 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Spike Synth", - category = "Instrument", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[A debug and test-instrumentation synth. This plugin is useful with Ardour's "Dummy" backend "Engine-Pulse" mode to verify capture alignment. This plugin generate the exact same audio-signal from MIDI data that the backend also generates: Note-on: +1, Note-off: -1.]] -} - -function dsp_ioconfig () - return { { midi_in = 1, audio_in = 0, audio_out = 1} } -end - -function dsp_run (ins, outs, n_samples) - local a = {} - for s = 1, n_samples do a[s] = 0 end - - for c = 1,#outs do - ARDOUR.DSP.memset (outs[c], 0, n_samples) - end - - assert (type(midiin) == "table") - for _,b in pairs (midiin) do - local t = b["time"] -- t = [ 1 .. n_samples ] - local d = b["data"] -- get midi-event - if (#d == 3 and (d[1] & 240) == 144) then -- note on - for c = 1,#outs do - outs[c]:array()[t] = 1.0 - end - end - if (#d == 3 and (d[1] & 240) == 128) then -- note off - for c = 1,#outs do - outs[c]:array()[t] = -1.0 - end - end - end -end diff --git a/scripts/_split_benchmark.lua b/scripts/_split_benchmark.lua deleted file mode 100644 index 6ab60e44bc..0000000000 --- a/scripts/_split_benchmark.lua +++ /dev/null @@ -1,58 +0,0 @@ --- [disable CPU freq scaling for benchmark] --- create a session --- add 16 mono tracks --- record 2-3 mins on each track starting at 00:00:00:00 --- rewind the playhead to 00:00:00:00 --- run this script in Menu > Window. Scripting 10 times -ardour { ["type"] = "EditorAction", name = "Split Benchmark" } - -function factory (params) return function () - - function split_at (pos) - local add_undo = false -- keep track if something has changed - Session:begin_reversible_command ("Auto Region Split") - for route in Session:get_tracks():iter() do - local playlist = route:to_track():playlist () - playlist:to_stateful ():clear_changes () - for region in playlist:regions_at (pos):iter () do - playlist:split_region (region, ARDOUR.MusicSample (pos, 0)) - end - if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then - add_undo = true - end - end - if add_undo then - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end - end - - function count_regions () - local total = 0 - for route in Session:get_tracks():iter() do - total = total + route:to_track():playlist():region_list():size() - end - return total - end - - for x = 1, 3 do - local playhead = Session:transport_sample () - - local step = Session:samples_per_timecode_frame() - local n_steps = 20 - - local t_start = ARDOUR.LuaAPI.monotonic_time () - for i = 1, n_steps do - split_at (playhead + step * i) - end - local t_end = ARDOUR.LuaAPI.monotonic_time () - - Session:request_locate((playhead + step * n_steps), ARDOUR.LocateTransportDisposition.MustStop, ARDOUR.TransportRequestSource.TRS_UI) - print (count_regions (), (t_end - t_start) / 1000 / n_steps) - collectgarbage (); - ARDOUR.LuaAPI.usleep(500000) - end - - -end end diff --git a/scripts/_stereo_to_mono.lua b/scripts/_stereo_to_mono.lua deleted file mode 100644 index 81fa0316a7..0000000000 --- a/scripts/_stereo_to_mono.lua +++ /dev/null @@ -1,57 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Stereo to Mono", - license = "MIT", - author = "Ardour Team", - description = [[Convert a Stereo Track into two Mono Tracks]] -} - - -function factory (params) return function () - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - -- the Ardour Selection can include multiple items - -- (regions, tracks, ranges, markers, automation, midi-notes etc) - local sel = Editor:get_selection () - - -- for each track.. - for t in sel.tracks:routelist ():iter () do - local track = t:to_track () - if track:isnil() then goto next end - - -- only audio tracks - local playlist = track:playlist () - if playlist:data_type ():to_string () ~= "audio" then goto next end - - -- skip tracks without any regions - if playlist:region_list ():size() == 0 then goto next end - - -- we can't access diskstream n_channels() - local channels = track:n_inputs(): n_audio() - - -- stereo only - if channels ~= 2 then goto next end - - -- create 2 new tracks (using the name of the original track)( - local newtracks = Session:new_audio_track (2, 2, nil, 2, t:name(), ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal) - assert (newtracks:size() == 2) - - for r in playlist:region_list ():iter () do - local region = r:to_audioregion () - local rl = ARDOUR.RegionVector () - local _, rv = region:separate_by_channel (rl) - assert (rv[1]:size () == 2) - -- 1:1 mapping of regions to new tacks - local plc = 1 - for nr in rv[1]:iter () do - local pl = newtracks:table()[plc]:playlist() - pl:add_region (nr, r:position(), 1, false, 0, 0, false) - plc = plc + 1 - end - end - - -- TODO remove the old track - - -- drop references for good. - collectgarbage () - ::next:: - end - -end end diff --git a/scripts/_system_exec.lua b/scripts/_system_exec.lua deleted file mode 100644 index 281f5dee0b..0000000000 --- a/scripts/_system_exec.lua +++ /dev/null @@ -1,20 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "System Exec" } - -function factory () return function () - -- ** EXAMPLES TO RUN EXTERNAL APPLICATIONS ** -- - - -- run a command in a shell and wait for it to complete. - -- - -- Details: basically just system(3), except on Unix like systems with - -- memory-locking, this call is special-cased to use vfork and close - -- file-descriptors. On other systems it defaults to Lua's os-library - -- built-in os.execute system() call. - os.execute ("date > /tmp/testdate") - - -- os.forkexec() works as fire-and-forget. execv(3) style - -- - -- Details: It calls vfork() and exec() under the hood, passing each - -- argument separately to exec (and needs a full-path to the binary). - os.forkexec ("/usr/bin/xterm") - os.forkexec ("/bin/sh", "-c", "import --frame \"/tmp/scr_$(date).png\"") -end end diff --git a/scripts/_tempo_map_dump.lua b/scripts/_tempo_map_dump.lua deleted file mode 100644 index 7e85cd5451..0000000000 --- a/scripts/_tempo_map_dump.lua +++ /dev/null @@ -1,14 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Tempo Map Dump" } - -function factory () return function () - - local tm = Session:tempo_map () - local ts = tm:tempo_section_at_sample (0) - - while true do - print ("TS @", ts:sample(), " | ", ts:to_tempo():note_types_per_minute (), "..", ts:to_tempo():end_note_types_per_minute (), "bpm") - ts = tm:next_tempo_section (ts) - if not ts then break end - end - -end end diff --git a/scripts/_toggle_monitor_section.lua b/scripts/_toggle_monitor_section.lua deleted file mode 100644 index e097a94a32..0000000000 --- a/scripts/_toggle_monitor_section.lua +++ /dev/null @@ -1,10 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Toggle Monitor Section" } - -function factory () return function () - if Session:monitor_out():isnil() then - ARDOUR.config():set_use_monitor_bus (true) - else - ARDOUR.config():set_use_monitor_bus (false) - collectgarbage () - end -end end diff --git a/scripts/_tx_raw_midi_from_file.lua b/scripts/_tx_raw_midi_from_file.lua deleted file mode 100644 index ac45cd1b10..0000000000 --- a/scripts/_tx_raw_midi_from_file.lua +++ /dev/null @@ -1,128 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Send Raw MIDI from File", - license = "MIT", - author = "Ardour Team", - description = [[Read raw binary midi (.syx) from file and send it to a control port]] -} - -function factory () return function () - - function portlist () - local rv = {} - local a = Session:engine() - local _, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList()) - for p in t[2]:iter() do - local amp = p:to_asyncmidiport () - if amp:isnil() or not amp:sends_output() then goto continue end - rv[amp:name()] = amp - print (amp:name(), amp:sends_output()) - ::continue:: - end - return rv - end - - local dialog_options = { - { type = "file", key = "file", title = "Select .syx MIDI file" }, - { type = "dropdown", key = "port", title = "Target Port", values = portlist () } - } - - local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run () - dialog_options = nil -- drop references (ports, shared ptr) - collectgarbage () -- and release the references immediately - - if not rv then return end -- user cancelled - - local f = io.open (rv["file"], "rb") - - if not f then - LuaDialog.Message ("Raw MIDI Tx", "File Not Found", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run () - goto out - end - - do -- scope for 'local' - local size = f:seek("end") -- determine file size - f:seek("set", 0) - - if size > 1048576 then - local ok = LuaDialog.Message ("Raw MIDI Tx", - string.format ("File is larger than 1MB.\nFile-size = %.1f kB\n\nContinue?", size / 1024), - LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run () - if ok ~= LuaDialog.Response.Yes then - f:close () - goto out - end - end - end - - do -- scope for 'local' - local midi_byte_count = 0 - local total_read = 0 - local message_count = 0 - local long_message = false - - local async_midi_port = rv["port"] -- reference to port - local parser = ARDOUR.RawMidiParser () -- construct a MIDI parser - - while true do - -- read file in 64byte chunks - local bytes = f:read (64) - if not bytes then break end - total_read = total_read + #bytes - - -- parse MIDI data byte-by-byte - for i = 1, #bytes do - if parser:process_byte (bytes:byte (i)) then - if parser:buffer_size () > 255 then - long_message = true - print ("WARNING -- single large message > 255, bytes: ", parser:buffer_size ()) - end - -- parsed complete normalized MIDI message, send it - async_midi_port:write (parser:midi_buffer (), parser:buffer_size (), 0) - - -- Physical MIDI is sent at 31.25kBaud. - -- Every message is sent as 10bit message on the wire, - -- so every MIDI byte needs 320usec. - ARDOUR.LuaAPI.usleep (400 * parser:buffer_size ()) - - -- count msgs and valid bytes sent - midi_byte_count = midi_byte_count + parser:buffer_size () - message_count = message_count + 1 - if 0 == message_count % 50 then - -- print() wakes up the GUI, prevent stalling the event loop - print ("Sent", message_count, "messages, bytes so far: ", midi_byte_count) - end - end - end - end - - f:close () - print ("Sent", message_count, "messages, total bytes: ", midi_byte_count, "/", total_read) - - if long_message then - LuaDialog.Message ("Raw MIDI Tx", "Dataset contained messages longer than 127 bytes. Which may or may not have been transmitted successfully.", LuaDialog.MessageType.Warning, LuaDialog.ButtonType.Close):run () - end - end - - ::out:: - rv = nil - collectgarbage () -end end - -function icon (params) return function (ctx, width, height, fg) - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .45) .. "px") - txt:set_text ("S") - ctx:move_to (1, 1) - txt:show_in_cairo_context (ctx) - - txt:set_text ("Y") - local tw, th = txt:get_pixel_size () - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:show_in_cairo_context (ctx) - - txt:set_text ("X") - tw, th = txt:get_pixel_size () - ctx:move_to ((width - tw - 1), (height - th -1)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/_vamp_example.lua b/scripts/_vamp_example.lua deleted file mode 100644 index 73552d638c..0000000000 --- a/scripts/_vamp_example.lua +++ /dev/null @@ -1,63 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Vamp Plugin Example" } - -function factory () return function () - - -- get a list of all available plugins - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp - -- returns a http://manual.ardour.org/lua-scripting/class_reference/#C:StringVector - local plugins = ARDOUR.LuaAPI.Vamp.list_plugins (); - for id in plugins:iter () do - print ("--", id) - end - - local sel = Editor:get_selection () - - -- load the Vamp Plugin with Id "libardourvampplugins:dBTP" - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp - local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:dBTP", Session:nominal_sample_rate()) - print (vamp:plugin():getName()) - - -- for each selected region - for r in sel.regions:regionlist ():iter () do - print ("Region:", r:name ()) - - -- run the plugin, analyze the first channel of the audio-region - vamp:analyze (r:to_readable (), 0, nil) - - -- get analysis results - local f = vamp:plugin ():getRemainingFeatures () - - -- f is-a Vamp::Plugin::FeatureSet aka std::map - -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet - for id, featlist in f:iter () do - print (id, featlist) - end - - -- get the first FeatureList - local featurelist = f:table()[0] - -- Vamp::Plugin::FeatureList is a typedef for std::vector - for feat in featurelist:iter () do - print ("-", feat.label) - end - - -- get the first feature.. - -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature - local feature = featurelist:at(0) - -- ..and the values of the feature, which is-a std::vector - local values = feature.values - -- iterate over the std::vector - for val in values:iter () do - print ("*", val) - end - - -- access the first element of Vamp::Plugin::Feature's "values" vector - -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatVector - local value = values:at(0) - -- in case of libardourvampplugins:dBTP that's the true-peak (signal value) - local dbtp = 20 * math.log (value) / math.log(10) -- convert it to dB - print (string.format ("Region '%s': %.2f dBTP", r:name (), dbtp)) - - -- reset the plugin for the next iteration - vamp:reset () - end -end end diff --git a/scripts/_vamp_note_example.lua b/scripts/_vamp_note_example.lua deleted file mode 100644 index 6a93dd8ee4..0000000000 --- a/scripts/_vamp_note_example.lua +++ /dev/null @@ -1,68 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Vamp Audio Transcription Example" } - -function factory () return function () - - -- get Editor selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - local sr = Session:nominal_sample_rate () - - -- Instantiate a Vamp Plugin - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp - local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-transcription", sr) - - -- prepare progress dialog - local progress_total = 0; - local progress_part = 0 - local pdialog = LuaDialog.ProgressWindow ("Audio to MIDI", true) - function cb (_, pos) - return pdialog:progress ((pos + progress_part) / progress_total, "Analyzing") - end - - -- calculate max progress - for r in sel.regions:regionlist ():iter () do - progress_total = progress_total + r:to_readable ():readable_length () - end - - -- for each selected region - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region - - vamp:analyze (r:to_readable (), 0, cb) - - if pdialog:canceled () then - goto out - end - - progress_part = progress_part + r:to_readable ():readable_length () - pdialog:progress (progress_part / progress_total, "Post Processing") - - print ("-- Post Processing: ", r:name ()) - - -- post-processing takes longer than actually parsing the data :( - local f = vamp:plugin ():getRemainingFeatures () - - local fl = f:table ()[0] - print (" Time (sample) | Len | Midi-Note"); - if fl then for f in fl:iter () do - assert (f.hasTimestamp and f.hasDuration); - local ft = Vamp.RealTime.realTime2Frame (f.timestamp, sr) - local fd = Vamp.RealTime.realTime2Frame (f.duration, sr) - local fn = f.values:at (0) -- midi note number - print (string.format (" %14d %7d %d", ft, fd, fn)) - end end - - -- reset the plugin (prepare for next iteration) - vamp:reset () - end - - ::out:: - -- hide modal progress dialog and destroy it - pdialog:done (); - pdialog = nil - vamp = nil; - collectgarbage () - -end end diff --git a/scripts/_vamp_onset_example.lua b/scripts/_vamp_onset_example.lua deleted file mode 100644 index adcdc16982..0000000000 --- a/scripts/_vamp_onset_example.lua +++ /dev/null @@ -1,83 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Vamp Onset Detection Example" } - -function factory () return function () - - -- get Editor selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- Instantiate a Vamp Plugin - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp - -- - -- here: the "Queen Mary Note Onset Detector" Vamp plugin (which comes with Ardour) - -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-onsetdetector - local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:qm-onsetdetector", Session:nominal_sample_rate()) - - -- prepare table to hold results - local onsets = {} - - -- for each selected region - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region - - -- prepare lua table to hold results for the given region (by name) - onsets[r:name ()] = {} - - -- callback to handle Vamp-Plugin analysis results - function callback (feats) - -- "feats" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet - -- get the first output. here: Detected note onset times - local fl = feats:table()[0] - -- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList - -- which may be empty or not nil - if fl then - -- iterate over returned features - for f in fl:iter () do - -- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature - if f.hasTimestamp then - local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000) - --print ("-", f.timestamp:toString(), fn) - table.insert (onsets[r:name ()], fn) - end - end - end - return false -- continue, !cancel - end - - -- Configure Vamp plugin - -- - -- One could query the Parameter and Program List: - -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin - -- but since the Plugin is known, we can directly refer to the plugin doc: - -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-onsetdetector - vamp:plugin ():setParameter ("dftype", 3); - vamp:plugin ():setParameter ("sensitivity", 50); - vamp:plugin ():setParameter ("whiten", 0); - -- in this case the above (3, 50, 0) is equivalent to - --vamp:plugin ():selectProgram ("General purpose"); - - -- run the plugin, analyze the first channel of the audio-region - -- - -- This uses a "high-level" convenience wrapper provided by Ardour - -- which reads raw audio-data from the region and and calls - -- f = vamp:plugin ():process (); callback (f) - vamp:analyze (r:to_readable (), 0, callback) - - -- get remaining features (end of analyis) - callback (vamp:plugin ():getRemainingFeatures ()) - - -- reset the plugin (prepare for next iteration) - vamp:reset () - end - - -- print results - for n,o in pairs(onsets) do - print ("Onset analysis for region:", n) - for _,t in ipairs(o) do - print ("-", t) - end - end - -end end diff --git a/scripts/_vamp_tempomap_example.lua b/scripts/_vamp_tempomap_example.lua deleted file mode 100644 index 1c216c06f3..0000000000 --- a/scripts/_vamp_tempomap_example.lua +++ /dev/null @@ -1,85 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Vamp TempoMap Test" } - -function factory () return function () - - -- get Editor selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- Instantiate the QM BarBeat Tracker - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp - -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-barbeattracker - local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:qm-barbeattracker", Session:nominal_sample_rate()) - - -- prepare table to hold results - local beats = {} - local bars = {} - - -- for each selected region - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region - - -- prepare lua table to hold results for the given region (by name) - beats[r:name ()] = {} - bars[r:name ()] = {} - - -- callback to handle Vamp-Plugin analysis results - function callback (feats) - -- "feats" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet - - -- get the first output. here: Beats, estimated beat locations & beat-number - local fl = feats:table()[0] - -- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList - -- which may be empty or not nil - if fl then - -- iterate over returned features - for f in fl:iter () do - -- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature - if f.hasTimestamp then - local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000) - table.insert (beats[r:name ()], {pos = fn, beat = f.label}) - end - end - end - - -- get the 2nd output. here: estimated bar locations - local fl = feats:table()[1] - if fl then - for f in fl:iter () do - if f.hasTimestamp then - local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000) - table.insert (bars[r:name ()], fn) - end - end - end - return false -- continue, !cancel - end - - vamp:plugin ():setParameter ("Beats Per Bar", 4); -- TODO ask - - -- run the plugin, analyze the first channel of the audio-region - vamp:analyze (r:to_readable (), 0, callback) - -- get remaining features (end of analyis) - callback (vamp:plugin ():getRemainingFeatures ()) - -- reset the plugin (prepare for next iteration) - vamp:reset () - end - - -- print results (for now) - -- TODO: calculate and set tempo-map - for n,o in pairs(bars) do - print ("Bar analysis for region:", n) - for _,t in ipairs(o) do - print ("-", t) - end - end - for n,o in pairs(beats) do - print ("Beat analysis for region:", n) - for _,t in ipairs(o) do - print ("-", t['pos'], t['beat']) - end - end - -end end diff --git a/scripts/_varispeed_callback.lua b/scripts/_varispeed_callback.lua deleted file mode 100644 index fe3f2721c1..0000000000 --- a/scripts/_varispeed_callback.lua +++ /dev/null @@ -1,32 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Varispeed Test - 100ms Callback", - author = "Ardour Lua Task Force", - description = "An example script that invokes a callback a every 0.1sec and modifies the transport speed", -} - -function signals () - s = LuaSignal.Set() - s:add ( - { - [LuaSignal.LuaTimerDS] = true, - } - ) - return s -end - -function factory (params) - -- upindex variables - local cnt = 0 - local speed = 0 - local delta = 0.01 - return function (signal, ref, ...) - cnt = (cnt + 1) % 5 -- divide clock: every half a second - if cnt == 0 then - if speed < -0.25 then delta = delta * -1 end - if speed > 0.25 then delta = delta * -1 end - speed = speed + delta - Session:request_transport_speed (speed, true, ARDOUR.TransportRequestSource.TRS_UI) - end - end -end diff --git a/scripts/_vca_slave_assign.lua b/scripts/_vca_slave_assign.lua deleted file mode 100644 index 616c0abd98..0000000000 --- a/scripts/_vca_slave_assign.lua +++ /dev/null @@ -1,73 +0,0 @@ -ardour { ["type"] = "Snippet", name = "VCA Slave Examples", - license = "MIT", - author = "Ardour Team", -} - -function factory () return function () - -- find possible masters & slave, allow selection in dropdown menu - local targets = {} - local sources = {} - local have_masters = false - local have_slaves = false - - for v in Session:vca_manager ():vcas() :iter () do -- for each VCA - sources [v:name ()] = v - have_masters = true - end - - for s in Session:get_stripables ():iter () do -- for every track/bus/vca - if s:is_monitor () or s:is_auditioner () then goto nextroute end -- skip special routes - targets [s:name ()] = s - have_slaves = true; - ::nextroute:: - end - - -- bail out if there are no parameters - if not have_slaves then - LuaDialog.Message ("VCA Slave Example", "No Slavables found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - sources = nil - collectgarbage () - return - end - if not have_masters then - LuaDialog.Message ("VCA Slave Example", "No VCA masters found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - targets = nil - collectgarbage () - return - end - - -- create a dialog, ask user which master to assign to which slave - local dialog_options = { - { type = "dropdown", key = "master", title = "Control Master", values = sources }, - { type = "dropdown", key = "slave", title = "Control Slave", values = targets } - } - local rv = LuaDialog.Dialog ("Select VCA assignment", dialog_options):run () - - targets = nil -- drop references (the table holds shared-pointer references to all strips) - collectgarbage () -- and release the references immediately - - if not rv then return end -- user canceled the operation - - -- parse user response - local mst = rv["master"] - local slv = rv["slave"] - assert (not slv:to_slavable ():isnil ()) - - -- test if mst is already controlled by slv (directly or indirectly) - -- if so, don't allow the connection - if (not slv:to_slavable ():assigned_to (Session:vca_manager(), mst)) then - -- if slv controlled by master, disconnect it - if (slv:slaved_to (mst)) then - slv:to_slavable ():unassign (mst) - else - slv:to_slavable ():assign (mst) - end - else - LuaDialog.Message ("VCA Slave Example", "Recursive VCA assignment ignored", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - end - - -- drop references - mst = nil slv = nil - collectgarbage () -end end - diff --git a/scripts/a_slow_mute.lua b/scripts/a_slow_mute.lua deleted file mode 100644 index a9ef12b754..0000000000 --- a/scripts/a_slow_mute.lua +++ /dev/null @@ -1,66 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-Slow-Mute", - category = "Amplifier", - license = "MIT", - author = "Ardour Team", - description = [[Mute button with slow fade in/out]] -} - -function dsp_ioconfig () - -- -1, -1 = any number of channels as long as input and output count matches - return { { audio_in = -1, audio_out = -1} } -end - - -function dsp_params () - return { { ["type"] = "input", name = "Mute", min = 0, max = 1, default = 0, toggled = true } } -end - -local cur_gain = 1 -local lpf = 0.002 -- parameter low-pass filter time-constant - -function dsp_init (rate) - lpf = 100 / rate -- interpolation time constant -end - -function low_pass_filter_param (old, new, limit) - if math.abs (old - new) < limit then - return new - else - return old + lpf * (new - old) - end -end - --- the DSP callback function --- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray -function dsp_run (ins, outs, n_samples) - local ctrl = CtrlPorts:array() -- get control port array - local target_gain = ctrl[1] > 0 and 0.0 or 1.0; -- when muted, target_gain = 0.0; otherwise use 1.0 - local siz = n_samples -- samples remaining to process - local off = 0 -- already processed samples - local changed = false - - -- if the target gain changes, process at most 32 samples at a time, - -- and interpolate gain until the current settings match the target values - if cur_gain ~= target_gain then - changed = true - siz = 32 - end - - while n_samples > 0 do - if siz > n_samples then siz = n_samples end - if changed then - cur_gain = low_pass_filter_param (cur_gain, target_gain, 0.001) - end - - for c = 1,#ins do -- process all channels - if ins[c] ~= outs[c] then - ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz) - end - ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, cur_gain); - end - n_samples = n_samples - siz - off = off + siz - end -end diff --git a/scripts/access_action.lua b/scripts/access_action.lua deleted file mode 100644 index b50c6386d8..0000000000 --- a/scripts/access_action.lua +++ /dev/null @@ -1,37 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Shortcut", - license = "MIT", - author = "me", - description = [[Trigger a keyboard shortcut. You will be prompted for the shortcut's action in the next step.]] -} - -function action_params () - local actionlist = { - { - type = "dropdown", key = "action", title = "Action", values = ArdourUI:actionlist(), - default = "Save" - } - } - - local rv = LuaDialog.Dialog ("Select Action", actionlist):run () - if not rv then -- user cancelled - return { ["x-script-abort"] = { title = "", preseeded = true} } - end - - local action = rv["action"] - local name = "Shortcut - " .. action - return { - ["action"] = { title = "Action to trigger", default = action, preseeded = true}, - ["x-script-name"] = { title = "Unique Script name", default = name, preseeded = true}, - } -end - -function factory (params) return function () - local p = params or { } - local as = assert (p["action"]) - local sp = assert (as:find('/')) - local group = assert (as:sub(0, sp - 1)) - local item = assert (as:sub(1 + sp)) - Editor:access_action (group, item) -end end diff --git a/scripts/add_filters.lua b/scripts/add_filters.lua deleted file mode 100644 index 77b41c17bf..0000000000 --- a/scripts/add_filters.lua +++ /dev/null @@ -1,54 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Add Filters", - license = "MIT", - author = "PSmith", - description = [[Add 'HPF/LPF' Lua Processor to all Tracks]] -} - -function action_params () - return - { - ["unique"] = { title = "Only add HPF/LPF if not already present (yes/no)", default = "yes"}, - ["position"] = { title = "Insert Position from top (0,..)", default = "0"}, - } -end - - -function factory (params) - return function () - -- get configuration - local p = params or {} - local uniq = p["unique"] or "yes" - local pos = p["position"] or 0 - - -- loop over all tracks - for t in Session:get_tracks():iter() do - local insert = true; - - -- check if filters are present - if uniq ~= "no" then - local proc; - local i = 0; - repeat - -- get Nth Ardour::Processor - proc = t:nth_plugin (i) - -- check if it's a filter - if (not proc:isnil() and proc:display_name () == "a-High/Low Pass Filter") then - insert = false; - end - i = i + 1 - until proc:isnil() or insert == false - end - - -- create a new processor and insert it - if insert then - local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-High/Low Pass Filter"); - if (not a:isnil()) then - t:add_processor_by_index(a, pos, nil, true) - a = nil -- explicitly drop shared-ptr reference - end - end - end - end -end diff --git a/scripts/addscopes.lua b/scripts/addscopes.lua deleted file mode 100644 index d180f03765..0000000000 --- a/scripts/addscopes.lua +++ /dev/null @@ -1,75 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Add Scopes", - license = "MIT", - author = "Ardour Team", - description = [[Add 'Inline Scope' Lua Processor to all Tracks]] -} - -function action_params () - return - { - ["unique"] = { title = "Only add Scope if non is present already (yes/no)", default = "yes"}, - ["position"] = { title = "Insert Position from top (0,..)", default = "0"}, - } -end - - -function factory (params) - return function () - -- get configuration - local p = params or {} - local uniq = p["unique"] or "yes" - local pos = p["position"] or 0 - - -- loop over all tracks - for t in Session:get_tracks():iter() do - local insert = true; - - -- check if a scope is already present - if uniq ~= "no" then - local proc; - local i = 0; - repeat - -- get Nth Ardour::Processor - proc = t:nth_plugin (i) - -- check if it's a scope - if (not proc:isnil() and proc:display_name () == "a-Inline Scope") then - insert = false; - end - i = i + 1 - until proc:isnil() or insert == false - end - - -- create a new processor and insert it - if insert then - local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-Inline Scope"); - if (not a:isnil()) then - t:add_processor_by_index(a, pos, nil, true) - ARDOUR.LuaAPI.set_processor_param (a, 0, 5) -- timescale 5sec - -- ARDOUR.LuaAPI.set_processor_param (a, 1, 1) -- logscale on - -- ARDOUR.LuaAPI.set_processor_param (a, 2, 3) -- "Max" height - a = nil -- explicitly drop shared-ptr reference - end - end - end - end -end - - -function icon (params) return function (ctx, width, height) - local wh = math.min (width, height) * .5 - local x0 = math.ceil (wh * .4) - local x1 = math.floor (wh * 1.6) - ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) - ctx:rectangle (wh * .4, wh * .4, wh * 1.2, wh * 1.2) - ctx:set_source_rgba (.7, .7, .7, 1) - ctx:fill () - ctx:set_line_width (1) - ctx:set_source_rgba (.0, .0, .0, 1) - ctx:move_to (x0, wh) - for x = x0, x1 do - ctx:line_to (x, wh - math.sin (2 * math.pi * (x-x0) / (x1-x0)) * wh * .5) - end - ctx:stroke () -end end diff --git a/scripts/amp4.lua b/scripts/amp4.lua deleted file mode 100644 index 276b4a0af6..0000000000 --- a/scripts/amp4.lua +++ /dev/null @@ -1,119 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-Amplifier", - category = "Amplifier", - license = "MIT", - author = "Ardour Team", - description = [[Versatile +/- 20dB multichannel amplifier]] -} - -function dsp_ioconfig () - return - { - -- -1, -1 = any number of channels as long as input and output count matches - { audio_in = -1, audio_out = -1}, - } -end - - -function dsp_params () - return - { - { ["type"] = "input", name = "Gain", min = -20, max = 20, default = 0, unit="dB"}, - } -end - -local lpf = 0.02 -- parameter low-pass filter time-constant -local cur_gain = 0 -- current smoothed gain (dB) - --- called once when plugin is instantiated -function dsp_init (rate) - lpf = 780 / rate -- interpolation time constant -end - -function low_pass_filter_param (old, new, limit) - if math.abs (old - new) < limit then - return new - else - return old + lpf * (new - old) - end -end - --- the DSP callback function --- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray -function dsp_run (ins, outs, n_samples) - local ctrl = CtrlPorts:array() -- get control port array (read/write) - local siz = n_samples -- samples remaining to process - local off = 0 -- already processed samples - local changed = false - - -- if the gain parameter was changed, process at most 32 samples at a time - -- and interpolate gain until the current settings match the target values - if cur_gain ~= ctrl[1] then - changed = true - siz = 32 - end - - while n_samples > 0 do - if siz > n_samples then siz = n_samples end -- process at most "remaining samples" - if changed then - -- smooth gain changes above 0.02 dB difference - cur_gain = low_pass_filter_param (cur_gain, ctrl[1], 0.02) - end - - local gain = ARDOUR.DSP.dB_to_coefficient (cur_gain) -- 10 ^ (0.05 * cur_gain) - - for c = 1,#ins do -- process all channels - -- check if output and input buffers for this channel are identical - -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray - if ins[c] ~= outs[c] then - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP - ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz) - end - ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, gain); -- apply-gain, process in-place - end - n_samples = n_samples - siz - off = off + siz - end - ---[[ - if changed then - self:queue_draw () -- notify display - end ---]] -end - -------------------------------------------------------------------------------- ---- inline display + text example - ---[[ -local txt = nil -- cache pango context globally - -function render_inline (ctx, w, max_h) - local ctrl = CtrlPorts:array () -- get control ports - - if not txt then - -- allocate PangoLayout and set font - --http://manual.ardour.org/lua-scripting/class_reference/#Cairo:PangoLayout - txt = Cairo.PangoLayout (ctx, "Mono 8px") - end - - txt:set_text (string.format ("%+.2f dB", ctrl[1])); - tw, th = txt:get_pixel_size () - - local h = th + 4 -- use text-height with 4px padding - if (h > max_h) then h = max_h end -- but at most max-height - - -- clear background - ctx:rectangle (0, 0, w, h) - ctx:set_source_rgba (.2, .2, .2, 1.0) - ctx:fill () - - -- center text - ctx:set_source_rgba (.8, .8, .8, 1.0) - ctx:move_to (.5 * (w - tw), .5 * (h - th)) - txt:show_in_cairo_context (ctx) - - return {w, h} -end ---]] diff --git a/scripts/bounce_replace.lua b/scripts/bounce_replace.lua deleted file mode 100644 index da657628ab..0000000000 --- a/scripts/bounce_replace.lua +++ /dev/null @@ -1,101 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Bounce+Replace Regions", - license = "MIT", - author = "Ardour Team", - description = [[Bounce selected regions with processing and replace region]] -} - -function factory (params) return function () - -- there is currently no direct way to find the track - -- corresponding to a [selected] region - function find_track_for_region (region_id) - for route in Session:get_tracks():iter() do - local track = route:to_track(); - local pl = track:playlist () - if not pl:region_by_id (region_id):isnil () then - return track - end - end - assert (0); -- can't happen, region must be in a playlist - end - - -- get selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- prepare undo operation - Session:begin_reversible_command ("Bounce+Replace Regions") - local add_undo = false -- keep track if something has changed - - -- Iterate over Regions part of the selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- each of the items 'r' is a - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region - - local track = find_track_for_region (r:to_stateful():id()) - local playlist = track:playlist () - - -- clear existing changes, prepare "diff" of state for undo - playlist:to_stateful ():clear_changes () - - -- bounce the region with processing - local region = track:bounce_range (r:position (), r:position() + r:length (), ARDOUR.InterThreadInfo (), track:main_outs (), false); - - -- remove old region.. - playlist:remove_region (r); - -- ..and add the newly bounced one - playlist:add_region (region, r:position (), 1, false, 0, 0, false) - - -- create a diff of the performed work, add it to the session's undo stack - -- and check if it is not empty - if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then - add_undo = true - end - end - - -- all done, commit the combined Undo Operation - if add_undo then - -- the 'nil' Command here mean to use the collected diffs added above - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end - -end end - -function icon (params) return function (ctx, width, height, fg) - local wh = math.min (width, height) * .5 - local ar = wh * .2 - - ctx:set_line_width (1) - function stroke_outline (c) - ctx:set_source_rgba (0, 0, 0, 1) - ctx:stroke_preserve () - ctx:set_source_rgba (c, c, c, 1) - ctx:fill () - end - - ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) - ctx:rectangle (wh - wh * .6, wh - .7 * wh, wh * 1.2, .5 * wh) - stroke_outline (.7) - - ctx:rectangle (wh - wh * .6, wh + .1 * wh, wh * 1.2, .5 * wh) - stroke_outline (.9) - - -- arrow - ctx:set_source_rgba (0, 1, 0, 1) - ctx:set_line_width (ar * .7) - - ctx:move_to (wh, wh - .5 * wh) - ctx:rel_line_to (0, wh) - ctx:stroke () - - ctx:move_to (wh, wh + .5 * wh) - ctx:rel_line_to (-ar, -ar) - ctx:rel_line_to (2 * ar, 0) - ctx:close_path () - ctx:stroke () - - - -end end diff --git a/scripts/bypass_all_plugins.lua b/scripts/bypass_all_plugins.lua deleted file mode 100644 index 913228101f..0000000000 --- a/scripts/bypass_all_plugins.lua +++ /dev/null @@ -1,22 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Bypass Plugins", - license = "MIT", - author = "Ardour Team", - description = [[Bypass Plugins on selected tracks]] -} - -function factory () return function () - local sel = Editor:get_selection () - for r in sel.tracks:routelist ():iter () do - local i = 0; - while 1 do -- iterate over all plugins/processors - local proc = r:nth_plugin (i) - if proc:isnil () then - break - end - proc:to_insert():deactivate() - i = i + 1 - end - end -end end diff --git a/scripts/create_drum_tracks.lua b/scripts/create_drum_tracks.lua deleted file mode 100644 index 0f16f87a70..0000000000 --- a/scripts/create_drum_tracks.lua +++ /dev/null @@ -1,73 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Create Drum Tracks", - author = "PSmith", - description = [[Creates 8 new tracks with representative names and colors.]] -} - -function factory () return function () - local names = { - "Kick", - "Snare", - "Hat", - "Fl Tom", - "OH L", - "OH R", - "Room 1", - "Room 2" - } - - local color = 0xff8800ff --orange - - local i = 1 - while names[i] do - local tl = Session:new_audio_track (1, 2, nil, 1, names[i], - ARDOUR.PresentationInfo.max_order, - ARDOUR.TrackMode.Normal) - - for track in tl:iter () do - track:presentation_info_ptr ():set_color (color) - end - - i = i + 1 - end --foreach track - -end end -- function factory - - -function icon (params) return function (ctx, width, height) - local x = width * .5 - local y = height * .5 - local r = math.min (x, y) * .7 - ctx:save () - ctx:translate (x, y) - ctx:scale (1, .5) - ctx:translate (-x, -y) - ctx:arc (x, y, r, 0, 2 * math.pi) - ctx:set_source_rgba (.9, .9, 1, 1) - ctx:fill () - ctx:arc (x, y, r, 0, math.pi) - ctx:arc_negative (x, y * 1.6, r, math.pi, 0) - ctx:set_source_rgba (.7, .7, .7, 1) - ctx:fill () - ctx:restore () - - ctx:set_source_rgba (.6, .4, .2, 1) - ctx:translate (x, y) - ctx:scale (.7, 1) - ctx:translate (-x, -y) - ctx:set_line_cap (Cairo.LineCap.Round) - - function drumstick (xp, lr) - ctx:set_line_width (r * .3) - ctx:move_to (x * xp, y) - ctx:close_path () - ctx:stroke () - ctx:set_line_width (r * .2) - ctx:move_to (x * xp, y) - ctx:rel_line_to (lr * x, y) - ctx:stroke () - end - drumstick (1.2, 1.2) - drumstick (0.7, -.5) -end end diff --git a/scripts/delete_xrun_markers.lua b/scripts/delete_xrun_markers.lua deleted file mode 100644 index 5512e653b6..0000000000 --- a/scripts/delete_xrun_markers.lua +++ /dev/null @@ -1,40 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Delete xrun markers", author = "Ardour Team", description = [[Delete all xrun markers in the current session]] } - -function factory () return function () - for l in Session:locations():list():iter() do - if l:is_mark() and string.find (l:name(), "^xrun%d*$") then - Session:locations():remove (l); - end - end -end end - -function icon (params) return function (ctx, width, height, fg) - local mh = height - 3.5; - local m3 = width / 3; - local m6 = width / 6; - - ctx:set_line_width (.5) - - ctx:set_source_rgba (.8, .2, .2, 1.0) - ctx:move_to (width / 2 - m6, 2) - ctx:rel_line_to (m3, 0) - ctx:rel_line_to (0, mh * 0.4) - ctx:rel_line_to (-m6, mh * 0.6) - ctx:rel_line_to (-m6, -mh * 0.6) - ctx:close_path () - ctx:fill_preserve () - ctx:set_source_rgba (.0, .0, .0, 1.0) - ctx:stroke () - - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - ctx:set_line_width (1) - - ctx:move_to (width * .2, height * .2) - ctx:line_to (width * .8, height * .8) - ctx:stroke () - - ctx:move_to (width * .8, height * .2) - ctx:line_to (width * .2, height * .8) - ctx:stroke () - -end end diff --git a/scripts/export_mp4chaps.lua b/scripts/export_mp4chaps.lua deleted file mode 100644 index 61c78c3a2f..0000000000 --- a/scripts/export_mp4chaps.lua +++ /dev/null @@ -1,78 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Export markers as mp4chaps", - author = "Johannes Mueller", - description = [[ -Exports MP4chaps of all markers except xruns. The markers are stored in the -export directory of the session in mp4 chapter marks format. The filename -is mp4chaps.txt - -Note that there's always a chapter mark "Intro" at 00:00:00.000 as some -players can't live without it. If there are no exportable markers, the file -is not created. - -This is a bit more convenient than the export option, as one does not -have to wait for the export. -]], - license = "GPLv2" -} - -function factory (unused_params) return function () - - local fr = Session:sample_rate() - local chaps = {} - - for l in Session:locations():list():iter() do - local name = l:name() - if not l:is_mark() or string.find(name, "^xrun%d*$") then - goto next end - - local t = l:start() - Session:current_start_sample() - local h = math.floor(t / (3600*fr)) - local r = t - (h*3600*fr) - local m = math.floor(r / (60*fr)) - r = r - m*60*fr - local s = math.floor(r / fr) - r = r - s*fr - local ms = math.floor(r*1000/fr) - table.insert(chaps, string.format("%02d:%02d:%02d.%03d %s\n", h, m, s, ms, name)) - ::next:: - end - - if next(chaps) == nil then - goto out end - - table.insert(chaps, "00:00:00.000 Intro\n") - table.sort(chaps) - - file = io.open(ARDOUR.LuaAPI.build_filename (Session:path(), "export", "mp4chaps.txt"), "w") - for i, line in ipairs(chaps) do - file:write(line) - end - file:close() - - ::out:: -end end - -function icon (params) return function (ctx, width, height, fg) - local mh = height - 3.5; - local m3 = width / 3; - local m6 = width / 6; - - ctx:set_line_width (.5) - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - - ctx:move_to (width / 2 - m6, 2) - ctx:rel_line_to (m3, 0) - ctx:rel_line_to (0, mh * 0.4) - ctx:rel_line_to (-m6, mh * 0.6) - ctx:rel_line_to (-m6, -mh * 0.6) - ctx:close_path () - ctx:stroke () - - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .5) .. "px") - txt:set_text ("MP4") - local tw, th = txt:get_pixel_size () - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/faders_to_trims.lua b/scripts/faders_to_trims.lua deleted file mode 100644 index 3fd62833f2..0000000000 --- a/scripts/faders_to_trims.lua +++ /dev/null @@ -1,72 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Faders to Trims", - license = "MIT", - author = "PSmith", - description = [[Add 'Trim' plugins to all tracks. Move the fader value into the trim.]] -} - -function action_params () - return - { - } -end - - -function factory (params) - return function () - -- loop over all tracks - for t in Session:get_tracks():iter() do - - fader_value = t:gain_control():get_value() - if fader_value == 1 then - goto skip - end - if t:gain_control():automation_state() ~= ARDOUR.AutoState.Off then - goto skip - end - - -- TODO: skip MIDI tracks without or with a post-fader synth - -- (fader is MIDI-velocity) - - v = math.log(fader_value, 10) - trim_gain = 20*v - fader_pos = 0 - local proc; - local i = 0; - - repeat - -- find the fader proc - proc = t:nth_processor (i) - if (not proc:isnil() and proc:display_name () == "Fader") then - fader_pos = i - end - i = i + 1 - until proc:isnil() - - -- apply the trim - trim = t:nth_processor (fader_pos+1) - if (not trim:isnil() and trim:display_name () == "a-Amplifier") then - --existing trim found; sum the trim and the fader gain, and set the trim to that value - cur_gain = ARDOUR.LuaAPI.get_processor_param (trim, 0) - ARDOUR.LuaAPI.set_processor_param (trim, 0, trim_gain+cur_gain) - else - --create a new Trim processor, and set its value to match the fader - local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-Amplifier"); - if (not a:isnil()) then - t:add_processor_by_index(a, fader_pos-1, nil, true) - ARDOUR.LuaAPI.set_processor_param (a, 0, trim_gain) - a = nil -- explicitly drop shared-ptr reference - end - end - - --zero the fader gain - t:gain_control():set_value(1, PBD.GroupControlDisposition.NoGroup) - - ::skip:: - - end --foreach track - - end --function - -end --factory diff --git a/scripts/jump_to_marker.lua b/scripts/jump_to_marker.lua deleted file mode 100644 index ea92020c83..0000000000 --- a/scripts/jump_to_marker.lua +++ /dev/null @@ -1,67 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Jump to Marker", - license = "MIT", - author = "Ardour Team", - description = [[Jump to the first marker matching a given pattern]] -} - -function factory () return function () - local keep = false - - ::restart:: - - local dlg = LuaDialog.Dialog ("Search and Jump to Marker", - { - { type = "entry", key = "marker", default = '', title = "Marker Prefix" }, - { type = "checkbox", key = "keep", default = keep, title = "Keep Dialog Open" }, - }) - - local rv = dlg:run() - if not rv then return end - - keep = rv['keep'] - - if (rv['marker'] == "") then - if keep then goto restart end - return - end - - for l in Session:locations():list():iter() do - if l:is_mark() and string.find (l:name(), "^" .. rv['marker'] .. ".*$") then - Session:request_locate (l:start (), ARDOUR.LocateTransportDisposition.RollIfAppropriate, ARDOUR.TransportRequestSource.TRS_UI) - if keep then goto restart end - return - end - end - - LuaDialog.Message ("Jump to Marker", "No marker matching the given pattern was found.", LuaDialog.MessageType.Warning, LuaDialog.ButtonType.Close):run () - - if keep then goto restart end -end end - - -function icon (params) return function (ctx, width, height, fg) - local mh = height - 3.5; - local m3 = width / 3; - local m6 = width / 6; - - ctx:set_line_width (.5) - - -- compare to gtk2_ardour/marker.cc "Marker" - ctx:set_source_rgba (.6, .6, .6, 1.0) - ctx:move_to (width / 2 - m6, 2) - ctx:rel_line_to (m3, 0) - ctx:rel_line_to (0, mh * 0.4) - ctx:rel_line_to (-m6, mh * 0.6) - ctx:rel_line_to (-m6, -mh * 0.6) - ctx:close_path () - ctx:fill_preserve () - ctx:set_source_rgba (.0, .0, .0, 1.0) - ctx:stroke () - - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .5) .. "px") - txt:set_text ("txt") - local tw, th = txt:get_pixel_size () - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/lfo_automation.lua b/scripts/lfo_automation.lua deleted file mode 100644 index a69df1af1d..0000000000 --- a/scripts/lfo_automation.lua +++ /dev/null @@ -1,189 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Add LFO automation to region", - version = "0.1.1", - license = "MIT", - author = "Daniel Appelt", - description = [[Add LFO-like plugin automation to selected region]] -} - -function factory (unused_params) - return function () - -- Retrieve the first selected region - -- TODO: the following statement should do just that, no!? - -- local region = Editor:get_selection().regions:regionlist():front() - local region = nil - for r in Editor:get_selection().regions:regionlist():iter() do - if region == nil then region = r end - end - - -- Bail out if no region was selected - if region == nil then - LuaDialog.Message("Add LFO automation to region", "Please select a region first!", - LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() - return - end - - -- Identify the track the region belongs to. There really is no better way?! - local track = nil - for route in Session:get_tracks():iter() do - for r in route:to_track():playlist():region_list():iter() do - if r == region then track = route:to_track() end - end - end - - -- Get a list of all available plugin parameters on the track. This looks ugly. For the original code - -- see https://github.com/Ardour/ardour/blob/master/scripts/midi_cc_to_automation.lua - local targets = {} - local i = 0 - while true do -- iterate over all plugins on the route - if track:nth_plugin(i):isnil() then break end - - local proc = track:nth_plugin(i) -- ARDOUR.LuaAPI.plugin_automation() expects a proc not a plugin - local plug = proc:to_insert():plugin(0) - local plug_label = i .. ": " .. plug:name() -- Handle ambiguity if there are multiple plugin instances - local n = 0 -- Count control-ports separately. ARDOUR.LuaAPI.plugin_automation() only returns those. - for j = 0, plug:parameter_count() - 1 do -- Iterate over all parameters - if plug:parameter_is_control(j) then - local label = plug:parameter_label(j) - if plug:parameter_is_input(j) and label ~= "hidden" and label:sub(1,1) ~= "#" then - -- We need two return values: the plugin-instance and the parameter-id. We use a function to - -- return both values in order to avoid another sub-menu level in the dropdown. - local nn = n -- local scope for return value function - targets[plug_label] = targets[plug_label] or {} - targets[plug_label][label] = function() return {["p"] = proc, ["n"] = nn} end - end - n = n + 1 - end - end - - i = i + 1 - end - - -- Bail out if there are no plugin parameters - if next(targets) == nil then - LuaDialog.Message("Add LFO automation to region", "No plugin parameters found.", - LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() - region, track, targets = nil, nil, nil - collectgarbage() - return - end - - -- Display dialog to select (plugin and) plugin parameter, and LFO cycle type + min / max - local dialog_options = { - { type = "heading", title = "Add LFO automation to region", align = "left"}, - { type = "dropdown", key = "param", title = "Plugin parameter", values = targets }, - { type = "dropdown", key = "wave", title = "Waveform", values = { - ["Ramp up"] = 1, ["Ramp down"] = 2, ["Triangle"] = 3, ["Sine"] = 4, - ["Exp up"] = 5, ["Exp down"] = 6, ["Log up"] = 7, ["Log down"] = 8 } }, - { type = "number", key = "cycles", title = "No. of cycles", min = 1, max = 16, step = 1, digits = 0 }, - { type = "slider", key = "min", title = "Minimum in %", min = 0, max = 100, digits = 1 }, - { type = "slider", key = "max", title = "Maximum in %", min = 0, max = 100, digits = 1, default = 100 } - } - local rv = LuaDialog.Dialog("Select target", dialog_options):run() - - -- Return if the user cancelled - if not rv then - region, track, targets = nil, nil, nil - collectgarbage() - return - end - - -- Parse user response - assert(type(rv["param"]) == "function") - local pp = rv["param"]() -- evaluate function, retrieve table {["p"] = proc, ["n"] = nn} - local al, _, pd = ARDOUR.LuaAPI.plugin_automation(pp["p"], pp["n"]) - local wave = rv["wave"] - local cycles = rv["cycles"] - -- Compute minimum and maximum requested parameter values - local lower = pd.lower + rv["min"] / 100 * (pd.upper - pd.lower) - local upper = pd.lower + rv["max"] / 100 * (pd.upper - pd.lower) - track, targets, rv, pd = nil, nil, nil, nil - assert(not al:isnil()) - - -- Define lookup tables for our waves. Empty ones will be calculated in a separate step. - -- TODO: at this point we already know which one is needed, still we compute all. - local lut = { - { 0, 1 }, -- ramp up - { 1, 0 }, -- ramp down - { 0, 1, 0 }, -- triangle - {}, -- sine - {}, -- exp up - {}, -- exp down - {}, -- log up - {} -- log down - } - - -- Calculate missing look up tables - local log_min = math.exp(-2 * math.pi) - for i = 0, 20 do - -- sine - lut[4][i+1] = 0.5 * math.sin(i * math.pi / 10) + 0.5 - -- exp up - lut[5][i+1] = math.exp(-2 * math.pi + i * math.pi / 10) - -- log up - lut[7][i+1] = -math.log(1 + (i / log_min - i) / 20) / math.log(log_min) - end - -- "down" variants just need the values in reverse order - for i = 21, 1, -1 do - -- exp down - lut[6][22-i] = lut[5][i] - -- log down - lut[8][22-i] = lut[7][i] - end - - -- Initialize undo - Session:begin_reversible_command("Add LFO automation to region") - local before = al:get_state() -- save previous state (for undo) - al:clear_list() -- clear target automation-list - - local values = lut[wave] - local last = nil - for i = 0, cycles - 1 do - -- cycle length = region:length() / cycles - local cycle_start = region:position() - region:start() + i * region:length() / cycles - local offset = region:length() / cycles / (#values - 1) - - for k, v in pairs(values) do - local pos = cycle_start + (k - 1) * offset - if k == 1 and v ~= last then - -- Move event one sample further. A larger offset might be needed to avoid unwanted effects. - pos = pos + 1 - end - - if k > 1 or v ~= last then - -- Create automation point re-scaled to parameter target range. Do not create a new point - -- at cycle start if the last cycle ended on the same value. - al:add(pos, lower + v * (upper - lower), false, true) - end - last = v - end - end - - -- remove dense events - al:thin (20) -- threashold of area below curve - - -- TODO: display the modified automation lane in the time line in order to make the change visible! - - -- Save undo - -- TODO: in Ardour 5.12 this does not lead to an actual entry in the undo list?! - Session:add_command(al:memento_command(before, al:get_state())) - Session:commit_reversible_command(nil) - - region, al, lut = nil, nil, nil - collectgarbage() - end -end - -function icon (params) return function (ctx, width, height, fg) - local yc = height * .5 - local x0 = math.ceil (width * .1) - local x1 = math.floor (width * .9) - ctx:set_line_width (1) - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - ctx:move_to (x0, yc * 1.5) - for x = x0, x1 do - ctx:line_to (x, yc + math.cos (3 * math.pi * (x-x0) / (x1-x0)) * yc * .5) - end - ctx:stroke () -end end diff --git a/scripts/list_plugins.lua b/scripts/list_plugins.lua deleted file mode 100644 index 6a6efe12b8..0000000000 --- a/scripts/list_plugins.lua +++ /dev/null @@ -1,50 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "List Plugins", - license = "MIT", - author = "Ardour Team", - description = [[List and count plugins used in this session]] -} - -function factory () return function () - local rv = "Plugins used in this session:\nCNT | TYPE | NAME" - local all_plugs = {} - - for r in Session:get_routes ():iter () do - if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes - local i = 0 - while true do - local proc = r:nth_plugin (i) - if proc:isnil () then break end - local pi = proc:to_insert () -- we know it's a plugin-insert (we asked for nth_plugin) - local pp = pi:plugin (0) - local id = pi:type() .. "-" .. pp:unique_id() - local cnt = 0 - if all_plugs[id] then cnt = all_plugs[id]['cnt'] end - all_plugs[id] = { name = proc:name(), ["type"] = pi:type(), id = pp:unique_id(), cnt = (cnt + 1) } - i = i + 1 - end - ::nextroute:: - end - - function plugintypestr (t) - if (t == ARDOUR.PluginType.LADSPA) then return "LADSPA" end - if (t == ARDOUR.PluginType.LV2) then return "LV2" end - if (t == ARDOUR.PluginType.AudioUnit) then return "AU" end - if (t == ARDOUR.PluginType.Windows_VST) then return "VST" end - if (t == ARDOUR.PluginType.LXVST) then return "VST" end - if (t == ARDOUR.PluginType.MacVST) then return "VST" end - if (t == ARDOUR.PluginType.Lua) then return "Lua" end - return "??" - end - - for k,v in pairs (all_plugs) do - print (string.format ("%2d * %-6s %-30s (%s)", v['cnt'], plugintypestr(v['type']), v['name'], v['id'])) - rv = rv .. "\n" .. string.format ("%2d * %-6s %s", v['cnt'], plugintypestr(v['type']), v['name']) - end - - LuaDialog.Message ("All Plugins", "" .. rv .. "", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() - - all_plugs = nil - rv = "" - collectgarbage (); - -end end diff --git a/scripts/ltc_reader.lua b/scripts/ltc_reader.lua deleted file mode 100644 index b86dc009ef..0000000000 --- a/scripts/ltc_reader.lua +++ /dev/null @@ -1,82 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "LTC Reader", - category = "Utility", - author = "Ardour Team", - license = "MIT", - description = [[Linear Timecode Decoder with mixer strip inline display]] -} - -function dsp_ioconfig () - return { { audio_in = 1, audio_out = 1}, } -end - -function dsp_init (rate) - timeout = rate - samplerate = rate - ltc_reader = ARDOUR.DSP.LTCReader (rate / 25, ARDOUR.DSP.LTC_TV_STANDARD.LTC_TV_FILM_24) - self:shmem():allocate(5) -end - -function dsp_run (ins, outs, n_samples) - if ins[1] ~= outs[1] then - ARDOUR.DSP.copy_vector (outs[1]:offset (0), ins[1]:offset (0), n_samples) - end - ltc_reader:write (ins[1]:offset (0), n_samples, 0) - timeout = timeout + n_samples - local to_ui = self:shmem():to_int(0):array() - local rv - repeat - local tc - rv, tc = ltc_reader:read (0, 0, 0, 0) - if rv >= 0 then - timeout = 0 - self:shmem():atomic_set_int (0, 1) - self:shmem():atomic_set_int (1, tc[1]) - self:shmem():atomic_set_int (2, tc[2]) - self:shmem():atomic_set_int (3, tc[3]) - self:shmem():atomic_set_int (4, tc[4]) - self:queue_draw () - end - until rv < 0 - if timeout > samplerate then - if 0 ~= self:shmem():atomic_get_int (0) then - self:shmem():atomic_set_int (0, 0) - self:queue_draw () - end - end -end - -------------------------------------------------------------------------------- --- inline UI --- -local txt = nil -- a pango context -local vpadding = 2 - -function render_inline (ctx, displaywidth, max_h) - if not txt then - txt = Cairo.PangoLayout (ctx, "Mono 10px") - end - - local d = self:shmem():to_int(0):array() - if d[1] == 0 then - txt:set_text("--:--:--:--") - else - txt:set_text(string.format("%02d:%02d:%02d:%02d", d[2], d[3], d[4], d[5])) - end - - -- compute the size of the display - local txtwidth, lineheight = txt:get_pixel_size() - local displayheight = math.min(vpadding + (lineheight + vpadding), max_h) - - -- clear background - ctx:rectangle (0, 0, displaywidth, displayheight) - ctx:set_source_rgba (.2, .2, .2, 1.0) - ctx:fill () - ctx:set_source_rgba (.8, .8, .8, 1.0) - ctx:move_to ((displaywidth - txtwidth) / 2, 1) - txt:show_in_cairo_context (ctx) - - return {displaywidth, displayheight} -end - diff --git a/scripts/meter_tap.lua b/scripts/meter_tap.lua deleted file mode 100644 index 645af522bd..0000000000 --- a/scripts/meter_tap.lua +++ /dev/null @@ -1,42 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Meter Tap", - author = "Ardour Lua Taskforce", - description = [[Change Metering Point for tracks in your session.]] -} - -function factory () return function () - - local dialog_options = { - { type = "label", colspan = 5, title = "" }, - { type = "radio", col = 1, colspan = 7, key = "select", title = "", values ={ ["Set All: Input"] = ARDOUR.MeterPoint.MeterInput, ["Set All: Pre Fader"] = ARDOUR.MeterPoint.MeterPreFader, ["Set All: Post Fader"] = ARDOUR.MeterPoint.MeterPostFader, ["Set All: Output"] = ARDOUR.MeterPoint.MeterOutput, ["Set All: Custom"] = ARDOUR.MeterPoint.MeterCustom}, default = "Set All: Input"}, - { type = "label", colspan = 5, title = "" }, - { type = "checkbox", col=1, colspan = 1, key = "select-tracks", default = true, title = "Selected tracks only"}, - { type = "checkbox", col=2, colspan = 1, key = "rec-tracks", default = true, title = "Record Enabled tracks only"}, - { type = "label", colspan = 5, title = "" }, - } - - local rv = LuaDialog.Dialog("Change all Meter Taps:", dialog_options):run() - if not rv then return end -- user cancelled - - local rl; - if rv['select-tracks'] then - rl = Editor:get_selection () - else - rl = Session:get_routes() - end - - local meter_point = rv['select'] - - for route in rl:iter() do - if not(route:to_track():isnil()) then - if rv['rec-tracks'] then - if route:rec_enable_control():get_value() == 1.0 then - route:to_track():set_meter_point(meter_point, false) - end - else - route:to_track():set_meter_point(meter_point, false) - end - end - end -end end diff --git a/scripts/midi_cc_to_automation.lua b/scripts/midi_cc_to_automation.lua deleted file mode 100644 index a1bab91a05..0000000000 --- a/scripts/midi_cc_to_automation.lua +++ /dev/null @@ -1,130 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "MIDI CC to Plugin Automation", - license = "MIT", - author = "Ardour Team", - description = [[Parse a given MIDI control changes (CC) from all selected MIDI regions and convert them into plugin parameter automation]] -} - -function factory () return function () - -- find possible target parameters, collect them in a nested table - -- [track-name] -> [plugin-name] -> [parameters] - -- to allow selection in a dropdown menu - local targets = {} - local have_entries = false - for r in Session:get_routes ():iter () do -- for every track/bus - if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes - local i = 0 - while true do -- iterate over all plugins on the route - local proc = r:nth_plugin (i) - if proc:isnil () then break end - local plug = proc:to_insert ():plugin (0) -- we know it's a plugin-insert (we asked for nth_plugin) - local n = 0 -- count control-ports - for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters - if plug:parameter_is_control (j) then - local label = plug:parameter_label (j) - if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then - local nn = n --local scope for return value function - -- create table parents only if needed (if there's at least one parameter) - if not targets [r:name ()] then targets [r:name ()] = {} end - -- TODO handle ambiguity if there are 2 plugins with the same name on the same track - if not targets [r:name ()][proc:display_name ()] then targets [r:name ()][proc:display_name ()] = {} end - -- we need 2 return values: the plugin-instance and the parameter-id, so we use a table (associative array) - -- however, we cannot directly use a table: the dropdown menu would expand it as another sub-menu. - -- so we produce a function that will return the table. - targets [r:name ()][proc:display_name ()][label] = function () return {["p"] = proc, ["n"] = nn} end - have_entries = true - end - n = n + 1 - end - end - i = i + 1 - end - ::nextroute:: - end - - -- bail out if there are no parameters - if not have_entries then - LuaDialog.Message ("CC to Plugin Automation", "No Plugins found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - targets = nil - collectgarbage () - return - end - - -- create a dialog, ask user which MIDI-CC to map and to what parameter - local dialog_options = { - { type = "heading", title = "MIDI CC Source", align = "left" }, - { type = "number", key = "channel", title = "Channel", min = 1, max = 16, step = 1, digits = 0 }, - { type = "number", key = "ccparam", title = "CC Parameter", min = 0, max = 127, step = 1, digits = 0 }, - { type = "heading", title = "Target Track and Plugin", align = "left"}, - { type = "dropdown", key = "param", title = "Target Parameter", values = targets } - } - local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run () - - targets = nil -- drop references (the table holds shared-pointer references to all plugins) - collectgarbage () -- and release the references immediately - - if not rv then return end -- user cancelled - - -- parse user response - - assert (type (rv["param"]) == "function") - local midi_channel = rv["channel"] - 1 -- MIDI channel 0..15 - local cc_param = rv["ccparam"] - local pp = rv["param"]() -- evaluate function, retrieve table {["p"] = proc, ["n"] = nn} - local al, _, pd = ARDOUR.LuaAPI.plugin_automation (pp["p"], pp["n"]) - rv = nil -- drop references - assert (not al:isnil ()) - assert (midi_channel >= 0 and midi_channel < 16) - assert (cc_param >= 0 and cc_param < 128) - - -- all systems go - local add_undo = false - Session:begin_reversible_command ("CC to Automation") - local before = al:get_state () -- save previous state (for undo) - al:clear_list () -- clear target automation-list - - -- for all selected MIDI regions - local sel = Editor:get_selection () - for r in sel.regions:regionlist ():iter () do - local mr = r:to_midiregion () - if mr:isnil () then goto next end - - -- get list of MIDI-CC events for the given channel and parameter - local ec = mr:control (Evoral.Parameter (ARDOUR.AutomationType.MidiCCAutomation, midi_channel, cc_param), false) - if ec:isnil () then goto next end - if ec:list ():events ():size () == 0 then goto next end - - -- MIDI events are timestamped in "bar-beat" units, we need to convert those - -- using the tempo-map, relative to the region-start - local bfc = ARDOUR.DoubleBeatsSamplesConverter (Session:tempo_map (), r:start ()) - - -- iterate over CC-events - for av in ec:list ():events ():iter () do - -- re-scale event to target range - local val = pd.lower + (pd.upper - pd.lower) * av.value / 127 - -- and add it to the target-parameter automation-list - al:add (r:position () - r:start () + bfc:to (av.when), val, false, true) - add_undo = true - end - ::next:: - end - - -- save undo - if add_undo then - local after = al:get_state () - Session:add_command (al:memento_command (before, after)) - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - LuaDialog.Message ("CC to Plugin Automation", "No data was converted. Was a MIDI-region with CC-automation selected? ", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - end - collectgarbage () -end end - -function icon (params) return function (ctx, width, height, fg) - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (height / 3) .. "px") - txt:set_text ("CC\nPA") - local tw, th = txt:get_pixel_size () - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/midi_remap.lua b/scripts/midi_remap.lua deleted file mode 100644 index 4fb7919e23..0000000000 --- a/scripts/midi_remap.lua +++ /dev/null @@ -1,97 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "MIDI Note Mapper", - category = "Utility", - license = "MIT", - author = "Alby Musaelian", - description = [[Map arbitrary MIDI notes to others. Affects Note On/Off and polyphonic key pressure. Note that if a single note is mapped multiple times, the last mapping wins -- MIDI events are never duplicated.]] -} - --- The number of remapping pairs to allow. Increasing this (at least in theory) --- decreases performace, so it's set fairly low as a default. The user can --- increase this if they have a need to. -N_REMAPINGS = 10 - -OFF_NOTE = -1 - -function dsp_ioconfig () - return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, } -end - - -function dsp_params () - - local map_scalepoints = {} - map_scalepoints["None"] = OFF_NOTE - for note=0,127 do - local name = ARDOUR.ParameterDescriptor.midi_note_name(note) - map_scalepoints[string.format("%03d (%s)", note, name)] = note - end - - local map_params = {} - - i = 1 - for mapnum = 1,N_REMAPINGS do - -- From and to - for _,name in pairs({"| #" .. mapnum .. " Map note", "|__ to"}) do - map_params[i] = { - ["type"] = "input", - name = name, - min = -1, - max = 127, - default = OFF_NOTE, - integer = true, - enum = true, - scalepoints = map_scalepoints - } - i = i + 1 - end - end - - return map_params -end - -function dsp_run (_, _, n_samples) - assert (type(midiin) == "table") - assert (type(midiout) == "table") - local cnt = 1; - - function tx_midi (time, data) - midiout[cnt] = {} - midiout[cnt]["time"] = time; - midiout[cnt]["data"] = data; - cnt = cnt + 1; - end - - -- We build the translation table every buffer because, as far as I can tell, - -- there's no way to only rebuild it when the parameters have changed. - -- As a result, it has to be updated every buffer for the parameters to have - -- any effect. - - -- Restore translation table - local translation_table = {} - local ctrl = CtrlPorts:array() - for i=1,N_REMAPINGS*2,2 do - if not (ctrl[i] == OFF_NOTE) then - translation_table[ctrl[i]] = ctrl[i + 1] - end - end - - -- for each incoming midi event - for _,b in pairs (midiin) do - local t = b["time"] -- t = [ 1 .. n_samples ] - local d = b["data"] -- get midi-event - local event_type - if #d == 0 then event_type = -1 else event_type = d[1] >> 4 end - - if (#d == 3) and (event_type == 9 or event_type == 8 or event_type == 10) then -- note on, note off, poly. afterpressure - -- Do the mapping - 2 is note byte for these types - d[2] = translation_table[d[2]] or d[2] - if not (d[2] == OFF_NOTE) then - tx_midi (t, d) - end - else - tx_midi (t, d) - end - end -end diff --git a/scripts/midimon.lua b/scripts/midimon.lua deleted file mode 100644 index 7f58fd34de..0000000000 --- a/scripts/midimon.lua +++ /dev/null @@ -1,213 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-MIDI Monitor", - category = "Visualization", - license = "GPLv2", - author = "Ardour Team", - description = [[Display recent MIDI events inline in the mixer strip]] -} - -local maxevents = 20 -local ringsize = maxevents * 3 -local evlen = 3 -local hpadding, vpadding = 4, 2 - -function dsp_ioconfig () - return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, } -end - -function dsp_params () - return - { - { ["type"] = "input", - name = "Font size", - doc = "Text size used by the monitor to display midi events", - min = 4, max = 12, default = 7, integer = true }, - { ["type"] = "input", - name = "Line count", - doc = "How many events will be shown at most", - min = 1, max = maxevents, default = 6, integer = true }, - { ["type"] = "input", - name = "Hexadecimal", - doc = "If enabled, values will be printed in hexadecimal notation", - min = 0, max = 1, default = 0, toggled = true }, - { ["type"] = "input", - name = "System messages", - doc = "If enabled, the monitor will show System Control and Real-Time messages", - min = 0, max = 1, default = 0, toggled = true }, - { ["type"] = "input", - name = "Numeric Notes", - doc = "If enabled, note-events displayed numerically", - min = 0, max = 1, default = 0, toggled = true }, - } -end - -function dsp_init (rate) - -- create a shmem space to hold latest midi events - -- a int representing the index of the last event, and - -- a C-table as storage for events. - self:shmem():allocate(1 + ringsize*evlen) - self:shmem():atomic_set_int(0, 1) - local buffer = self:shmem():to_int(1):array() - for i = 1, ringsize*evlen do - buffer[i] = -1 -- sentinel for empty slot - end -end - -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - local pos = self:shmem():atomic_get_int(0) - local buffer = self:shmem():to_int(1):array() - - -- passthrough all data - ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio")) - ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi")) - - -- then fill the event buffer - local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- index of 1st midi input - - if ib ~= ARDOUR.ChanMapping.Invalid then - local events = bufs:get_midi (ib):table () -- copy event list into a lua table - - -- iterate over all MIDI events - for _, e in pairs (events) do - local ev = e:buffer():array() - pos = pos % ringsize + 1 - -- copy the data - for j = 1, math.min(e:size(),evlen) do - buffer[(pos-1)*evlen + j] = ev[j] - end - -- zero unused slots - for j = e:size()+1, evlen do - buffer[(pos-1)*evlen + j] = 0 - end - end - end - - self:shmem():atomic_set_int(0, pos) - - self:queue_draw () -end - -local txt = nil -- a pango context -local cursize = 0 -local hex = nil -local format_note = nil -local show_scm = nil - -function format_note_name(b) - return string.format ("%5s", ARDOUR.ParameterDescriptor.midi_note_name (b)) -end - -function format_note_num(b) - return string.format (hex, b) -end - - -function show_midi(ctx, x, y, buffer, event) - local base = (event - 1) * evlen - if buffer[base+1] == -1 then return false end - local evtype = buffer[base + 1] >> 4 - local channel = (buffer[base + 1] & 15) + 1 -- for System Common Messages this has no use - if evtype == 8 then - txt:set_text(string.format("%02u \u{2669}Off%s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3])) - elseif evtype == 9 then - txt:set_text(string.format("%02u \u{2669}On %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3])) - elseif evtype == 10 then - txt:set_text(string.format("%02u \u{2669}KP %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3])) - elseif evtype == 11 then - txt:set_text(string.format("%02u CC " .. hex .. hex, channel, buffer[base+2], buffer[base+3])) - elseif evtype == 12 then - txt:set_text(string.format("%02u PRG " .. hex, channel, buffer[base+2])) - elseif evtype == 13 then - txt:set_text(string.format("%02u KP " .. hex, channel, buffer[base+2])) - elseif evtype == 14 then - txt:set_text(string.format("%02u PBnd" .. hex, channel, buffer[base+2] | buffer[base+3] << 7)) - elseif show_scm > 0 then -- System Common Message - local message = buffer[base + 1] & 15 - if message == 0 then - txt:set_text("-- SysEx") - elseif message == 1 then - txt:set_text(string.format("-- Time Code" .. hex, buffer[base+2])) - elseif message == 2 then - txt:set_text(string.format("-- Song Pos" .. hex, buffer[base+2] | buffer[base+3] << 7)) - elseif message == 3 then - txt:set_text(string.format("-- Select Song" .. hex, buffer[base+2])) - elseif message == 6 then - txt:set_text("-- Tune Rq") - elseif message == 8 then - txt:set_text("-- Timing") - elseif message == 10 then - txt:set_text("-- Start") - elseif message == 11 then - txt:set_text("-- Continue") - elseif message == 12 then - txt:set_text("-- Stop") - elseif message == 14 then - txt:set_text("-- Active") - elseif message == 15 then - txt:set_text("-- Reset") - else - return false - end - else - return false - end - ctx:move_to (x, y) - txt:show_in_cairo_context (ctx) - return true -end - -function render_inline (ctx, displaywidth, max_h) - local ctrl = CtrlPorts:array () - local pos = self:shmem():atomic_get_int(0) - local buffer = self:shmem():to_int(1):array() - local count = ctrl[2] - - if not txt or cursize ~= ctrl[1] then - cursize = math.floor(ctrl[1]) - txt = Cairo.PangoLayout (ctx, "Mono " .. cursize) - end - - if ctrl[3] > 0 then hex = " %02X" else hex = " %3u" end - show_scm = ctrl[4] - - if ctrl[5] > 0 then - format_note = format_note_num - else - format_note = format_note_name - end - - -- compute the size of the display - txt:set_text("0") - local _, lineheight = txt:get_pixel_size() - local displayheight = math.min(vpadding + (lineheight + vpadding) * count, max_h) - - -- compute starting position (pango anchors text at north-west corner) - local x, y = hpadding, displayheight - lineheight - vpadding - - -- clear background - ctx:rectangle (0, 0, displaywidth, displayheight) - ctx:set_source_rgba (.2, .2, .2, 1.0) - ctx:fill () - - -- color of latest event - ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0) - - -- print events - for i = pos, 1, -1 do - if y < 0 then break end - if show_midi(ctx, x, y, buffer, i) then - y = y - lineheight - vpadding - ctx:set_source_rgba (.8, .8, .8, 1.0) - end - end - for i = ringsize, pos+1, -1 do - if y < 0 then break end - if show_midi(ctx, x, y, buffer, i) then - y = y - lineheight - vpadding - ctx:set_source_rgba (.8, .8, .8, 1.0) - end - end - - return {displaywidth, displayheight} -end diff --git a/scripts/mixer_screenshot.lua b/scripts/mixer_screenshot.lua deleted file mode 100644 index f8e8ded2db..0000000000 --- a/scripts/mixer_screenshot.lua +++ /dev/null @@ -1,46 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Mixer Screenshot", - author = "Ardour Team", - description = [[Save a screenshot of the complete mixer-window, regardless of scrollbars or visible screen area]] -} - -function factory () return function () - local rv = LuaDialog.Dialog ("Save Mixer Screenshot", - { - { type = "createfile", key = "file", title = "", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "mixer.png") }, - }):run() - - if (rv) then - if (ARDOUR.LuaAPI.file_test (rv['file'], ARDOUR.LuaAPI.FileTest.Exists)) then - local ok = LuaDialog.Message ("File Exists", "File '".. rv['file'] .. "' exists.\nReplace?", LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run () - if ok ~= LuaDialog.Response.Yes then - return - end - end - ArdourUI.mixer_screenshot (rv['file']) - end - collectgarbage () -end end - -function icon (params) return function (ctx, width, height, fg) - local wh = math.min (width, height) * .5 - ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) - - ctx:rectangle (wh * .6, wh * .6, wh * .8, wh * .8) - ctx:set_source_rgba (.1, .1, .1, 1) - ctx:fill () - - ctx:set_line_width (1) - ctx:set_source_rgba (.9, .9, .9, 1) - - ctx:move_to (wh * 0.3, wh * 0.6) - ctx:line_to (wh * 1.5, wh * 0.6) - ctx:line_to (wh * 1.5, wh * 1.7) - ctx:stroke () - - ctx:move_to (wh * 0.6, wh * 0.3) - ctx:line_to (wh * 0.6, wh * 1.4) - ctx:line_to (wh * 1.8, wh * 1.4) - ctx:stroke () -end end diff --git a/scripts/mixer_settings_recall.lua b/scripts/mixer_settings_recall.lua deleted file mode 100644 index c8f683eb98..0000000000 --- a/scripts/mixer_settings_recall.lua +++ /dev/null @@ -1,470 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Recall Mixer Settings", - author = "Mixbus Team", - description = [[ - - Recalls mixer settings outined by files - created by Store Mixer Settings. - - Allows for some room to change Source - and Destination. - - ]] -} - -function factory () - - local acoraida_monicas_last_used_recall_file - - return function () - - local user_cfg = ARDOUR.user_config_directory(-1) - local local_path = ARDOUR.LuaAPI.build_filename(Session:path(), 'mixer_settings') - local global_path = ARDOUR.LuaAPI.build_filename(user_cfg, 'mixer_settings') - - local invalidate = {} - - function exists(file) - local ok, err, code = os.rename(file, file) - if not ok then - if code == 13 then -- Permission denied, but it exists - return true - end - end return ok, err - end - - function whoami() - if not pcall(function() local first_check = Session:get_mixbus(0) end) then - return "Ardour" - else - local second_check = Session:get_mixbus(11) - if second_check:isnil() then - return "Mixbus" - else - return "32C" - end - end - end - - function isdir(path) - return exists(path.."/") - end - - function get_processor_by_name(track, name) - local i = 0 - local proc = track:nth_processor(i) - repeat - if(proc:display_name() == name) then - return proc - else - i = i + 1 - end - proc = track:nth_processor(i) - until proc:isnil() - end - - function new_plugin(name, type) - local plugin = ARDOUR.LuaAPI.new_plugin(Session, name, type, "") - if not(plugin:isnil()) then return plugin end - end - - function group_by_id(id) - local id = tonumber(id) - for g in Session:route_groups():iter() do - local group_id = tonumber(g:to_stateful():id():to_s()) - if group_id == id then return g end - end - end - - function group_by_name(name) - for g in Session:route_groups():iter() do - if g:name() == name then return g end - end - end - - function route_groupid_interrogate(t) - local group = false - for g in Session:route_groups():iter() do - for r in g:route_list():iter() do - if r:name() == t:name() then group = g:to_stateful():id():to_s() end - end - end return group - end - - function route_group_interrogate(t) - for g in Session:route_groups():iter() do - for r in g:route_list():iter() do - if r:name() == t:name() then return g end - end - end - end - - function recall(debug, path, dry_run) - local file = io.open(path, "r") - assert(file, "File not found!") - local bypass_routes = {} - - local i = 0 - for l in file:lines() do - --print(i, l) - - local create_groups = dry_run["create_groups"] - local skip_line = false - - local plugin, route, group = false, false, false - local f = load(l) - - if debug then - --print('create_groups ' .. tostring(create_groups)) - print(i, string.sub(l, 0, 29), f) - end - - if f then f() end - - if instance["route_id"] then route = true end - if instance["plugin_id"] then plugin = true end - if instance["group_id"] then group = true end - - if group then - local g_id = instance["group_id"] - local routes = instance["routes"] - local name = instance["name"] - local group = group_by_id(g_id) - if not(group) then - if create_groups then - local group = Session:new_route_group(name) - for _, v in pairs(routes) do - local rt = Session:route_by_id(PBD.ID(v)) - if rt:isnil() then rt = Session:route_by_name(name) end - if not(rt:isnil()) then group:add(rt) end - end - end - end - end - - if route then - local substitution = tonumber(dry_run["destination-"..i]) - if substitution == 0 then - bypass_routes[#bypass_routes + 1] = instance["route_id"] - goto nextline - end - - local old_order = ARDOUR.ProcessorList() - local route_id = instance["route_id"] - local r_id = PBD.ID(instance["route_id"]) - local muted, soloed = instance["muted"], instance["soloed"] - local order = instance["order"] - local cache = instance["cache"] - local group = instance["group"] - local group_name = instance["group_name"] - local name = instance["route_name"] - local gc, tc, pc = instance["gain_control"], instance["trim_control"], instance["pan_control"] - local sends = instance["sends"] - - if not(substitution == instance["route_id"]) then - print('SUBSTITUTION FOR: ', name, substitution, Session:route_by_id(PBD.ID(substitution)):name()) - --bypass_routes[#bypass_routes + 1] = route_id - was_subbed = true - r_id = PBD.ID(substitution) - end - - local rt = Session:route_by_id(r_id) - if rt:isnil() then rt = Session:route_by_name(name) end - if rt:isnil() then goto nextline end - - if sends then - for i, data in pairs(sends) do - i = i-1 - for j, ctrl in pairs({ - rt:send_level_controllable(i), - rt:send_enable_controllable(i), - rt:send_pan_azimuth_controllable(i), - rt:send_pan_azimuth_enable_controllable(i), - }) do - if not(ctrl:isnil()) then - local value = data[j] - if value then - if debug then - print("Setting " .. ctrl:name() .. " to value " .. value) - end - ctrl:set_value(value, PBD.GroupControlDisposition.NoGroup) - end - end - end - end - end - - local cur_group_id = route_groupid_interrogate(rt) - if not(group) and (cur_group_id) then - local g = group_by_id(cur_group_id) - if g then g:remove(rt) end - end - - local rt_group = group_by_name(group_name) - if rt_group then rt_group:add(rt) end - - well_known = {'PRE', 'Trim', 'EQ', 'Comp', 'Fader', 'POST'} - protected_instrument = false - for k, v in pairs(order) do - local proc = Session:processor_by_id(PBD.ID(1)) - if not(was_subbed) then - proc = Session:processor_by_id(PBD.ID(v)) - end - if proc:isnil() then - for id, sub_tbl in pairs(cache) do - local name = sub_tbl[1] - local type = sub_tbl[2] - if v == id then - proc = new_plugin(name, type) - for _, control in pairs(well_known) do - if name == control then - proc = get_processor_by_name(rt, control) - invalidate[v] = proc:to_stateful():id():to_s() - goto nextproc - end - end - if not(proc) then goto nextproc end - if not(proc:isnil()) then - rt:add_processor_by_index(proc, 0, nil, true) - invalidate[v] = proc:to_stateful():id():to_s() - end - end - end - end - ::nextproc:: - if proc and not(proc:isnil()) then old_order:push_back(proc) end - if not(old_order:empty()) and not(protected_instrument) then - if not(rt:to_track():to_midi_track():isnil()) then - if not(rt:the_instrument():isnil()) then - protected_instrument = true - old_order:push_back(rt:the_instrument()) - end - end - end - end - rt:reorder_processors(old_order, nil) - if muted then rt:mute_control():set_value(1, 1) else rt:mute_control():set_value(0, 1) end - if soloed then rt:solo_control():set_value(1, 1) else rt:solo_control():set_value(0, 1) end - rt:gain_control():set_value(gc, 1) - rt:trim_control():set_value(tc, 1) - if pc ~= false and not(rt:is_master()) then rt:pan_azimuth_control():set_value(pc, 1) end - end - - if plugin then - --if the plugin is owned by a route - --we decided not to use, skip it - for _, v in pairs(bypass_routes) do - if instance["owned_by_route_id"] == v then - goto nextline - end - end - - local enable = {} - local params = instance["parameters"] - local p_id = instance["plugin_id"] - local act = instance["active"] - - for k, v in pairs(invalidate) do --invalidate any deleted plugin's id - if p_id == k then - p_id = v - end - end - - local proc = Session:processor_by_id(PBD.ID(p_id)) - if proc:isnil() then goto nextline end - local plug = proc:to_insert():plugin(0) - - for k, v in pairs(params) do - local label = plug:parameter_label(k) - if string.find(label, "Assign") or string.find(label, "Enable") then --@ToDo: Check Plugin type == LADSPA or VST? - enable[k] = v --queue any assignments/enables for after the initial parameter recalling to duck the 'in-on-change' feature - end - print(string.format("%s (Port: %s) -> %s", label, k, v)) - ARDOUR.LuaAPI.set_processor_param(proc, k, v) - end - - for k, v in pairs(enable) do - ARDOUR.LuaAPI.set_processor_param(proc, k, v) - end - if act then proc:activate() else proc:deactivate() end - end - - ::nextline:: - i = i + 1 - - end - end - - function dry_run(debug, path) - --returns a dialog-able table of - --everything we do (logically) - --in the recall function - local route_values = {['----'] = "0"} - for r in Session:get_routes():iter() do - route_values[r:name()] = r:to_stateful():id():to_s() - end - - local i = 0 - local dry_table = { - {type = "label", align="right", key="col-1-title", col=0, colspan=1, title = 'Source:'}, - {type = "label", align="left", key="col-2-title", col=1, colspan=1, title = 'Destination:'}, - } - local file = io.open(path, "r") - assert(file, "File not found!") - pad = 0 - for l in file:lines() do - local do_plugin, do_route, do_group = false, false, false - local f = load(l) - - if debug then - print(i, string.sub(l, 0, 29), f) - end - - if f then f() end - - if instance["route_id"] then do_route = true end - if instance["plugin_id"] then do_plugin = true end - if instance["group_id"] then do_group = true end - - if do_group then - local group_id = instance["group_id"] - local group_name = instance["name"] - local dlg_title, action_title = "", "" - - local group_ptr = group_by_id(group_id) - - if not(group_ptr) then - dlg_title = string.format("(Group) %s.", group_name) - --action_title = "will create and use settings" - else - dlg_title = string.format("(Group) %s.", group_ptr:name()) - --action_title = "will use group settings" - end - table.insert(dry_table, { - order=pad, type = "label", align="right", key = "group-"..i , col = 0, colspan = 1, title = dlg_title - }) - pad = pad + 1 - end - - if do_route then - local route_id = instance["route_id"] - local route_name = instance["route_name"] - local dlg_title = "" - - local route_ptr = Session:route_by_id(PBD.ID(route_id)) - - if route_ptr:isnil() then - route_ptr = Session:route_by_name(route_name) - if not(route_ptr:isnil()) then - dlg_title = string.format("%s", route_ptr:name()) - --action_title = "will use route settings" - else - dlg_title = string.format("%s", route_name) - --action_title = "will be ignored" - end - else - dlg_title = string.format("%s", route_ptr:name()) - --action_title = "will use route settings" - end - if route_ptr:isnil() then name = route_name else name = route_ptr:name() end - - table.insert(dry_table, { - order=instance['pi_order']+pad, type = "label", align="right", key = "route-"..i , col = 0, colspan = 1, title = dlg_title - }) - table.insert(dry_table, { - type = "dropdown", align="left", key = "destination-"..i, col = 1, colspan = 1, title = "", values = route_values, default = name or "----" - }) - end - i = i + 1 - end - table.insert(dry_table, { - type = "checkbox", col=0, colspan=2, align="left", key = "create_groups", default = true, title = "Create Groups if necessary?" - }) - return dry_table - end - - local global_vs_local_dlg = { - { type = "label", col=0, colspan=20, align="left", title = "" }, - { - type = "radio", col=0, colspan=20, align="left", key = "recall-dir", title = "", values = - { - ['Pick from Global Settings'] = 1, ['Pick from Local Settings'] = 2, ['Last Used Recall File'] = 3, - }, - default = 'Last Used Recall File' - }, - { type = "label", col=0, colspan=20, align="left", title = ""}, - } - - local recall_options = { - { type = "label", col=0, colspan=10, align="left", title = "" }, - { type = "file", col=0, colspan=15, align="left", key = "file", title = "Select a Settings File", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") }, - { type = "label", col=0, colspan=10, align="left", title = "" }, - } - - local gvld = LuaDialog.Dialog("Recall Mixer Settings:", global_vs_local_dlg):run() - - if not(gvld) then - return - else - if gvld['recall-dir'] == 1 then - local global_ok = isdir(global_path) - local global_default_path = ARDOUR.LuaAPI.build_filename(global_path, string.format("FactoryDefault-%s.lua", whoami())) - print(global_default_path) - if global_ok then - recall_options[2]['path'] = global_default_path - local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run() - if not(rv) then return end - local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run() - if dry_return then - acoraida_monicas_last_used_recall_file = rv['file'] - recall(false, rv['file'], dry_return) - else - return - end - else - LuaDialog.Message ("Recall Mixer Settings:", - global_path .. ' does not exist!\nPlease run Store Mixer Settings first.', - LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() - end - end - - if gvld['recall-dir'] == 2 then - local local_ok = isdir(local_path) - local local_default_path = ARDOUR.LuaAPI.build_filename(local_path, 'asdf') - print(local_default_path) - if local_ok then - recall_options[2]['path'] = local_default_path - local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run() - if not(rv) then return end - local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run() - if dry_return then - acoraida_monicas_last_used_recall_file = rv['file'] - recall(true, rv['file'], dry_return) - else - return - end - else - LuaDialog.Message ("Recall Mixer Settings:", - local_path .. 'does not exist!\nPlease run Store Mixer Settings first.', - LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() - end - end - - if gvld['recall-dir'] == 3 then - if acoraida_monicas_last_used_recall_file then - local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, acoraida_monicas_last_used_recall_file)):run() - if dry_return then - recall(true, acoraida_monicas_last_used_recall_file, dry_return) - else - return - end - else - LuaDialog.Message ("Script has no record of last used file:", - 'Please pick a recall file and then this option will be available', - LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() - end - end - end - -end end diff --git a/scripts/mixer_settings_store.lua b/scripts/mixer_settings_store.lua deleted file mode 100644 index 1718a83ddd..0000000000 --- a/scripts/mixer_settings_store.lua +++ /dev/null @@ -1,383 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Store Mixer Settings", - author = "Mixbus Team", - description = [[ - - Stores the current Mixer state as a file - that can be read and recalled arbitrarily - by it's companion script, Recall Mixer Settings. - - Supports: processor settings, grouping, - mute, solo, gain, trim, pan and processor ordering, - plus re-adding certain deleted plugins. - - ]] -} - -function factory () return function () - - local user_cfg = ARDOUR.user_config_directory(-1) - local local_path = ARDOUR.LuaAPI.build_filename(Session:path(), 'mixer_settings') - local global_path = ARDOUR.LuaAPI.build_filename(user_cfg, 'mixer_settings') - - function exists(file) - local ok, err, code = os.rename(file, file) - if not ok then - if code == 13 then -- Permission denied, but it exists - return true - end - end return ok, err - end - - function whoami() - if not pcall(function() local first_check = Session:get_mixbus(0) end) then - return "Ardour" - else - local second_check = Session:get_mixbus(11) - if second_check:isnil() then - return "Mixbus" - else - return "32C" - end - end - end - - function isdir(path) - return exists(path.."/") - end - - function setup_paths() - local global_ok, local_ok = false, false - - if not(isdir(global_path)) then - global_ok, _, _ = os.execute('mkdir '.. global_path) - if global_ok == 0 then - global_ok = true - end - else - global_ok = true - end - if not(isdir(local_path)) then - local_ok, _, _ = os.execute('mkdir '.. local_path) - if local_ok == 0 then - local_ok = true - end - else - local_ok = true - end - return global_ok, local_ok - end - - function get_processor_by_name(track, name) - local i = 0 - local proc = track:nth_processor(i) - repeat - if(proc:display_name() == name) then - return proc - else - i = i + 1 - end - proc = track:nth_processor(i) - until proc:isnil() - end - - function group_by_id(id) - local id = tonumber(id) - for g in Session:route_groups():iter() do - local group_id = tonumber(g:to_stateful():id():to_s()) - if group_id == id then return g end - end - end - - function group_by_name(name) - for g in Session:route_groups():iter() do - if g:name() == name then return g end - end - end - - function route_groupid_interrogate(t) - local group = false - for g in Session:route_groups():iter() do - for r in g:route_list():iter() do - if r:name() == t:name() then group = g:to_stateful():id():to_s() end - end - end return group - end - - function route_group_interrogate(t) - for g in Session:route_groups():iter() do - for r in g:route_list():iter() do - if r:name() == t:name() then return g end - end - end - end - - function empty_last_store(path) --empty current file from last run - local file = io.open(path, "w") - --file:write(string.format("instance = { whoami = '%s' }", whoami()) - file:write("") - file:close() - end - - function mark_tracks(selected, path) - - empty_last_store(path) - - local route_string = [[instance = { - route_id = %d, - route_name = '%s', - gain_control = %s, - trim_control = %s, - pan_control = %s, - sends = {%s}, - muted = %s, - soloed = %s, - order = {%s}, - cache = {%s}, - group = %s, - group_name = '%s', - pi_order = %d - }]] - - local group_string = [[instance = { - group_id = %s, - name = '%s', - routes = {%s}, - }]] - - local processor_string = [[instance = { - plugin_id = %d, - type = %d, - display_name = '%s', - owned_by_route_name = '%s', - owned_by_route_id = %d, - parameters = {%s}, - active = %s, - }]] - - local group_route_string = " [%d] = %s," - local proc_order_string = " [%d] = %d," - local proc_cache_string = " [%d] = {'%s', %d}," - local params_string = " [%d] = %s," - - --ensure easy-to-read formatting doesn't make it through - local route_string = string.gsub(route_string, "[\n\t%s]", "") - local group_string = string.gsub(group_string, "[\n\t%s]", "") - local processor_string = string.gsub(processor_string, "[\n\t%s]", "") - - local sel = Editor:get_selection () - local groups_to_write = {} - local i = 0 - - local tracks = Session:get_stripables() - - if selected then tracks = sel.tracks:routelist() end - - for r in tracks:iter() do - local group = route_group_interrogate(r) - if group then - local already_there = false - for _, v in pairs(groups_to_write) do - if group == v then - already_there = true - end - end - if not(already_there) then - groups_to_write[#groups_to_write + 1] = group - end - end - end - - for _, g in pairs(groups_to_write) do - local tmp_str = "" - for t in g:route_list():iter() do - tmp_str = tmp_str .. string.format(group_route_string, i, t:to_stateful():id():to_s()) - i = i + 1 - end - local group_str = string.format( - group_string, - g:to_stateful():id():to_s(), - g:name(), - tmp_str - ) - - file = io.open(path, "a") - file:write(group_str, "\r\n") - file:close() - end - - for r in tracks:iter() do - if r:is_monitor () or r:is_auditioner () or not(r:to_vca():isnil()) then goto nextroute end -- skip special routes - r = r:to_route() - if r:isnil() then goto nextroute end - local order = ARDOUR.ProcessorList() - local x = 0 - repeat - local proc = r:nth_processor(x) - if not proc:isnil() then - order:push_back(proc) - end - x = x + 1 - until proc:isnil() - - - local route_group = route_group_interrogate(r) - if route_group then route_group = route_group:name() else route_group = "" end - local rid = r:to_stateful():id():to_s() - local pan = r:pan_azimuth_control() - if pan:isnil() then pan = false else pan = pan:get_value() end --sometimes a route doesn't have pan, like the master. - - -- Get send information, if any. - local send_string = "" - local i = 0 - repeat - local fmt = "{%s, %s, %s, %s}" - string.gsub(fmt, "[\n\t]", "") - local values = {} - for j, ctrl in pairs({ - r:send_level_controllable(i), - r:send_enable_controllable(i), - r:send_pan_azimuth_controllable(i), - r:send_pan_azimuth_enable_controllable(i), - }) do - if not(ctrl:isnil()) then - values[#values + 1] = ctrl:get_value() - else - values[#values + 1] = "nil" - end - end - send_string = send_string .. string.format(fmt, table.unpack(values)) - send_string = send_string .. "," - i = i + 1 - until r:send_enable_controllable(i):isnil() - - print(send_string) - - local order_nmbr = 0 - local tmp_order_str, tmp_cache_str = "", "" - for p in order:iter() do - local ptype - if not(p:to_insert():isnil()) then - ptype = p:to_insert():plugin(0):get_info().type - else - ptype = 99 - end - local pid = p:to_stateful():id():to_s() - if not(string.find(p:display_name(), "latcomp")) then - tmp_order_str = tmp_order_str .. string.format(proc_order_string, order_nmbr, pid) - tmp_cache_str = tmp_cache_str .. string.format(proc_cache_string, pid, p:display_name(), ptype) - end - order_nmbr = order_nmbr + 1 - end - - local route_str = string.format( - route_string, - rid, - r:name(), - ARDOUR.LuaAPI.ascii_dtostr(r:gain_control():get_value()), - ARDOUR.LuaAPI.ascii_dtostr(r:trim_control():get_value()), - tostring(pan), - send_string, - r:muted(), - r:soloed(), - tmp_order_str, - tmp_cache_str, - route_groupid_interrogate(r), - route_group, - r:presentation_info_ptr():order() - ) - - file = io.open(path, "a") - file:write(route_str, "\n") - file:close() - - local i = 0 - while true do - local params = {} - local proc = r:nth_plugin (i) - if proc:isnil () then break end - local active = proc:active() - local id = proc:to_stateful():id():to_s() - local plug = proc:to_insert ():plugin (0) - local ptype = proc:to_insert():plugin(0):get_info().type - local n = 0 -- count control-ports - for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters - if plug:parameter_is_control (j) then - local label = plug:parameter_label (j) - if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then - --local _, _, pd = ARDOUR.LuaAPI.plugin_automation(proc, n) - local val = ARDOUR.LuaAPI.get_processor_param(proc, j, true) - print(r:name(), "->", proc:display_name(), label, val) - params[j] = val - end - n = n + 1 - end - end - i = i + 1 - - local tmp_params_str = "" - for k, v in pairs(params) do - tmp_params_str = tmp_params_str .. string.format(params_string, k, ARDOUR.LuaAPI.ascii_dtostr(v)) - end - - local proc_str = string.format( - processor_string, - id, - ptype, - proc:display_name(), - r:name(), - r:to_stateful():id():to_s(), - tmp_params_str, - active - ) - file = io.open(path, "a") - file:write(proc_str, "\n") - file:close() - end - ::nextroute:: - end - end - - local store_options = { - { type = "label", col=0, colspan=1, align="right", title = "Name:" }, - { type = "entry", col=1, colspan=1, align="left" , key = "filename", default = Session:name(), title=""}, - { type = "label", col=0, colspan=1, align="right", title = "Location:" }, - { - type = "radio", col=1, colspan=3, align="left", key = "store-dir", title = "", values = - { - ['Global (accessible from any session)'] = 1, ['Local (this session only)'] = 2 - }, - default = 'Locally (this session only)' - }, - { type = "hseparator", title="", col=0, colspan = 3}, - { type = "label", col=0, colspan=1, align="right", title = "Selected Tracks Only:" }, - { type = "checkbox", col=1, colspan=1, align="left", key = "selected", default = false, title = ""}, - --{ type = "label", col=0, colspan=2, align="left", title = ''}, - --{ type = "label", col=0, colspan=2, align="left", title = "Global Path: " .. global_path}, - --{ type = "label", col=0, colspan=2, align="left", title = "Local Path: " .. local_path}, - } - - local global_ok, local_ok = setup_paths() - - if global_ok and local_ok then - local rv = LuaDialog.Dialog("Store Mixer Settings:", store_options):run() - - if not(rv) then return end - - local filename = rv['filename'] - if rv['store-dir'] == 1 then - local store_path = ARDOUR.LuaAPI.build_filename(global_path, string.format("%s-%s.lua", filename, whoami())) - local selected = rv['selected'] - mark_tracks(selected, store_path) - end - - if rv['store-dir'] == 2 then - local store_path = ARDOUR.LuaAPI.build_filename(local_path, string.format("%s-%s.lua", filename, whoami())) - print(store_path) - local selected = rv['selected'] - mark_tracks(selected, store_path) - end - end - -end end diff --git a/scripts/mute_all_tracks.lua b/scripts/mute_all_tracks.lua deleted file mode 100644 index e15c0530c0..0000000000 --- a/scripts/mute_all_tracks.lua +++ /dev/null @@ -1,21 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Mute All Tracks", - license = "MIT", - author = "Ardour Team", - description = [[Mute All Tracks in the Session]] -} - -function factory () return function () - local ctrls = ARDOUR.ControlListPtr () -- create a list of controls to change - - for r in Session:get_tracks ():iter () do -- iterate over all tracks in the session - ctrls:push_back (r:mute_control()) -- add the track's mute-control to the list of of controls to be changed - end - - -- of more than one control is to be changed.. - if ctrls:size() > 0 then - -- do it (queue change in realtime-context) set to "1" ; here 'muted' - Session:set_controls (ctrls, 1, PBD.GroupControlDisposition.NoGroup) - end -end end diff --git a/scripts/new_playlist.lua b/scripts/new_playlist.lua deleted file mode 100644 index 54563cd6a5..0000000000 --- a/scripts/new_playlist.lua +++ /dev/null @@ -1,17 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "New Playlist", - license = "MIT", - author = "Ardour Lua Taskforce", - description = [[Prompts and builds a new playlist for every track in the session.]] -} - -function factory () return function () - - for r in Session:get_tracks():iter() do - local rtav = Editor:rtav_from_route(r) -- lookup RTAV - Editor:new_playlists(rtav:to_timeaxisview()) - end - -collectgarbage() -end end diff --git a/scripts/noisegen.lua b/scripts/noisegen.lua deleted file mode 100644 index bf7b006160..0000000000 --- a/scripts/noisegen.lua +++ /dev/null @@ -1,111 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "NoiseGen", - category = "Instrument", - license = "MIT", - author = "Ardour Team", - description = [[Noise Generator (v-1.02)]] -} - -function dsp_params () - return - { - { ["type"] = "input", name = "White/Pink", min = 0, max = 1, default = 0, toggled = true }, - { ["type"] = "input", name = "Gain", min = -60, max = 0, default = -18, unit="dB" }, - } -end - -function dsp_ioconfig () - return { [1] = { audio_in = -1, audio_out = -1}, } -end - -local sr = 0 - -function dsp_init (rate) - sr = rate -end - -local ao = 0 -local draw = 0 - -function dsp_run (ins, outs, n_samples) - - local a = {} -- init array - local ctrl = CtrlPorts:array () - local noise = ctrl[1] or 0 - local amplitude = ARDOUR.DSP.dB_to_coefficient (ctrl[2]) or ARDOUR.DSP.dB_to_coefficient (-18) - - local b0 = 0.0 - local b1 = 0.0 - local b2 = 0.0 - local b3 = 0.0 - local b4 = 0.0 - local b5 = 0.0 - local b6 = 0.0 - - --Pink noise generation courtesy of Paul Kellet's refined method - --http://www.musicdsp.org/files/pink.txt - --If 'white' consists of uniform random numbers, - --the pink noise will have an almost gaussian distribution. - for s = 1, n_samples do - if noise == 0 then - a[s] = amplitude * 2 * (math.random() - 0.5) - end - if noise == 1 then - white = (amplitude * 0.25) * 2 * (math.random() - 0.5) - b0 = 0.99886 * b0 + white * 0.0555179; - b1 = 0.99332 * b1 + white * 0.0750759; - b2 = 0.96900 * b2 + white * 0.1538520; - b3 = 0.86650 * b3 + white * 0.3104856; - b4 = 0.55000 * b4 + white * 0.5329522; - b5 = -0.7616 * b5 - white * 0.0168980; - b6 = white * 0.115926; - a[s] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; - end - end - - if (draw > (sr/15)) then - self:queue_draw() - draw = 0 - end - - -- passes array a {} into buffer - for c = 1,#outs do - outs[c]:set_table(a, n_samples) - end - draw = draw + n_samples -end - -function render_inline (ctx, w, max_h) --inline display - local ctrl = CtrlPorts:array() - h = 30 - p = 0 - inc = 0 - ycy = 0.5 - pink = false - local amplitude = ARDOUR.DSP.dB_to_coefficient(ctrl[2]) - if ctrl[1] == 1 then pink = true end - if pink then inc = 0.7/w end - - --draw rectangle - ctx:rectangle(0, 0, w, h) - ctx:set_source_rgba(0, 0, 0, 1.0) - ctx:fill() - ctx:set_line_width(1.5) - ctx:set_source_rgba(0.8, 0.8, 0.8, 1.0) - - l_x = 0 - l_y = 0 - for x = 0,w do - if pink then ycy = 0.3 else ycy = 0.5 end --slant slightly like an actual pink noise spectrum - y = math.log(20^amplitude) * (math.random() - 0.5) - p - yc = ycy * h + ((-0.5 * h) * y) - ctx:move_to (x, yc + 3) - ctx:line_to (l_x, l_y + 3) - l_x = x - l_y = yc - ctx:stroke() - p = p + inc - end - return {w, h + 6} -end diff --git a/scripts/normalize_all_tracks.lua b/scripts/normalize_all_tracks.lua deleted file mode 100644 index 2886c9b5c5..0000000000 --- a/scripts/normalize_all_tracks.lua +++ /dev/null @@ -1,58 +0,0 @@ -ardour { ["type"] = "EditorAction", - name = "Normalize All Tracks", - license = "MIT", - author = "Ardour Team", - description = [[Normalize all regions using a common gain-factor per track.]] -} - -function factory () return function () - -- target values -- TODO: use a LuaDialog.Dialog and ask.. - local target_peak = -1 --dBFS - local target_rms = -18 --dBFS/RMS - - -- prepare undo operation - Session:begin_reversible_command ("Normalize Tracks") - local add_undo = false -- keep track if something has changed - - -- loop over all tracks in the session - for track in Session:get_tracks():iter() do - local norm = 0 -- per track gain - -- loop over all regions on track - for r in track:to_track():playlist():region_list():iter() do - -- test if it's an audio region - local ar = r:to_audioregion () - if ar:isnil () then goto next end - - local peak = ar:maximum_amplitude (nil) - local rms = ar:rms (nil) - -- check if region is silent - if (peak > 0) then - local f_rms = rms / 10 ^ (.05 * target_rms) - local f_peak = peak / 10 ^ (.05 * target_peak) - local tg = (f_peak > f_rms) and f_peak or f_rms -- max (f_peak, f_rms) - norm = (tg > norm) and tg or norm -- max (tg, norm) - end - ::next:: - end - - -- apply same gain to all regions on track - if norm > 0 then - for r in track:to_track():playlist():region_list():iter() do - local ar = r:to_audioregion () - if ar:isnil () then goto skip end - ar:to_stateful ():clear_changes () - ar:set_scale_amplitude (1 / norm) - add_undo = true - ::skip:: - end - end - end - - -- all done. now commit the combined undo operation - if add_undo then - -- the 'nil' command here means to use all collected diffs - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end -end end diff --git a/scripts/notch_bank.lua b/scripts/notch_bank.lua deleted file mode 100644 index 76901920d9..0000000000 --- a/scripts/notch_bank.lua +++ /dev/null @@ -1,125 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-Notch Bank", - category = "Filter", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[Notch Filter Bank; useful to remove noise with a harmonic spectum (e.g, mains hum, GSM signals, charge-pump noise, etc). -Note: this plugin is not suitable to be automated, it is intended for static noise only.]] -} - ------------------------------------------------------------------- --- this is a quick/dirty example filter: no de-click, no de-zipper -------------------------------------------------------------------- - --- configuration -local max_stages = 100 - --- plugin i/o ports -function dsp_ioconfig () - return - { - -- allow any number of I/O as long as port-count matches - { audio_in = -1, audio_out = -1}, - } -end - --- plugin control ports -function dsp_params () - return - { - { ["type"] = "input", name = "Base Freq", min = 10, max = 2000, default = 100, unit="Hz", logarithmic = true }, - { ["type"] = "input", name = "Quality", min = 1.0, max = 100.0, default = 8.0, logarithmic = true }, - { ["type"] = "input", name = "Stages", min = 1.0, max = max_stages, default = 8.0, integer = true }, - } -end - --- plugin instance state -local filters = {} -- the biquad filter instances -local chn = 0 -- configured channel count -local sample_rate = 0 -- configured sample-rate -local limit = 0 -- max number of stages (given freq & sample-rate) - --- cached control ports (keep track of changed) -local freq = 0 -local qual = 0 - --- dsp_init is called once when instantiating the plugin -function dsp_init (rate) - -- remember the sample-rate - sample_rate = rate -end - --- dsp_configure is called every time when the channel-count --- changes, and at least once at the beginning. -function dsp_configure (ins, outs) - assert (ins:n_audio () == outs:n_audio ()) - - -- explicit cleanup - filters = {} - collectgarbage () - - -- remember audio-channels - chn = ins:n_audio () - - -- set up filter instances for all channels - for c = 1, chn do - filters[c] = {} - for i = 1, max_stages do - filters[c][i] = ARDOUR.DSP.Biquad (sample_rate) - end - end -end - --- the actual process function, called every cycle --- ins, outs are audio-data arrays --- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray --- n_samples are the number of samples to process -function dsp_run (ins, outs, n_samples) - -- make sure input and output count matches... - assert (#ins == #outs) - -- ...and matches the configured number of channels - assert (#ins == chn) - - local ctrl = CtrlPorts:array () -- get control parameters as array - -- ctrl[] .. correspond to the parameters given in in dsp_params() - - -- test if the plugin-parameters have changed - if freq ~= ctrl[1] or qual ~= ctrl[2] then - -- remember current settings - freq = ctrl[1] - qual = ctrl[2] - -- calc max number of states to configure/process - limit = math.floor (sample_rate / (2 * freq)) -- at most up to SR / 2 - if limit > max_stages then limit = max_stages end - - -- re-compute the filter coefficients for all filters - for c = 1, chn do -- for each channel - for i = 1, limit do -- and for each filter stage - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad - -- and for a list of available types, see - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.DSP.Biquad.Type - -- the parameters are type, frequency, quality(bandwidth), gain - filters[c][i]:compute (ARDOUR.DSP.BiquadType.Notch, freq * i, qual * i, 0) - end - end - end - - -- limit the number of process stages - local stages = math.floor (ctrl['3']) -- current user-set parameter - if stages < 1 then stages = 1 end -- at least one stage... - if stages > limit then stages = limit end - - -- process all channels - for c = 1, chn do - -- when not processing in-place, copy the data from input to output first - if ins[c] ~= outs[c] then - ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) - end - - -- run all stages, in-place on the output buffer - for i = 1, stages do - filters[c][i]:run (outs[c], n_samples) - end - end -end diff --git a/scripts/periodic_backup.lua b/scripts/periodic_backup.lua deleted file mode 100644 index ff274a7a08..0000000000 --- a/scripts/periodic_backup.lua +++ /dev/null @@ -1,47 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Periodically Save Snapshot", - author = "Ardour Lua Task Force", - description = "Save a session-snapshot peridocally (every 15mins) named after the current date-time", -} - --- subscribe to signals --- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal -function signals () - return LuaSignal.Set():add ({[LuaSignal.LuaTimerS] = true}) -end - --- create callback function -function factory () - local _last_snapshot_time = 0 -- persistent variable - local _snapshot_interval = 60 * 15 -- 15 minutes - - -- callback function which invoked when signal is emitted, every 100ms - return function (signal, ref, ...) - - local now = os.time (); -- unix-time, seconds since 1970 - - -- skip initial save when script is loaded - if (_last_snapshot_time == 0) then - _last_snapshot_time = now; - end - - -- every 15 mins - if (now > _last_snapshot_time + _snapshot_interval) then - - -- don't save while recording, may interfere with recording - if Session:actively_recording() then - -- queue 30 sec after rec-stop - _last_snapshot_time = now - _snapshot_interval + 30 - return - end - - _last_snapshot_time = now - -- format date-time (avoid colon) - local snapshot_name = os.date ("%Y-%m-%d %H.%M.%S", now) - -- save session -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session - Session:save_state ("backup " .. snapshot_name, false, false, false) - end - - end -end diff --git a/scripts/post_export_save_hook.lua b/scripts/post_export_save_hook.lua deleted file mode 100644 index 3b31ee320b..0000000000 --- a/scripts/post_export_save_hook.lua +++ /dev/null @@ -1,26 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Save Snapshot after Export", - author = "Ardour Lua Task Force", - description = "Save a session-snapshot on export, named after the export-timespan", -} - --- subscribe to signals --- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal -function signals () - s = LuaSignal.Set() - s:add ({[LuaSignal.Exported] = true}) - return s -end - --- create callback functions -function factory () - -- callback function which invoked when signal is emitted - return function (signal, ref, ...) - -- 'Exported' passes 2 strings: current time-span name, path to exported file - -- (see C++ libs/ardour/export_handler.cc Session::Exported ) - local timespan_name, file_path = ... - -- save session -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session - Session:save_state ("export-" .. timespan_name, false, false, false) - end -end diff --git a/scripts/preare_record_example.lua b/scripts/preare_record_example.lua deleted file mode 100644 index 48e063d326..0000000000 --- a/scripts/preare_record_example.lua +++ /dev/null @@ -1,82 +0,0 @@ ---[[ - -# Example script to prepare the Ardour session for recording - -Usually there's a certain state needed to actually start the recording. This example -script treats the situation of a podcast recording. When starting the recording the -following settings have to be ensured. - -* Session has to be recenabled -* Tracks have to be recenabled -* Gain automation have to set on write in order to record events from mute buttons -* The playhead has to be at 00:00:00.000 -* The last (failed) capture has to be cleared -* Location markers have to be cleared - -So this script automizes away the task and lets the podcast moderator by just one -action (for example triggerd by a Wiimote) prepare the session for recording. - -It can be used for example with the python script of the Linux podcasting hacks: -https://github.com/linux-podcasting-hacks/wiimote-recording-control - -Not that this script is more meant as an demo script to demonstrate the -possibilities of the Lua interface. - ---]] - -ardour { - ["type"] = "EditorAction", - name = "Prepare recording for podcast", - author = "Johannes Mueller", - description = [[ -Prepares the Ardour session for podcast recording. - -* Sets the gain automation to "Write" so that muting buttons work. -* Recenables all tracks. -* Clears all markers. -* Erases the last capture (assuming that it was a failed one) -* Rewinds the session to starting point. -* Recenables the session. -]] -} - -function factory (unused) return function() - if Session:actively_recording() then - return end - - for t in Session:get_tracks():iter() do - t:gain_control():set_automation_state(ARDOUR.AutoState.Write) - t:rec_enable_control():set_value(1, PBD.GroupControlDisposition.UseGroup) - end - - for l in Session:locations():list():iter() do - if l:is_mark() then - Session:locations():remove(l) - end - end - - Session:goto_start() - Editor:remove_last_capture() - Session:maybe_enable_record() - -end end - -function icon (params) return function (ctx, width, height) - local x = width * .5 - local y = height * .5 - local r = math.min (x, y) * .55 - - ctx:arc (x, y, r, 0, 2 * math.pi) - ctx:set_source_rgba (.9, .3, .3, 1.) - ctx:fill_preserve () - ctx:set_source_rgba (0, 0, 0, .8) - ctx:set_line_width (1) - ctx:stroke () - - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(r * 1.5) .. "px") - txt:set_text ("P") - local tw, th = txt:get_pixel_size () - ctx:set_source_rgba (0, 0, 0, 1.0) - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/remove_unknown_procs.lua b/scripts/remove_unknown_procs.lua deleted file mode 100644 index 044e85b3b8..0000000000 --- a/scripts/remove_unknown_procs.lua +++ /dev/null @@ -1,44 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Remove Unknown Plugins", - license = "MIT", - author = "Ardour Team", - description = [[Remove all unknown plugins/processors from all tracks and busses]] -} - -function factory (params) return function () - -- iterate over all tracks and busses (aka routes) - for route in Session:get_routes ():iter () do - -- route is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route - local i = 0; - -- we need to iterate one-by one, removing a processor invalidates the list - repeat - proc = route:nth_processor (i) - -- proc is a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Processor - -- try cast it to http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:UnknownProcessor - if not proc:isnil () and not proc:to_unknownprocessor ():isnil () then - route:remove_processor (proc, nil, true) - else - i = i + 1 - end - until proc:isnil () - end -end end - - -function icon (params) return function (ctx, width, height, fg) - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (math.min (width, height) * .5) .. "px") - txt:set_text ("Fx") - local tw, th = txt:get_pixel_size () - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:layout_cairo_path (ctx) - - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - ctx:set_line_width (3) - ctx:stroke_preserve () - - ctx:set_source_rgba (.8, .2, .2, 1) - ctx:set_line_width (2) - ctx:stroke_preserve () - - ctx:set_source_rgba (0, 0, 0, 1) - ctx:fill () -end end diff --git a/scripts/reset_mixer.lua b/scripts/reset_mixer.lua deleted file mode 100644 index faf7aad477..0000000000 --- a/scripts/reset_mixer.lua +++ /dev/null @@ -1,249 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Reset Mixer", - license = "MIT", - author = "Ben Loftis, Nikolaus Gullotta, Maxime Lecoq", - description = [[Resets key Mixer settings after user-prompt (warning: this cannot be undone)]] -} - -function factory() return function() - local sp_radio_buttons = {Bypass="bypass", Remove="remove", Nothing=false} - local dlg = { - {type="label", align="left", colspan="3", title="Please select below the items you want to reset:" }, - {type="label", align="left", colspan="3", title="(Warning: this cannot be undone!)\n" }, - - {type="heading", align ="center", colspan="3", title = "Common Controls:" }, - {type="checkbox", key="fader", default=true, title="Fader" }, - {type="checkbox", key="mute", default=true, title="Mute" }, - {type="checkbox", key="solo", default=true, title="Solo" }, - {type="checkbox", key="trim", default=true, title="Trim" }, - {type="checkbox", key="pan", default=true, title="Pan (All)" }, - {type="checkbox", key="phase", default=true, title="Phase" }, - {type="checkbox", key="sends", default=true, title="Sends" }, - {type="checkbox", key="eq", default=true, title="EQ" }, - {type="checkbox", key="comp", default=true, title="Compressor" }, - - {type="heading", align="center", colspan="3", title="Processors:" }, - {type="radio", key="plugins", title="Plug-ins", values=sp_radio_buttons, default="Bypass" }, - {type="radio", key="io", title="Sends/Inserts", values=sp_radio_buttons, default="Bypass" }, - - {type="hseparator", title=""}, - - {type="heading", align="center", colspan="3", title="Misc." }, - {type="checkbox", key="auto", colspan="3", title = "Automation (switch to manual mode)" }, - {type="checkbox", key="rec", colspan="3", title = "Disable Record" }, - {type="checkbox", key="groups", colspan="3", title = "Groups" }, - {type="checkbox", key="vcas", colspan="3", title = "VCAs (unassign all)" }, - } - - function reset(ctrl, disp, auto) - local disp = disp or PBD.GroupControlDisposition.NoGroup - - if not(ctrl:isnil()) then - local pd = ctrl:desc() - ctrl:set_value(pd.normal, disp) - - if auto then - ctrl:set_automation_state(auto) - end - end - end - - function reset_eq_controls(route, disp, auto) - if route:isnil() then - return - end - - local disp = disp or PBD.GroupControlDisposition.NoGroup - - reset(route:eq_enable_controllable(), disp, auto) - - local i = 0 - repeat - for _,ctrl in pairs({ - route:eq_freq_controllable(i), - route:eq_gain_controllable(i), - route:eq_q_controllable(i), - }) do - reset(ctrl, disp, auto) - end - i = i + 1 - until route:eq_freq_controllable(i):isnil() - end - - function reset_comp_controls(route, disp, auto) - if route:isnil() then - return - end - - local disp = disp or PBD.GroupControlDisposition.NoGroup - - for _,ctrl in pairs({ - route:comp_enable_controllable(), - route:comp_makeup_controllable(), - route:comp_mode_controllable(), - route:comp_speed_controllable(), - route:comp_threshold_controllable(), - }) do - reset(ctrl, disp, auto) - end - end - - function reset_send_controls(route, disp, auto) - if route:isnil() then - return - end - - local disp = disp or PBD.GroupControlDisposition.NoGroup - - local i = 0 - repeat - for _,ctrl in pairs({ - route:send_level_controllable(i), - route:send_enable_controllable(i), - route:send_pan_azimuth_controllable(i), - route:send_pan_azimuth_enable_controllable(i), - }) do - reset(ctrl, disp, auto) - end - i = i + 1 - until route:send_enable_controllable(i):isnil() - end - - function reset_plugin_automation(plugin, state) - if plugin:to_insert():isnil() then - return - end - - local plugin = plugin:to_insert() - local pc = plugin:plugin(0):parameter_count() - for c = 0, pc do - local ac = plugin:to_automatable():automation_control(Evoral.Parameter(ARDOUR.AutomationType.PluginAutomation, 0, c), false) - if not(ac:isnil()) then - ac:set_automation_state(state) - end - end - end - - function reset_plugins(route, prefs, auto) - if route:isnil() then - return - end - - local i = 0 - local queue = {} - repeat - -- Plugins are queued to not invalidate this loop - local proc = route:nth_processor(i) - if not(proc:isnil()) then - if prefs["auto"] then - reset_plugin_automation(proc, auto) - end - if prefs["plugins"] then - local insert = proc:to_insert() - if not(insert:isnil()) then - if insert:is_channelstrip() or not(insert:display_to_user()) then - ARDOUR.LuaAPI.reset_processor_to_default(insert) - else - queue[#queue + 1] = proc - end - end - end - if prefs["io"] then - local io_proc = proc:to_ioprocessor() - if not(io_proc:isnil()) then - queue[#queue + 1] = proc - end - end - end - i = i + 1 - until proc:isnil() - - -- Deal with queue now - for _, proc in pairs(queue) do - if not(proc:to_insert():isnil()) then - if prefs["plugins"] == "remove" then - route:remove_processor(proc, nil, true) - elseif prefs["plugins"] == "bypass" then - proc:deactivate() - end - end - if not(proc:to_ioprocessor():isnil()) then - if prefs["io"] == "remove" then - route:remove_processor(proc, nil, true) - elseif prefs["io"] == "bypass" then - proc:deactivate() - end - end - end - end - - local pref = LuaDialog.Dialog("Reset Mixer", dlg):run() - - if not(pref) then goto pass_script end - assert(pref, "Dialog box was cancelled or is nil") - - for route in Session:get_routes():iter() do - local disp = PBD.GroupControlDisposition.NoGroup - local auto = nil - - if pref["auto"] then - auto = ARDOUR.AutoState.Off - end - - if pref["eq"] then reset_eq_controls(route, disp, auto) end - if pref["comp"] then reset_comp_controls(route, disp, auto) end - if pref["sends"] then reset_send_controls(route, disp, auto) end - reset_plugins(route, pref, auto) - - if pref["rec"] then - reset(route:rec_enable_control(), disp, auto) - reset(route:rec_safe_control(), disp, auto) - end - - if pref["fader"] then - reset(route:gain_control(), disp, auto) - end - - if pref["phase"] then - reset(route:phase_control(), disp, auto) - end - - if pref["trim"] then - reset(route:trim_control(), disp, auto) - end - - if pref["mute"] then - reset(route:mute_control(), disp, auto) - end - - if pref["solo"] then - reset(route:solo_control(), disp, auto) - end - - if pref["pan"] then - reset(route:pan_azimuth_control(), disp, auto) - reset(route:pan_elevation_control(), disp, auto) - reset(route:pan_frontback_control(), disp, auto) - reset(route:pan_lfe_control(), disp, auto) - reset(route:pan_width_control(), disp, auto) - end - - if pref["vcas"] then - local slave = route:to_slavable() - if not(slave:isnil()) then - for vca in Session:vca_manager():vcas():iter() do - slave:unassign(vca) - end - end - end - end - - if pref["groups"] then - for group in Session:route_groups():iter() do - Session:remove_route_group(group) - end - end - ::pass_script:: - collectgarbage() -end end \ No newline at end of file diff --git a/scripts/s_chanmap.lua b/scripts/s_chanmap.lua deleted file mode 100644 index 7bc070c8cc..0000000000 --- a/scripts/s_chanmap.lua +++ /dev/null @@ -1,34 +0,0 @@ -ardour { ["type"] = "Snippet", name = "plugin channel-map dev" } - -function factory () return function () - -- first track needs to be stereo and have a stereo plugin - -- (x42-eq with spectrum display, per channel processing, - -- and pre/post visualization is very handy here) - - function checksetup (r) - -- fail if Route ID 1 is not present or not stereo - assert (r and not r:isnil()) - assert (r:n_inputs():n_audio() == 2) - -- check first Plugin and make sure it is a "Plugin Insert" - if not r:nth_plugin(0):isnil() and not r:nth_plugin(0):to_insert():isnil() then return end - -- insert x42-eq at the top. - local proc = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#stereo", ARDOUR.PluginType.LV2, ""); - r:add_processor_by_index(proc, 0, nil, true) - end - - r = Session:get_remote_nth_route(1) - checksetup (r) - pi = r:nth_plugin(0):to_insert() - - pi:set_no_inplace (true) - - cm = ARDOUR.ChanMapping() - --cm:set(ARDOUR.DataType("Audio"), 0, 0) - cm:set(ARDOUR.DataType("Audio"), 1, 0) - pi:set_input_map (0, cm) - - cm = ARDOUR.ChanMapping() - --cm:set(ARDOUR.DataType("Audio"), 0, 0) - cm:set(ARDOUR.DataType("Audio"), 1, 0) - pi:set_output_map (0, cm) -end end diff --git a/scripts/s_fader_automation.lua b/scripts/s_fader_automation.lua deleted file mode 100644 index 3a9cb30eef..0000000000 --- a/scripts/s_fader_automation.lua +++ /dev/null @@ -1,58 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Fader Automation" } - -function factory () return function () - local playhead = Session:transport_sample () - local samplerate = Session:nominal_sample_rate () - - -- get selected tracks - rl = Editor:get_selection ().tracks:routelist () - - -- prepare undo operation - Session:begin_reversible_command ("Fancy Fade Out") - local add_undo = false -- keep track if something has changed - - -- iterate over selected tracks - for r in rl:iter () do - local ac = r:amp ():gain_control () -- ARDOUR:AutomationControl - local al = ac:alist () -- ARDOUR:AutomationList (state, high-level) - - -- set automation state to "Touch" - ac:set_automation_state (ARDOUR.AutoState.Touch) - - -- query the value at the playhead position - local g = al:eval (playhead) - - -- get state for undo - local before = al:get_state () - - -- delete all events after the playhead... - al:truncate_end (playhead) - - -- ...and generate some new ones. - for i=0,50 do - -- use a sqrt fade-out (the shape is recognizable, and otherwise - -- not be possible to achieve with existing ardour fade shapes) - al:add (playhead + i * samplerate / 50, - g * (1 - math.sqrt (i / 50)), - false, true) - end - - -- remove dense events - al:thin (20) - - -- save undo - local after = al:get_state () - Session:add_command (al:memento_command (before, after)) - add_undo = true - - ::out:: - end - - -- all done, commit the combined Undo Operation - if add_undo then - -- the 'nil' Commend here mean to use the collected diffs added above - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end -end end diff --git a/scripts/s_foreach_track.lua b/scripts/s_foreach_track.lua deleted file mode 100644 index c1c6ed8da1..0000000000 --- a/scripts/s_foreach_track.lua +++ /dev/null @@ -1,10 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Foreach Track" } - -function factory () return function () - for r in Session:get_tracks():iter() do - print (r:name()) - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Track - -- for available methods e.g. - r:set_active (true, nil) - end -end end diff --git a/scripts/s_group_color.lua b/scripts/s_group_color.lua deleted file mode 100644 index d8c0785ed7..0000000000 --- a/scripts/s_group_color.lua +++ /dev/null @@ -1,11 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Randomize Group Colors" } - -function factory () return function () - for grb in Session:route_groups ():iter () do - local r = math.random (0, 255) - local g = math.random (0, 255) - local b = math.random (0, 255) - local rgba = (r << 24) + (g << 16) + (b << 8) + 0xff - grp:set_rgba(rgba) - end -end end diff --git a/scripts/s_import_files.lua b/scripts/s_import_files.lua deleted file mode 100644 index 72cca29f8e..0000000000 --- a/scripts/s_import_files.lua +++ /dev/null @@ -1,14 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Import File(s) Example" } - -function factory (params) return function () - local files = C.StringVector(); - - files:push_back("/tmp/test.wav") - - local pos = -1 - Editor:do_import (files, - Editing.ImportDistinctFiles, Editing.ImportAsTrack, ARDOUR.SrcQuality.SrcBest, - ARDOUR.MidiTrackNameSource.SMFTrackName, ARDOUR.MidiTempoMapDisposition.SMFTempoIgnore, - pos, ARDOUR.PluginInfo()) - -end end diff --git a/scripts/s_plugin_automation.lua b/scripts/s_plugin_automation.lua deleted file mode 100644 index 2b04bae638..0000000000 --- a/scripts/s_plugin_automation.lua +++ /dev/null @@ -1,43 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Plugin automation" } - -function factory () return function () - -- query playhead position and session sample-rate - local playhead = Session:transport_sample () - local samplerate = Session:nominal_sample_rate () - - -- get Track/Bus with RID 3 - local r = Session:get_remote_nth_route(3) - -- make sure the track object exists - assert (not r:isnil ()) - - -- get AutomationList, ControlList and ParameterDescriptor - -- of the first plugin's first parameter - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI - local al, cl, pd = ARDOUR.LuaAPI.plugin_automation (r:nth_plugin (0), 0) - - if not al:isnil () then - print ("Parameter Range", pd.lower, pd.upper) - print ("Current value", cl:eval (playhead)) - - -- prepare undo operation - Session:begin_reversible_command ("Automatix") - -- remember current AutomationList state - local before = al:get_state() - - -- remove future automation - cl:truncate_end (playhead) - - -- add new data points after the playhead 1 sec, min..max - -- without guard-points, but with initial (..., false, true) - for i=0,10 do - cl:add (playhead + i * samplerate / 10, - pd.lower + math.sqrt (i / 10) * (pd.upper - pd.lower), - false, true) - end - - -- save undo - local after = al:get_state() - Session:add_command (al:memento_command(before, after)) - Session:commit_reversible_command (nil) - end -end end diff --git a/scripts/s_plugin_reorder.lua b/scripts/s_plugin_reorder.lua deleted file mode 100644 index 11fb0e056a..0000000000 --- a/scripts/s_plugin_reorder.lua +++ /dev/null @@ -1,23 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Plugin Order Reverse" } - -function factory () return function () - local sel = Editor:get_selection () - -- for each selected track/bus - for r in sel.tracks:routelist ():iter () do - print ("Route:", r:name ()) - local neworder = ARDOUR.ProcessorList(); -- create a PluginList - local i = 0; - repeat -- iterate over all plugins/processors - local proc = r:nth_processor (i) - if not proc:isnil () then - -- append plugin to list - neworder:push_back(proc) - end - i = i + 1 - until proc:isnil () - -- reverse list - neworder:reverse() - -- and set new order - r:reorder_processors (neworder, nil) - end -end end diff --git a/scripts/s_pluginutils.lua b/scripts/s_pluginutils.lua deleted file mode 100644 index e99bef7b93..0000000000 --- a/scripts/s_pluginutils.lua +++ /dev/null @@ -1,57 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Plugin Utils" } - -function factory () return function () - - ------------------------------------------------------------------------------- - -- List all Plugins - for p in ARDOUR.LuaAPI.list_plugins():iter() do - print (p.name, p.unique_id, p.type) - local psets = p:get_presets() - if not empty:empty() then - for pset in psets:iter() do - print (" - ", pset.label) - end - end - end - - ------------------------------------------------------------------------------- - -- add a Plugin (here LV2) to all mono tracks that contain the pattern "dru" - -- and load a plugin-preset (if it exists) - for r in Session:get_routes():iter() do - if (string.match (r:name(), "dru") and r:n_inputs():n_audio() == 1) then - local proc = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#mono", ARDOUR.PluginType.LV2, "cutbass"); - assert (not proc:isnil()) - r:add_processor_by_index(proc, 0, nil, true) - end - end - - - ------------------------------------------------------------------------------- - -- load a plugin preset - route = Session:get_remote_nth_route(2) - assert (route) - -- to 4th plugin (from top), ardour starts counting at zero - plugin = route:nth_plugin(3):to_insert():plugin(0) - assert (not plugin:isnil()) - ps = plugin:preset_by_label("cutbass") -- get preset by name - assert (ps) - print (ps.uri) - plugin:load_preset (ps) - - - ------------------------------------------------------------------------------- - -- add a LuaProcessor (here "Scope") to all tracks - for t in Session:get_tracks():iter() do - local pos = 0 -- insert at the top - - -- the following two lines are equivalent - --local proc = ARDOUR.LuaAPI.new_luaproc(Session, "a-Inline Scope"); - local proc = ARDOUR.LuaAPI.new_plugin (Session, "a-Inline Scope", ARDOUR.PluginType.Lua, ""); - assert (not proc:isnil()) - - t:add_processor_by_index(proc, pos, nil, true) - -- optionally set some parameters - ARDOUR.LuaAPI.set_processor_param (proc, 0, 5) -- timescale 5sec - end - -end end diff --git a/scripts/s_portengine.lua b/scripts/s_portengine.lua deleted file mode 100644 index ebca9ff696..0000000000 --- a/scripts/s_portengine.lua +++ /dev/null @@ -1,35 +0,0 @@ -ardour { ["type"] = "Snippet", name = "portengine" } -function factory () return function () - - local a = Session:engine() - print ("----- Port objects from Ardour's engine ----"); - _, t = a:get_ports (ARDOUR.DataType("audio"), ARDOUR.PortList()) - -- table 't' holds argument references. t[2] is the PortList - for p in t[2]:iter() do - local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true) - local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false) - print (p:name(), " -- Play lat.", lp[1].min, lp[1].max, "Capt lat.", lc[1].min, lc[1].max) - end - - print ("----- Port names queries from the backend ----"); - _, t = a:get_backend_ports ("", ARDOUR.DataType("audio"), 0, C.StringVector()) - -- table 't' holds argument references. t[4] is the StringVector - for n in t[4]:iter() do - print (n) - end - - print ("----- Connections from the backend ----"); - _, t = a:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput, C.StringVector()) - for n in t[4]:iter() do - local printed_name = false; - local _, ct = a:get_connections (n, C.StringVector()) - for c in ct[2]:iter() do - if (not printed_name) then - printed_name = true; - print (n) - end - print (" ->", c) - end - end - -end end diff --git a/scripts/s_region_gain.lua b/scripts/s_region_gain.lua deleted file mode 100644 index 648e76629c..0000000000 --- a/scripts/s_region_gain.lua +++ /dev/null @@ -1,82 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Set Region Gain" } - -function factory () return function () - -- get Editor GUI Selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- allocate a buffer (float* in C) - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm - local cmem = ARDOUR.DSP.DspShm (8192) - - -- prepare undo operation - Session:begin_reversible_command ("Lua Region Gain") - local add_undo = false -- keep track if something has changed - - -- iterate over selected regions - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- test if it's an audio region - if r:to_audioregion ():isnil () then - goto next - end - - -- to read the Region data, we use the Readable interface of the Region - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable - local rd = r:to_readable () - - local n_samples = rd:readable_length () - local n_channels = rd:n_channels () - - local peak = 0 -- the audio peak to be calculated - - -- iterate over all channels in Audio Region - for c = 0, n_channels -1 do - local pos = 0 - repeat - -- read at most 8K samples of channel 'c' starting at 'pos' - local s = rd:read (cmem:to_float (0), pos, 8192, c) - pos = pos + s - -- access the raw audio data - -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray - local d = cmem:to_float (0):array() - -- iterate over the audio sample data - for i = 0, s do - if math.abs (d[i]) > peak then - peak = math.abs (d[i]) - end - end - until s < 8192 - assert (pos == n_samples) - end - - if (peak > 0) then - print ("Region:", r:name (), "peak:", 20 * math.log (peak) / math.log(10), "dBFS") - else - print ("Region:", r:name (), " is silent") - end - - -- normalize region - if (peak > 0) then - -- prepare for undo - r:to_stateful ():clear_changes () - -- apply gain - r:to_audioregion (): set_scale_amplitude (1 / peak) - -- save changes (if any) to undo command - if not Session:add_stateful_diff_command (r:to_statefuldestructible ()):empty () then - add_undo = true - end - end - - ::next:: - end - - -- all done. now commit the combined undo operation - if add_undo then - -- the 'nil' command here means to use all collected diffs - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end - -end end diff --git a/scripts/s_region_gain2.lua b/scripts/s_region_gain2.lua deleted file mode 100644 index cec56ca412..0000000000 --- a/scripts/s_region_gain2.lua +++ /dev/null @@ -1,63 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Normalize Regions" } - -function factory () return function () - -- get Editor GUI Selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- prepare undo operation - Session:begin_reversible_command ("Lua Normalize") - local add_undo = false -- keep track if something has changed - - -- iterate over selected regions - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- test if it's an audio region - local ar = r:to_audioregion (); - if ar:isnil () then - goto next - end - - local peak = ar:maximum_amplitude (nil); - local rms = ar:rms (nil); - - if (peak > 0) then - print ("Region:", r:name (), "peak:", 20 * math.log (peak) / math.log(10), "dBFS") - print ("Region:", r:name (), "rms :", 20 * math.log (rms) / math.log(10), "dBFS") - else - print ("Region:", r:name (), " is silent") - end - - -- normalize region - if (peak > 0) then - -- prepare for undo - r:to_stateful ():clear_changes () - -- calculate gain. - local f_rms = rms / 10 ^ (.05 * -18) -- -18dBFS/RMS - local f_peak = peak / 10 ^ (.05 * -1) -- -1dbFS/peak - -- apply gain - if (f_rms > f_peak) then - print ("Region:", r:name (), "RMS normalized by:", -20 * math.log (f_rms) / math.log(10), "dB") - ar:set_scale_amplitude (1 / f_rms) - else - print ("Region:", r:name (), "peak normalized by:", -20 * math.log (f_peak) / math.log(10), "dB") - ar:set_scale_amplitude (1 / f_peak) - end - -- save changes (if any) to undo command - if not Session:add_stateful_diff_command (r:to_statefuldestructible ()):empty () then - add_undo = true - end - end - - ::next:: - end - - -- all done. now commit the combined undo operation - if add_undo then - -- the 'nil' command here means to use all collected diffs - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end - -end end diff --git a/scripts/s_replaceplugin.lua b/scripts/s_replaceplugin.lua deleted file mode 100644 index 4cef07c7ba..0000000000 --- a/scripts/s_replaceplugin.lua +++ /dev/null @@ -1,11 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Replace Plugin" } - -function factory () return function () - - route = Session:get_remote_nth_route(1) - old = route:nth_plugin(0) - new = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#stereo", ARDOUR.PluginType.LV2, ""); - route:replace_processor (old, new, nil) - old = nil new = nil -- explicitly drop references (unless they're local vars) - -end end diff --git a/scripts/s_selection.lua b/scripts/s_selection.lua deleted file mode 100644 index 5dd58a43c7..0000000000 --- a/scripts/s_selection.lua +++ /dev/null @@ -1,60 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Editor Selection" } - -function factory () return function () - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - -- the Ardour Selection can include multiple items - -- (regions, tracks, ranges, markers, automation, midi-notes etc) - local sel = Editor:get_selection () - - -- - -- At the point of writing the following data items are available - -- - - -- Range selection, total span of all ranges (0, 0 if no time range is selected) - if sel.time:start () < sel.time:end_sample () then - print ("Total Range:", sel.time:start (), sel.time:end_sample ()) - end - - -- Range selection, individual ranges. - for ar in sel.time:iter () do - -- each of the items is a - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioRange - print ("Range:", ar.id, ar.start, ar._end) - end - - -- Track/Bus Selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:TrackSelection - for r in sel.tracks:routelist ():iter () do - -- each of the items is a - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route - print ("Route:", r:name ()) - end - - -- Region selection - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection - for r in sel.regions:regionlist ():iter () do - -- each of the items is a - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region - print ("Region:", r:name ()) - end - - -- Markers - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:MarkerSelection - -- Note: Marker selection is not cleared and currently (Ardour-4.7) points - -- to the most recently selected marker. - for m in sel.markers:iter () do - -- each of the items is a - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOURUI::ArdourMarker - print ("Marker:", m:name (), m:position(), m:_type()) - end - - ---------------------------------------------------------- - -- The total time extents of all selected regions and ranges - local ok, ext = Editor:get_selection_extents (0, 0) - if ok then - print ("Selection Extents:", ext[1], ext[2]) - else - print ("No region or range is selected") - end - -end end diff --git a/scripts/s_showhide_track.lua b/scripts/s_showhide_track.lua deleted file mode 100644 index 289367643d..0000000000 --- a/scripts/s_showhide_track.lua +++ /dev/null @@ -1,23 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Show/Hide TimeAxisView" } - -function factory () return function () - -- get a route from the session by Presentation-Order - -- http://ardourman/lua-scripting/class_reference/#ARDOUR:Session - local route = Session:get_remote_nth_route(2) - assert (route) -- abort if it does not exist - print (route:name()) - - -- the GUI timeline representation of a Track/Bus is a "Route Time Axis View" Object - local rtav = Editor:rtav_from_route (route) -- lookup RTAV - - -- the show/hide state applies to any "Time Axis View", cast RTAV to TAV. - Editor:hide_track_in_display (rtav:to_timeaxisview(), false --[[true: only if selected; false: any]]) - - - -- look up the route named "Audio" - route = Session:route_by_name("Audio") - assert (route) -- abort if it does not exist - - Editor:show_track_in_display (Editor:rtav_from_route (route):to_timeaxisview(), false --[[move into view]]) - -end end diff --git a/scripts/s_thin_automation.lua b/scripts/s_thin_automation.lua deleted file mode 100644 index a86b6c71d0..0000000000 --- a/scripts/s_thin_automation.lua +++ /dev/null @@ -1,47 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Thin Fader Automation" } - --- --TODO-- --- For a fully fledged EditorAction this script should --- offer a dropdown to select automation of all paramaters --- (not just the fader) --- see scripts/midi_cc_to_automation.lua and --- scripts/mixer_settings_store.lua --- Thinning Area should also be a numeric-entry or slider - -function factory () return function () - -- get selected tracks - rl = Editor:get_selection ().tracks:routelist () - - -- prepare undo operation - Session:begin_reversible_command ("Thin Automation") - local add_undo = false -- keep track if something has changed - - -- iterate over selected tracks - for r in rl:iter () do - - -- get the Fader (aka "amp") control - local ac = r:amp ():gain_control () -- ARDOUR:AutomationControl - local al = ac:alist () -- ARDOUR:AutomationList - - -- get state for undo - local before = al:get_state () - - -- remove dense events - al:thin (50) -- threashold of area below curve - - -- save undo - local after = al:get_state () - Session:add_command (al:memento_command (before, after)) - add_undo = true - - ::out:: - end - - -- all done, commit the combined Undo Operation - if add_undo then - -- the 'nil' Commend here mean to use the collected diffs added above - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end -end end diff --git a/scripts/s_timecode.lua b/scripts/s_timecode.lua deleted file mode 100644 index 9f6601f903..0000000000 --- a/scripts/s_timecode.lua +++ /dev/null @@ -1,21 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Timecode" } - -function factory () return function () - - local samplerate = 48000 -- samples per second - - -- generic convert, explicitly provide Timecode (fps) and sample-rate - -- http://manual.ardour.org/lua-scripting/class_reference/#Timecode.TimecodeFormat - hh, mm, ss, ff = ARDOUR.LuaAPI.sample_to_timecode (Timecode.TimecodeFormat.TC25, samplerate, 1920) - print (ARDOUR.LuaAPI.sample_to_timecode (Timecode.TimecodeFormat.TC25, samplerate, 1920)) - - -- generic convert, explicitly provide Timecode (fps) and sample-rate - local s = ARDOUR.LuaAPI.timecode_to_sample (Timecode.TimecodeFormat.TC25, samplerate, 10, 11, 12, 13) - assert (25 * (10 * 3600 + 11 * 60 + 12 ) + 13 == s * 25 / samplerate) - - -- use session-settings: sample-rate and timecode format is taken from the - -- current session. Note that the sample-rate includes pull-up/down - print (Session:sample_to_timecode_lua (12345)) - print (Session:timecode_to_sample_lua (10, 11, 12, 13)) - -end end diff --git a/scripts/s_track_props.lua b/scripts/s_track_props.lua deleted file mode 100644 index ad983f8364..0000000000 --- a/scripts/s_track_props.lua +++ /dev/null @@ -1,48 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Track Properties" } - -function factory () return function () - --- iterate over all tracks - for t in Session:get_tracks():iter() do - -- t is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Track - - -- operate one those with "Drum" in the name - if (t:name ():find ("Drum")) then - - -- print the name, and number of audio in/out - -- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:ChanCount - print (t:name (), "| Audio In:", t:n_inputs ():n_audio (), "Audio Out:", t:n_outputs ():n_audio ()) - - -- get the track's http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:PresentationInfo - pi = t:presentation_info_ptr () - - -- set the track's color to orange - hex RGBA - pi:set_color (0xff8800ff) - - -- phase invert the 1st channel - t:phase_control():set_phase_invert (1, true) - - -- solo the track -- and only the track, not other tracks grouped with it. - -- - -- Note that changing solo/mute needs to propagate implicit solo/mute. - -- These changes have to be done atomically, so that all - -- related solo/mute change simultaneously at the same time. - -- This can only be done from realtime-context, so we need to queue a session-rt - -- event using the session realtime-event dispatch mechanism: - Session:set_control (t:solo_control(), 1, PBD.GroupControlDisposition.NoGroup) - - -- unmute the track, this also examplifies how one could use lists to modify - -- multiple controllables at the same time (they should be of the same - -- paramater type - e.g. mute_control() of multiple tracks, they'll all - -- change simultaneously in rt-context) - local ctrls = ARDOUR.ControlListPtr () - ctrls:push_back (t:mute_control()) -- we could add more controls to change via push_back - Session:set_controls (ctrls, 0, PBD.GroupControlDisposition.NoGroup) - - -- add a track comment - t:set_comment ("This is a Drum Track", nil) - - -- and set the fader to -7dB == 10 ^ (0.05 * -7) - t:gain_control():set_value (10 ^ (0.05 * -7), PBD.GroupControlDisposition.NoGroup) - end - end -end end diff --git a/scripts/s_vamp_plugin_index.lua b/scripts/s_vamp_plugin_index.lua deleted file mode 100644 index b559f57e89..0000000000 --- a/scripts/s_vamp_plugin_index.lua +++ /dev/null @@ -1,45 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Vamp Plugin List" } -function factory () return function () - - local plugins = ARDOUR.LuaAPI.Vamp.list_plugins (); - for id in plugins:iter () do - local vamp = ARDOUR.LuaAPI.Vamp(id, Session:nominal_sample_rate()) - local vp = vamp:plugin () - print (" --- VAMP Plugin ---") - print ("Id:", vp:getIdentifier ()) - print ("Name:", vp:getName ()) - print ("Description:", vp:getDescription ()) - - local progs = vp:getPrograms(); - if not progs:empty () then - print ("Preset(s):") - for p in progs:iter () do - print (" *", p) - end - end - - local params = vp:getParameterDescriptors () - if not params:empty () then - print ("Parameters(s):") - for p in params:iter () do - -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:PluginBase:ParameterDescriptor - print (" * Id:", p.identifier, "Name:", p.name, "Desc:", p.description) - local i = 0; for vn in p.valueNames:iter() do - print (" ^^ ", i, " -> ", vn) - i = i + 1 - end - end - end - - local feats = vp:getOutputDescriptors () - if not feats:empty () then - print ("Output(s):") - for p in feats:iter () do - -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:OutputDescriptor - print (" * Id:", p.identifier, "Name:", p.name, "Desc:", p.description) - end - end - - end -end end - diff --git a/scripts/s_whoami.lua b/scripts/s_whoami.lua deleted file mode 100644 index d391d65095..0000000000 --- a/scripts/s_whoami.lua +++ /dev/null @@ -1,22 +0,0 @@ -ardour { ["type"] = "Snippet", name = "Who Am I?" } - -function factory() return function() - -function whoami() - --pcall is the lua equivalent - --of try: ... catch: ... - if not pcall(function() local first_check = Session:get_mixbus(0) end) then - return "Ardour" - else - local second_check = Session:get_mixbus(11) - if second_check:isnil() then - return "Mixbus" - else - return "32C" - end - end -end - -print(whoami()) - -end end diff --git a/scripts/scope.lua b/scripts/scope.lua deleted file mode 100644 index 1952c100c4..0000000000 --- a/scripts/scope.lua +++ /dev/null @@ -1,229 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-Inline Scope", - category = "Visualization", - license = "MIT", - author = "Ardour Team", - description = [[Mixer strip inline waveform display]] -} - --- return possible i/o configurations -function dsp_ioconfig () - -- -1, -1 = any number of channels as long as input and output count matches - return { [1] = { audio_in = -1, audio_out = -1}, } -end - -function dsp_params () - return - { - { ["type"] = "input", name = "Timescale", min = .1, max = 5, default = 2, unit="sec", logarithmic = true }, - { ["type"] = "input", name = "Logscale", min = 0, max = 1, default = 0, toggled = true }, - { ["type"] = "input", name = "Height (Aspect)", min = 0, max = 3, default = 1, enum = true, scalepoints = - { - ["Min"] = 0, - ["16:10"] = 1, - ["1:1"] = 2, - ["Max"] = 3 - } - }, - } -end - - -function dsp_init (rate) - -- global variables (DSP part only) - samplerate = rate - bufsiz = 6 * rate - dpy_hz = rate / 25 - dpy_wr = 0 -end - -function dsp_configure (ins, outs) - -- store configuration in global variable - audio_ins = ins:n_audio () - -- allocate shared memory area - -- this is used to speed up DSP computaton (using a C array) - -- and to share data with the GUI - self:shmem ():allocate (4 + bufsiz * audio_ins) - self:shmem ():clear () - self:shmem ():atomic_set_int (0, 0) - local cfg = self:shmem ():to_int (1):array () - cfg[1] = samplerate - cfg[2] = bufsiz - cfg[3] = audio_ins -end - -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - local shmem = self:shmem () - local write_ptr = shmem:atomic_get_int (0) - - for c = 1,audio_ins do - -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0 - local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped input buffer for given cannel - local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped output buffer for given cannel - local chn_off = 4 + bufsiz * (c - 1) - if (ib ~= ARDOUR.ChanMapping.Invalid) then - if (write_ptr + n_samples < bufsiz) then - ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), n_samples) - else - local w0 = bufsiz - write_ptr; - ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), w0) - ARDOUR.DSP.copy_vector (shmem:to_float (chn_off) , bufs:get_audio (ib):data (offset + w0), n_samples - w0) - end - if (ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob) then - ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples) - end - else - if (write_ptr + n_samples < bufsiz) then - ARDOUR.DSP.memset (shmem:to_float (write_ptr + chn_off), 0, n_samples) - else - local w0 = bufsiz - write_ptr; - ARDOUR.DSP.memset (shmem:to_float (write_ptr + chn_off), 0, w0) - ARDOUR.DSP.memset (shmem:to_float (chn_off) , 0, n_samples - w0) - end - end - end - -- clear unconnected inplace buffers - for c = 1,audio_ins do - local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped input buffer for given cannel - local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped output buffer for given cannel - if (ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid) then - bufs:get_audio (ob):silence (n_samples, offset) - end - end - - write_ptr = (write_ptr + n_samples) % bufsiz - shmem:atomic_set_int (0, write_ptr) - - -- emit QueueDraw every FPS - dpy_wr = dpy_wr + n_samples - if (dpy_wr > dpy_hz) then - dpy_wr = dpy_wr % dpy_hz; - self:queue_draw () - end -end - - --- helper function for drawing symmetric grid -function gridline (ctx, x, xr, h, val) - ctx:move_to (math.floor (.5 + x + val * xr) -.5, 1) - ctx:line_to (math.floor (.5 + x + val * xr) -.5, h - 1) - ctx:stroke () - ctx:move_to (math.floor (.5 + x - val * xr) -.5, 1) - ctx:line_to (math.floor (.5 + x - val * xr) -.5, h - 1) - ctx:stroke () -end - -function render_inline (ctx, w, max_h) - local ctrl = CtrlPorts:array () -- get control port array (read/write) - local shmem = self:shmem () -- get shared memory region - local cfg = shmem:to_int (1):array () -- "cast" into lua-table - local rate = cfg[1] - local buf_size = cfg[2] - local n_chn = cfg[3] - - -- get settings - local timescale = ctrl[1] or 1.0 -- display size in seconds - local logscale = ctrl[2] or 0; logscale = logscale > 0 -- logscale - local hmode = ctrl[3] or 1 -- height mode - - -- calc height - if hmode == 0 then - h = math.ceil (w * 10 / 16) - if (h > 44) then - h = 44 - end - elseif (hmode == 2) then - h = w - elseif (hmode == 3) then - h = max_h - else - h = math.ceil (w * 10 / 16) - end - - if (h > max_h) then - h = max_h - end - - -- display settings - local spp = math.floor (timescale * rate / (h - 2)) -- samples per pixel - local spl = spp * (h - 1) -- total number of audio samples to read - local read_ptr = (shmem:atomic_get_int (0) + buf_size - spl - 1) % buf_size -- read pointer - local xr = math.ceil ((w - 2) * (0.47 / n_chn)) -- x-axis range (per channel) - - -- clear background - ctx:rectangle (0, 0, w, h) - ctx:set_source_rgba (.2, .2, .2, 1.0) - ctx:fill () - - -- prepare drawing - ctx:set_line_width (1.0) - local dash3 = C.DoubleVector () - dash3:add ({1, 3}) - local dash4 = C.DoubleVector () - dash4:add ({1, 4}) - - -- plot every channel - for c = 1,n_chn do - local x = math.floor ((w - 2) * (c - .5) / n_chn) + 1.5 -- x-axis center for given channel - - -- draw grid -- - ctx:set_source_rgba (.5, .5, .5, 1.0) - ctx:move_to (x, 1) ctx:line_to (x, h - 1) ctx:stroke () - - ctx:set_dash (dash4, 2) - ctx:set_source_rgba (.4, .4, .4, 1.0) - if (logscale) then - gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-18)) - gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-6)) - ctx:set_dash (dash3, 2) - ctx:set_source_rgba (.5, .1, .1, 1.0) - gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-3)) - else - gridline (ctx, x, xr, h, .1258) - gridline (ctx, x, xr, h, .5) - ctx:set_dash (dash3, 2) - ctx:set_source_rgba (.5, .1, .1, 1.0) - gridline (ctx, x, xr, h, .7079) - end - ctx:unset_dash () - ctx:set_source_rgba (.5, .1, .1, 0.7) - gridline (ctx, x, xr, h, 1) - - - -- prepare waveform display drawing - ctx:set_source_rgba (.8, .8, .8, .7) - ctx:save () - ctx:rectangle (math.floor (x - xr), 0, math.ceil (2 * xr), h) - ctx:clip () - - local chn_off = 4 + buf_size * (c - 1) - local buf_off = read_ptr; - - -- iterate over every y-axis pixel - for y = 1, h - 1 do - local s_min = 0 - local s_max = 0 - -- calc min/max values for given range - if (buf_off + spp < buf_size) then - _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, spp)) - else - local r0 = buf_size - buf_off; - _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, r0)) - _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off) , s_min, s_max, spp - r0)) - end - buf_off = (buf_off + spp) % buf_size; - - if (logscale) then - s_max = ARDOUR.DSP.log_meter_coeff (s_max) - s_min = - ARDOUR.DSP.log_meter_coeff (-s_min) - end - - ctx:move_to (x + s_min * xr, h - y + .5) - ctx:line_to (x + s_max * xr, h - y + .5) - end - ctx:stroke () - ctx:restore () - end - return {w, h} -end diff --git a/scripts/select_every_2nd_region.lua b/scripts/select_every_2nd_region.lua deleted file mode 100644 index 30ad519483..0000000000 --- a/scripts/select_every_2nd_region.lua +++ /dev/null @@ -1,61 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Region Select/2", - license = "MIT", - author = "Ardour Team", - description = [[select every 2nd region on all selected tracks]] -} - --- select every 2nd region on all selected tracks -function factory () return function () - - local sl = ArdourUI.SelectionList () -- empty selection list - - local sel = Editor:get_selection () -- get current selection - -- for each selected track/bus.. - for route in sel.tracks:routelist ():iter () do - -- consider only tracks - local track = route:to_track () - if track:isnil() then - goto continue - end - - local skip = false; - -- iterate over all regions of the given track - for region in track:playlist():region_list():iter() do - if skip then - -- skip every 2nd region - skip = false; - else - skip = true; - -- get RegionView (GUI object to be selected) - local rv = Editor:regionview_from_region (region) - -- add it to the list of Objects to be selected - sl:push_back (rv); - end - end - ::continue:: - end - - -- set/replace current selection in the editor - Editor:set_selection (sl, ArdourUI.SelectionOp.Set); -end end - -function icon (params) return function (ctx, width, height, fg) - local wh = math.min (width, height) * .5 - ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) - - ctx:set_line_width (1) - ctx:rectangle (wh * .25, wh * .75, wh * 1.5 , .5 * wh) - ctx:set_source_rgba (0, 0, 0, 1) - ctx:stroke_preserve () - ctx:set_source_rgba (.9, .9, .9, 1) - ctx:fill () - - ctx:set_source_rgba (1, 0, 0, 1) - ctx:rectangle (.5 + math.ceil(wh * 0.25), .5 + math.ceil(wh * .75), math.floor(wh * .5) - 1, math.floor(.5 * wh) - 1) - ctx:stroke_preserve () - - ctx:rectangle (.5 + math.ceil(wh * 1.25), .5 + math.ceil(wh * .75), math.floor(wh * .5) - 1, math.floor(.5 * wh) - 1) - ctx:stroke_preserve () -end end diff --git a/scripts/send_to_bus.lua b/scripts/send_to_bus.lua deleted file mode 100644 index 95a13f95ca..0000000000 --- a/scripts/send_to_bus.lua +++ /dev/null @@ -1,41 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Send Tracks to Bus", - license = "MIT", - author = "Ardour Team", - description = [[Create a Bus and add aux-sends from all selected tracks]] -} - -function factory () return function () - -- find number of channels to use for the new bus, follow master-bus' inputs - local chn = 2 - local mst = Session:master_out (); - if not mst:isnil () then - chn = mst:n_inputs ():n_audio () - end - mst = nil -- explicitly drop reference - - local sel = Editor:get_selection () -- get selection - local tracks = ARDOUR.RouteListPtr () -- create a new list - - -- find selected *tracks*, add to tracks list - for r in sel.tracks:routelist ():iter () do - if not r:to_track ():isnil () then - tracks:push_back (r) - end - end - - if tracks:size () > 0 then - local bus = Session:new_audio_route (chn, chn, nil, 1, "", ARDOUR.PresentationInfo.Flag.AudioBus, ARDOUR.PresentationInfo.max_order) - if bus:size () > 0 then - Session:add_internal_sends (bus:front (), ARDOUR.Placement.PostFader, tracks); - end - end -end end - -function icon (params) return function (ctx, width, height, fg) - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (math.min (width, height) * .5) .. "px") - txt:set_text ("\u{2192}B") -- "->B" - local tw, th = txt:get_pixel_size () - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/session_template_advanced.lua b/scripts/session_template_advanced.lua deleted file mode 100644 index 1eec0349ce..0000000000 --- a/scripts/session_template_advanced.lua +++ /dev/null @@ -1,59 +0,0 @@ -ardour { - ["type"] = "SessionInit", - name = "Advanced Session", - description = [[Allows to configure master-bus and autoconnect]], - master_bus = 0 -} - -function factory () return function () - - local auto_connect_in = { - [0] = "Manually", - [1] = "automatically to physical inputs", - } - - local auto_connect_out = { - [0] = "Manually", - [1] = "automatically to physical outputs", - [2] = "automatically to master bus", - } - - local dialog_options = { - { type = "heading", title = "Customize Session: " .. Session:name () }, - { type = "number", key = "master", title = "Master bus channels", min = 0, max = 24, step = 1, digits = 0, default = 2 }, - { type = "checkbox", key = "monitor", title = "Add monitor section", default = ARDOUR.config():get_use_monitor_bus () }, - { type = "dropdown", key = "ac_input", title = "Autoconnect Inputs", - values = { - [auto_connect_in[0]] = 0, - [auto_connect_in[1]] = 1, - }, - default = auto_connect_in[ARDOUR.config():get_input_auto_connect ()] - }, - { type = "dropdown", key = "ac_output", title = "Autoconnect Outputs", - values = { - [auto_connect_out[0]] = 0, - [auto_connect_out[1]] = 1, - [auto_connect_out[2]] = 2, - }, - default = auto_connect_out[ARDOUR.config():get_output_auto_connect ()] - }, - } - - local dlg = LuaDialog.Dialog ("Template Setup", dialog_options) - local rv = dlg:run() - if (not rv) then return end - - if rv['master'] > 0 then - local count = ARDOUR.ChanCount ( ARDOUR.DataType("audio"), rv['master']) - Session:add_master_bus (count) - end - - if rv['monitor'] then - ARDOUR.config():set_use_monitor_bus (true) - end - - ARDOUR.config():set_input_auto_connect (rv['ac_input']) - ARDOUR.config():set_output_auto_connect (rv['ac_output']) - - Session:save_state(""); -end end diff --git a/scripts/session_template_record.lua b/scripts/session_template_record.lua deleted file mode 100644 index 9656fd1473..0000000000 --- a/scripts/session_template_record.lua +++ /dev/null @@ -1,60 +0,0 @@ -ardour { - ["type"] = "SessionInit", - name = "Recording Session", - description = [[Add as many mono tracks to the new session as there are physical audio inputs and optionally record-arm them.]] -} - ----- For use with templates: Session Template setup-hook --- --- If a script named 'template.lua' exists in a session-template folder --- the function produced by the 'factory' function of the script is called --- once after creating the session from the template. --- --- (e.g. ~/.config/ardour5/templates/Template-Name/template.lua) --- --- ----- For use as meta-session (specic session-setup scripts) --- --- Every Lua script in the script-folder of type "SessionInit" --- is listed as implicit template in the new-session dialog. --- The function produced by the scripts `factory` function is called --- once after creating a new, empty session. --- ----- For use as meta-session (general purpose Actions) --- --- In some cases normal action scripts can also serve as session-setup --- To include those ActionScripts in the template-list the script needs --- to implement an additional function --- function session_setup () return true end; --- The script's factory will be called without any parameters - -function factory () return function () - local e = Session:engine() - -- from the engine's POV readable/capture ports are "outputs" - local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector()) - -- table 't' holds argument references. t[4] is the C.StringVector (return value) - local tracks = t[4]:size(); - - local dialog_options = { - { type = "heading", title = "Customize Session: " .. Session:name () }, - { type = "number", key = "tracks", title = "Create Tracks", min = 1, max = 128, step = 1, digits = 0, default = tracks }, - { type = "checkbox", key = "recarm", default = false, title = "Record Arm Tracks" }, - } - - local dlg = LuaDialog.Dialog ("Template Setup", dialog_options) - local rv = dlg:run() - if (not rv or rv['tracks'] == 0) then - return - end - - -- create tracks - local tl = Session:new_audio_track (1, 2, nil, rv['tracks'], "", ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal) - -- and optionally record-arm them - if rv['recarm'] then - for track in tl:iter() do - track:rec_enable_control ():set_value (1, PBD.GroupControlDisposition.NoGroup) - end - end - - Session:save_state(""); -end end diff --git a/scripts/set_automation_mode.lua b/scripts/set_automation_mode.lua deleted file mode 100644 index ee99ac1a1b..0000000000 --- a/scripts/set_automation_mode.lua +++ /dev/null @@ -1,83 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Engage Automation", - author = "Ardour Team", - description = [[Set automation mode of various controls (fader, trim, mute, pan), for all selected tracks.]] -} - -function factory() return function() - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.AutoState - local auto_state = ARDOUR.AutoState.Touch - local with_plugins = true - - -- Ask user which mode to use, and whether to include plugins - local dialog_options = { - { type = "label", align="left", title = "Select automation state to apply all selected tracks:" }, - { - type = "dropdown", key = "as", title="", values = - { - ["Manual"] = ARDOUR.AutoState.Off, - ["Touch"] = ARDOUR.AutoState.Touch, - ["Write"] = ARDOUR.AutoState.Write, - ["Play"] = ARDOUR.AutoState.Play, - }, - default = "Touch" - }, - { type = "checkbox", key = "plug", default = true, title = "Also set plugin controls" } - } - local rv = LuaDialog.Dialog ("Select Automation State", dialog_options):run() - if not rv then return end - auto_state = rv['as'] - with_plugins = rv['plug'] - - -- helper function to check if given ARDOUR:AutomationControl exists - function maybe_set_automation_state (ac) - if not ac:isnil() then - ac:set_automation_state (auto_state) - end - end - - -- helper function to iterate over all automatable parameters of a plugin - function set_plugin_control_mode (pi) - local pc = pi:plugin (0):parameter_count() - for c = 0, pc do - local ac = pi:to_automatable():automation_control (Evoral.Parameter (ARDOUR.AutomationType.PluginAutomation, 0, c), false) - if not ac:isnil () then - ac:set_automation_state (auto_state) - end - end - end - - -- get selected tracks - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - if sel.tracks:routelist ():empty() then - LuaDialog.Message ("Select Automation State", "No Tracks are selected.", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () - return - end - - -- iterate over selected tracks - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:TrackSelection - for r in sel.tracks:routelist ():iter () do - -- r is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route - -- which interhits from ARDOUR:Stripable - - -- set route's direct control - r:gain_control ():set_automation_state (auto_state) - maybe_set_automation_state (r:trim_control ()) - maybe_set_automation_state (r:mute_control ()) - maybe_set_automation_state (r:pan_azimuth_control ()) - maybe_set_automation_state (r:pan_width_control ()) - - -- for every plugin - local i = 0 - while with_plugins do - local proc = r:nth_plugin (i) - if proc:isnil () then break end - set_plugin_control_mode (proc:to_insert ()) - i = i + 1 - end - - end -end end diff --git a/scripts/singen.lua b/scripts/singen.lua deleted file mode 100644 index 8b22c1257b..0000000000 --- a/scripts/singen.lua +++ /dev/null @@ -1,94 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "SinGen", - category = "Instrument", - license = "MIT", - author = "Ardour Team", - description = [[Sine Wave Generator (v1.2)]] -} - -local lpf = 0 - -function dsp_params () - return - { - { ["type"] = "input", name = "Frequency", min = 20, max = 20000, default = 1000, unit="Hz", logarithmic = true }, - { ["type"] = "input", name = "Gain", min = -90, max = 0, default = -18, unit="dB" }, - } -end - -function dsp_ioconfig () - return { [1] = { audio_in = -1, audio_out = -1}, } -end - -function dsp_init (rate) - r = rate - lpf = 2048 / rate -end - -function low_pass_filter_param(old, new, limit) - if math.abs (old - new) < limit then - return new - else - return old + lpf * (new - old) - end -end - -local p = 0 -local fo = 0 -local ao = 0 - -function dsp_run (ins, outs, n_samples) - local ctrl = CtrlPorts:array() --call parameters - - local a = {} --init array - local f = ctrl[1] or 1000 - local amp = low_pass_filter_param(ao, ARDOUR.DSP.dB_to_coefficient(ctrl[2]), 0.02) - local inc = f / r - - for s = 1, n_samples do --fill table with fragments of a sine wave - p = p + inc - a[s] = amp * math.sin(p * (2 * math.pi)) - end - - for c = 1,#outs do - outs[c]:set_table(a, n_samples) --passes array into buffer - end - - if (f ~= fo) or (a ~= ao) then - self:queue_draw() - end - fo = f - ao = amp -end - -function render_inline (ctx, w, max_h) --inline display - local ctrl = CtrlPorts:array() - h = 30 - p = 0 - inc = 1/w - f = ctrl[1] / 1000 - if f < 0.5 then f = 0.5 end - if f > 8 then f = 8 end - - --draw rectangle - ctx:rectangle(0, 0, w, h) - ctx:set_source_rgba(0, 0, 0, 1.0) - ctx:fill() - ctx:set_line_width(1.5) - ctx:set_source_rgba(0.8, 0.8, 0.8, 1.0) - - l_x = 0 - l_y = 0 - for x = 0,w do - y = ARDOUR.DSP.dB_to_coefficient(ctrl[2]) * math.sin(f * (2 * math.pi * (p))) - yc = 0.5 * h + ((-0.5 * h) * y) - ctx:move_to (x, yc + 3) - ctx:line_to (l_x, l_y + 3) - l_x = x - l_y = yc - ctx:stroke() - p = p + inc - end - return {w, h + 6} -end \ No newline at end of file diff --git a/scripts/spectrogram.lua b/scripts/spectrogram.lua deleted file mode 100644 index 86419682b4..0000000000 --- a/scripts/spectrogram.lua +++ /dev/null @@ -1,363 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "a-Inline Spectrogram", - category = "Visualization", - license = "MIT", - author = "Ardour Team", - description = [[Mixer strip inline spectrum display]] -} - --- return possible i/o configurations -function dsp_ioconfig () - -- -1, -1 = any number of channels as long as input and output count matches - return { [1] = { audio_in = -1, audio_out = -1}, } -end - -function dsp_params () - return - { - { ["type"] = "input", name = "Logscale", min = 0, max = 1, default = 0, toggled = true }, - { ["type"] = "input", name = "1/f scale", min = 0, max = 1, default = 1, toggled = true }, - { ["type"] = "input", name = "FFT Size", min = 0, max = 4, default = 3, enum = true, scalepoints = - { - ["512"] = 0, - ["1024"] = 1, - ["2048"] = 2, - ["4096"] = 3, - ["8192"] = 4, - } - }, - { ["type"] = "input", name = "Height (Aspect)", min = 0, max = 3, default = 1, enum = true, scalepoints = - { - ["Min"] = 0, - ["16:10"] = 1, - ["1:1"] = 2, - ["Max"] = 3 - } - }, - { ["type"] = "input", name = "Range", min = 20, max = 160, default = 60, unit="dB"}, - { ["type"] = "input", name = "Offset", min = -40, max = 40, default = 0, unit="dB"}, - } -end - --- symbolic names for shmem offsets -local SHMEM_RATE = 0 -local SHMEM_WRITEPTR = 1 -local SHMEM_AUDIO = 2 - --- a C memory area. --- It needs to be in global scope. --- When the variable is set to nil, the allocated memory is free()ed. --- the memory can be interpeted as float* for use in DSP, or read/write --- to a C++ Ringbuffer instance. --- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm -local cmem = nil - -function dsp_init (rate) - -- global variables (DSP part only) - dpy_hz = rate / 25 - dpy_wr = 0 - - -- create a shared memory area to hold the sample rate, the write_pointer, - -- and (float) audio-data. Make it big enough to store 2s of audio which - -- should be enough. If not, the DSP will overwrite the oldest data anyway. - self:shmem ():allocate(2 + 2 * rate) - self:shmem ():clear() - self:shmem ():atomic_set_int (SHMEM_RATE, rate) - self:shmem ():atomic_set_int (SHMEM_WRITEPTR, 0) - - -- allocate memory, local mix buffer - cmem = ARDOUR.DSP.DspShm (8192) -end - --- "dsp_runmap" uses Ardour's internal processor API, eqivalent to --- 'connect_and_run()". There is no overhead (mapping, translating buffers). --- The lua implementation is responsible to map all the buffers directly. -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - -- here we sum all audio input channels and then copy the data to a - -- custom-made circular table for the GUIs to process later - - local audio_ins = in_map:count (): n_audio () -- number of audio input buffers - local ccnt = 0 -- processed channel count - local mem = cmem:to_float(0) -- a "FloatArray", float* for direct C API usage from the previously allocated buffer - local rate = self:shmem ():atomic_get_int (SHMEM_RATE) - local write_ptr = self:shmem ():atomic_get_int (SHMEM_WRITEPTR) - - local ringsize = 2 * rate - local ptr_wrap = math.floor(2^50 / ringsize) * ringsize - - for c = 1,audio_ins do - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:ChanMapping - -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0 - local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped input buffer - local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped output buffer - - -- check if the input is connected to a buffer - if (ib ~= ARDOUR.ChanMapping.Invalid) then - - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioBuffer - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP - if c == 1 then - -- first channel, copy as-is - ARDOUR.DSP.copy_vector (mem, bufs:get_audio (ib):data (offset), n_samples) - else - -- all other channels, add to existing data. - ARDOUR.DSP.mix_buffers_no_gain (mem, bufs:get_audio (ib):data (offset), n_samples) - end - ccnt = ccnt + 1; - - -- copy data to output (if not processing in-place) - if (ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob) then - ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples) - end - end - end - - -- Clear unconnected output buffers. - -- In case we're processing in-place some buffers may be identical, - -- so this must be done *after processing*. - for c = 1,audio_ins do - local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) - local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) - if (ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid) then - bufs:get_audio (ob):silence (n_samples, offset) - end - end - - -- Normalize gain (1 / channel-count) - if ccnt > 1 then - ARDOUR.DSP.apply_gain_to_buffer (mem, n_samples, 1 / ccnt) - end - - -- if no channels were processed, feed silence. - if ccnt == 0 then - ARDOUR.DSP.memset (mem, 0, n_samples) - end - - -- write data to the circular table - if (write_ptr % ringsize + n_samples < ringsize) then - ARDOUR.DSP.copy_vector (self:shmem ():to_float (SHMEM_AUDIO + write_ptr % ringsize), mem, n_samples) - else - local chunk = ringsize - write_ptr % ringsize - ARDOUR.DSP.copy_vector (self:shmem ():to_float (SHMEM_AUDIO + write_ptr % ringsize), mem, chunk) - ARDOUR.DSP.copy_vector (self:shmem ():to_float (SHMEM_AUDIO), cmem:to_float (chunk), n_samples - chunk) - end - self:shmem ():atomic_set_int (SHMEM_WRITEPTR, (write_ptr + n_samples) % ptr_wrap) - - -- emit QueueDraw every FPS - -- TODO: call every FFT window-size worth of samples, at most every FPS - dpy_wr = dpy_wr + n_samples - if (dpy_wr > dpy_hz) then - dpy_wr = dpy_wr % dpy_hz - self:queue_draw () - end -end - ----------------------------------------------------------------- --- GUI - -local fft = nil -local read_ptr = 0 -local line = 0 -local img = nil -local fft_size = 0 -local last_log = false - - -function render_inline (ctx, w, max_h) - local ctrl = CtrlPorts:array () -- get control port array (read/write) - local rate = self:shmem ():atomic_get_int (SHMEM_RATE) - if not cmem then - cmem = ARDOUR.DSP.DspShm (0) - end - - -- get settings - local logscale = ctrl[1] or 0; logscale = logscale > 0 -- x-axis logscale - local pink = ctrl[2] or 0; pink = pink > 0 -- 1/f scale - local fftsizeenum = ctrl[3] or 3 -- fft-size enum - local hmode = ctrl[4] or 1 -- height mode enum - local dbrange = ctrl[5] or 60 - local gaindb = ctrl[6] or 0 - - local fftsize - if fftsizeenum == 0 then fftsize = 512 - elseif fftsizeenum == 1 then fftsize = 1024 - elseif fftsizeenum == 2 then fftsize = 2048 - elseif fftsizeenum == 4 then fftsize = 8192 - else fftsize = 4096 - end - - if fftsize ~= fft_size then - fft_size = fftsize - fft = nil - end - - if dbrange < 20 then dbrange = 20; end - if dbrange > 160 then dbrange = 160; end - if gaindb < -40 then dbrange = -40; end - if gaindb > 40 then dbrange = 40; end - - - if not fft then - fft = ARDOUR.DSP.FFTSpectrum (fft_size, rate) - cmem:allocate (fft_size) - end - - if last_log ~= logscale then - last_log = logscale - img = nil - line = 0 - end - - -- calc height - if hmode == 0 then - h = math.ceil (w * 10 / 16) - if (h > 44) then - h = 44 - end - elseif (hmode == 2) then - h = w - elseif (hmode == 3) then - h = max_h - else - h = math.ceil (w * 10 / 16) - end - if (h > max_h) then - h = max_h - end - - -- re-create image surface - if not img or img:get_width() ~= w or img:get_height () ~= h then - img = Cairo.ImageSurface (Cairo.Format.ARGB32, w, h) - line = 0 - end - local ictx = img:context () - - local bins = fft_size / 2 - 1 -- fft bin count - local bpx = bins / w -- bins per x-pixel (linear) - local fpb = rate / fft_size -- freq-step per bin - local f_e = rate / 2 / fpb -- log-scale exponent - local f_b = w / math.log (fft_size / 2) -- inverse log-scale base - local f_l = math.log (fft_size / rate) * f_b -- inverse logscale lower-bound - - local mem = cmem:to_float (0) - - local ringsize = 2 * rate - local ptr_wrap = math.floor(2^50 / ringsize) * ringsize - - local write_ptr - function read_space() - write_ptr = self:shmem ():atomic_get_int (SHMEM_WRITEPTR) - local space = (write_ptr - read_ptr + ptr_wrap) % ptr_wrap - if space > ringsize then - -- the GUI lagged too much and unread data was overwritten - -- jump to the oldest audio still present in the ringtable - read_ptr = write_ptr - ringsize - space = ringsize - end - return space - end - - while (read_space() >= fft_size) do - -- read one window from the circular table - if (read_ptr % ringsize + fft_size < ringsize) then - ARDOUR.DSP.copy_vector (mem, self:shmem ():to_float (SHMEM_AUDIO + read_ptr % ringsize), fft_size) - else - local chunk = ringsize - read_ptr % ringsize - ARDOUR.DSP.copy_vector (mem, self:shmem ():to_float (SHMEM_AUDIO + read_ptr % ringsize), chunk) - ARDOUR.DSP.copy_vector (cmem:to_float(chunk), self:shmem ():to_float (SHMEM_AUDIO), fft_size - chunk) - end - read_ptr = (read_ptr + fft_size) % ptr_wrap - - -- process one line - fft:set_data_hann (mem, fft_size, 0) - fft:execute () - - -- draw spectrum - assert (bpx >= 1) - - -- scroll - if line == 0 then line = h - 1; else line = line - 1; end - - -- clear this line - ictx:set_source_rgba (0, 0, 0, 1) - ictx:rectangle (0, line, w, 1) - ictx:fill () - - for x = 0, w - 1 do - local pk = 0 - local b0, b1 - if logscale then - -- 20 .. 20k - b0 = math.floor (f_e ^ (x / w)) - b1 = math.floor (f_e ^ ((x + 1) / w)) - else - b0 = math.floor (x * bpx) - b1 = math.floor ((x + 1) * bpx) - end - - if b1 >= b0 and b1 <= bins and b0 >= 0 then - for i = b0, b1 do - local level = gaindb + fft:power_at_bin (i, pink and i or 1) -- pink ? i : 1 - if level > -dbrange then - local p = (dbrange + level) / dbrange - if p > pk then pk = p; end - end - end - end - if pk > 0.0 then - if pk > 1.0 then pk = 1.0; end - ictx:set_source_rgba (ARDOUR.LuaAPI.hsla_to_rgba (.70 - .72 * pk, .9, .3 + pk * .4)); - ictx:rectangle (x, line, 1, 1) - ictx:fill () - end - end - end - - -- copy image surface - if line == 0 then - img:set_as_source (ctx, 0, 0) - ctx:rectangle (0, 0, w, h) - ctx:fill () - else - local yp = h - line - 1; - img:set_as_source (ctx, 0, yp) - ctx:rectangle (0, yp, w, line) - ctx:fill () - - img:set_as_source (ctx, 0, -line) - ctx:rectangle (0, 0, w, yp) - ctx:fill () - end - - - -- draw grid on top - function x_at_freq (f) - if logscale then - return f_l + f_b * math.log (f) - else - return 2 * w * f / rate; - end - end - - function grid_freq (f) - -- draw vertical grid line - local x = .5 + math.floor (x_at_freq (f)) - ctx:move_to (x, 0) - ctx:line_to (x, h) - ctx:stroke () - end - - -- draw grid on top - local dash3 = C.DoubleVector () - dash3:add ({1, 3}) - ctx:set_line_width (1.0) - ctx:set_dash (dash3, 2) -- dotted line - ctx:set_source_rgba (.5, .5, .5, .8) - grid_freq (100) - grid_freq (1000) - grid_freq (10000) - ctx:unset_dash () - - return {w, h} -end diff --git a/scripts/split_all_markers.lua b/scripts/split_all_markers.lua deleted file mode 100644 index 001c4ae432..0000000000 --- a/scripts/split_all_markers.lua +++ /dev/null @@ -1,122 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Marker Split", - license = "MIT", - author = "Ardour Team", - description = [[Split regions on selected tracks at all locations markers]] -} - -function factory (params) return function () - - local loc = Session:locations () -- all marker locations - - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection - local sel = Editor:get_selection () - - -- prepare undo operation - -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session - Session:begin_reversible_command ("Auto Region Split") - local add_undo = false -- keep track if something has changed - - -- Track/Bus Selection -- iterate over all Editor-GUI selected tracks - -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:TrackSelection - for r in sel.tracks:routelist ():iter () do - -- each of the items 'r' is-a - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route - - local track = r:to_track () -- see if it's a track - if track:isnil () then - -- if not, skip it - goto continue - end - - -- get track's playlist - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Playlist - local playlist = track:playlist () - - -- clear existing changes, prepare "diff" of state for undo - playlist:to_stateful ():clear_changes () - - -- iterate over all location markers - for l in loc:list ():iter () do - if l:is_mark() then - -- get all regions on the given track's playlist (may be stacked) - for reg in playlist:regions_at (l:start ()):iter () do - playlist:split_region (reg, ARDOUR.MusicSample (l:start(), 0)) - -- the above operation will invalidate the playlist's region list: - -- split creates 2 new regions. - -- - -- Hence this script does it the way it does: the inner-most loop - -- is over playlist-regions. - end - end - end - - -- collect undo data - if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then - -- is something has changed, we need to save it at the end. - add_undo = true - end - - ::continue:: - end - - -- all done, commit the combined Undo Operation - if add_undo then - -- the 'nil' Command here mean to use the collected diffs added above - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end - -end end - - --- render an icon for the toolbar action-button --- this is genrally square width == height. --- The background is set according to the theme (leave transparent when drawing). --- A foreground color is passed as parameter 'fg' --- --- ctx is-a http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context --- 2D vector graphics http://cairographics.org/ -function icon (params) return function (ctx, width, height, fg) - local mh = height - 3.5; - local m3 = width / 3; - local m6 = width / 6; - - ctx:set_line_width (.5) - - -- compare to gtk2_ardour/marker.cc "Marker" - ctx:set_source_rgba (.8, .8, .2, 1.0) - ctx:move_to (width / 2 - m6, 2) - ctx:rel_line_to (m3, 0) - ctx:rel_line_to (0, mh * 0.4) - ctx:rel_line_to (-m6, mh * 0.6) - ctx:rel_line_to (-m6, -mh * 0.6) - ctx:close_path () - ctx:fill_preserve () - ctx:set_source_rgba (.0, .0, .0, 1.0) - ctx:stroke () - - -- draw an arrow <--|--> on top, using the foreground color - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - ctx:set_line_width (1) - - ctx:move_to (width * .5, height * .4) - ctx:line_to (width * .5, height * .6) - ctx:stroke () - - ctx:move_to (2, height * .5) - ctx:line_to (width - 2, height * .5) - ctx:stroke () - - ctx:move_to (width - 2, height * .5) - ctx:rel_line_to (-m6, -m6) - ctx:rel_line_to (0, m3) - ctx:close_path () - ctx:fill () - - ctx:move_to (2, height * .5) - ctx:rel_line_to (m6, -m6) - ctx:rel_line_to (0, m3) - ctx:close_path () - ctx:fill () -end end diff --git a/scripts/stop_at_marker.lua b/scripts/stop_at_marker.lua deleted file mode 100644 index 35de3510c4..0000000000 --- a/scripts/stop_at_marker.lua +++ /dev/null @@ -1,49 +0,0 @@ -ardour { - ["type"] = "session", - name = "Stop at Marker", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An example session script which stops the transport on every location marker when rolling forward.]] -} - -function factory () - return function (n_samples) - if (not Session:transport_rolling ()) then - -- not rolling, nothing to do. - return - end - - local pos = Session:transport_sample () -- current playhead position - local loc = Session:locations () -- all marker locations - - -- find first marker after the current playhead position, ignore loop + punch ranges - -- (this only works when rolling forward, to extend this example see - -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Locations ) - -- - local m = loc:first_mark_after (pos, false) - - if (m == -1) then - -- no marker was found - return - end - - -- due to `first_mark_after(pos)` "m" is always > "pos": - -- assert(pos < m) - -- - -- This callback happens from within the process callback: - -- - -- this cycle's end = next cycle start = pos + n_samples. - -- - -- Note that if "m" is exactly at cycle's end, that marker - -- will be at "pos" in the next cycle. Since we ask for - -- "first_mark_after pos", the marker would not be found. - -- - -- So even though "pos + n_samples" is barely reached, - -- we need to stop at "m" in the cycle that crosses or ends at "m". - if (pos + n_samples >= m) then - -- asking to locate to "m" ensures that playback continues at "m" - -- and the same marker will not be taken into account. - Session:request_locate (m, ARDOUR.LocateTransportDisposition.MustStop, ARDOUR.TransportRequestSource.TRS_Engine) - end - end -end diff --git a/scripts/store_recall_mixer.lua b/scripts/store_recall_mixer.lua deleted file mode 100644 index 53562b7ee2..0000000000 --- a/scripts/store_recall_mixer.lua +++ /dev/null @@ -1,575 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Mixer Store", - author = "Ardour Lua Taskforce", - description = [[ - Stores the current Mixer state as a file - that can be read and recalled arbitrarily. - Supports: processor settings, grouping, - mute, solo, gain, trim, pan and processor ordering, - plus re-adding certain deleted plugins. - ]] -} - -function factory() return function() - - local invalidate = {} - local path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") - - function mismatch_dialog(mismatch_str, checkbox_str) - --string.format("Track didn't match ID: %d, but did match track in session: %s", 999, 'track') - local dialog = { - { type = "label", colspan = 5, title = mismatch_str }, - { type = "checkbox", col=1, colspan = 1, key = "use", default = true, title = checkbox_str }, - } - local mismatch_return = LuaDialog.Dialog("", dialog):run() - if mismatch_return then - return mismatch_return['use'] - else - return false - end - end - - function get_processor_by_name(track, name) - local i = 0 - local proc = track:nth_processor(i) - repeat - if(proc:display_name() == name) then - return proc - else - i = i + 1 - end - proc = track:nth_processor(i) - until proc:isnil() - end - - function new_plugin(name) - for x = 0, 6 do - local plugin = ARDOUR.LuaAPI.new_plugin(Session, name, x, "") - if not(plugin:isnil()) then return plugin end - end - end - - function group_by_id(id) - local id = tonumber(id) - for g in Session:route_groups():iter() do - local group_id = tonumber(g:to_stateful():id():to_s()) - if group_id == id then return g end - end - end - - function group_by_name(name) - for g in Session:route_groups():iter() do - if g:name() == name then return g end - end - end - - function route_groupid_interrogate(t) - local group = false - for g in Session:route_groups():iter() do - for r in g:route_list():iter() do - if r:name() == t:name() then group = g:to_stateful():id():to_s() end - end - end return group - end - - function route_group_interrogate(t) - for g in Session:route_groups():iter() do - for r in g:route_list():iter() do - if r:name() == t:name() then return g end - end - end - end - - function empty_last_store() --empty current file from last run - local file = io.open(path, "w") - file:write("") - file:close() - end - - function mark_tracks(selected) - - empty_last_store() - - local route_string = [[instance = { - route_id = %d, - route_name = '%s', - gain_control = %f, - trim_control = %f, - pan_control = %s, - muted = %s, - soloed = %s, - order = {%s}, - cache = {%s}, - group = %s, - group_name = '%s' - }]] - - local group_string = [[instance = { - group_id = %s, - name = '%s', - routes = {%s}, - }]] - - local processor_string = [[instance = { - plugin_id = %d, - display_name = '%s', - owned_by_route_name = '%s', - owned_by_route_id = %d, - parameters = {%s}, - active = %s, - }]] - - local group_route_string = " [%d] = %s," - local proc_order_string = " [%d] = %d," - local proc_cache_string = " [%d] = '%s'," - local params_string = " [%d] = %f," - - --ensure easy-to-read formatting doesn't make it through - local route_string = string.gsub(route_string, "[\n\t]", "") - local group_string = string.gsub(group_string, "[\n\t]", "") - local processor_string = string.gsub(processor_string, "[\n\t]", "") - - local sel = Editor:get_selection () - local groups_to_write = {} - local i = 0 - - local tracks = Session:get_routes() - - if selected then tracks = sel.tracks:routelist() end - - for r in tracks:iter() do - local group = route_group_interrogate(r) - if group then - local already_there = false - for _, v in pairs(groups_to_write) do - if group == v then - already_there = true - end - end - if not(already_there) then - groups_to_write[#groups_to_write + 1] = group - end - end - end - - for _, g in pairs(groups_to_write) do - local tmp_str = "" - for t in g:route_list():iter() do - tmp_str = tmp_str .. string.format(group_route_string, i, t:to_stateful():id():to_s()) - i = i + 1 - end - local group_str = string.format( - group_string, - g:to_stateful():id():to_s(), - g:name(), - tmp_str - ) - - file = io.open(path, "a") - file:write(group_str, "\r\n") - file:close() - end - - for r in tracks:iter() do - if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes - - local order = ARDOUR.ProcessorList() - local x = 0 - repeat - local proc = r:nth_processor(x) - if not proc:isnil() then - order:push_back(proc) - end - x = x + 1 - until proc:isnil() - - local route_group = route_group_interrogate(r) - if route_group then route_group = route_group:name() else route_group = "" end - local rid = r:to_stateful():id():to_s() - local pan = r:pan_azimuth_control() - if pan:isnil() then pan = false else pan = pan:get_value() end --sometimes a route doesn't have pan, like the master. - - local order_nmbr = 0 - local tmp_order_str, tmp_cache_str = "", "" - for p in order:iter() do - local pid = p:to_stateful():id():to_s() - if not(string.find(p:display_name(), "latcomp")) then - tmp_order_str = tmp_order_str .. string.format(proc_order_string, order_nmbr, pid) - tmp_cache_str = tmp_cache_str .. string.format(proc_cache_string, pid, p:display_name()) - end - order_nmbr = order_nmbr + 1 - end - - local route_str = string.format( - route_string, - rid, - r:name(), - r:gain_control():get_value(), - r:trim_control():get_value(), - tostring(pan), - r:muted(), - r:soloed(), - tmp_order_str, - tmp_cache_str, - route_groupid_interrogate(r), - route_group - ) - - file = io.open(path, "a") - file:write(route_str, "\n") - file:close() - - local i = 0 - while true do - local params = {} - local proc = r:nth_plugin (i) - if proc:isnil () then break end - local active = proc:active() - local id = proc:to_stateful():id():to_s() - local plug = proc:to_insert ():plugin (0) - local n = 0 -- count control-ports - for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters - if plug:parameter_is_control (j) then - local label = plug:parameter_label (j) - if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then - local _, _, pd = ARDOUR.LuaAPI.plugin_automation(proc, n) - local val = ARDOUR.LuaAPI.get_processor_param(proc, j, true) - --print(r:name(), "->", proc:display_name(), label, val) - params[n] = val - end - n = n + 1 - end - end - i = i + 1 - - local tmp_params_str = "" - for k, v in pairs(params) do - tmp_params_str = tmp_params_str .. string.format(params_string, k, v) - end - - local proc_str = string.format( - processor_string, - id, - proc:display_name(), - r:name(), - r:to_stateful():id():to_s(), - tmp_params_str, - active - ) - file = io.open(path, "a") - file:write(proc_str, "\n") - file:close() - end - ::nextroute:: - end - end - - function recall(debug, dry_run) - local file = io.open(path, "r") - assert(file, "File not found!") - local bypass_routes = {} - - local i = 0 - for l in file:lines() do - --print(i, l) - - local exec_line = dry_run["dothis-"..i] - local skip_line = false - if not(exec_line == nil) and not(exec_line) then - skip_line = true - end - - local plugin, route, group = false, false, false - local f = load(l) - - if debug then - print(i, string.sub(l, 0, 29), f) - end - - if f then f() end - - if instance["route_id"] then route = true end - if instance["plugin_id"] then plugin = true end - if instance["group_id"] then group = true end - - if group then - if skip_line then goto nextline end - - local g_id = instance["group_id"] - local routes = instance["routes"] - local name = instance["name"] - local group = group_by_id(g_id) - if not(group) then - local group = Session:new_route_group(name) - for _, v in pairs(routes) do - local rt = Session:route_by_id(PBD.ID(v)) - if rt:isnil() then rt = Session:route_by_name(name) end - if not(rt:isnil()) then group:add(rt) end - end - end - end - - if route then - local substitution = tonumber(dry_run["destination-"..i]) - if skip_line or (substitution == 0) then - bypass_routes[#bypass_routes + 1] = instance["route_id"] - goto nextline - end - - local old_order = ARDOUR.ProcessorList() - local route_id = instance["route_id"] - local r_id = PBD.ID(instance["route_id"]) - local muted, soloed = instance["muted"], instance["soloed"] - local order = instance["order"] - local cache = instance["cache"] - local group = instance["group"] - local group_name = instance["group_name"] - local name = instance["route_name"] - local gc, tc, pc = instance["gain_control"], instance["trim_control"], instance["pan_control"] - - if not(substitution == instance["route_id"]) then - print('SUBSTITUTION FOR: ', name, substitution, Session:route_by_id(PBD.ID(substitution)):name()) - --bypass_routes[#bypass_routes + 1] = route_id - was_subbed = true - r_id = PBD.ID(substitution) - end - - local rt = Session:route_by_id(r_id) - if rt:isnil() then rt = Session:route_by_name(name) end - if rt:isnil() then goto nextline end - - local cur_group_id = route_groupid_interrogate(rt) - if not(group) and (cur_group_id) then - local g = group_by_id(cur_group_id) - if g then g:remove(rt) end - end - - local rt_group = group_by_name(group_name) - if rt_group then rt_group:add(rt) end - - well_known = {'PRE', 'Trim', 'EQ', 'Comp', 'Fader', 'POST'} - - for k, v in pairs(order) do - local proc = Session:processor_by_id(PBD.ID(1)) - if not(was_subbed) then - proc = Session:processor_by_id(PBD.ID(v)) - end - if proc:isnil() then - for id, name in pairs(cache) do - if v == id then - proc = new_plugin(name) - for _, control in pairs(well_known) do - if name == control then - proc = get_processor_by_name(rt, control) - invalidate[v] = proc:to_stateful():id():to_s() - goto nextproc - end - end - if not(proc) then goto nextproc end - if not(proc:isnil()) then - rt:add_processor_by_index(proc, 0, nil, true) - invalidate[v] = proc:to_stateful():id():to_s() - end - end - end - end - ::nextproc:: - if proc and not(proc:isnil()) then old_order:push_back(proc) end - end - rt:reorder_processors(old_order, nil) - if muted then rt:mute_control():set_value(1, 1) else rt:mute_control():set_value(0, 1) end - if soloed then rt:solo_control():set_value(1, 1) else rt:solo_control():set_value(0, 1) end - rt:gain_control():set_value(gc, 1) - rt:trim_control():set_value(tc, 1) - if pc ~= false then rt:pan_azimuth_control():set_value(pc, 1) end - end - - if plugin then - if skip_line then goto nextline end - - --if the plugin is owned by a route - --we decided not to use, skip it - for _, v in pairs(bypass_routes) do - if instance["owned_by_route_id"] == v then - goto nextline - end - end - - local enable = {} - local params = instance["parameters"] - local p_id = instance["plugin_id"] - local act = instance["active"] - - for k, v in pairs(invalidate) do --invalidate any deleted plugin's id - if p_id == k then - p_id = v - end - end - - local proc = Session:processor_by_id(PBD.ID(p_id)) - if proc:isnil() then goto nextline end - local plug = proc:to_insert():plugin(0) - - for k, v in pairs(params) do - local label = plug:parameter_label(k) - if string.find(label, "Assign") or string.find(label, "Enable") then --@ToDo: Check Plugin type == LADSPA or VST? - enable[k] = v --queue any assignments/enables for after the initial parameter recalling to duck the 'in-on-change' feature - end - ARDOUR.LuaAPI.set_processor_param(proc, k, v) - end - - for k, v in pairs(enable) do - ARDOUR.LuaAPI.set_processor_param(proc, k, v) - end - if act then proc:activate() else proc:deactivate() end - end - - ::nextline:: - i = i + 1 - - end - end - - function dry_run(debug) - --returns a dialog-able table of - --everything we do (logically) - --in the recall function - local route_values = {['----'] = "0"} - for r in Session:get_routes():iter() do - route_values[r:name()] = r:to_stateful():id():to_s() - end - - local i = 0 - local dry_table = { - {type = "label", align = "left", key = "col-0-title" , col = 0, colspan = 1, title = 'Source Settings:'}, - {type = "label", align = "left", key = "col-0-title" , col = 1, colspan = 1, title = 'Actions:'}, - {type = "label", align = "left", key = "col-2-title" , col = 2, colspan = 1, title = 'Destination:'}, - {type = "label", align = "left", key = "col-2-title" , col = 3, colspan = 1, title = 'Do this?'}, - } - local file = io.open(path, "r") - assert(file, "File not found!") - - for l in file:lines() do - local do_plugin, do_route, do_group = false, false, false - local f = load(l) - - if debug then - print(i, string.sub(l, 0, 29), f) - end - - if f then f() end - - if instance["route_id"] then do_route = true end - if instance["plugin_id"] then do_plugin = true end - if instance["group_id"] then do_group = true end - - if do_group then - local group_id = instance["group_id"] - local group_name = instance["name"] - local dlg_title, action_title = "", "" - - local group_ptr = group_by_id(group_id) - - if not(group_ptr) then - new_group = Session:new_route_group(group_name) - dlg_title = string.format("Cannot Find: (Group) %s.", group_name, new_group:name()) - action_title = "will create and use settings" - else - dlg_title = string.format("Found by ID: (Group) %s.", group_ptr:name()) - action_title = "will use group settings" - end - table.insert(dry_table, { - type = "label", align = "left", key = "group-"..i , col = 0, colspan = 1, title = dlg_title - }) - table.insert(dry_table, { - type = "label", align = "left", key = "group-"..i , col = 1, colspan = 1, title = action_title - }) - table.insert(dry_table, { - type = "checkbox", col=3, colspan = 1, key = "dothis-"..i, default = true, title = "line:"..i - }) - end - - if do_route then - local route_id = instance["route_id"] - local route_name = instance["route_name"] - local dlg_title = "" - - local route_ptr = Session:route_by_id(PBD.ID(route_id)) - - if route_ptr:isnil() then - route_ptr = Session:route_by_name(route_name) - if not(route_ptr:isnil()) then - dlg_title = string.format("Found by Name: (Rotue) %s", route_ptr:name()) - action_title = "will use route settings" - else - dlg_title = string.format("Cannot Find: (Route) %s", route_name) - action_title = "will be ignored" - end - else - dlg_title = string.format("Found by ID: (Route) %s", route_ptr:name()) - action_title = "will use route settings" - end - if route_ptr:isnil() then name = route_name else name = route_ptr:name() end - table.insert(dry_table, { - type = "label", align = "left", key = "route-"..i , col = 0, colspan = 1, title = dlg_title - }) - table.insert(dry_table, { - type = "label", align = "left", key = "action-"..i , col = 1, colspan = 1, title = action_title - }) - table.insert(dry_table, { - type = "dropdown", align = "left", key = "destination-"..i, col = 2, colspan = 1, title = "", values = route_values, default = name or "----" - }) - table.insert(dry_table, { - type = "checkbox", col=3, colspan = 1, key = "dothis-"..i, default = true, title = "line"..i - }) - end - i = i + 1 - end - return dry_table - end - - local dialog_options = { - { type = "label", colspan = 5, title = "" }, - { type = "radio", col = 1, colspan = 7, key = "select", title = "", values ={ ["Store"] = "store", ["Recall"] = "recall" }, default = "Store"}, - { type = "label", colspan = 5, title = "" }, - } - - local store_options = { - { type = "label", colspan = 5, title = "" }, - { type = "checkbox", col=1, colspan = 1, key = "selected", default = false, title = "Selected tracks only"}, - { type = "entry", col=2, colspan = 10, key = "filename", default = "params", title = "Store name" }, - { type = "label", colspan = 5, title = "" }, - } - - local recall_options = { - { type = "label", colspan = 5, title = "" }, - { type = "file", col =1, colspan = 10, key = "file", title = "Select a File", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") }, - { type = "label", colspan = 5, title = "" }, - } - - local rv = LuaDialog.Dialog("Mixer Store:", dialog_options):run() - - if rv then - local choice = rv["select"] - if choice == "store" then - local srv = LuaDialog.Dialog("Mixer Store:", store_options):run() - if srv then - empty_last_store() --ensures that params.lua will exist for the recall dialog - path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", srv["filename"] .. ".lua") - mark_tracks(srv['selected']) - end - end - - if choice == "recall" then - local rrv = LuaDialog.Dialog("Mixer Store:", recall_options):run() - if rrv then - if rrv['file'] ~= path then path = rrv['file'] end - --recall(true) - local dry_return = LuaDialog.Dialog("Mixer Store:", dry_run(true)):run() - if dry_return then recall(true, dry_return) end - end - end - end -collectgarbage() -end end diff --git a/scripts/synth1.lua b/scripts/synth1.lua deleted file mode 100644 index de68c58e46..0000000000 --- a/scripts/synth1.lua +++ /dev/null @@ -1,100 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Simple Synth", - category = "Instrument", - license = "MIT", - author = "Ardour Lua Task Force", - description = [[An Example Synth for Prototyping.]] -} - -function dsp_ioconfig () - return - { - -- { midi_in = 1, audio_in = 0, audio_out = -1}, -- any number of channels - -- { midi_in = 1, audio_in = 0, audio_out = 1}, -- values > 0, precisely N channels - { midi_in = 1, audio_in = 0, audio_out = 2}, -- values > 0, precisely N channels - { midi_in = 1, audio_in = 0, audio_out = 4}, -- values > 0, precisely N channels - { midi_in = 1, audio_in = 0, audio_out = 8}, -- values > 0, precisely N channels - -- { midi_in = 1, audio_in = 0, audio_out = -6}, -- values < -2, up to -N channels, here 1,..,6 - } -end - -local note_table = {} -local active_notes = {} -local phases = {} -local env = .01; - -function dsp_init (rate) - for n = 1,128 do - note_table [n] = (440 / 32) * 2^((n - 10.0) / 12.0) / rate - end - env = 100 / rate -end - -function dsp_run (ins, outs, n_samples) - -- initialize output buffer - local a = {} - for s = 1, n_samples do a[s] = 0 end - - - -- very basic synth, simple sine, basic envelope - local function synth (s_start, s_end) - for n,v in pairs (active_notes) do - local vel = v["vel"] or 0 - local tgt = v["tvel"]; - for s = s_start,s_end do - local phase = phases[n] or 0 - vel = vel + env * (tgt - vel) - a[s] = a[s] + math.sin(6.283185307 * phase) * vel / 167 - phase = phase + note_table[n] - if (phase > 1.0) then - phases[n] = phase - 2.0 - else - phases[n] = phase - end - end - if vel < 1 and tgt == 0 then - active_notes[n] = nil - else - active_notes[n]["vel"] = vel; - end - end - end - - local tme = 1 - -- parse midi messages - assert (type(midiin) == "table") -- global table of midi events (for now) - for _,b in pairs (midiin) do - local t = b["time"] -- t = [ 1 .. n_samples ] - - -- synth sound until event - synth(tme, t) - tme = t + 1 - - local d = b["data"] -- get midi-event - -- we ignore the midi channel - if (#d == 3 and (d[1] & 240) == 144) then -- note on - local n = 1 + d[2]; - active_notes[n] = active_notes[n] or {} - active_notes[n]["tvel"] = d[3] - end - if (#d == 3 and (d[1] & 240) == 128) then -- note off - local n = 1 + d[2]; - active_notes[n] = active_notes[n] or {} - active_notes[n]["tvel"] = 0 - end - if (#d == 3 and (d[1] & 240) == 176) then -- CC - if (d[2] == 120 or d[2] == 123) then -- panic - active_notes = {} - end - end - end - - -- synth rest of cycle - synth(tme, n_samples) - - -- copy - for c = 1,#outs do - outs[c]:set_table(a, n_samples) - end -end diff --git a/scripts/tomsloop.lua b/scripts/tomsloop.lua deleted file mode 100644 index d397ab9e60..0000000000 --- a/scripts/tomsloop.lua +++ /dev/null @@ -1,315 +0,0 @@ -ardour { ["type"] = "EditorAction", name = "Tom's Loop", - license = "MIT", - author = "Ardour Team", - description = [[Bounce the loop-range of all non muted audio tracks, paste at playhead]] -} - --- main method, every custom (i.e. non-ardour) method must be defined *inside* factory() -function factory (params) return function () - --- when this script is called as an action, the output will be printed to the ardour log window - function print_help() - printf("See source for help.") ----[[ - print("") - print("---------------------------------------------------------------------") - print("") - print("Manual for \"Tom’s Loop\" Ardour Lua Script") - print("") - print("---------------------------------------------------------------------") - print("---------------------------------------------------------------------") - print("") - print("Table of Contents") - print("") - print("1. The first test") - print("2. Using mute and solo") - print("3. Combining region clouds to a defined length") - print("") - print("Abstract: This script for Ardour (>=4.7 git) operates on the time") - print("line. It allows to copy and combine specific portions within the loop") - print("range to a later point on the time line with one single action") - print("command. Everything that can be heard within the loop range is") - print("considered for this process, namely non-muted regions on non-muted or") - print("soloed tracks that are fully or partially inside the loop range. This") - print("still sounds a bit abstract and will be more obvious with the") - print("following example cases of use.") - print("") - print("For convenience, it’s recommended to bind the script to a keyboard") - print("shortcut in order to quickly and easily access the \"Tom’s Loop\"") - print("scripted action.") - print("") - print("-Open dialog \"Script Manager\" via menu Edit/Scripted Actions/Script") - print("Manager") - print("") - print("-In tab \"Action Scripts\", select a line and press button \"Add/Set\"") - print("") - print("-In dialog \"Add Lua Action\", select \"Tom’s Loop\" from the drop down") - print("menu and hit \"Add\"") - print("") - print("-In dialog \"Set Script Parameter\" just hit \"Add\" again") - print("") - print("-Close dialog \"Script Manager\"") - print("") - print("-Open dialog \"Bindings Editor\" via menu Window/Bindings Editor") - print("") - print("-In tab \"Editor\", expand \"Editor\", look for entry \"Tom’s loop\",") - print("select it") - print("") - print("-Hit the keyboard shortcut to assign to this scripted action") - print("") - print("-Close dialog \"Key Bindings\"") - print("") - print("An alternative way to quickly access a scripted action is to enable") - print("\"Action Script Button Visibility\" in \"Preferences/GUI\".") - print("") - print("---------------------------------------------------------------------") - print("") - print("1. The first test") - print("") - print("---------------------------------------------------------------------") - print("") - print("-Record a short sequence of audio input or import a wave file to a") - print("track to get a region") - print("") - print("-Set a loop range inside that one region") - print("") - print("-Place the playhead after the loop range, possibly after the region,") - print("non-rolling") - print("") - print(" _L====L_ V") - print(" .____|____|____________. |") - print(" |R1__|_x__|____________| |") - print("") - print("-Call \"Tom’s Loop\" via the previously created shortcut") - print("") - print("This results in a new region created at the playhead, with the length") - print("of the loop range, containing audio of the original region. The") - print("playhead moved to the end of this new region so that subsequent calls") - print("to \"Tom’s Loop\" will result in a gap less series of regions.") - print("") - print(" _L====L_ --> V") - print(" .____|____|____________. .____|") - print(" |R1__|_x__|____________| |_x__|") - print("") - print("-Repeat calling \"Tom’s Loop\"") - print("") - print("This creates multiple copies of the loop range to line up one after") - print("each other.") - print("") - print(" _L====L_ --> V") - print(" .____|____|____________. .______________|") - print(" |R1__|_x__|____________| |_x__|_x__|_x__|") - print("") - print("-Set a different loop range and call \"Tom’s Loops\" again") - print("") - print("This will create a new region with the length of the new loop range") - print("at the playhead.") - print("") - print(" _L=======L_ --> V") - print(" ._______|_______|______. .______________________|") - print(" |R1_____|_X_____|______| |_x__|_x__|_x__|_X_____|") - print("") - print("By using \"Tom’s Loop\", the loop range - which can be easily set with") - print("the handles - and the playhead it’s easy to create any sequence of") - print("existing regions on the time line. This can be useful during the") - print("arrangement phase where macro parts of the session are already") - print("temporally layed out (in the loop) but not part of the final") - print("arrangement yet. The process is non-destructive in a sense that the") - print("existing regions layout in the current loop range won’t be touched or") - print("replaced. The newly created regions are immediately visible on the") - print("time line at the playhead position.") - print("") - print("") - print("---------------------------------------------------------------------") - print("") - print("2. Using mute and solo") - print("") - print("---------------------------------------------------------------------") - print("") - print("Creating a sequence of regions like described above respects the") - print("current mute and solo state of a track. Variations of the loop are") - print("thus easy to create, further supporting the arrangement process.") - print("") - print(" _L====L_ --> V") - print(" .____|____|____________. ._________. |") - print(" |R1__|_x__|____________| |_x__|_x__| |") - print(" .__|R2|_y__|________|_. |_y__|_________|") - print(" |R3___|_z__|__________| |_z__|_z__|") - print("") - print("") - print("---------------------------------------------------------------------") - print("") - print("3. Combining region clouds to a defined length") - print("") - print("---------------------------------------------------------------------") - print("") - print("Multiple small regions say on a percussive track can be simplified") - print("for later arrangement keeping the temporal relations by combining") - print("them. Using \"Tom’s Loop\", the resulting regions will not only combine") - print("the regions but also automatically extend or shrink the new regions") - print("start and end point so that it is exactly of the wished length equal") - print("to the loop range.") - print("") - print("_L======================L_ --> V") - print(" | .____ .___. _____|_______. .______________________|") - print(" | |R1_| |R2_| |R3__|_______| |______________________|") - print("") - print("See also: Lua Action Bounce+Replace Regions") - print("") - print("") ---]] - end -- print_help() - - local n_paste = 1 - assert (n_paste > 0) - - local proc = ARDOUR.LuaAPI.nil_proc () -- bounce w/o processing - local itt = ARDOUR.InterThreadInfo () -- bounce progress info (unused) - - local loop = Session:locations ():auto_loop_location () - local playhead = Session:transport_sample () - - -- make sure we have a loop, and the playhead (edit point) is after it - if not loop then - print_help(); - print ("Error: A Loop range must be set.") - goto errorout - end - assert (loop:start () < loop:_end ()) - if loop:_end () > playhead then - print_help(); - print ("Error: The Playhead (paste point) needs to be after the loop.") - goto errorout - end - - -- prepare undo operation - Session:begin_reversible_command ("Tom's Loop") - local add_undo = false -- keep track if something has changed - - -- prefer solo'ed tracks - local soloed_track_found = false - for route in Session:get_tracks ():iter () do - if route:soloed () then - soloed_track_found = true - break - end - end - - -- count regions that are bounced - local n_regions_created = 0 - - -- loop over all tracks in the session - for route in Session:get_tracks ():iter () do - if soloed_track_found then - -- skip not soloed tracks - if not route:soloed () then - goto continue - end - end - - -- skip muted tracks (also applies to soloed + muted) - if route:muted () then - goto continue - end - - -- at this point the track is either soloed (if at least one track is soloed) - -- or not muted (if no track is soloed) - - -- test if bouncing is possible - local track = route:to_track () - if not track:bounceable (proc, false) then - goto continue - end - - -- only audio tracks - local playlist = track:playlist () - if playlist:data_type ():to_string () ~= "audio" then - goto continue - end - - -- check if there is at least one unmuted region in the loop-range - local reg_unmuted_count = 0 - for reg in playlist:regions_touched (loop:start (), loop:_end ()):iter () do - if not reg:muted() then - reg_unmuted_count = reg_unmuted_count + 1 - end - end - - if reg_unmuted_count < 1 then - goto continue - end - - -- clear existing changes, prepare "diff" of state for undo - playlist:to_stateful ():clear_changes () - - -- do the actual work - local region = track:bounce_range (loop:start (), loop:_end (), itt, proc, false) - playlist:add_region (region, playhead, n_paste, false, 0, 0, false) - - n_regions_created = n_regions_created + 1 - - -- create a diff of the performed work, add it to the session's undo stack - -- and check if it is not empty - if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then - add_undo = true - end - - ::continue:: - end -- for all routes - - --advance playhead so it's just after the newly added regions - if n_regions_created > 0 then - Session:request_locate (playhead + loop:length() * n_paste, ARDOUR.LocateTransportDisposition.MustStop, ARDOUR.TransportRequestSource.TRS_UI) - end - - -- all done, commit the combined Undo Operation - if add_undo then - -- the 'nil' Command here mean to use the collected diffs added above - Session:commit_reversible_command (nil) - else - Session:abort_reversible_command () - end - - print ("bounced " .. n_regions_created .. " regions from loop range (" .. loop:length() .. " samples) to playhead @ sample # " .. playhead) - ::errorout:: -end -- end of anonymous action script function -end -- end of script factory - - -function icon (params) return function (ctx, width, height) - local x = width * .5 - local y = height * .5 - local r = math.min (x, y) - - ctx:set_line_width (1) - - function stroke_outline () - ctx:set_source_rgba (0, 0, 0, 1) - ctx:stroke_preserve () - ctx:set_source_rgba (1, 1, 1, 1) - ctx:fill () - end - - ctx:rectangle (x - r * .6, y - r * .05, r * .6, r * .3) - stroke_outline () - - ctx:arc (x, y, r * .61, math.pi, 0.2 * math.pi) - ctx:arc_negative (x, y, r * .35, 0.2 * math.pi, math.pi); - stroke_outline () - - function arc_arrow (rad, ang) - return x - rad * math.sin (ang * 2.0 * math.pi), y - rad * math.cos (ang * 2.0 * math.pi) - end - - ctx:move_to (arc_arrow (r * .36, .72)) - ctx:line_to (arc_arrow (r * .17, .72)) - ctx:line_to (arc_arrow (r * .56, .60)) - ctx:line_to (arc_arrow (r * .75, .72)) - ctx:line_to (arc_arrow (r * .62, .72)) - - ctx:set_source_rgba (0, 0, 0, 1) - ctx:stroke_preserve () - ctx:close_path () - ctx:set_source_rgba (1, 1, 1, 1) - ctx:fill () -end end diff --git a/scripts/track_organizer.lua b/scripts/track_organizer.lua deleted file mode 100644 index 15caf2c9ff..0000000000 --- a/scripts/track_organizer.lua +++ /dev/null @@ -1,100 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Track Organizer", - author = "Mixbus Lua Taskforce", - description = [[Easily modifiable session overview and track property editor]] -} - -function factory () return function () - - local rbow = { ["----"] = "", ["Red"] = 0xD10000FF, ["Orange"] = 0xFF6622FF, ["Yellow"] = 0xFFDA21FF, - ["Green"] = 0x33DD00FF, ["Blue"] = 0x1133CCFF, ["Indigo"] = 0x220066FF, ["Violet"] = 0x330044FF -} - - --now starting to build our dialog - local dialog_options = { - { type = "label", colspan="4", title = "Change your Track settings here:" }, - { type = "heading", title = "Track", col = 0, colspan = 1 }, - { type = "heading", title = "Group", col = 1, colspan = 1 }, - { type = "heading", title = "Comment", col = 2, colspan = 1 }, - { type = "heading", title = "Color", col = 3, colspan = 1 }, - } - - --group option payload - --@ToDo: Add 'fake' groups for people to select, create them if they want it - local pl = {["----"] = "", ["Drums"] = "Drums", ["Bass"] = "Bass", ["Guitars"] = "Guitars", - ["Keys"] = "Keys", ["Strings"] = "Strings", ["Vox"] = "Vox" -} - - for g in Session:route_groups():iter() do - pl[g:name()] = g - end - - --helper function to find default group option - function interrogate(t) - local v = "----" - for g in Session:route_groups():iter() do - for r in g:route_list():iter() do - if r:name() == t:name() then v = g:name() end - end - end return v - end - - function find_color(t) - local c = "----" - for k, v in pairs(rbow) do - if(t:presentation_info_ptr():color() == v) then c = k end - end return c - end - - --insert an entry into our dialog_options table for each track with appropriate info - for t in Session:get_tracks():iter() do - table.insert(dialog_options, { - type = "entry", key = t:name() .. ' n', col = 0, colspan = 1, default = t:name(), title = "" --@ToDo: Shorten Names so they can still see what track they're changing? - }) --name - table.insert(dialog_options, { - type = "dropdown", key = t:name() .. ' g', col = 1, colspan = 1, title = "", values = pl, default = interrogate(t) - }) --group - table.insert(dialog_options, { - type = "entry", key = t:name() .. ' cm', col = 2, colspan = 1, default = t:comment(), title = "" - }) --comment - table.insert(dialog_options, { - type = "dropdown", key = t:name() .. ' c', col = 3, colspan = 1, title = "", values = rbow, default = find_color(t) - }) --color - end - - --run dialog_options - local rv = LuaDialog.Dialog("Track Organizer", dialog_options):run() - if not(rv) then goto script_end end - assert(rv, 'Dialog box was cancelled or is ' .. type(rv)) - - --begin track operation - for t in Session:get_tracks():iter() do - local cgrp = interrogate(t) - local name = rv[t:name() .. ' n' ] - local ngrp = rv[t:name() .. ' g' ] - local cmnt = rv[t:name() .. ' cm'] - local colr = rv[t:name() .. ' c' ] - - if t:name() ~= name then t:set_name(name) end - - if t:comment() ~= cmnt then t:set_comment(cmnt, nil) end - - if not(colr == "") then t:presentation_info_ptr():set_color(colr) end - - if type(ngrp) == "userdata" then - if cgrp ~= ngrp:name() then - ngrp:add(t) - end - end - - if type(ngrp) == "string" and not(ngrp == "") then - ngrp = Session:new_route_group(ngrp) - if cgrp ~= ngrp:name() then - ngrp:add(t) - end - end - end - ::script_end:: - collectgarbage() -end end diff --git a/scripts/transparent_regions.lua b/scripts/transparent_regions.lua deleted file mode 100644 index 7db81fb7af..0000000000 --- a/scripts/transparent_regions.lua +++ /dev/null @@ -1,28 +0,0 @@ -ardour { - ["type"] = "EditorHook", - name = "Make all Regions Transparent", - author = "Ardour Lua Task Force", - description = "While the transport is looping, all regions become transparent.", -} - -function signals () - return LuaSignal.Set():add ( - { - [LuaSignal.TransportStateChange] = true, - [LuaSignal.TransportLooped] = true, - } - ) -end - -function factory () - return function (signal, ref, ...) - local all_regions = ARDOUR.RegionFactory.regions() - for _, r in all_regions:iter() do - local ar = r:to_audioregion (); - if ar:isnil () then goto next end - if ar:opaque () then - ar:set_opaque (false) - end - ::next:: - end -end end \ No newline at end of file diff --git a/scripts/vamp_audio_to_midi.lua b/scripts/vamp_audio_to_midi.lua deleted file mode 100644 index 2f9101196c..0000000000 --- a/scripts/vamp_audio_to_midi.lua +++ /dev/null @@ -1,112 +0,0 @@ -ardour { - ["type"] = "EditorAction", - name = "Polyphonic Audio to MIDI", - license = "MIT", - author = "Ardour Team", -description = [[ -Analyze audio from the selected audio region to a selected MIDI region. - -A MIDI region on the target track will have to be created first (use the pen tool). - -This script uses the Polyphonic Transcription VAMP plugin from Queen Mary Univ, London. -The plugin works best at 44.1KHz input sample rate, and is tuned for piano and guitar music. Velocity is not estimated. -]] -} - -function factory () return function () - local sel = Editor:get_selection () - local sr = Session:nominal_sample_rate () - local tm = Session:tempo_map () - local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-transcription", sr) - local midi_region = nil - local audio_regions = {} - local start_time = Session:current_end_sample () - local end_time = Session:current_start_sample () - local max_pos = 0 - local cur_pos = 0 - for r in sel.regions:regionlist ():iter () do - if r:to_midiregion():isnil() then - local st = r:position() - local ln = r:length() - local et = st + ln - if st < start_time then - start_time = st - end - if et > end_time then - end_time = et - end - table.insert(audio_regions, r) - max_pos = max_pos + r:to_readable ():readable_length () - else - midi_region = r:to_midiregion() - end - end - - if #audio_regions == 0 then - LuaDialog.Message ("Polyphonic Audio to MIDI", "No source audio region(s) selected.\nAt least one audio-region to be analyzed need to be selected.", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run () - return - end - if not midi_region then - LuaDialog.Message ("Polyphonic Audio to MIDI", "No target MIDI region selected.\nA MIDI region, ideally empty, and extending beyond the selected audio-region(s) needs to be selected.", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run () - return - end - - midi_region:set_initial_position(start_time) - midi_region:set_length(end_time - start_time, 0) - - local pdialog = LuaDialog.ProgressWindow ("Audio to MIDI", true) - function progress (_, pos) - return pdialog:progress ((cur_pos + pos) / max_pos, "Analyzing") - end - - for i,ar in pairs(audio_regions) do - local a_off = ar:position () - local b_off = midi_region:quarter_note () - midi_region:start_beats () - - vamp:analyze (ar:to_readable (), 0, progress) - - if pdialog:canceled () then - goto out - end - - cur_pos = cur_pos + ar:to_readable ():readable_length () - pdialog:progress (cur_pos / max_pos, "Generating MIDI") - - local fl = vamp:plugin ():getRemainingFeatures ():at (0) - if fl and fl:size() > 0 then - local mm = midi_region:midi_source(0):model() - local midi_command = mm:new_note_diff_command ("Audio2Midi") - for f in fl:iter () do - local ft = Vamp.RealTime.realTime2Frame (f.timestamp, sr) - local fd = Vamp.RealTime.realTime2Frame (f.duration, sr) - local fn = f.values:at (0) - - local bs = tm:exact_qn_at_sample (a_off + ft, 0) - local be = tm:exact_qn_at_sample (a_off + ft + fd, 0) - - local pos = Evoral.Beats (bs - b_off) - local len = Evoral.Beats (be - bs) - local note = ARDOUR.LuaAPI.new_noteptr (1, pos, len, fn + 1, 0x7f) - midi_command:add (note) - end - mm:apply_command (Session, midi_command) - end - -- reset the plugin (prepare for next iteration) - vamp:reset () - end - - ::out:: - pdialog:done (); - pdialog = nil - vamp = nil; - collectgarbage () -end end - -function icon (params) return function (ctx, width, height, fg) - local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(width * .7) .. "px") - txt:set_text ("\u{2669}") -- quarter note symbol UTF8 - local tw, th = txt:get_pixel_size () - ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) - ctx:move_to (.5 * (width - tw), .5 * (height - th)) - txt:show_in_cairo_context (ctx) -end end diff --git a/scripts/voice_activate.lua b/scripts/voice_activate.lua deleted file mode 100644 index ef4380b9bb..0000000000 --- a/scripts/voice_activate.lua +++ /dev/null @@ -1,53 +0,0 @@ -ardour { - ["type"] = "dsp", - name = "Voice/Level Activate", - category = "Utility", - author = "Ardour Lua Task Force", - license = "MIT", - description = [[Roll the transport when the signal level on the plugin's input exceeds a given threshold.]] -} - -function dsp_ioconfig () - return - { - -- support all in/out as long as input port count equals output port count - { audio_in = -1, audio_out = -1}, - } -end - -function dsp_params () - return - { - { ["type"] = "input", name = "Threshold", min = -20, max = 0, default = -6, doc = "Threshold in dBFS for all channels" }, - { ["type"] = "output", name = "Level", min = -120, max = 0 }, - } -end - -function dsp_configure (ins, outs) - n_channels = ins:n_audio() -end - -function dsp_runmap (bufs, in_map, out_map, n_samples, offset) - local ctrl = CtrlPorts:array() -- get control port array (read/write) - - if Session:transport_rolling() then - -- don't do anything if the transport is already rolling - ctrl[2] = -math.huge -- set control output port value - return - end - - local threshold = 10 ^ (.05 * ctrl[1]) -- dBFS to coefficient - local level = -math.huge - - for c = 1,n_channels do - local b = in_map:get(ARDOUR.DataType("audio"), c - 1) -- get id of audio-buffer for the given channel - if b ~= ARDOUR.ChanMapping.Invalid then -- check if channel is mapped - local a = ARDOUR.DSP.compute_peak(bufs:get_audio(b):data(offset), n_samples, 0) -- compute digital peak - if a > threshold then - Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI) - end - if a > level then level = a end -- max level of all channels - end - end - ctrl[2] = ARDOUR.DSP.accurate_coefficient_to_dB (level) -- set control output port value -end diff --git a/scripts/wscript b/scripts/wscript deleted file mode 100644 index a820a1fc13..0000000000 --- a/scripts/wscript +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python - -import os - -top = '.' -out = 'build' - -def configure(conf): - pass - -def build(bld): - scripts = bld.path.ant_glob ('*.lua', excl=['^_*']) - bld.install_files (os.path.join(bld.env['DATADIR'], 'scripts'), scripts) - -def options(opt): - pass diff --git a/share/scripts/HiAndLowPass.lua b/share/scripts/HiAndLowPass.lua new file mode 100644 index 0000000000..75704d9701 --- /dev/null +++ b/share/scripts/HiAndLowPass.lua @@ -0,0 +1,396 @@ +ardour { + ["type"] = "dsp", + name = "a-High/Low Pass Filter", + category = "Filter", + license = "GPLv2", + author = "Ardour Team", + description = [[High and Low Pass Filter with de-zipped controls, written in Ardour-Lua]] +} + +function dsp_ioconfig () + return + { + -- allow any number of I/O as long as port-count matches + { audio_in = -1, audio_out = -1}, + } +end + + +function dsp_params () + return + { + { ["type"] = "input", name = "High Pass Steepness", min = 0, max = 4, default = 1, enum = true, scalepoints = + { + ["Off"] = 0, + ["12dB/oct"] = 1, + ["24dB/oct"] = 2, + ["36dB/oct"] = 3, + ["48dB/oct"] = 4, + } + }, + { ["type"] = "input", name = "High Pass Cut off frequency", min = 5, max = 20000, default = 100, unit="Hz", logarithmic = true }, + { ["type"] = "input", name = "High Pass Resonance", min = 0.1, max = 6, default = .707, logarithmic = true }, + + { ["type"] = "input", name = "Low Pass Steepness", min = 0, max = 4, default = 1, enum = true, scalepoints = + { + ["Off"] = 0, + ["12dB/oct"] = 1, + ["24dB/oct"] = 2, + ["36dB/oct"] = 3, + ["48dB/oct"] = 4, + } + }, + { ["type"] = "input", name = "Low Pass Cut off frequency", min = 20, max = 20000, default = 18000, unit="Hz", logarithmic = true }, + { ["type"] = "input", name = "Low Pass Resonance", min = 0.1, max = 6, default = .707, logarithmic = true }, + } +end + +-- these globals are *not* shared between DSP and UI +local hp = {} -- the biquad high-pass filter instances (DSP) +local lp = {} -- the biquad high-pass filter instances (DSP) +local filt = nil -- the biquad filter instance (GUI, response) +local cur = {0, 0, 0, 0, 0, 0} -- current parameters +local lpf = 0.03 -- parameter low-pass filter time-constant +local chn = 0 -- channel/filter count +local lpf_chunk = 0 -- chunk size for audio processing when interpolating parameters +local max_freq = 20000 + +local mem = nil -- memory x-fade buffer + +function dsp_init (rate) + -- allocate some mix-buffer + mem = ARDOUR.DSP.DspShm (8192) + + -- max allowed cut-off frequency + max_freq = .499 * rate + + -- create a table of objects to share with the GUI + local tbl = {} + tbl['samplerate'] = rate + tbl['max_freq'] = max_freq + self:table ():set (tbl) + + + -- Parameter smoothing: we want to filter out parameter changes that are + -- faster than 15Hz, and interpolate between parameter values. + -- For performance reasons, we want to ensure that two consecutive values + -- of the interpolated "steepness" are less that 1 apart. By choosing the + -- interpolation chunk size to be 64 in most cases, but 32 if the rate is + -- strictly less than 22kHz (there's only 8kHz in standard rates), we can + -- ensure that steepness interpolation will never change the parameter by + -- more than ~0.86. + lpf_chunk = 64 + if rate < 22000 then lpf_chunk = 32 end + -- We apply a discrete version of the standard RC low-pass, with a cutoff + -- frequency of 15Hz. For more information about the underlying math, see + -- https://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization + -- (here Δt is lpf_chunk / rate) + local R = 2 * math.pi * lpf_chunk * 15 -- Hz + lpf = R / (R + rate) +end + +function dsp_configure (ins, outs) + assert (ins:n_audio () == outs:n_audio ()) + local tbl = self:table ():get () -- get shared memory table + + chn = ins:n_audio () + cur = {0, 0, 0, 0, 0, 0} + + hp = {} + lp = {} + + collectgarbage () + + for c = 1, chn do + hp[c] = {} + lp[c] = {} + -- initialize filters + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad + + -- A different Biquad is needed for each pass and channel because they + -- remember the last two samples seen during the last call of Biquad:run(). + -- For continuity these have to come from the previous audio chunk of the + -- same channel and pass and would be clobbered if the same Biquad was + -- called several times by cycle. + for k = 1,4 do + hp[c][k] = ARDOUR.DSP.Biquad (tbl['samplerate']) + lp[c][k] = ARDOUR.DSP.Biquad (tbl['samplerate']) + end + end +end + +function santize_params (ctrl) + -- don't allow manual cross-fades. enforce enums + ctrl[1] = math.floor(ctrl[1] + .5) + ctrl[4] = math.floor(ctrl[4] + .5) + + -- high pass, clamp range + ctrl[2] = math.min (max_freq, math.max (5, ctrl[2])) + ctrl[3] = math.min (6, math.max (0.1, ctrl[3])) + + -- low pass, clamp range + ctrl[5] = math.min (max_freq, math.max (20, ctrl[5])) + ctrl[6] = math.min (6, math.max (0.1, ctrl[6])) + return ctrl +end + +-- helper functions for parameter interpolation +function param_changed (ctrl) + for p = 1,6 do + if ctrl[p] ~= cur[p] then + return true + end + end + return false +end + +function low_pass_filter_param (old, new, limit) + if math.abs (old - new) < limit then + return new + else + return old + lpf * (new - old) + end +end + +-- apply parameters, re-compute filter coefficients if needed +function apply_params (ctrl) + if not param_changed (ctrl) then + return + end + + -- low-pass filter ctrl parameter values, smooth transition + cur[1] = low_pass_filter_param (cur[1], ctrl[1], 0.05) -- HP order x-fade + cur[2] = low_pass_filter_param (cur[2], ctrl[2], 1.0) -- HP freq/Hz + cur[3] = low_pass_filter_param (cur[3], ctrl[3], 0.01) -- HP quality + cur[4] = low_pass_filter_param (cur[4], ctrl[4], 0.05) -- LP order x-fade + cur[5] = low_pass_filter_param (cur[5], ctrl[5], 1.0) -- LP freq/Hz + cur[6] = low_pass_filter_param (cur[6], ctrl[6], 0.01) -- LP quality + + for c = 1, chn do + for k = 1,4 do + hp[c][k]:compute (ARDOUR.DSP.BiquadType.HighPass, cur[2], cur[3], 0) + lp[c][k]:compute (ARDOUR.DSP.BiquadType.LowPass, cur[5], cur[6], 0) + end + end +end + + +-- the actual DSP callback +function dsp_run (ins, outs, n_samples) + assert (n_samples <= 8192) + assert (#ins == chn) + local ctrl = santize_params (CtrlPorts:array ()) + + local changed = false + local siz = n_samples + local off = 0 + + -- if a parameter was changed, process at most lpf_chunk samples + -- at a time and interpolate parameters until the current settings + -- match the target values + if param_changed (ctrl) then + changed = true + siz = lpf_chunk + end + + while n_samples > 0 do + if changed then apply_params (ctrl) end + if siz > n_samples then siz = n_samples end + + local ho = math.floor(cur[1]) + local lo = math.floor(cur[4]) + + -- process all channels + for c = 1, #ins do + + -- High Pass + local xfade = cur[1] - ho + + -- prepare scratch memory + ARDOUR.DSP.copy_vector (mem:to_float (off), ins[c]:offset (off), siz) + + -- run at least |ho| biquads... + for k = 1,ho do + hp[c][k]:run (mem:to_float (off), siz) + end + ARDOUR.DSP.copy_vector (outs[c]:offset (off), mem:to_float (off), siz) + + -- mix the output of |ho| biquads (with weight |1-xfade|) + -- with the output of |ho+1| biquads (with weight |xfade|) + if xfade > 0 then + ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, 1 - xfade) + hp[c][ho+1]:run (mem:to_float (off), siz) + ARDOUR.DSP.mix_buffers_with_gain (outs[c]:offset (off), mem:to_float (off), siz, xfade) + -- also run the next biquad because it needs to have the correct state + -- in case it start affecting the next chunck of output. Higher order + -- ones are guaranteed not to be needed for the next run because the + -- interpolated order won't increase more than 0.86 in one step thanks + -- to the choice of the value of |lpf|. + if ho + 2 <= 4 then hp[c][ho+2]:run (mem:to_float (off), siz) end + elseif ho + 1 <= 4 then + -- run the next biquad in case it is used next chunk + hp[c][ho+1]:run (mem:to_float (off), siz) + end + + -- Low Pass + xfade = cur[4] - lo + + -- prepare scratch memory (from high pass output) + ARDOUR.DSP.copy_vector (mem:to_float (off), outs[c]:offset (off), siz) + + -- run at least |lo| biquads... + for k = 1,lo do + lp[c][k]:run (mem:to_float (off), siz) + end + ARDOUR.DSP.copy_vector (outs[c]:offset (off), mem:to_float (off), siz) + + -- mix the output of |lo| biquads (with weight |1-xfade|) + -- with the output of |lo+1| biquads (with weight |xfade|) + if xfade > 0 then + ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, 1 - xfade) + lp[c][lo+1]:run (mem:to_float (off), siz) + ARDOUR.DSP.mix_buffers_with_gain (outs[c]:offset (off), mem:to_float (off), siz, xfade) + -- also run the next biquad in case it start affecting the next + -- chunck of output. + if lo + 2 <= 4 then lp[c][lo+2]:run (mem:to_float (off), siz) end + elseif lo + 1 <= 4 then + -- run the next biquad in case it is used next chunk + lp[c][lo+1]:run (mem:to_float (off), siz) + end + + end + + n_samples = n_samples - siz + off = off + siz + end + + if changed then + -- notify display + self:queue_draw () + end +end + + +------------------------------------------------------------------------------- +--- inline display + +function round (n) + return math.floor (n + .5) +end + +function freq_at_x (x, w) + -- frequency in Hz at given x-axis pixel + return 20 * 1000 ^ (x / w) +end + +function x_at_freq (f, w) + -- x-axis pixel for given frequency, power-scale + return w * math.log (f / 20.0) / math.log (1000.0) +end + +function db_to_y (db, h) + -- y-axis gain mapping + if db < -60 then db = -60 end + if db > 12 then db = 12 end + return -.5 + round (0.2 * h) - h * db / 60 +end + +function grid_db (ctx, w, h, db) + -- draw horizontal grid line + -- note that a cairo pixel at Y spans [Y - 0.5 to Y + 0.5] + local y = -.5 + round (db_to_y (db, h)) + ctx:move_to (0, y) + ctx:line_to (w, y) + ctx:stroke () +end + +function grid_freq (ctx, w, h, f) + -- draw vertical grid line + local x = -.5 + round (x_at_freq (f, w)) + ctx:move_to (x, 0) + ctx:line_to (x, h) + ctx:stroke () +end + +function response (ho, lo, f) + -- calculate transfer function response for given + -- hi/po pass order at given frequency [Hz] + local db = ho * filt['hp']:dB_at_freq (f) + return db + lo * filt['lp']:dB_at_freq (f) +end + +function render_inline (ctx, w, max_h) + if not filt then + local tbl = self:table ():get () -- get shared memory table + -- instantiate filter (to calculate the transfer function's response) + filt = {} + filt['hp'] = ARDOUR.DSP.Biquad (tbl['samplerate']) + filt['lp'] = ARDOUR.DSP.Biquad (tbl['samplerate']) + max_freq = tbl['max_freq'] + end + + local ctrl = santize_params (CtrlPorts:array ()) + -- set filter coefficients if they have changed + if param_changed (ctrl) then + for k = 1,6 do cur[k] = ctrl[k] end + filt['hp']:compute (ARDOUR.DSP.BiquadType.HighPass, cur[2], cur[3], 0) + filt['lp']:compute (ARDOUR.DSP.BiquadType.LowPass, cur[5], cur[6], 0) + end + + -- calc height of inline display + local h = 1 | math.ceil (w * 9 / 16) -- use 16:9 aspect, odd number of y pixels + if (h > max_h) then h = max_h end -- but at most max-height + + -- ctx is a http://cairographics.org/ context + -- http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context + + -- clear background + ctx:rectangle (0, 0, w, h) + ctx:set_source_rgba (.2, .2, .2, 1.0) + ctx:fill () + ctx:rectangle (0, 0, w, h) + ctx:clip () + + -- set line width: 1px + ctx:set_line_width (1.0) + + -- draw grid + local dash3 = C.DoubleVector () + local dash2 = C.DoubleVector () + dash2:add ({1, 2}) + dash3:add ({1, 3}) + ctx:set_dash (dash2, 2) -- dotted line: 1 pixel 2 space + ctx:set_source_rgba (.5, .5, .5, .8) + grid_db (ctx, w, h, 0) + ctx:set_dash (dash3, 2) -- dashed line: 1 pixel 3 space + ctx:set_source_rgba (.5, .5, .5, .5) + grid_db (ctx, w, h, -12) + grid_db (ctx, w, h, -24) + grid_db (ctx, w, h, -36) + grid_freq (ctx, w, h, 100) + grid_freq (ctx, w, h, 1000) + grid_freq (ctx, w, h, 10000) + ctx:unset_dash () + + -- draw transfer function line + local ho = math.floor(cur[1]) + local lo = math.floor(cur[4]) + + ctx:set_source_rgba (.8, .8, .8, 1.0) + ctx:move_to (-.5, db_to_y (response(ho, lo, freq_at_x (0, w)), h)) + for x = 1,w do + local db = response(ho, lo, freq_at_x (x, w)) + ctx:line_to (-.5 + x, db_to_y (db, h)) + end + -- stoke a line, keep the path + ctx:stroke_preserve () + + -- fill area to zero under the curve + ctx:line_to (w, -.5 + round (db_to_y (0, h))) + ctx:line_to (0, -.5 + round (db_to_y (0, h))) + ctx:close_path () + ctx:set_source_rgba (.5, .5, .5, .5) + ctx:fill () + + return {w, h} +end diff --git a/share/scripts/README b/share/scripts/README new file mode 100644 index 0000000000..2527498ee3 --- /dev/null +++ b/share/scripts/README @@ -0,0 +1,29 @@ +Ardour Lua Scripts +================== + +https://manual.ardour.org/lua-scripting/ + +For upstream script addition, please file a pull-request at +https://github.com/Ardour/ardour/tree/master/scripts + +Script Naming conventions: + +^_ + A script filename with a leading underscore indicates an example script. + These scripts are only available from ardour's git repository and not + installed nor included with binary bundles. + +^__ + Scripts with a filename starting with two underscores are excluded from + unit-tests. This is currently the case for convolver, fluidsynth and + plugin-modulation. + They depend on external files (soundfont, impulse-response) or a specific + session-setup (plugin-modulation needs an automatable plugin). + +^s_ + A filename beginning with "s_" indicates a code snippet. + These scripts can only be used in the interactive interpreter + (Window > Scripting). They may be useful by themselves or handy for copy/edit + to create EditorActions. + The filename prefix is only for convenience, "type" = "Snippet" is used when + scripts are listed at runtime. diff --git a/share/scripts/__convolv.lua b/share/scripts/__convolv.lua new file mode 100644 index 0000000000..23ad9034ed --- /dev/null +++ b/share/scripts/__convolv.lua @@ -0,0 +1,59 @@ +ardour { ["type"] = "dsp", name = "Lua Convolver", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] } + +function dsp_ioconfig () return + { + { audio_in = 1, audio_out = 1}, + { audio_in = 1, audio_out = 2}, + { audio_in = 2, audio_out = 2}, + } +end + +local conv, mode, ir_file + +ir_file = "/tmp/reverbs/St Nicolaes Church.wav" +ir_file = "/tmp/reverbs/Large Wide Echo Hall.wav" + +function dsp_configure (ins, outs) + if outs:n_audio() == 1 then + assert (ins:n_audio() == 1) + mode = ARDOUR.DSP.IRChannelConfig.Mono + elseif ins:n_audio() == 1 then + assert (outs:n_audio() == 2) + mode = ARDOUR.DSP.IRChannelConfig.MonoToStereo + else + assert (ins:n_audio() == 2) + assert (outs:n_audio() == 2) + mode = ARDOUR.DSP.IRChannelConfig.Stereo + end + + local irs = ARDOUR.DSP.IRSettings() + irs.gain = 0.5 + conv = ARDOUR.DSP.Convolver (Session, ir_file, mode, irs) + collectgarbage () +end + +function dsp_latency () + if conv then + return conv:latency() + else + return 0 + end +end + +-- the DSP callback function to process audio audio +-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray +function dsp_run (ins, outs, n_samples) + assert (#ins <= #outs) + + for c = 1, #ins do + if ins[c] ~= outs[c] then -- if processing is not in-place.. + ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) -- ..copy data from input to output. + end + end + + if #outs == 1 then + conv:run_mono (outs[1], n_samples) + else + conv:run_stereo (outs[1], outs[2], n_samples) + end +end diff --git a/share/scripts/__fluidsynth1.lua b/share/scripts/__fluidsynth1.lua new file mode 100644 index 0000000000..f2e0ea2b52 --- /dev/null +++ b/share/scripts/__fluidsynth1.lua @@ -0,0 +1,51 @@ +ardour { + ["type"] = "dsp", + name = "Lua Fluid Synth", + category = "Instrument", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An Example Synth for Prototyping.]] +} + +function dsp_ioconfig () + return + { + { midi_in = 1, audio_in = 0, audio_out = 2}, + } +end + +fluidsynth = nil + +function dsp_init (rate) + fluidsynth = ARDOUR.FluidSynth (rate, 32) + assert (fluidsynth:load_sf2 ("/usr/share/sounds/sf2/FluidR3_GM.sf2")) +end + +function dsp_run (ins, outs, n_samples) + local tme = 1 + assert (#outs == 2) + + -- parse midi messages + assert (type(midiin) == "table") -- global table of midi events (for now) + for _, e in pairs (midiin) do + local t = e["time"] -- t = [ 1 .. n_samples ] + + -- synth sound until event + if t > tme then + local off = tme - 1 + local len = t - tme + fluidsynth:synth (outs[1]:offset (off), outs[2]:offset (off), len) + end + + tme = t + 1 + + fluidsynth:midi_event (e["bytes"], e["size"]) -- parse midi event + end + + -- synth rest of cycle + if tme <= n_samples then + local off = tme - 1 + local len = 1 + n_samples - tme + fluidsynth:synth (outs[1]:offset (off), outs[2]:offset (off), len) + end +end diff --git a/share/scripts/__plugin_modulation.lua b/share/scripts/__plugin_modulation.lua new file mode 100644 index 0000000000..bb94b64a00 --- /dev/null +++ b/share/scripts/__plugin_modulation.lua @@ -0,0 +1,46 @@ +--- session-script example to modulate plugin parameter(s) globally +-- +-- Ardour > Menu > Session > Scripting > Add Lua Script +-- "Add" , select "Modulate Plugin Parameter", click "Add" + OK. +-- +----------------------------------------------------------------------------- +-- This script currently assumes you have a track named "Audio" +-- which as a plugin at the top, where the first parameter has a range > 200 +-- e.g. "No Delay Line" +-- +-- edit below.. + + +-- plugin descriptor +ardour { + ["type"] = "session", + name = "Modulate Plugin Parameter", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An example session to modulate a plugin parameter.]] +} + +function factory () -- generate a new script instance + + local count = 0 -- script-instance "global" variable + + -- the "run" function called at the beginning of every process cycle + return function (n_samples) + count = (count + 1) % 200; -- count process cycles + local tri = math.abs (100 - count) -- triangle wave 0..100 + + -- get the track named "Audio" + -- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session + -- and http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route + local route = Session:route_by_name ("Audio") + assert (not route:isnil ()) -- make sure it exists + + -- the 1st plugin (from top) on that track, ardour starts counting at zero + -- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Processor + local plugin = route:nth_plugin (0) + assert (not plugin:isnil ()) -- make sure it exists + + -- modulate the plugin's first parameter (0) from 200 .. 300 + ARDOUR.LuaAPI.set_processor_param (plugin, 0, 200.0 + tri) + end +end diff --git a/share/scripts/__readable.lua b/share/scripts/__readable.lua new file mode 100644 index 0000000000..b9a991a782 --- /dev/null +++ b/share/scripts/__readable.lua @@ -0,0 +1,47 @@ +ardour { ["type"] = "Snippet", name = "Readable Test" } + +function factory () return function () + + local file = "/tmp/reverbs/Large Wide Echo Hall.wav" + local rl = ARDOUR.Readable.load (Session, file) + local cmem = ARDOUR.DSP.DspShm (8192) + + local d = cmem:to_float (0):array() + for i = 1, 8192 do d[i] = 0 end + d[1] = .5 + + local ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 8192) + + rl:push_back (ar) + + local c = 1 + for rd in rl:iter () do + local n_samples = rd:readable_length () + assert (rd:n_channels () == 1) + + local peak = 0 + local pos = 0 + repeat + -- read at most 8K samples starting at 'pos' + local s = rd:read (cmem:to_float (0), pos, 8192, 0) + pos = pos + s + -- access the raw audio data + -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray + local d = cmem:to_float (0):array() + -- iterate over the audio sample data + for i = 0, s do + if math.abs (d[i]) > peak then + peak = math.abs (d[i]) + end + end + until s < 8192 + assert (pos == n_samples) + + if (peak > 0) then + print ("File:", file, "channel", c, "peak:", 20 * math.log (peak) / math.log(10), "dBFS") + else + print ("File:", file, "channel", c, " is silent") + end + c = c + 1; + end +end end diff --git a/share/scripts/_amp1.lua b/share/scripts/_amp1.lua new file mode 100644 index 0000000000..03be3961b6 --- /dev/null +++ b/share/scripts/_amp1.lua @@ -0,0 +1,47 @@ +ardour { + ["type"] = "dsp", + name = "Simple Amp", + category = "Example", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[ + An Example DSP Plugin for processing audio, to + be used with Ardour's Lua scripting facility.]] +} + + +-- return possible i/o configurations +function dsp_ioconfig () + -- -1, -1 = any number of channels as long as input and output count matches + return { [1] = { audio_in = -1, audio_out = -1}, } +end + +-- optional function, called when configuring the plugin +function dsp_configure (ins, outs) + -- store configuration in global variable + audio_ins = ins:n_audio(); + local audio_outs = outs:n_audio() + assert (audio_ins == audio_outs) +end + +-- this variant asks for a complete *copy* of the +-- audio data in a lua-table. +-- after processing the data is copied back. +-- +-- this also exemplifies the direct "connect and run" process function, +-- where the channel-mapping needs to be done in lua. + +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + for c = 1,audio_ins do + -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0 + local ib = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped input buffer for given channel + local ob = out_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped output buffer for given channel + assert (ib ~= ARDOUR.ChanMapping.Invalid); + assert (ob ~= ARDOUR.ChanMapping.Invalid); + local a = bufs:get_audio (ib):data (offset):get_table(n_samples) -- copy audio-data from input buffer + for s = 1,n_samples do + a[s] = a[s] * 2; -- amplify data in lua table + end + bufs:get_audio(ob):data(offset):set_table(a, n_samples) -- copy back + end +end diff --git a/share/scripts/_amp2.lua b/share/scripts/_amp2.lua new file mode 100644 index 0000000000..5a594d57e3 --- /dev/null +++ b/share/scripts/_amp2.lua @@ -0,0 +1,51 @@ +ardour { + ["type"] = "dsp", + name = "Simple Amp II", + category = "Example", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[ + An Example DSP Plugin for processing audio, to + be used with Ardour's Lua scripting facility.]] +} + +-- see amp1.lua +function dsp_ioconfig () + return { [1] = { audio_in = -1, audio_out = -1}, } +end + +function dsp_configure (ins, outs) + audio_ins = ins:n_audio(); + local audio_outs = outs:n_audio() + assert (audio_ins == audio_outs) +end + + +-- this variant modifies the audio data in-place +-- in Ardour's buffer. +-- +-- It relies on the fact that by default Ardour requires +-- plugins to process data in-place (zero copy). +-- +-- Every assignment directly calls a c-function behind +-- the scenes to get/set the value. +-- It's a bit more efficient than "Amp I" on most systems. + +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + for c = 1,audio_ins do + -- ensure that processing does happen in-place + local ib = in_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped input buffer for given cannel + local ob = out_map:get(ARDOUR.DataType("audio"), c - 1); -- get id of mapped output buffer for given cannel + assert (ib ~= ARDOUR.ChanMapping.Invalid); + assert (ob ~= ARDOUR.ChanMapping.Invalid); + + local bi = bufs:get_audio(ib) + local bo = bufs:get_audio(ob) + assert (bi == bo) + + local a = bufs:get_audio(ib):data(offset):array() -- get a reference (pointer to array) + for s = 1,n_samples do + a[s] = a[s] * 2; -- modify data in-place (shared with ardour) + end + end +end diff --git a/share/scripts/_amp3.lua b/share/scripts/_amp3.lua new file mode 100644 index 0000000000..006a22afbb --- /dev/null +++ b/share/scripts/_amp3.lua @@ -0,0 +1,44 @@ +ardour { + ["type"] = "dsp", + name = "Simple Amp III", + category = "Example", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[ + An Example DSP Plugin for processing audio, to + be used with Ardour's Lua scripting facility.]] +} + +function dsp_ioconfig () + return + { + { audio_in = -1, audio_out = -1}, + } +end + + +function dsp_params () + return + { + { ["type"] = "input", name = "Gain", min = -20, max = 20, default = 6, unit="dB", scalepoints = { ["0"] = 0, ["twice as loud"] = 6 , ["half as loud"] = -6 } }, + } +end + + +-- use ardour's vectorized functions +-- +-- This is as efficient as Ardour doing it itself in C++ +-- Lua function overhead is negligible +-- +-- this also exemplifies the /simpler/ way of delegating the +-- channel-mapping to ardour. + +function dsp_run (ins, outs, n_samples) + local ctrl = CtrlPorts:array() -- get control port array (read/write) + local gain = ARDOUR.DSP.dB_to_coefficient (ctrl[1]) + assert (#ins == #outs) -- ensure that we can run in-place (channel count matches) + for c = 1,#ins do + assert (ins[c] == outs[c]) -- check in-place + ARDOUR.DSP.apply_gain_to_buffer (ins[c], n_samples, gain); -- process in-place + end +end diff --git a/share/scripts/_biquad_filter.lua b/share/scripts/_biquad_filter.lua new file mode 100644 index 0000000000..5dc6e3be1b --- /dev/null +++ b/share/scripts/_biquad_filter.lua @@ -0,0 +1,280 @@ +ardour { + ["type"] = "dsp", + name = "Biquad Filter", + category = "Filter", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[A Versatile Filter Plugin]] +} + +function dsp_ioconfig () + return + { + -- allow any number of I/O as long as port-count matches + { audio_in = -1, audio_out = -1}, + } +end + + +function dsp_params () + return + { + { ["type"] = "input", name = "Enable", min = 0, max = 1, default = 1, bypass = true, toggled = true }, + { ["type"] = "input", name = "Type", min = 0, max = 4, default = 0, enum = true, scalepoints = + { + ["Peaking"] = 0, + ["Low Shelf"] = 1, + ["High Shelf"] = 2, + ["Low Pass"] = 3, + ["High Pass"] = 4, + } + }, + { ["type"] = "input", name = "Gain", min = -20, max = 20, default = 0, unit="dB" }, + { ["type"] = "input", name = "Freq", min = 20, max = 20000, default = 1000, unit="Hz", logarithmic = true }, + { ["type"] = "input", name = "Q", min = 0.1, max = 8, default = .707, logarithmic = true }, + } +end + +-- translate type parameter to DSP enum +-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.DSP.Biquad.Type +function map_type (t) + if t == 1 then + return ARDOUR.DSP.BiquadType.LowShelf + elseif t == 2 then + return ARDOUR.DSP.BiquadType.HighShelf + elseif t == 3 then + return ARDOUR.DSP.BiquadType.LowPass + elseif t == 4 then + return ARDOUR.DSP.BiquadType.HighPass + else + return ARDOUR.DSP.BiquadType.Peaking + end +end + +function ctrl_data () + local ctrl = CtrlPorts:array () + if ctrl[1] <= 0 then -- when disabled + ctrl[3] = 0; -- force gain to 0dB + end + return ctrl +end + +-- these globals are *not* shared between DSP and UI +local filters = {} -- the biquad filter instances (DSP) +local filt -- the biquad filter instance (GUI, response) +local cur = {0, 0, 0, 0, 0} -- current parameters +local lpf = 0.03 -- parameter low-pass filter time-constant +local chn = 0 -- channel/filter count + +function dsp_init (rate) + self:shmem ():allocate (1) -- shared mem to tell the GUI the samplerate + local cfg = self:shmem ():to_int (0):array () + cfg[1] = rate + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad + filt = ARDOUR.DSP.Biquad (rate) -- initialize filter + lpf = 13000 / rate -- interpolation time constant +end + +function dsp_configure (ins, outs) + assert (ins:n_audio () == outs:n_audio ()) + local cfg = self:shmem ():to_int (0):array () + local rate = cfg[1] + chn = ins:n_audio () + for c = 1, chn do + filters[c] = ARDOUR.DSP.Biquad (rate) -- initialize filters + end + cur = {0, 0, 0, 0, 0} +end + +-- helper functions for parameter interpolation +function param_changed (ctrl) + if ctrl[2] == cur[2] and ctrl[3] == cur[3] and ctrl[4] == cur[4] and ctrl[5] == cur[5] then + return false + end + return true +end + +function low_pass_filter_param (old, new, limit) + if math.abs (old - new) < limit then + return new + else + return old + lpf * (new - old) + end +end + +-- apply parameters, re-compute filter coefficients if needed +function apply_params (ctrl) + if not param_changed (ctrl) then + return + end + + if cur[2] ~= ctrl[2] then + -- reset filter state when type changes + filt:reset () + for k = 2,5 do cur[k] = ctrl[k] end + else + -- low-pass filter ctrl parameter values, smooth transition + cur[3] = low_pass_filter_param (cur[3], ctrl[3], 0.1) -- gain/dB + cur[4] = low_pass_filter_param (cur[4], ctrl[4], 1.0) -- freq/Hz + cur[5] = low_pass_filter_param (cur[5], ctrl[5], 0.01) -- quality + end + + for c = 1, chn do + filters[c]:compute (map_type (cur[2]), cur[4], cur[5], cur[3]) + end +end + + +-- the actual DSP callback +function dsp_run (ins, outs, n_samples) + local changed = false + local siz = n_samples + local off = 0 + local ctrl = ctrl_data () + + -- if a parameter was changed, process at most 64 samples at a time + -- and interpolate parameters until the current settings match + -- the target values + if param_changed (ctrl) then + changed = true + siz = 64 + end + + while n_samples > 0 do + if changed then apply_params (ctrl) end + if siz > n_samples then siz = n_samples end + + -- process all channels + for c = 1,#ins do + -- check if output and input buffers for this channel are identical + -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray + if ins[c] == outs[c] then + filters[c]:run (ins[c]:offset (off), siz) -- in-place processing + else + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP + ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz) + filters[c]:run (outs[c]:offset (off), siz) + end + end + + n_samples = n_samples - siz + off = off + siz + end + + if changed then + -- notify display + self:queue_draw () + end +end + + +------------------------------------------------------------------------------- +--- inline display + +function round (n) + return math.floor (n + .5) +end + +function freq_at_x (x, w) + -- x-axis pixel for given freq, power-scale + return 20 * 1000 ^ (x / w) +end + +function x_at_freq (f, w) + -- frequency at given x-axis pixel + return w * math.log (f / 20.0) / math.log (1000.0) +end + +function db_to_y (db, h) + -- y-axis gain mapping + if db < -20 then db = -20 end + if db > 20 then db = 20 end + return -.5 + 0.5 * h * (1 - db / 20) +end + +function grid_db (ctx, w, h, db) + -- draw horizontal grid line + local y = -.5 + round (db_to_y (db, h)) + ctx:move_to (0, y) + ctx:line_to (w, y) + ctx:stroke () +end + +function grid_freq (ctx, w, h, f) + -- draw vertical grid line + local x = -.5 + round (x_at_freq (f, w)) + ctx:move_to (x, 0) + ctx:line_to (x, h) + ctx:stroke () +end + +function render_inline (ctx, w, max_h) + if not filt then + -- the GUI is separate from the DSP, but the GUI needs to know + -- the sample-rate that the DSP is using. + local shmem = self:shmem () -- get shared memory region + local cfg = shmem:to_int (0):array () -- "cast" into lua-table + -- instantiate filter (to calculate the transfer function's response) + filt = ARDOUR.DSP.Biquad (cfg[1]) + end + + -- set filter coefficients if they have changed + if param_changed (CtrlPorts:array ()) then + local ctrl = ctrl_data () + for k = 2,5 do cur[k] = ctrl[k] end + filt:compute (map_type (cur[2]), cur[4], cur[5], cur[3]) + end + + -- calc height of inline display + local h = math.ceil (w * 10 / 16) -- use 16:10 aspect + h = 2 * round (h / 2) -- with an even number of vertical pixels + if (h > max_h) then h = max_h end -- but at most max-height + + -- ctx is a http://cairographics.org/ context + -- http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context + + -- clear background + ctx:rectangle (0, 0, w, h) + ctx:set_source_rgba (.2, .2, .2, 1.0) + ctx:fill () + + -- set line width: 1px + -- Note: a cairo pixel at [1,1] spans [0.5->1.5 , 0.5->1.5] + -- hence the offset -0.5 in various move_to(), line_to() calls + ctx:set_line_width (1.0) + + -- draw grid + local dash3 = C.DoubleVector () + dash3:add ({1, 3}) + ctx:set_dash (dash3, 2) -- dotted line + ctx:set_source_rgba (.5, .5, .5, .5) + grid_db (ctx, w, h, 0) + grid_db (ctx, w, h, 6) + grid_db (ctx, w, h, 12) + grid_db (ctx, w, h, 18) + grid_db (ctx, w, h, -6) + grid_db (ctx, w, h, -12) + grid_db (ctx, w, h, -18) + grid_freq (ctx, w, h, 100) + grid_freq (ctx, w, h, 1000) + grid_freq (ctx, w, h, 10000) + ctx:unset_dash () + + -- draw transfer function line + ctx:set_source_rgba (.8, .8, .8, 1.0) + ctx:move_to (-.5, db_to_y (filt:dB_at_freq (freq_at_x (0, w)), h)) + for x = 1,w do + local db = filt:dB_at_freq (freq_at_x (x, w)) + ctx:line_to (-.5 + x, db_to_y (db, h)) + end + ctx:stroke_preserve () + + -- fill area to zero under the curve + ctx:line_to (w, -.5 + h * .5) + ctx:line_to (0, -.5 + h * .5) + ctx:close_path () + ctx:set_source_rgba (.5, .5, .5, .5) + ctx:fill () + + return {w, h} +end diff --git a/share/scripts/_calc_dsp_statistics.lua b/share/scripts/_calc_dsp_statistics.lua new file mode 100644 index 0000000000..2d676055a8 --- /dev/null +++ b/share/scripts/_calc_dsp_statistics.lua @@ -0,0 +1,27 @@ +ardour { ["type"] = "Snippet", name = "Calculate DSP stats", + license = "MIT", + author = "Ardour Team", +} + +function factory () return function () + + for t in Session:get_routes ():iter () do + local i = 0 + while true do + local rv, stats + local proc = t:nth_processor (i) + if proc:isnil () then break end + if proc:to_plugininsert():isnil() then goto continue end + + rv, stats = proc:to_plugininsert():get_stats (0, 0, 0, 0) + if not rv then goto continue end + + print (string.format (" * %-28s | min: %.2f max: %.2f avg: %.3f std-dev: %.3f [ms]", + string.sub (proc:name() .. ' (' .. t:name() .. ')', 0, 28), + stats[1] / 1000.0, stats[2] / 1000.0, stats[3] / 1000.0, stats[4] / 1000.0)) + + ::continue:: + i = i + 1 + end + end +end end diff --git a/share/scripts/_cron.lua b/share/scripts/_cron.lua new file mode 100644 index 0000000000..3f412d9ddd --- /dev/null +++ b/share/scripts/_cron.lua @@ -0,0 +1,37 @@ +ardour { + ["type"] = "EditorHook", + name = "Timed Event Example", + author = "Ardour Lua Task Force", + description = "Perform actions at specific wallclock time, example record", +} + +function signals () + return LuaSignal.Set():add ({[LuaSignal.LuaTimerDS] = true}) +end + +function factory () + local _last_time = 0 + return function (signal, ref, ...) + + -- calculate seconds since midnight + function hhmmss (hh, mm, ss) return hh * 3600 + mm * 60 + ss end + + -- current seconds since midnight UTC + -- (unix-time is UTC, no leap seconds, a day always has 86400 sec) + local now = os.time () % 86400 + + -- event at 09:30:00 UTC (here: rec-arm + roll) + if (now >= hhmmss (09, 30, 00) and _last_time < hhmmss (09, 30, 00)) then + Session:maybe_enable_record (false) + Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI) + end + + -- event at 09:32:00 UTC (here: rec-stop) + if (now >= hhmmss (09, 32, 00) and _last_time < hhmmss (09, 32, 00)) then + Session:disable_record (false, false) + Session:request_transport_speed (0.0, true, ARDOUR.TransportRequestSource.TRS_UI) + end + + _last_time = now + end +end diff --git a/share/scripts/_dialog_test.lua b/share/scripts/_dialog_test.lua new file mode 100644 index 0000000000..cc02f82a46 --- /dev/null +++ b/share/scripts/_dialog_test.lua @@ -0,0 +1,95 @@ +ardour { ["type"] = "Snippet", name = "Dialog Test" } + +function factory () return function () + local md = LuaDialog.Message ("title", "hello", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close) + print (md:run()) + + md = nil + collectgarbage () + + ----------------------------------------------------------------- + function basic_serialize (o) + if type(o) == "number" then + return tostring(o) + else + return string.format("%q", o) + end + end + + function serialize (name, value) + local rv = name .. ' = ' + collectgarbage() + if type(value) == "number" or type(value) == "string" or type(value) == "nil" or type (value) == "boolean" then + return rv .. basic_serialize(value) .. ' ' + elseif type(value) == "table" then + rv = rv .. '{} ' + for k,v in pairs(value) do + local fieldname = string.format("%s[%s]", name, basic_serialize(k)) + rv = rv .. serialize(fieldname, v) .. ' ' + end + return rv + elseif type(value) == "function" then + --return rv .. string.format("%q", string.dump(value, true)) + return rv .. "(function)" + else + error('cannot serialize a ' .. type(value)) + end + end + ----------------------------------------------------------------- + + function func () print "Hello" end + + local dialog_options = { + { type = "checkbox", key = "onoff", default = true, title = "OnOff" }, + + { type = "entry", key = "text", default = "changeme", title = "Text Entry" }, + + { + type = "radio", key = "select", title = "RadioBtn", values = + { + ["Option 1"] = 1, ["Option 2"] = "2", ["Option A"] = 'A' + }, + default = "Option 1" + }, + + { + type = "dropdown", key = "dropdown", title = "Menu", values = + { + ["Option 1"] = 1, ["Option 2"] = "2", ["Callback"] = func, + ["Option 4"] = + { + ["Option 4a"] = "test", ["Option 4b"] = 4.2 + } + }, + default = "Option 2" + }, + + { type = "fader", key = "gain", title = "Level", default = -10 }, -- unit = 'dB" + + { + type = "slider", key = "freq", title = "Frequency", min = 20, max = 20000, scalepoints = + { + [20] = "20", [200] = "nice", [2000] = "2k", [10000] = "too much" + }, + default = 500 + }, + + { type = "heading", title = "Heading" }, + + { type = "number", key = "number", title = "Whatever", min = 0, max = 10, step = 1, digits = 2 }, + + { type = "file", key = "file", title = "Select a File", path = ARDOUR.LuaAPI.build_filename (Session:path (), Session:name () .. ".ardour") }, + + { type = "folder", key = "folder", title = "Select a Folder", path = Session:path() } + } + + local od = LuaDialog.Dialog ("title", dialog_options) + local rv = od:run() + if (rv) then + print (serialize ("dialog", rv)) + end + + od = nil + collectgarbage () + +end end diff --git a/share/scripts/_dsp_plugin_communication.lua b/share/scripts/_dsp_plugin_communication.lua new file mode 100644 index 0000000000..e422e08cca --- /dev/null +++ b/share/scripts/_dsp_plugin_communication.lua @@ -0,0 +1,78 @@ +ardour { ["type"] = "dsp", name = "DSP Plugin Communication" } +function dsp_ioconfig () return { { audio_in = -1, audio_out = -1} } end + +function dsp_init (rate) + self:shmem ():allocate (1) +end + +function dsp_configure (ins, outs) +end + +function dsp_params () + return + { + { ["type"] = "output", name = "self", min = 0, max = 8}, + { ["type"] = "output", name = "gain", min = 0, max = 2}, + } +end + +function dsp_run (ins, outs, n_samples) + local ctrl = CtrlPorts:array () + local route = self:route () + local shmem = self:shmem () + + -- count plugins + local i = 0; + local l = 0; + local s = -1; -- 'self' this plugin instance + + -- iterate overall plugins on this track, + -- find all LuaProc instances of this plugin (unique_id), + repeat + local proc = route:nth_plugin (i) + if not proc:isnil () + and not proc:to_insert():plugin (0):to_luaproc():isnil () + and proc:to_insert():plugin (0):unique_id () == self:unique_id () then + if (self:id ():to_s() == proc:to_insert():plugin (0):id ():to_s()) then + s = l; -- *this* plugin instance + end + if l == 0 then + -- use shared-memory are of the first plugin instance for all. + -- + -- (the first plugin writes there, all later plugins only read, + -- plugins on a track are executed in order, in the same thread) + shmem = proc:to_insert():plugin (0):to_luaproc():shmem () + end + l = l + 1 -- count total instances of this plugin-type + end + i = i + 1 + until proc:isnil () + + assert (s >= 0) + ctrl[1] = s; + + -- calculate digital peak of all channels + local peak = 0 + for c = 1,#ins do + if not ins[c]:sameinstance (outs[c]) then + ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) + end + peak = ARDOUR.DSP.compute_peak(outs[c], n_samples, peak) + end + + + -- actual inter-plugin communication + local a = shmem:to_float (0):array () + if s == 0 then + -- the first plugin saves the peak + a[0] = peak + ctrl[2] = -1 + else + -- all later plugins display the difference to the first. + if (a[0] == 0) then + ctrl[2] = 1 + else + ctrl[2] = peak / a[0] + end + end +end diff --git a/share/scripts/_dump_latency.lua b/share/scripts/_dump_latency.lua new file mode 100644 index 0000000000..9303e29508 --- /dev/null +++ b/share/scripts/_dump_latency.lua @@ -0,0 +1,77 @@ +ardour { ["type"] = "Snippet", name = "Dump Latency", + license = "MIT", + author = "Ardour Team", +} + +function factory () return function () + local all_procs = true + local show_ports = true + + print (" -- Session --") + print ("Worst Output Latency: ", Session:worst_output_latency ()) + print ("Worst Input Latency: ", Session:worst_input_latency ()) + print ("Worst Track Latency: ", Session:worst_route_latency ()) + print ("Worst Latency Preroll: ", Session:worst_latency_preroll ()) + + print (" -- Routes --") + for t in Session:get_routes ():iter () do + print (string.format ("%-30s signal-latency: %4d align: %4d play: %4d || in: %4d out: %4d", + t:name(), + t:signal_latency (), t:playback_latency (false), t:playback_latency (true), + t:input():latency(), t:output():latency())) + local i = 0 + while true do + local proc = t:nth_processor (i) + if proc:isnil () then break end + if all_procs and not proc:to_send():isnil () then + print (string.format (" * %-27s L: %4d in: %4d out: %4d capt: %4d play %4d DLY-SRC: %4d DLY-DST: %4d", + string.sub (proc:name(), 0, 27) , proc:signal_latency(), + proc:input_latency(), proc:output_latency(), + proc:capture_offset(), proc:playback_offset(), + proc:to_send():get_delay_in(), proc:to_send():get_delay_out() + )) + elseif all_procs or not proc:to_diskioprocessor():isnil () then + print (string.format (" * %-27s L: %4d in: %4d out: %4d capt: %4d play %4d", + string.sub (proc:name(), 0, 27) , proc:signal_latency(), + proc:input_latency(), proc:output_latency(), + proc:capture_offset(), proc:playback_offset() + )) + end + i = i + 1 + end + end + + if show_ports then + print (" -- Ports -- (latencies: port, priv, pub)") + local a = Session:engine() + _, t = a:get_ports (ARDOUR.DataType("audio"), ARDOUR.PortList()) + -- table 't' holds argument references. t[2] is the PortList + for p in t[2]:iter() do + local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true) + local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false) + local ppl = p:private_latency_range (true) + local pcl = p:private_latency_range (false) + local bpl = p:public_latency_range (true) + local bcl = p:public_latency_range (false) + print (string.format ("%-30s play: (%4d, %4d) (%4d, %4d) (%4d, %4d) capt: (%4d, %4d) (%4d, %4d) (%4d, %4d)", + p:name(), + lp[1].min, lp[1].max, ppl.min, ppl.max, bpl.min, bpl.max, + lc[1].min, lc[1].max, pcl.min, pcl.max, bcl.min, bcl.max)) + end + _, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList()) + -- table 't' holds argument references. t[2] is the PortList + for p in t[2]:iter() do + local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true) + local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false) + local ppl = p:private_latency_range (true) + local pcl = p:private_latency_range (false) + local bpl = p:public_latency_range (true) + local bcl = p:public_latency_range (false) + print (string.format ("%-30s play: (%4d, %4d) (%4d, %4d) (%4d, %4d) capt: (%4d, %4d) (%4d, %4d) (%4d, %4d)", + p:name(), + lp[1].min, lp[1].max, ppl.min, ppl.max, bpl.min, bpl.max, + lc[1].min, lc[1].max, pcl.min, pcl.max, bcl.min, bcl.max)) + end + end + collectgarbage () +end end diff --git a/share/scripts/_dump_midiregion.lua b/share/scripts/_dump_midiregion.lua new file mode 100644 index 0000000000..0c62a56479 --- /dev/null +++ b/share/scripts/_dump_midiregion.lua @@ -0,0 +1,20 @@ +ardour { ["type"] = "Snippet", name = "Dump MIDI Region" } + +function factory () return function () + local sel = Editor:get_selection () + for r in sel.regions:regionlist ():iter () do + local mr = r:to_midiregion () + if mr:isnil () then goto next end + + print (r:name (), "Pos:", r:position (), "Start:", r:start ()) + local bfc = ARDOUR.BeatsSamplesConverter (Session:tempo_map (), r:position ()) + local nl = ARDOUR.LuaAPI.note_list (mr:model ()) + for n in nl:iter () do + print (" Note @", bfc:to (n:time ()), + ARDOUR.ParameterDescriptor.midi_note_name (n:note ()), + "Vel:", n:velocity ()) + end + print ("----") + ::next:: + end +end end diff --git a/share/scripts/_dump_playlists.lua b/share/scripts/_dump_playlists.lua new file mode 100644 index 0000000000..656a5ead97 --- /dev/null +++ b/share/scripts/_dump_playlists.lua @@ -0,0 +1,31 @@ +ardour { ["type"] = "Snippet", name = "Dump Playlists" } + +function factory () return function () + + print ("Number of playlists:", Session:playlists():n_playlists()) + + print () + print ("Used playlists:") + for p in Session:playlists():get_used():iter() do + print ("-", p:name(), p:n_regions()) + end + + print () + print ("Unused playlists:") + for p in Session:playlists():get_unused():iter() do + print ("-", p:name(), p:n_regions()) + end + + print () + print ("Playlists by Track:") + for r in Session:get_tracks():iter() do + print ("*", r:name()) + for p in Session:playlists():playlists_for_track (r:to_track()):iter() do + if (p == r:to_track():playlist()) then + print (" >-", p:name(), p:n_regions()) + else + print (" -", p:name(), p:n_regions()) + end + end + end +end end diff --git a/share/scripts/_export_plugins_on_save.lua b/share/scripts/_export_plugins_on_save.lua new file mode 100644 index 0000000000..c732717bd8 --- /dev/null +++ b/share/scripts/_export_plugins_on_save.lua @@ -0,0 +1,44 @@ +ardour { + ["type"] = "EditorHook", + name = "Save Extra Data (instruments)", + author = "Ardour Lua Task Force", + description = "Export custom data when the session is saved", +} + +-- subscribe to signals +-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal +function signals () + s = LuaSignal.Set() + s:add ({[LuaSignal.StateSaved] = true}) + return s +end + +-- create callback functions +function factory () return function (signal, ...) + assert (signal == LuaSignal.StateSaved) + + local all_instruments = {} + + -- iterate over all routes + for r in Session:get_routes():iter() do + local proc = r:the_instrument() -- get instrument processor (if any) + if proc:isnil() then goto nextroute end -- skip tracks/busses without instrument + local pi = proc:to_insert() -- check if it's a plugin-insert + if pi:isnil() then goto nextroute end + + local pp = pi:plugin (0) -- get first instance + all_instruments[r:name()] = string.format ("%s (%s)", proc:name(), pp:unique_id()) + + ::nextroute:: + end + + if next (all_instruments) ~= nil then -- check if table is not empty + -- write to a file in the session-folder + file = io.open (ARDOUR.LuaAPI.build_filename (Session:path(), Session:name () .. ".instruments.txt"), "w") + for nme, nfo in pairs (all_instruments) do + file:write (nme .. ": " .. nfo) + end + file:close() + end + +end end diff --git a/share/scripts/_export_tracks.lua b/share/scripts/_export_tracks.lua new file mode 100644 index 0000000000..7db8dabe84 --- /dev/null +++ b/share/scripts/_export_tracks.lua @@ -0,0 +1,10 @@ +ardour { ["type"] = "Snippet", name = "Export Track XML" } + +function factory () return function () + local rlp = ARDOUR.RouteListPtr () + local sel = Editor:get_selection () + for r in sel.tracks:routelist ():iter () do + rlp:push_back (r) + end + print (Session:export_track_state (rlp, "/tmp/rexport")) +end end diff --git a/share/scripts/_fan_out_instrument.lua b/share/scripts/_fan_out_instrument.lua new file mode 100644 index 0000000000..3fdf4ef907 --- /dev/null +++ b/share/scripts/_fan_out_instrument.lua @@ -0,0 +1,69 @@ +ardour { ["type"] = "EditorAction", name = "Fan Out Instrument", + license = "MIT", + author = "Ardour Team", + description = [[Create Busses for every Instrument Output on selected Tracks]] +} + +function factory () return function () + + local outputs = 2 + local mst = Session:master_out(); + if not mst:isnil() then + outputs = mst:n_inputs():n_audio() + end + mst = nil -- drop reference + + local sel = Editor:get_selection () + for r in sel.tracks:routelist ():iter () do + local proc = r:the_instrument ():to_insert() + if proc:isnil () then + print ("Track", r:name(), "does not have an instrument plugin") + goto next + end + local plugin = proc:plugin(0); + + if (r:n_outputs ():n_audio() ~= proc:output_streams():n_audio()) then + print ("Instrument ", proc:name(), "outputs", proc:output_streams():n_audio(), "do not match track outputs", r:n_outputs ():n_audio()) + goto next + end + + -- collect port-group information, count target bus width + local targets = {} + for i = 1, proc:output_streams():n_audio() do + local pd = plugin:describe_io_port (ARDOUR.DataType("Audio"), false, i - 1) + local nn = proc:name() .. " " .. pd.group_name; -- TODO use track-name prefix? + targets[nn] = targets[nn] or 0 + targets[nn] = targets[nn] + 1 + end + + if #targets < 2 then + print ("Instrument ", proc:name(), "has only 1 output bus. Nothing to fan out.") + goto next + end + + -- create busses ; TODO retain order + for t,c in pairs (targets) do + local rt = Session:route_by_name (t) + if rt:isnil () then + Session:new_audio_route (c, outputs, nil, 1, t, ARDOUR.PresentationInfo.Flag.AudioBus, ARDOUR.PresentationInfo.max_order) + end + end + + r:output():disconnect_all (nil) + r:panner_shell():set_bypassed (true) + + -- connect the busses + for i = 1, proc:output_streams():n_audio() do + local pd = plugin:describe_io_port (ARDOUR.DataType("Audio"), false, i - 1) + local nn = proc:name() .. " " .. pd.group_name; + local rt = Session:route_by_name (nn) + assert (rt) + + local op = r:output():audio (i - 1) + local ip = rt:input():audio (pd.group_channel) + op:connect (ip:name()) + end + + ::next:: + end +end end diff --git a/share/scripts/_find_nonzero_sample.lua b/share/scripts/_find_nonzero_sample.lua new file mode 100644 index 0000000000..c23d2b0b4d --- /dev/null +++ b/share/scripts/_find_nonzero_sample.lua @@ -0,0 +1,74 @@ +ardour { + ["type"] = "EditorAction", + name = "Find non zero audio sample", + author = "Ardour Team", + description = [[Find the position of first non-zero audio sample in selected regions.]] +} + +function factory () return function () + -- get Editor GUI Selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- allocate a buffer (float* in C) + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm + local cmem = ARDOUR.DSP.DspShm (8192) + local msg = "" + + -- iterate over selected regions + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- test if it's an audio region + if r:to_audioregion ():isnil () then + goto next + end + + -- to read the Region data, we use the Readable interface of the Region + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable + local rd = r:to_readable () + + local n_samples = rd:readable_length () + local n_channels = rd:n_channels () + + local nonzeropos = -1 + + -- iterate over all channels in Audio Region + for c = 0, n_channels -1 do + local pos = 0 + repeat + -- read at most 8K samples of channel 'c' starting at 'pos' + local s = rd:read (cmem:to_float (0), pos, 8192, c) + -- access the raw audio data + -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray + local d = cmem:to_float (0):array() + -- iterate over the audio sample data + for i = 0, s do + if math.abs (d[i]) > 0 then + if (nonzeropos < 0 or pos + i < nonzeropos) then + nonzeropos = pos + i + end + break + end + end + pos = pos + s + if (nonzeropos >= 0 and pos > nonzeropos) then + break + end + until s < 8192 + end + + if (nonzeropos >= 0) then + msg = msg .. string.format("%s: %d\n", r:name (), nonzeropos + r:position()) + else + msg = msg .. "Region: '%s' is silent\n" + end + + ::next:: + end + + if (msg ~= "") then + local md = LuaDialog.Message ("First Non Zero Sample", msg, LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close) + md:run() + end + +end end diff --git a/share/scripts/_fir.lua b/share/scripts/_fir.lua new file mode 100644 index 0000000000..64448ef8f6 --- /dev/null +++ b/share/scripts/_fir.lua @@ -0,0 +1,36 @@ +ardour { ["type"] = "dsp", name = "Lua FIR Convolver", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] } + +function dsp_ioconfig () return + { + { audio_in = 1, audio_out = 1}, + } +end + +local conv + +function dsp_configure (ins, outs) + conv = ARDOUR.DSP.Convolution (Session, ins:n_audio (), outs:n_audio ()) + + local cmem = ARDOUR.DSP.DspShm (4) + cmem:clear () + local d = cmem:to_float (0):array() + d[1] = .5 + d[2] = .5 + local ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 4) + conv:add_impdata (0, 0, ar, 1.0, 0, 0, 0, 0) + + cmem:to_float (0):set_table({1, -1, 0, 0}, 4) + ar = ARDOUR.AudioRom.new_rom (cmem:to_float (0), 3) + conv:add_impdata (0, 0, ar, 1.0, 0, 0, 0, 0) + + conv:restart () + collectgarbage () +end + +function dsp_latency () + return conv:latency() +end + +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + conv:run (bufs, in_map, out_map, n_samples, offset) +end diff --git a/share/scripts/_hook_test.lua b/share/scripts/_hook_test.lua new file mode 100644 index 0000000000..c5b00445ce --- /dev/null +++ b/share/scripts/_hook_test.lua @@ -0,0 +1,41 @@ +ardour { + ["type"] = "EditorHook", + name = "Callback Example", + author = "Ardour Lua Task Force", + description = "Rewind On Solo Change, Write a file when regions are moved", +} + +function signals () + s = LuaSignal.Set() + --s:add ({[LuaSignal.SoloActive] = true, [LuaSignal.RegionPropertyChanged] = true}) + s:add ( + { + [LuaSignal.SoloActive] = true, + [LuaSignal.RegionPropertyChanged] = true + } + ) + --for k,v in pairs (s:table()) do print (k, v) end + return s +end + +function factory (params) + return function (signal, ref, ...) + print (signal, ref, ...) + + if (signal == LuaSignal.SoloActive) then + Session:goto_start() + end + + if (signal == LuaSignal.RegionPropertyChanged) then + obj,pch = ... + file = io.open ("/tmp/test" ,"a") + io.output (file) + io.write (string.format ("Region: '%s' pos-changed: %s, length-changed: %s\n", + obj:name (), + tostring (pch:containsSamplePos (ARDOUR.Properties.Start)), + tostring (pch:containsSamplePos (ARDOUR.Properties.Length)) + )) + io.close (file) + end + end +end diff --git a/share/scripts/_insert_region_gaps.lua b/share/scripts/_insert_region_gaps.lua new file mode 100644 index 0000000000..3a7f5ed8e9 --- /dev/null +++ b/share/scripts/_insert_region_gaps.lua @@ -0,0 +1,62 @@ +ardour { + ["type"] = "EditorAction", + name = "Insert Gaps", + license = "MIT", + author = "Ardour Team", + description = [[Insert gaps between all regions on selected tracks]] +} + +function action_params () + return + { + ["gap"] = { title = "Gap size (in sec)", default = "2" }, + } +end + +function factory () return function () + -- get configuration + local p = params or {} + local gap = p["gap"] or 2 + if gap <= 0 then gap = 2 end + + local sel = Editor:get_selection () -- get current selection + + local add_undo = false -- keep track of changes + Session:begin_reversible_command ("Insert Gaps") + + -- iterate over all selected tracks + for route in sel.tracks:routelist ():iter () do + local track = route:to_track () + if track:isnil () then goto continue end + + -- get track's playlist + local playlist = track:playlist () + local offset = 0 + + -- iterate over all regions in the playlist + for region in playlist:region_list():iter() do + + -- preare for undo operation + region:to_stateful ():clear_changes () + + -- move region + region:set_position (region:position() + offset, 0) + offset = offset + Session:nominal_sample_rate () * gap + + -- create a diff of the performed work, add it to the session's undo stack + -- and check if it is not empty + if not Session:add_stateful_diff_command (region:to_statefuldestructible ()):empty () then + add_undo = true + end + end + ::continue:: + end + + -- all done, commit the combined Undo Operation + if add_undo then + -- the 'nil' Command here mean to use the collected diffs added above + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end +end end diff --git a/share/scripts/_midi_lfo.lua b/share/scripts/_midi_lfo.lua new file mode 100644 index 0000000000..fd8b2b07ef --- /dev/null +++ b/share/scripts/_midi_lfo.lua @@ -0,0 +1,74 @@ +ardour { + ["type"] = "dsp", + name = "MIDI LFO", + category = "Example", -- Utility + license = "MIT", + author = "Ardour Lua Task Force", + description = [[MIDI CC LFO Example -- Triangle full scale CC Parameter automation]] +} + +function dsp_ioconfig () + return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, } +end + +function dsp_params () + return + { + { ["type"] = "input", name = "BPM", min = 40, max = 200, default = 60, unit="BPM"}, + { ["type"] = "input", name = "CC", min = 0, max = 127, default = 1, integer = true }, + } +end + +local samplerate +local time = 0 +local step = 0 + +function dsp_init (rate) + samplerate = rate + local bpm = 120 + spb = rate * 60 / bpm +end + +function dsp_run (_, _, n_samples) + assert (type(midiin) == "table") + assert (type(midiout) == "table") + + local ctrl = CtrlPorts:array () + local bpm = ctrl[1] + local cc = ctrl[2] + + local spb = samplerate * 60 / bpm -- samples per beat + local sps = spb / 254 -- samples per step (0..127..1 = 254 steps) + + assert (sps > 1) + local i = 1 + local m = 1 + + for ts = 1, n_samples do + time = time + 1 + + -- forward incoming midi + if i <= #midiin then + while midiin[i]["time"] == ts do + midiout[m] = midiin[i] + i = i + 1 + m = m + 1 + if i > #midiin then break end + end + end + + -- inject LFO events + if time >= spb then + local val + if step > 127 then val = 254 - step else val = step end + + midiout[m] = {} + midiout[m]["time"] = ts + midiout[m]["data"] = { 0xb0, cc, val } + + m = m + 1 + time = time - sps + if step == 253 then step = 0 else step = step + 1 end + end + end +end diff --git a/share/scripts/_midi_rec_start.lua b/share/scripts/_midi_rec_start.lua new file mode 100644 index 0000000000..b2e03ba28b --- /dev/null +++ b/share/scripts/_midi_rec_start.lua @@ -0,0 +1,37 @@ +ardour { + ["type"] = "session", + name = "MIDI Record Enable", + category = "Example", -- "Utility" + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An example script to start recording on note-on.]] +} + +function factory () + return function (n_samples) + if Session:actively_recording() then return end -- when recording already, do nothing + -- iterate over all MIDI ports + _, t = Session:engine ():get_ports (ARDOUR.DataType.midi (), ARDOUR.PortList ()) + for p in t[2]:iter () do + -- skip output ports + if not p:receives_input () then goto next end + local midiport = p:to_midiport () + -- and skip async event ports + if midiport:isnil () then goto next end + local mb = midiport:get_midi_buffer (n_samples) -- get the midi-data buffers + local events = mb:table () -- copy event list into lua table + for _,e in pairs (events) do -- iterate over all events in the midi-buffer + if (e:buffer():array()[1] & 0xf0) == 0x90 then -- note on + Session:maybe_enable_record (true) -- global record-enable from rt-context + -- maybe-enable may fail if there are no tracks or step-entry is active + -- roll transport if record-enable suceeded: + if ARDOUR.Session.RecordState.Enabled == Session:record_status() then + Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI) -- ...and go. + end + return + end + end + ::next:: + end + end +end diff --git a/share/scripts/_midi_rewrite.lua b/share/scripts/_midi_rewrite.lua new file mode 100644 index 0000000000..4dfc28a6c3 --- /dev/null +++ b/share/scripts/_midi_rewrite.lua @@ -0,0 +1,34 @@ +ardour { + ["type"] = "session", + name = "Rewrite Midi", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An example session script preprocesses midi buffers.]] +} + +function factory () + -- this function is called in every process cycle, before processing + return function (n_samples) + _, t = Session:engine ():get_ports (ARDOUR.DataType.midi (), ARDOUR.PortList ()) + for p in t[2]:iter () do + if not p:receives_input () then goto next end + + if not p:name () == "MIDI/midi_in 1" then goto next end + + midiport = p:to_midiport () + assert (not midiport:isnil ()) + mb = midiport:get_midi_buffer (n_samples); + + events = mb:table() -- copy event list into lua table + mb:silence (n_samples, 0); -- clear existing buffer + + for _,e in pairs (events) do + -- e is-a http://manual.ardour.org/lua-scripting/class_reference/#Evoral:MidiEvent + e:set_channel (2) + mb:push_event (e) + end + + ::next:: + end + end +end diff --git a/share/scripts/_midifilter.lua b/share/scripts/_midifilter.lua new file mode 100644 index 0000000000..48f61a8277 --- /dev/null +++ b/share/scripts/_midifilter.lua @@ -0,0 +1,39 @@ +ardour { + ["type"] = "dsp", + name = "Midi Filter", + category = "Example", -- "Utility" + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An Example Midi Filter for prototyping.]] +} + +function dsp_ioconfig () + return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, } +end + +function dsp_run (_, _, n_samples) + assert (type(midiin) == "table") + assert (type(midiout) == "table") + local cnt = 1; + + function tx_midi (time, data) + midiout[cnt] = {} + midiout[cnt]["time"] = time; + midiout[cnt]["data"] = data; + cnt = cnt + 1; + end + + -- for each incoming midi event + for _,b in pairs (midiin) do + local t = b["time"] -- t = [ 1 .. n_samples ] + local d = b["data"] -- get midi-event + local event_type + if #d == 0 then event_type = -1 else event_type = d[1] >> 4 end + + if (#d == 3 and event_type == 9) then -- note on + tx_midi (t, d) + elseif (#d == 3 and event_type == 8) then -- note off + tx_midi (t, d) + end + end +end diff --git a/share/scripts/_midigenerator.lua b/share/scripts/_midigenerator.lua new file mode 100644 index 0000000000..9c093946f9 --- /dev/null +++ b/share/scripts/_midigenerator.lua @@ -0,0 +1,48 @@ +ardour { + ["type"] = "dsp", + name = "MIDI Generator", + category = "Example", -- "Utility" + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An Example Midi Generator for prototyping.]] +} + +function dsp_ioconfig () + return { { midi_out = 1, audio_in = 0, audio_out = 0}, } +end + +local tme = 0 -- sample-counter +local seq = 1 -- sequence-step +local spb = 0 -- samples per beat + +local midi_sequence = { + { 0x90, 64, 127 }, + { 0x80, 64, 0 }, +} + +function dsp_init (rate) + local bpm = 120 + spb = rate * 60 / bpm + if spb < 2 then spb = 2 end +end + +function dsp_run (_, _, n_samples) + assert (type(midiout) == "table") + assert (spb > 1) + local m = 1 + + for time = 1,n_samples do -- not very efficient + -- TODO, timestamp the sequence in beats, calc/skip to next event + tme = tme + 1 + + if tme >= spb then + midiout[m] = {} + midiout[m]["time"] = time + midiout[m]["data"] = midi_sequence[seq] + + tme = 0 + m = m + 1 + if seq == #midi_sequence then seq = 1 else seq = seq + 1 end + end + end +end diff --git a/share/scripts/_midigenerator2.lua b/share/scripts/_midigenerator2.lua new file mode 100644 index 0000000000..6bd904b097 --- /dev/null +++ b/share/scripts/_midigenerator2.lua @@ -0,0 +1,34 @@ +ardour { + ["type"] = "dsp", + name = "MIDI Generator II", + category = "Example", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An Example Midi Generator for prototyping.]] +} + +function dsp_ioconfig () + return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, } +end + +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + local ob = out_map:get (ARDOUR.DataType ("midi"), 0) + if ob ~= ARDOUR.ChanMapping.Invalid then + local mb = bufs:get_midi (ob) + + -- see _midigenerator.lua for + -- how to use a timed sequence + + local ba = C.ByteVector () -- construct a byte vector + ba:add ({0x90, 64, 127}) -- add some data to the vector + -- send a message at cycle-start + mb:push_back (offset, ba:size (), ba:to_array()); + + ba:clear () + ba:add ({0x80, 64, 127}) + mb:push_back (n_samples - 1 - offset, ba:size (), ba:to_array()); + end + + -- passthrough audio, apply pin/channel mapping + ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio")) +end diff --git a/share/scripts/_osc_hook_example.lua b/share/scripts/_osc_hook_example.lua new file mode 100644 index 0000000000..2ef81cd5bb --- /dev/null +++ b/share/scripts/_osc_hook_example.lua @@ -0,0 +1,52 @@ +ardour { + ["type"] = "EditorHook", + name = "OSC Callback Example", + author = "Ardour Lua Task Force", + description = "Send OSC messages", +} + +function action_params () + return + { + ["uri"] = { title = "OSC URI ", default = "osc.udp://localhost:7890"}, + } +end + + +function signals () + s = LuaSignal.Set() + s:add ( + { + [LuaSignal.SoloActive] = true, + [LuaSignal.RegionPropertyChanged] = true, + [LuaSignal.Exported] = true, + [LuaSignal.TransportStateChange] = true + } + ) + return s +end + +function factory (params) + return function (signal, ref, ...) + local uri = params["uri"] or "osc.udp://localhost:7890" + local tx = ARDOUR.LuaOSC.Address (uri) + -- debug print (stdout) + -- print (signal, ref, ...) + + if (signal == LuaSignal.Exported) then + tx:send ("/session/exported", "ss", ...) + elseif (signal == LuaSignal.SoloActive) then + tx:send ("/session/solo_changed", "") + elseif (signal == LuaSignal.TransportStateChange) then + tx:send ("/session/transport", "if", + Session:transport_sample(), Session:transport_speed()) + elseif (signal == LuaSignal.RegionPropertyChanged) then + obj,pch = ... + tx:send ("/region_property_changed", "sTTiii", + obj:name (), + (pch:containsSamplePos (ARDOUR.Properties.Start)), + (pch:containsSamplePos (ARDOUR.Properties.Length)), + obj:position (), obj:start (), obj:length ()) + end + end +end diff --git a/share/scripts/_plot_graph.lua b/share/scripts/_plot_graph.lua new file mode 100644 index 0000000000..9c8876dbbb --- /dev/null +++ b/share/scripts/_plot_graph.lua @@ -0,0 +1,24 @@ +ardour { + ["type"] = "EditorAction", + name = "Plot Process Graph", + author = "Ardour Team", + description = [[Export process graph to a graphviz file, and launch xdot]] +} + +function factory () return function () + if Session:plot_process_graph ("/tmp/ardour_graph.gv") then + os.forkexec ("/bin/sh", "-c", "xdot /tmp/ardour_graph.gv") + end +end end + +function icon (params) return function (ctx, width, height, fg) + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + local txt = Cairo.PangoLayout (ctx, "Sans ".. math.ceil(height / 3) .. "px") + txt:set_alignment (Cairo.Alignment.Center); + txt:set_width (width); + txt:set_ellipsize (Cairo.EllipsizeMode.Middle); + txt:set_text ("plot\ngrph") + local tw, th = txt:get_pixel_size () + ctx:move_to (0, .5 * (height - th)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/_pong.lua b/share/scripts/_pong.lua new file mode 100644 index 0000000000..8eef49f004 --- /dev/null +++ b/share/scripts/_pong.lua @@ -0,0 +1,260 @@ +ardour { + ["type"] = "dsp", + name = "a-Pong", + category = "Toy", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[A console classic for your console]] +} + +-- return possible i/o configurations +function dsp_ioconfig () + -- -1, -1 = any number of channels as long as input and output count matches + return { [1] = { audio_in = -1, audio_out = -1}, } +end + +-- control port(s) +function dsp_params () + return + { + { ["type"] = "input", name = "Bar", min = 0, max = 1, default = 0.5 }, + { ["type"] = "input", name = "Reset", min = 0, max = 1, default = 0, toggled = true }, + { ["type"] = "input", name = "Difficulty", min = 1, max = 10, default = 3}, + } +end + + +-- Game State (for this instance) +-- NOTE: these variables are for the DSP part (not shared with the GUI instance) +local sample_rate -- sample-rate +local fps -- audio samples per game-step +local game_time -- counts up to fps +local game_score +local ball_x, ball_y -- ball position [0..1] +local dx, dy -- current ball speed +local lost_sound -- audio-sample counter for game-over [0..3*fps] +local ping_sound -- audio-sample counter for ping-sound [0..fps] +local ping_phase -- ping note phase-difference per sample +local ping_pitch + +function dsp_init (rate) + -- allocate a "shared memory" area to transfer state to the GUI + self:shmem ():allocate (3) + self:shmem ():clear () + -- initialize some variables + sample_rate = rate + fps = rate / 25 + ping_pitch = 752 / rate + ball_x = 0.5 + ball_y = 0 + dx = 0.00367 + dy = 0.01063 + game_score = 0 + game_time = fps -- start the ball immediately (notify GUI) + ping_sound = fps -- set to end of synth cycle + lost_sound = 3 * fps +end + +function queue_beep () + -- queue 'ping' sound (unless one is already playing to prevent clicks) + if (ping_sound >= fps) then + -- major scale, 2 octaves + local scale = { 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24 } + local midi_note = 60 + scale[1 + math.floor (math.random () * 14)] + ping_pitch = (440 / 32) * 2^((midi_note - 10.0) / 12.0) / sample_rate + ping_sound = 0 + ping_phase = 0 + end +end + +-- callback: process "n_samples" of audio +-- ins, outs are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray +-- pointers to the audio buffers +function dsp_run (ins, outs, n_samples) + local ctrl = CtrlPorts:array () -- get control port array (read/write) + + local changed = false -- flag to notify GUI on every game-step + game_time = game_time + n_samples + + -- reset (allow to write automation from a given start-point) + -- ctrl[2] corresponds to the "Reset" input control + if ctrl[2] > 0 then + game_time = 0 + ball_x = 0.5 + ball_y = 0 + dx = 0.00367 + dy = 0.01063 + game_score = 0 + end + + -- simple game engine + while game_time > fps and ctrl[2] <= 0 do + changed = true + game_time = game_time - fps + + -- move the ball + ball_x = ball_x + dx * ctrl[3] + ball_y = ball_y + dy * ctrl[3] + + -- reflect left/right + if ball_x >= 1 or ball_x <= 0 then + dx = -dx + queue_beep () + end + + -- single player (reflect top) -- TODO "stereo" version, 2 ctrls :) + if ball_y <= 0 then + dy = - dy y = 0 + queue_beep () + end + + -- keep the ball in the field at all times + if ball_x >= 1 then ball_x = 1 end + if ball_x <= 0 then ball_x = 0 end + + -- bottom edge + if ball_y > 1 then + local bar = ctrl[1] -- get bar position + if math.abs (bar - ball_x) < 0.1 then + -- reflect the ball + dy = - dy + ball_y = 1.0 + dx = dx - 0.04 * (bar - ball_x) + -- make sure it's moving (not stuck on borders) + if math.abs (dx) < 0.0001 then dx = 0.0001 end + game_score = game_score + 1 + queue_beep () + else + -- game over, reset game + lost_sound = 0 -- re-start noise + ball_y = 0 + game_score = 0 + dx = 0.00367 + end + end + end + + -- forward audio if processing is not in-place + for c = 1,#outs do + -- check if output and input buffers for this channel are identical + -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray + if ins[c] ~= outs[c] then + -- fast (accelerated) copy + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP + ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) + end + end + + -- simple synth -- TODO Optimize + if ping_sound < fps then + -- cache audio data buffers for direct access, later + local abufs = {} + for c = 1,#outs do + abufs[c] = outs[c]:array() + end + -- simple sine synth with a sine-envelope + for s = 1, n_samples do + ping_sound = ping_sound + 1 + ping_phase = ping_phase + ping_pitch + local snd = 0.7 * math.sin(6.283185307 * ping_phase) * math.sin (3.141592 * ping_sound / fps) + -- add synthesized sound to all channels + for c = 1,#outs do + abufs[c][s] = abufs[c][s] + snd + end + -- break out of the loop when the sound finished + if ping_sound >= fps then goto ping_end end + end + ::ping_end:: + end + + if lost_sound < 3 * fps then + local abufs = {} + for c = 1,#outs do + abufs[c] = outs[c]:array() + end + for s = 1, n_samples do + lost_sound = lost_sound + 1 + -- -12dBFS white noise + local snd = 0.5 * (math.random () - 0.5) + for c = 1,#outs do + abufs[c][s] = abufs[c][s] + snd + end + if lost_sound >= 3 * fps then goto noise_end end + end + ::noise_end:: + end + + if changed then + -- notify the GUI + local shmem = self:shmem () -- get the shared memory region + local state = shmem:to_float (0):array () -- "cast" into lua-table + -- update data.. + state[1] = ball_x + state[2] = ball_y + state[3] = game_score + -- ..and wake up the UI + self:queue_draw () + end +end + + +------------------------------------------------------------------------------- +--- inline display + +local txt = nil -- cache font description (in GUI context) + +function render_inline (ctx, w, max_h) + local ctrl = CtrlPorts:array () -- control port array + local shmem = self:shmem () -- shared memory region (game state from DSP) + local state = shmem:to_float (0):array () -- cast to lua-table + + if (w > max_h) then + h = max_h + else + h = w + end + + -- prepare text rendering + if not txt then + -- allocate PangoLayout and set font + --http://manual.ardour.org/lua-scripting/class_reference/#Cairo:PangoLayout + txt = Cairo.PangoLayout (ctx, "Mono 10px") + end + + -- ctx is-a http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context + -- 2D vector graphics http://cairographics.org/ + + -- clear background + ctx:rectangle (0, 0, w, h) + ctx:set_source_rgba (.2, .2, .2, 1.0) + ctx:fill () + + -- print the current score + if (state[3] > 0) then + txt:set_text (string.format ("%.0f", state[3])); + local tw, th = txt:get_pixel_size () + ctx:set_source_rgba (1, 1, 1, 1.0) + ctx:move_to (w - tw - 3, 3) + txt:show_in_cairo_context (ctx) + end + + -- prepare line and dot rendering + ctx:set_line_cap (Cairo.LineCap.Round) + ctx:set_line_width (3.0) + ctx:set_source_rgba (.8, .8, .8, 1.0) + + -- display bar + local bar_width = w * .1 + local bar_space = w - bar_width + + ctx:move_to (bar_space * ctrl[1], h - 3) + ctx:rel_line_to (bar_width, 0) + ctx:stroke () + + -- display ball + ctx:move_to (1 + state[1] * (w - 3), state[2] * (h - 5)) + ctx:close_path () + ctx:stroke () + + return {w, h} +end diff --git a/share/scripts/_rawmidi.lua b/share/scripts/_rawmidi.lua new file mode 100644 index 0000000000..dba45f5483 --- /dev/null +++ b/share/scripts/_rawmidi.lua @@ -0,0 +1,110 @@ +ardour { + ["type"] = "dsp", + name = "Midi Passthru", + category = "Example", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An Example Audio/MIDI Passthrough Plugin using Buffer Pointers]] +} + +-- return possible audio i/o configurations +function dsp_ioconfig () + -- -1, -1 = any number of channels as long as input and output count matches + -- require 1 MIDI in, 1 MIDI out. + return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, } +end + +-- "dsp_runmap" uses Ardour's internal processor API, eqivalent to +-- 'connect_and_run()". There is no overhead (mapping, translating buffers). +-- The lua implementation is responsible to map all the buffers directly. +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:ChanMapping + + local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- get index of the 1st mapped midi input buffer + + if ib ~= ARDOUR.ChanMapping.Invalid then + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:MidiBuffer + local mb = bufs:get_midi (ib) -- get the mapped buffer + local events = mb:table () -- copy event list into a lua table + + -- iterate over all MIDI events + for _, e in pairs (events) do + -- e is-a http://manual.ardour.org/lua-scripting/class_reference/#Evoral:MidiEvent + + -- do something with the event e.g. + print (e:channel (), e:time (), e:size (), e:buffer ():array ()[1], e:buffer ():get_table (e:size ())[1]) + end + end + + ---- + -- The following code is needed with "dsp_runmap" to work for arbitrary pin connections + -- this passes though all audio/midi data unprocessed. + + ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio")) + ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi")) + + -- equivalent lua code. + -- NOTE: the lua implementation below is intended for io-config [-1,-1]. + -- It only works for actually mapped channels due to in_map:count() out_map:count() + -- being identical to the i/o pin count in this case. + -- + -- Plugins that have multiple possible configurations will need to implement + -- dsp_configure() and remember the actual channel count. + -- + -- ARDOUR.DSP.process_map() does iterate over the mapping itself and works generally. + -- Still the lua code below does lend itself as elaborate example. + -- + --[[ + + local audio_ins = in_map:count (): n_audio () -- number of mapped audio input buffers + local audio_outs = out_map:count (): n_audio () -- number of mapped audio output buffers + assert (audio_outs, audio_ins) -- ioconfig [-1, -1]: must match + + -- copy audio data if any + for c = 1, audio_ins do + local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped input buffer + local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped output buffer + if ib ~= ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob then + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioBuffer + ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples) + end + end + -- Clear unconnected output buffers. + -- In case we're processing in-place some buffers may be identical, + -- so this must be done *after* copying relvant data from that port. + for c = 1, audio_outs do + local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) + local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) + if ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid then + bufs:get_audio (ob):silence (n_samples, offset) + end + end + + -- copy midi data + local midi_ins = in_map:count (): n_midi () -- number of midi input buffers + local midi_outs = out_map:count (): n_midi () -- number of midi input buffers + + -- with midi_in=1, midi_out=1 in dsp_ioconfig + -- the following will always be true + assert (midi_ins == 1) + assert (midi_outs == 1) + + for c = 1, midi_ins do + local ib = in_map:get (ARDOUR.DataType ("midi"), c - 1) + local ob = out_map:get (ARDOUR.DataType ("midi"), c - 1) + if ib ~= ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob then + bufs:get_midi (ob):copy (bufs:get_midi (ib)) + end + end + -- silence unused midi outputs + for c = 1, midi_outs do + local ib = in_map:get (ARDOUR.DataType ("midi"), c - 1) + local ob = out_map:get (ARDOUR.DataType ("midi"), c - 1) + if ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid then + bufs:get_midi (ob):silence (n_samples, offset) + end + end + --]] +end diff --git a/share/scripts/_region_transients.lua b/share/scripts/_region_transients.lua new file mode 100644 index 0000000000..de42992dd1 --- /dev/null +++ b/share/scripts/_region_transients.lua @@ -0,0 +1,16 @@ +ardour { ["type"] = "Snippet", name = "Region Transient List" } + +function factory () return function () + local sel = Editor:get_selection () + for r in sel.regions:regionlist ():iter () do + local region_pos = r:position() + local region_off = r:start() + print (r:name(), r:position(), r:start()) + local trans = r:transients() + for t in trans:iter() do + -- print absolute timeline position of transients + print (t + region_pos - region_off) + end + print ("----") + end +end end diff --git a/share/scripts/_remember_file.lua b/share/scripts/_remember_file.lua new file mode 100644 index 0000000000..7a79fc861b --- /dev/null +++ b/share/scripts/_remember_file.lua @@ -0,0 +1,37 @@ +ardour { + ["type"] = "EditorAction", + name = "File Name Test", + author = "Ardour Lua Taskforce", + description = [[Example Plugin to show to to select a file and remember the most recently used file.]] +} + +function factory () + local file_name_testscript_last_filename -- this acts as "global" variable, use a unique name + return function () + print (file_name_testscript_last_filename) -- debug + + --set filename to most recently used, fall back to use a default + local fn = file_name_testscript_last_filename or ARDOUR.LuaAPI.build_filename (Session:path (), Session:name () .. ".ardour") + + -- prepare a dialog + local dialog_options = { + { type = "file", key = "file", title = "Select a File", path = fn } + } + + -- show dialog + local od = LuaDialog.Dialog ("title", dialog_options) + local rv = od:run() + + if rv then + -- remember most recently selected file + file_name_testscript_last_filename = rv['file'] + LuaDialog.Message ("title", "set path to " .. file_name_testscript_last_filename, LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() + else + -- unset most recently used filename on dialog "cancel" + file_name_testscript_last_filename = nil + end + + od = nil + collectgarbage () + end +end diff --git a/share/scripts/_rewind.lua b/share/scripts/_rewind.lua new file mode 100644 index 0000000000..88e150612c --- /dev/null +++ b/share/scripts/_rewind.lua @@ -0,0 +1,12 @@ +ardour { + ["type"] = "EditorAction", + name = "Rewind", + author = "Ardour Lua Task Force", + description = [[An Example Ardour Editor Action Script.]] +} + +function factory (params) + return function () + Session:goto_start() + end +end diff --git a/share/scripts/_rgh_midi_track_trick.lua b/share/scripts/_rgh_midi_track_trick.lua new file mode 100644 index 0000000000..85308be0b1 --- /dev/null +++ b/share/scripts/_rgh_midi_track_trick.lua @@ -0,0 +1,81 @@ +ardour { + ["type"] = "EditorAction", + name = "Rob's 16 MIDI Trick Pony", + description = [[clearly broken approach to go about things]] +} + +function route_setup () + return { + ['Insert_at'] = ARDOUR.PresentationInfo.max_order, + ['name'] = 'Sweet16', + ['group'] = false, -- return value will be a RouteGroup* or nil + } +end + +function factory (p) return function () + local name = "Sweet16" + local insert_at = ARDOUR.PresentationInfo.max_order + local group = nil + + -- check for "MIDI Channel Map" LV2 from x42 midifilters.lv2 + if ARDOUR.LuaAPI.new_plugin_info ("http://gareus.org/oss/lv2/midifilter#channelmap", ARDOUR.PluginType.LV2):isnil () then + LuaDialog.Message ("16 MIDI Tracks", "Error: Plugin 'MIDI Simple Channel Map' was not found.", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + return + end + + if type (p) == 'table' and p['how_many'] ~= nil then + -- used from the AddRouteDialog (or w/action_params) + name = p["name"] or 'Sweet16' + insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order; + group = p["group"] or nil + else + -- used standalone, prompt for name and insert position + local dialog_options = { + { type = "entry", key = "name", default = 'Sweet16', title = "Name Prefix" }, + { type = "entry", key = "group", default = '', title = "Group (empty for none)" }, + { type = "dropdown", key = "insertpos", title = "Position", default = "Last", values = + { + ["First"] = ArdourUI.InsertAt.First, + ["Before Selection"] = ArdourUI.InsertAt.BeforeSelection, + ["After Selection"] = ArdourUI.InsertAt.AfterSelection, + ["Last"] = ArdourUI.InsertAt.Last + } + } + } + + local od = LuaDialog.Dialog ("16 MIDI Tracks", dialog_options) + local rv = od:run() + if (not rv) then return end + name = rv['name'] or 'Sweet16' + if rv['insertpos'] then + insert_at = ArdourUI.translate_order (rv['insertpos']) + end + if rv['group'] and rv['group'] ~= '' then + group = Session:new_route_group (rv['group']) + end + end + collectgarbage () + + -- all systems go + + local tl = Session:new_midi_track ( + ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1), + ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1), + true, -- strict i/o + ARDOUR.PluginInfo(), nil, -- no instrument, no instrument preset + group, + 16, -- how many + name, insert_at, ARDOUR.TrackMode.Normal) + + local i = 1 + for track in tl:iter() do + local p = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/midifilter#channelmap", ARDOUR.PluginType.LV2, "") + assert (not p:isnil ()) + track:add_processor_by_index(p, 0, nil, true) + for j = 1, 16 do + ARDOUR.LuaAPI.set_processor_param (p, j, i) + end + i = i + 1 + end + collectgarbage () -- drop references to tracks. +end end diff --git a/share/scripts/_route_template_generic_audio.lua b/share/scripts/_route_template_generic_audio.lua new file mode 100644 index 0000000000..7dabce8aef --- /dev/null +++ b/share/scripts/_route_template_generic_audio.lua @@ -0,0 +1,119 @@ +ardour { + ["type"] = "EditorAction", + name = "Generic Audio Track", + description = [[Add Audio tracks, by default as many as there are physical inputs]] +} + +-- If a route_setup function is present in an Editor Action Script +-- the script is also listed in the "add track/bus" dialog as meta-template +-- +-- The function is expected to return a Lua table. The table may be empty. +function route_setup () + local e = Session:engine() + local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector()) + return + { + -- keys control which AddRouteDialog controls are made sensitive. + -- The following keys accept a default value to pre-seed the dialog. + ['how_many'] = t[4]:size(), + ['name'] = 'Audio', + ['channels'] = 2, + ['track_mode'] = ARDOUR.TrackMode.Normal, + ['strict_io'] = true, + -- these keys just need to be set (to something other than nil) + -- in order to set the control sensitives + ['insert_at'] = ARDOUR.PresentationInfo.max_order, + ['group'] = false, -- return value will be a RouteGroup* + ['instrument'] = nil, -- return value will be a PluginInfoPtr + } +end + +-- The Script can be used as EditorAction in which case it *could* +-- optionally provide instantiation parmaters.. +--[[ +function action_params () + return + { + ['how_many'] = { title = "How Many tracks to add", default = "1" }, + ["name"] = { title = "Track Name Prefix", default = "Audio" }, + } +end +--]] + + +function factory (p) + -- when used from the AddRouteDialog (or w/action_params) + if type (p) == 'table' and p['how_many'] ~= nil then + return function () + -- When called from the AddRouteDialog, 'p' will be a table with + -- keys as described in route_setup() above. + local name = p["name"] or 'Audio' + local how_many = p["how_many"] or 1 + local channels = p["channels"] or 1 + local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order; + local group = p["group"] or nil + local mode = p["track_mode"] or ARDOUR.TrackMode.Normal + local strict_io = p["strict_io"] or false + local chan_out = 0 + + if ARDOUR.config():get_output_auto_connect() == ARDOUR.AutoConnectOption.AutoConnectMaster then + if not Session:master_out():isnil() then + chan_out = Session:master_out():n_inputs ():n_audio () + end + end + + if chan_out == 0 then + chan_out = channels; + end + + local tl = Session:new_audio_track (channels, chan_out, group, how_many, name, insert_at, mode) + + if strict_io then + for t in tl:iter() do + t:set_strict_io (true) + end + end + end + end + -- when used as standalone (no action parameters): interactive + return function () + local e = Session:engine() + local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector()) + local tracks = t[4]:size(); + + local dialog_options = { + { type = "number", key = "tracks", title = "Create Tracks", min = 1, max = 128, step = 1, digits = 0, default = tracks }, + { type = "entry", key = "name", default = 'Audio', title = "Name Prefix" }, + { type = "checkbox", key = "stereo", default = false, title = "Stereo" }, + { type = "checkbox", key = "recarm", default = false, title = "Record Arm Tracks" }, + } + + local dlg = LuaDialog.Dialog ("Create Audio Tracks", dialog_options) + local rv = dlg:run() + if (not rv or rv['tracks'] == 0) then + return + end + + local chan_in = stereo and 2 or 1 + local chan_out = 0 + + if ARDOUR.config():get_output_auto_connect() == ARDOUR.AutoConnectOption.AutoConnectMaster then + if not Session:master_out():isnil() then + chan_out = Session:master_out():n_inputs ():n_audio () + end + end + + if chan_out == 0 then + chan_out = chan_in; + end + + -- create tracks + local tl = Session:new_audio_track (chan_in, chan_out, nil, rv['tracks'], "", ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal) + -- and optionally record-arm them + if rv['recarm'] then + for track in tl:iter() do + track:rec_enable_control ():set_value (1, PBD.GroupControlDisposition.NoGroup) + end + end + end +end diff --git a/share/scripts/_route_template_generic_midi.lua b/share/scripts/_route_template_generic_midi.lua new file mode 100644 index 0000000000..a62197d137 --- /dev/null +++ b/share/scripts/_route_template_generic_midi.lua @@ -0,0 +1,78 @@ +ardour { + ["type"] = "EditorAction", + name = "Generic MIDI Track", + description = [[Example]] +} + +-- If a route_setup function is present in an Editor Action Script +-- the script is also listed in the "add track/bus" dialog as meta-template +-- +-- The function is expected to return a Lua table. The table may be empty. +function route_setup () + return + { + -- keys control which AddRouteDialog controls are made sensitive. + -- The following keys accept a default value to pre-seed the dialog. + ['how_many'] = 1, + ['name'] = 'MIDI', + ['channels'] = nil, + ['track_mode'] = nil, + ['strict_io'] = true, + -- these keys just need to be set (to something other than nil) + -- in order to set the control sensitives + ['insert_at'] = ARDOUR.PresentationInfo.max_order, + ['group'] = false, -- return value will be a RouteGroup* + ['instrument'] = true, -- return value will be a PluginInfoPtr + } +end + +-- The Script can be used as EditorAction in which case it can +-- optionally provide instantiation parmaters +function action_params () + return + { + ['how_many'] = { title = "How Many tracks to add", default = "1" }, + ["name"] = { title = "Track Name Prefix", default = "MIDI" }, + ["instrument"] = { title = "Add Instrument", default = "true" }, + } +end + + +function factory (params) return function () + -- When called from the AddRouteDialog, 'params' will be a table with + -- keys as described in route_setup() above. + + local p = params or route_setup () + local name = p["name"] or 'Audio' + local how_many = p["how_many"] or 1 + local insert_at = p["insert_at"] or ARDOUR.PresentationInfo.max_order; + local group = p["group"] or nil + local strict_io = p["strict_io"] or false + local instrument = p["instrument"] or nil + + -- used in 'action-script mode' + if instrument == "true" then + instrument = ARDOUR.LuaAPI.new_plugin_info ("http://gareus.org/oss/lv2/gmsynth", ARDOUR.PluginType.LV2) -- general midi synth + if instrument:isnil () then + instrument = ARDOUR.LuaAPI.new_plugin_info ("https://community.ardour.org/node/7596", ARDOUR.PluginType.LV2) -- reasonable synth + end + if instrument:isnil () then + LuaDialog.Message ("MIDI track add", "Cannot find instrument plugin", + LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + return + end + end + + -- add no instrument + if type (instrument) ~= "userdata" then + instrument = ARDOUR.PluginInfo () + end + + Session:new_midi_track( + ARDOUR.ChanCount(ARDOUR.DataType ("midi"), 1), + ARDOUR.ChanCount(ARDOUR.DataType ("audio"), 2), + strict_io, + instrument, nil, + group, how_many, name, insert_at, ARDOUR.TrackMode.Normal) + +end end diff --git a/share/scripts/_rubberband_swing.lua b/share/scripts/_rubberband_swing.lua new file mode 100644 index 0000000000..f9c703284d --- /dev/null +++ b/share/scripts/_rubberband_swing.lua @@ -0,0 +1,175 @@ +ardour { + ["type"] = "EditorAction", + name = "Swing It (Rubberband)", + license = "MIT", + author = "Ardour Team", +description = [[ +Create a 'swing feel' in selected regions. + +The beat position of selected audio regions is analyzed, +then the audio is time-stretched, moving 8th notes back in +time while keeping 1/4-note beats in place to produce +a rhythmic swing style. + +(This script also servers as example for both VAMP +analysis as well as Rubberband region stretching.) + +Kudos to Chris Cannam. +]] +} + +function factory () return function () + + -- helper function -- + -- there is currently no direct way to find the track + -- corresponding to a [selected] region + function find_track_for_region (region_id) + for route in Session:get_tracks ():iter () do + local track = route:to_track () + local pl = track:playlist () + if not pl:region_by_id (region_id):isnil () then + return track + end + end + assert (0) -- can't happen, region must be in a playlist + end + + -- get Editor selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- Instantiate the QM BarBeat Tracker + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp + -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-barbeattracker + local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-barbeattracker", Session:nominal_sample_rate ()) + + -- prepare undo operation + Session:begin_reversible_command ("Rubberband Regions") + local add_undo = false -- keep track if something has changed + + -- for each selected region + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region + + -- test if it's an audio region + local ar = r:to_audioregion () + if ar:isnil () then + goto next + end + + -- create Rubberband stretcher + local rb = ARDOUR.LuaAPI.Rubberband (ar, false) + + -- the rubberband-filter also implements the readable API. + -- https://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable + -- This allows to read from the master-source of the given audio-region. + -- Any prior time-stretch or pitch-shift are ignored when reading, however + -- processing retains the previous settings + local max_pos = rb:readable ():readable_length () + + -- prepare table to hold analysis results + -- the beat-map is a table holding audio-sample positions: + -- [from] = to + local beat_map = {} + local prev_beat = 0 + + -- construct a progress-dialog with cancle button + local pdialog = LuaDialog.ProgressWindow ("Rubberband", true) + -- progress dialog callbacks + function vamp_callback (_, pos) + return pdialog:progress (pos / max_pos, "Analyzing") + end + function rb_progress (_, pos) + return pdialog:progress (pos / max_pos, "Stretching") + end + + -- run VAMP plugin, analyze the first channel of the audio-region + vamp:analyze (rb:readable (), 0, vamp_callback) + + -- getRemainingFeatures returns a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet + -- get the first output. here: Beats, estimated beat locations & beat-number + -- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList + local fl = vamp:plugin ():getRemainingFeatures ():at (0) + local beatcount = 0 + -- iterate over returned features + for f in fl:iter () do + -- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature + local fn = Vamp.RealTime.realTime2Frame (f.timestamp, Session:nominal_sample_rate ()) + beat_map[fn] = fn -- keep beats (1/4 notes) unchanged + if prev_beat > 0 then + -- move the half beats (1/8th) back + local diff = (fn - prev_beat) / 2 + beat_map[fn - diff] = fn - diff + diff / 3 -- moderate swing 2:1 (triplet) + --beat_map[fn - diff] = fn - diff + diff / 2 -- hard swing 3:1 (dotted 8th) + beatcount = beatcount + 1 + end + prev_beat = fn + end + -- reset the plugin state (prepare for next iteration) + vamp:reset () + + if pdialog:canceled () then goto out end + + -- skip regions shorter than a bar + if beatcount < 8 then + pdialog:done () + goto next + end + + -- configure rubberband stretch tool + rb:set_strech_and_pitch (1, 1) -- no overall stretching, no pitch-shift + rb:set_mapping (beat_map) -- apply beat-map from/to + + -- now stretch the region + local nar = rb:process (rb_progress) + + if pdialog:canceled () then goto out end + + -- hide modal progress dialog and destroy it + pdialog:done () + pdialog = nil + + -- replace region + if not nar:isnil () then + print ("new audio region: ", nar:name (), nar:length ()) + local track = find_track_for_region (r:to_stateful ():id ()) + local playlist = track:playlist () + playlist:to_stateful ():clear_changes () -- prepare undo + playlist:remove_region (r) + playlist:add_region (nar, r:position (), 1, false, 0, 0, false) + -- create a diff of the performed work, add it to the session's undo stack + -- and check if it is not empty + if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then + add_undo = true + end + end + + ::next:: + end + + ::out:: + + -- all done, commit the combined Undo Operation + if add_undo then + -- the 'nil' Command here mean to use the collected diffs added above + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end + + -- clean up, unload vamp plugin + vamp = nil + collectgarbage () +end end + + +function icon (params) return function (ctx, width, height, fg) + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(width * .7) .. "px") + txt:set_text ("\u{266b}\u{266a}") -- 8th note symbols + local tw, th = txt:get_pixel_size () + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/_session_load_hook.lua b/share/scripts/_session_load_hook.lua new file mode 100644 index 0000000000..82546814f8 --- /dev/null +++ b/share/scripts/_session_load_hook.lua @@ -0,0 +1,32 @@ +ardour { + ["type"] = "EditorHook", + name = "Load Session Hook Example", + author = "Ardour Lua Task Force", + description = "Display some dialogs during session load and execute actions", +} + +-- subscribe to signals +-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal +function signals () + s = LuaSignal.Set() + s:add ({[LuaSignal.SetSession] = true}) + return s +end + +-- create callback functions +function factory () return function (signal, ...) + assert (signal == LuaSignal.SetSession) + local md = LuaDialog.Message ("Set Session", "Loading Session:" .. Session:name(), LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close) + md:run() + + local dialog_options = { + { type = "checkbox", key = "tempo", default = true, title = "Show Tempo Ruler" }, + { type = "checkbox", key = "meter", default = true, title = "Show Meter Ruler" }, + } + local dlg = LuaDialog.Dialog ("Tweak Rulers", dialog_options) + local rv = dlg:run() + if (rv) then + Editor:set_toggleaction ("Rulers", "toggle-tempo-ruler", rv['tempo']) + Editor:set_toggleaction ("Rulers", "toggle-meter-ruler", rv['meter']) + end +end end diff --git a/share/scripts/_session_test.lua b/share/scripts/_session_test.lua new file mode 100644 index 0000000000..f6adea38c7 --- /dev/null +++ b/share/scripts/_session_test.lua @@ -0,0 +1,34 @@ +ardour { + ["type"] = "session", + name = "Good Night", + author = "Ardour Lua Task Force", + description = [[ + Example Ardour Session Script. + Session scripts are called at the beginning of every process-callback (before doing any audio processing). + This example stops the transport after rolling for a configurable time which can be set when instantiating the script.]] +} + +function sess_params () + return + { + ["print"] = { title = "Debug Print (yes/no)", default = "no", optional = true }, + ["time"] = { title = "Timeout (sec)", default = "90", optional = false }, + } +end + +function factory (params) + return function (n_samples) + local p = params["print"] or "no" + local timeout = params["time"] or 90 + a = a or 0 + if p ~= "no" then print (a, n_samples, Session:sample_rate (), Session:transport_rolling ()) end -- debug output (not rt safe) + if (not Session:transport_rolling()) then + a = 0 + return + end + a = a + n_samples + if (a > timeout * Session:sample_rate()) then + Session:request_transport_speed(0.0, true, ARDOUR.TransportRequestSource.TRS_Engine) + end + end +end diff --git a/share/scripts/_smash.lua b/share/scripts/_smash.lua new file mode 100644 index 0000000000..2ebcac19aa --- /dev/null +++ b/share/scripts/_smash.lua @@ -0,0 +1,31 @@ +ardour { ["type"] = "dsp", name = "Sound Smasher", category = "Dynamics", license = "MIT", author = "Ardour Lua Task Force", description = [[Another simple DSP example]] } + +function dsp_ioconfig () return + -- -1, -1 = any number of channels as long as input and output count matches + { { audio_in = -1, audio_out = -1}, } +end + + +-- the DSP callback function to process audio audio +-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray +function dsp_run (ins, outs, n_samples) + for c = 1, #outs do -- for each output channel (count from 1 to number of output channels) + + if ins[c] ~= outs[c] then -- if processing is not in-place.. + ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) -- ..copy data from input to output. + end + + -- direct audio data access, in-place processing of output buffer + local buf = outs[c]:array() -- get channel's 'c' data as lua array reference + + -- process all audio samples + for s = 1, n_samples do + buf[s] = math.atan (1.5707 * buf[s]) -- some non-linear gain. + + -- NOTE: doing the maths per sample in lua is not super-efficient + -- (vs C/C++ vectorized functions -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP) + -- but it is very convenient, especially for prototypes and quick solutions. + end + + end +end diff --git a/share/scripts/_sort_tracks_by_name.lua b/share/scripts/_sort_tracks_by_name.lua new file mode 100644 index 0000000000..36dfdc970c --- /dev/null +++ b/share/scripts/_sort_tracks_by_name.lua @@ -0,0 +1,36 @@ +ardour { + ["type"] = "EditorAction", + name = "Track Sort", + author = "Ardour Lua Taskforce", + description = [[Sort tracks alphabetically by name]] +} + +function factory () return function () + + -- sort compare function + -- a,b here are http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route + -- return true if route "a" should be ordered before route "b" + function tsort (a, b) + return a:name() < b:name() + end + + -- create a sortable list of tracks + local tracklist = {} + for t in Session:get_tracks():iter() do + table.insert(tracklist, t) + end + + -- sort the list using the compare function + table.sort(tracklist, tsort) + + -- traverse the sorted list and assign "presentation-order" to each track + local pos = 1; + for _, t in ipairs(tracklist) do + t:set_presentation_order(pos) + pos = pos + 1 + end + + -- drop all track references + tracklist = nil + collectgarbage () +end end diff --git a/share/scripts/_spike_synth.lua b/share/scripts/_spike_synth.lua new file mode 100644 index 0000000000..167e0e27d2 --- /dev/null +++ b/share/scripts/_spike_synth.lua @@ -0,0 +1,37 @@ +ardour { + ["type"] = "dsp", + name = "Spike Synth", + category = "Instrument", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[A debug and test-instrumentation synth. This plugin is useful with Ardour's "Dummy" backend "Engine-Pulse" mode to verify capture alignment. This plugin generate the exact same audio-signal from MIDI data that the backend also generates: Note-on: +1, Note-off: -1.]] +} + +function dsp_ioconfig () + return { { midi_in = 1, audio_in = 0, audio_out = 1} } +end + +function dsp_run (ins, outs, n_samples) + local a = {} + for s = 1, n_samples do a[s] = 0 end + + for c = 1,#outs do + ARDOUR.DSP.memset (outs[c], 0, n_samples) + end + + assert (type(midiin) == "table") + for _,b in pairs (midiin) do + local t = b["time"] -- t = [ 1 .. n_samples ] + local d = b["data"] -- get midi-event + if (#d == 3 and (d[1] & 240) == 144) then -- note on + for c = 1,#outs do + outs[c]:array()[t] = 1.0 + end + end + if (#d == 3 and (d[1] & 240) == 128) then -- note off + for c = 1,#outs do + outs[c]:array()[t] = -1.0 + end + end + end +end diff --git a/share/scripts/_split_benchmark.lua b/share/scripts/_split_benchmark.lua new file mode 100644 index 0000000000..6ab60e44bc --- /dev/null +++ b/share/scripts/_split_benchmark.lua @@ -0,0 +1,58 @@ +-- [disable CPU freq scaling for benchmark] +-- create a session +-- add 16 mono tracks +-- record 2-3 mins on each track starting at 00:00:00:00 +-- rewind the playhead to 00:00:00:00 +-- run this script in Menu > Window. Scripting 10 times +ardour { ["type"] = "EditorAction", name = "Split Benchmark" } + +function factory (params) return function () + + function split_at (pos) + local add_undo = false -- keep track if something has changed + Session:begin_reversible_command ("Auto Region Split") + for route in Session:get_tracks():iter() do + local playlist = route:to_track():playlist () + playlist:to_stateful ():clear_changes () + for region in playlist:regions_at (pos):iter () do + playlist:split_region (region, ARDOUR.MusicSample (pos, 0)) + end + if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then + add_undo = true + end + end + if add_undo then + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end + end + + function count_regions () + local total = 0 + for route in Session:get_tracks():iter() do + total = total + route:to_track():playlist():region_list():size() + end + return total + end + + for x = 1, 3 do + local playhead = Session:transport_sample () + + local step = Session:samples_per_timecode_frame() + local n_steps = 20 + + local t_start = ARDOUR.LuaAPI.monotonic_time () + for i = 1, n_steps do + split_at (playhead + step * i) + end + local t_end = ARDOUR.LuaAPI.monotonic_time () + + Session:request_locate((playhead + step * n_steps), ARDOUR.LocateTransportDisposition.MustStop, ARDOUR.TransportRequestSource.TRS_UI) + print (count_regions (), (t_end - t_start) / 1000 / n_steps) + collectgarbage (); + ARDOUR.LuaAPI.usleep(500000) + end + + +end end diff --git a/share/scripts/_stereo_to_mono.lua b/share/scripts/_stereo_to_mono.lua new file mode 100644 index 0000000000..81fa0316a7 --- /dev/null +++ b/share/scripts/_stereo_to_mono.lua @@ -0,0 +1,57 @@ +ardour { ["type"] = "EditorAction", name = "Stereo to Mono", + license = "MIT", + author = "Ardour Team", + description = [[Convert a Stereo Track into two Mono Tracks]] +} + + +function factory (params) return function () + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + -- the Ardour Selection can include multiple items + -- (regions, tracks, ranges, markers, automation, midi-notes etc) + local sel = Editor:get_selection () + + -- for each track.. + for t in sel.tracks:routelist ():iter () do + local track = t:to_track () + if track:isnil() then goto next end + + -- only audio tracks + local playlist = track:playlist () + if playlist:data_type ():to_string () ~= "audio" then goto next end + + -- skip tracks without any regions + if playlist:region_list ():size() == 0 then goto next end + + -- we can't access diskstream n_channels() + local channels = track:n_inputs(): n_audio() + + -- stereo only + if channels ~= 2 then goto next end + + -- create 2 new tracks (using the name of the original track)( + local newtracks = Session:new_audio_track (2, 2, nil, 2, t:name(), ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal) + assert (newtracks:size() == 2) + + for r in playlist:region_list ():iter () do + local region = r:to_audioregion () + local rl = ARDOUR.RegionVector () + local _, rv = region:separate_by_channel (rl) + assert (rv[1]:size () == 2) + -- 1:1 mapping of regions to new tacks + local plc = 1 + for nr in rv[1]:iter () do + local pl = newtracks:table()[plc]:playlist() + pl:add_region (nr, r:position(), 1, false, 0, 0, false) + plc = plc + 1 + end + end + + -- TODO remove the old track + + -- drop references for good. + collectgarbage () + ::next:: + end + +end end diff --git a/share/scripts/_system_exec.lua b/share/scripts/_system_exec.lua new file mode 100644 index 0000000000..281f5dee0b --- /dev/null +++ b/share/scripts/_system_exec.lua @@ -0,0 +1,20 @@ +ardour { ["type"] = "EditorAction", name = "System Exec" } + +function factory () return function () + -- ** EXAMPLES TO RUN EXTERNAL APPLICATIONS ** -- + + -- run a command in a shell and wait for it to complete. + -- + -- Details: basically just system(3), except on Unix like systems with + -- memory-locking, this call is special-cased to use vfork and close + -- file-descriptors. On other systems it defaults to Lua's os-library + -- built-in os.execute system() call. + os.execute ("date > /tmp/testdate") + + -- os.forkexec() works as fire-and-forget. execv(3) style + -- + -- Details: It calls vfork() and exec() under the hood, passing each + -- argument separately to exec (and needs a full-path to the binary). + os.forkexec ("/usr/bin/xterm") + os.forkexec ("/bin/sh", "-c", "import --frame \"/tmp/scr_$(date).png\"") +end end diff --git a/share/scripts/_tempo_map_dump.lua b/share/scripts/_tempo_map_dump.lua new file mode 100644 index 0000000000..7e85cd5451 --- /dev/null +++ b/share/scripts/_tempo_map_dump.lua @@ -0,0 +1,14 @@ +ardour { ["type"] = "Snippet", name = "Tempo Map Dump" } + +function factory () return function () + + local tm = Session:tempo_map () + local ts = tm:tempo_section_at_sample (0) + + while true do + print ("TS @", ts:sample(), " | ", ts:to_tempo():note_types_per_minute (), "..", ts:to_tempo():end_note_types_per_minute (), "bpm") + ts = tm:next_tempo_section (ts) + if not ts then break end + end + +end end diff --git a/share/scripts/_toggle_monitor_section.lua b/share/scripts/_toggle_monitor_section.lua new file mode 100644 index 0000000000..e097a94a32 --- /dev/null +++ b/share/scripts/_toggle_monitor_section.lua @@ -0,0 +1,10 @@ +ardour { ["type"] = "Snippet", name = "Toggle Monitor Section" } + +function factory () return function () + if Session:monitor_out():isnil() then + ARDOUR.config():set_use_monitor_bus (true) + else + ARDOUR.config():set_use_monitor_bus (false) + collectgarbage () + end +end end diff --git a/share/scripts/_tx_raw_midi_from_file.lua b/share/scripts/_tx_raw_midi_from_file.lua new file mode 100644 index 0000000000..ac45cd1b10 --- /dev/null +++ b/share/scripts/_tx_raw_midi_from_file.lua @@ -0,0 +1,128 @@ +ardour { + ["type"] = "EditorAction", + name = "Send Raw MIDI from File", + license = "MIT", + author = "Ardour Team", + description = [[Read raw binary midi (.syx) from file and send it to a control port]] +} + +function factory () return function () + + function portlist () + local rv = {} + local a = Session:engine() + local _, t = a:get_ports (ARDOUR.DataType("midi"), ARDOUR.PortList()) + for p in t[2]:iter() do + local amp = p:to_asyncmidiport () + if amp:isnil() or not amp:sends_output() then goto continue end + rv[amp:name()] = amp + print (amp:name(), amp:sends_output()) + ::continue:: + end + return rv + end + + local dialog_options = { + { type = "file", key = "file", title = "Select .syx MIDI file" }, + { type = "dropdown", key = "port", title = "Target Port", values = portlist () } + } + + local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run () + dialog_options = nil -- drop references (ports, shared ptr) + collectgarbage () -- and release the references immediately + + if not rv then return end -- user cancelled + + local f = io.open (rv["file"], "rb") + + if not f then + LuaDialog.Message ("Raw MIDI Tx", "File Not Found", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run () + goto out + end + + do -- scope for 'local' + local size = f:seek("end") -- determine file size + f:seek("set", 0) + + if size > 1048576 then + local ok = LuaDialog.Message ("Raw MIDI Tx", + string.format ("File is larger than 1MB.\nFile-size = %.1f kB\n\nContinue?", size / 1024), + LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run () + if ok ~= LuaDialog.Response.Yes then + f:close () + goto out + end + end + end + + do -- scope for 'local' + local midi_byte_count = 0 + local total_read = 0 + local message_count = 0 + local long_message = false + + local async_midi_port = rv["port"] -- reference to port + local parser = ARDOUR.RawMidiParser () -- construct a MIDI parser + + while true do + -- read file in 64byte chunks + local bytes = f:read (64) + if not bytes then break end + total_read = total_read + #bytes + + -- parse MIDI data byte-by-byte + for i = 1, #bytes do + if parser:process_byte (bytes:byte (i)) then + if parser:buffer_size () > 255 then + long_message = true + print ("WARNING -- single large message > 255, bytes: ", parser:buffer_size ()) + end + -- parsed complete normalized MIDI message, send it + async_midi_port:write (parser:midi_buffer (), parser:buffer_size (), 0) + + -- Physical MIDI is sent at 31.25kBaud. + -- Every message is sent as 10bit message on the wire, + -- so every MIDI byte needs 320usec. + ARDOUR.LuaAPI.usleep (400 * parser:buffer_size ()) + + -- count msgs and valid bytes sent + midi_byte_count = midi_byte_count + parser:buffer_size () + message_count = message_count + 1 + if 0 == message_count % 50 then + -- print() wakes up the GUI, prevent stalling the event loop + print ("Sent", message_count, "messages, bytes so far: ", midi_byte_count) + end + end + end + end + + f:close () + print ("Sent", message_count, "messages, total bytes: ", midi_byte_count, "/", total_read) + + if long_message then + LuaDialog.Message ("Raw MIDI Tx", "Dataset contained messages longer than 127 bytes. Which may or may not have been transmitted successfully.", LuaDialog.MessageType.Warning, LuaDialog.ButtonType.Close):run () + end + end + + ::out:: + rv = nil + collectgarbage () +end end + +function icon (params) return function (ctx, width, height, fg) + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .45) .. "px") + txt:set_text ("S") + ctx:move_to (1, 1) + txt:show_in_cairo_context (ctx) + + txt:set_text ("Y") + local tw, th = txt:get_pixel_size () + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:show_in_cairo_context (ctx) + + txt:set_text ("X") + tw, th = txt:get_pixel_size () + ctx:move_to ((width - tw - 1), (height - th -1)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/_vamp_example.lua b/share/scripts/_vamp_example.lua new file mode 100644 index 0000000000..73552d638c --- /dev/null +++ b/share/scripts/_vamp_example.lua @@ -0,0 +1,63 @@ +ardour { ["type"] = "Snippet", name = "Vamp Plugin Example" } + +function factory () return function () + + -- get a list of all available plugins + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp + -- returns a http://manual.ardour.org/lua-scripting/class_reference/#C:StringVector + local plugins = ARDOUR.LuaAPI.Vamp.list_plugins (); + for id in plugins:iter () do + print ("--", id) + end + + local sel = Editor:get_selection () + + -- load the Vamp Plugin with Id "libardourvampplugins:dBTP" + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp + local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:dBTP", Session:nominal_sample_rate()) + print (vamp:plugin():getName()) + + -- for each selected region + for r in sel.regions:regionlist ():iter () do + print ("Region:", r:name ()) + + -- run the plugin, analyze the first channel of the audio-region + vamp:analyze (r:to_readable (), 0, nil) + + -- get analysis results + local f = vamp:plugin ():getRemainingFeatures () + + -- f is-a Vamp::Plugin::FeatureSet aka std::map + -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet + for id, featlist in f:iter () do + print (id, featlist) + end + + -- get the first FeatureList + local featurelist = f:table()[0] + -- Vamp::Plugin::FeatureList is a typedef for std::vector + for feat in featurelist:iter () do + print ("-", feat.label) + end + + -- get the first feature.. + -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature + local feature = featurelist:at(0) + -- ..and the values of the feature, which is-a std::vector + local values = feature.values + -- iterate over the std::vector + for val in values:iter () do + print ("*", val) + end + + -- access the first element of Vamp::Plugin::Feature's "values" vector + -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatVector + local value = values:at(0) + -- in case of libardourvampplugins:dBTP that's the true-peak (signal value) + local dbtp = 20 * math.log (value) / math.log(10) -- convert it to dB + print (string.format ("Region '%s': %.2f dBTP", r:name (), dbtp)) + + -- reset the plugin for the next iteration + vamp:reset () + end +end end diff --git a/share/scripts/_vamp_note_example.lua b/share/scripts/_vamp_note_example.lua new file mode 100644 index 0000000000..6a93dd8ee4 --- /dev/null +++ b/share/scripts/_vamp_note_example.lua @@ -0,0 +1,68 @@ +ardour { ["type"] = "Snippet", name = "Vamp Audio Transcription Example" } + +function factory () return function () + + -- get Editor selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + local sr = Session:nominal_sample_rate () + + -- Instantiate a Vamp Plugin + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp + local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-transcription", sr) + + -- prepare progress dialog + local progress_total = 0; + local progress_part = 0 + local pdialog = LuaDialog.ProgressWindow ("Audio to MIDI", true) + function cb (_, pos) + return pdialog:progress ((pos + progress_part) / progress_total, "Analyzing") + end + + -- calculate max progress + for r in sel.regions:regionlist ():iter () do + progress_total = progress_total + r:to_readable ():readable_length () + end + + -- for each selected region + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region + + vamp:analyze (r:to_readable (), 0, cb) + + if pdialog:canceled () then + goto out + end + + progress_part = progress_part + r:to_readable ():readable_length () + pdialog:progress (progress_part / progress_total, "Post Processing") + + print ("-- Post Processing: ", r:name ()) + + -- post-processing takes longer than actually parsing the data :( + local f = vamp:plugin ():getRemainingFeatures () + + local fl = f:table ()[0] + print (" Time (sample) | Len | Midi-Note"); + if fl then for f in fl:iter () do + assert (f.hasTimestamp and f.hasDuration); + local ft = Vamp.RealTime.realTime2Frame (f.timestamp, sr) + local fd = Vamp.RealTime.realTime2Frame (f.duration, sr) + local fn = f.values:at (0) -- midi note number + print (string.format (" %14d %7d %d", ft, fd, fn)) + end end + + -- reset the plugin (prepare for next iteration) + vamp:reset () + end + + ::out:: + -- hide modal progress dialog and destroy it + pdialog:done (); + pdialog = nil + vamp = nil; + collectgarbage () + +end end diff --git a/share/scripts/_vamp_onset_example.lua b/share/scripts/_vamp_onset_example.lua new file mode 100644 index 0000000000..adcdc16982 --- /dev/null +++ b/share/scripts/_vamp_onset_example.lua @@ -0,0 +1,83 @@ +ardour { ["type"] = "Snippet", name = "Vamp Onset Detection Example" } + +function factory () return function () + + -- get Editor selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- Instantiate a Vamp Plugin + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp + -- + -- here: the "Queen Mary Note Onset Detector" Vamp plugin (which comes with Ardour) + -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-onsetdetector + local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:qm-onsetdetector", Session:nominal_sample_rate()) + + -- prepare table to hold results + local onsets = {} + + -- for each selected region + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region + + -- prepare lua table to hold results for the given region (by name) + onsets[r:name ()] = {} + + -- callback to handle Vamp-Plugin analysis results + function callback (feats) + -- "feats" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet + -- get the first output. here: Detected note onset times + local fl = feats:table()[0] + -- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList + -- which may be empty or not nil + if fl then + -- iterate over returned features + for f in fl:iter () do + -- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature + if f.hasTimestamp then + local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000) + --print ("-", f.timestamp:toString(), fn) + table.insert (onsets[r:name ()], fn) + end + end + end + return false -- continue, !cancel + end + + -- Configure Vamp plugin + -- + -- One could query the Parameter and Program List: + -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin + -- but since the Plugin is known, we can directly refer to the plugin doc: + -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-onsetdetector + vamp:plugin ():setParameter ("dftype", 3); + vamp:plugin ():setParameter ("sensitivity", 50); + vamp:plugin ():setParameter ("whiten", 0); + -- in this case the above (3, 50, 0) is equivalent to + --vamp:plugin ():selectProgram ("General purpose"); + + -- run the plugin, analyze the first channel of the audio-region + -- + -- This uses a "high-level" convenience wrapper provided by Ardour + -- which reads raw audio-data from the region and and calls + -- f = vamp:plugin ():process (); callback (f) + vamp:analyze (r:to_readable (), 0, callback) + + -- get remaining features (end of analyis) + callback (vamp:plugin ():getRemainingFeatures ()) + + -- reset the plugin (prepare for next iteration) + vamp:reset () + end + + -- print results + for n,o in pairs(onsets) do + print ("Onset analysis for region:", n) + for _,t in ipairs(o) do + print ("-", t) + end + end + +end end diff --git a/share/scripts/_vamp_tempomap_example.lua b/share/scripts/_vamp_tempomap_example.lua new file mode 100644 index 0000000000..1c216c06f3 --- /dev/null +++ b/share/scripts/_vamp_tempomap_example.lua @@ -0,0 +1,85 @@ +ardour { ["type"] = "Snippet", name = "Vamp TempoMap Test" } + +function factory () return function () + + -- get Editor selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Editor + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- Instantiate the QM BarBeat Tracker + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI:Vamp + -- http://vamp-plugins.org/plugin-doc/qm-vamp-plugins.html#qm-barbeattracker + local vamp = ARDOUR.LuaAPI.Vamp("libardourvampplugins:qm-barbeattracker", Session:nominal_sample_rate()) + + -- prepare table to hold results + local beats = {} + local bars = {} + + -- for each selected region + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- "r" is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region + + -- prepare lua table to hold results for the given region (by name) + beats[r:name ()] = {} + bars[r:name ()] = {} + + -- callback to handle Vamp-Plugin analysis results + function callback (feats) + -- "feats" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureSet + + -- get the first output. here: Beats, estimated beat locations & beat-number + local fl = feats:table()[0] + -- "fl" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:FeatureList + -- which may be empty or not nil + if fl then + -- iterate over returned features + for f in fl:iter () do + -- "f" is-a http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:Feature + if f.hasTimestamp then + local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000) + table.insert (beats[r:name ()], {pos = fn, beat = f.label}) + end + end + end + + -- get the 2nd output. here: estimated bar locations + local fl = feats:table()[1] + if fl then + for f in fl:iter () do + if f.hasTimestamp then + local fn = Vamp.RealTime.realTime2Frame (f.timestamp, 48000) + table.insert (bars[r:name ()], fn) + end + end + end + return false -- continue, !cancel + end + + vamp:plugin ():setParameter ("Beats Per Bar", 4); -- TODO ask + + -- run the plugin, analyze the first channel of the audio-region + vamp:analyze (r:to_readable (), 0, callback) + -- get remaining features (end of analyis) + callback (vamp:plugin ():getRemainingFeatures ()) + -- reset the plugin (prepare for next iteration) + vamp:reset () + end + + -- print results (for now) + -- TODO: calculate and set tempo-map + for n,o in pairs(bars) do + print ("Bar analysis for region:", n) + for _,t in ipairs(o) do + print ("-", t) + end + end + for n,o in pairs(beats) do + print ("Beat analysis for region:", n) + for _,t in ipairs(o) do + print ("-", t['pos'], t['beat']) + end + end + +end end diff --git a/share/scripts/_varispeed_callback.lua b/share/scripts/_varispeed_callback.lua new file mode 100644 index 0000000000..fe3f2721c1 --- /dev/null +++ b/share/scripts/_varispeed_callback.lua @@ -0,0 +1,32 @@ +ardour { + ["type"] = "EditorHook", + name = "Varispeed Test - 100ms Callback", + author = "Ardour Lua Task Force", + description = "An example script that invokes a callback a every 0.1sec and modifies the transport speed", +} + +function signals () + s = LuaSignal.Set() + s:add ( + { + [LuaSignal.LuaTimerDS] = true, + } + ) + return s +end + +function factory (params) + -- upindex variables + local cnt = 0 + local speed = 0 + local delta = 0.01 + return function (signal, ref, ...) + cnt = (cnt + 1) % 5 -- divide clock: every half a second + if cnt == 0 then + if speed < -0.25 then delta = delta * -1 end + if speed > 0.25 then delta = delta * -1 end + speed = speed + delta + Session:request_transport_speed (speed, true, ARDOUR.TransportRequestSource.TRS_UI) + end + end +end diff --git a/share/scripts/_vca_slave_assign.lua b/share/scripts/_vca_slave_assign.lua new file mode 100644 index 0000000000..616c0abd98 --- /dev/null +++ b/share/scripts/_vca_slave_assign.lua @@ -0,0 +1,73 @@ +ardour { ["type"] = "Snippet", name = "VCA Slave Examples", + license = "MIT", + author = "Ardour Team", +} + +function factory () return function () + -- find possible masters & slave, allow selection in dropdown menu + local targets = {} + local sources = {} + local have_masters = false + local have_slaves = false + + for v in Session:vca_manager ():vcas() :iter () do -- for each VCA + sources [v:name ()] = v + have_masters = true + end + + for s in Session:get_stripables ():iter () do -- for every track/bus/vca + if s:is_monitor () or s:is_auditioner () then goto nextroute end -- skip special routes + targets [s:name ()] = s + have_slaves = true; + ::nextroute:: + end + + -- bail out if there are no parameters + if not have_slaves then + LuaDialog.Message ("VCA Slave Example", "No Slavables found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + sources = nil + collectgarbage () + return + end + if not have_masters then + LuaDialog.Message ("VCA Slave Example", "No VCA masters found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + targets = nil + collectgarbage () + return + end + + -- create a dialog, ask user which master to assign to which slave + local dialog_options = { + { type = "dropdown", key = "master", title = "Control Master", values = sources }, + { type = "dropdown", key = "slave", title = "Control Slave", values = targets } + } + local rv = LuaDialog.Dialog ("Select VCA assignment", dialog_options):run () + + targets = nil -- drop references (the table holds shared-pointer references to all strips) + collectgarbage () -- and release the references immediately + + if not rv then return end -- user canceled the operation + + -- parse user response + local mst = rv["master"] + local slv = rv["slave"] + assert (not slv:to_slavable ():isnil ()) + + -- test if mst is already controlled by slv (directly or indirectly) + -- if so, don't allow the connection + if (not slv:to_slavable ():assigned_to (Session:vca_manager(), mst)) then + -- if slv controlled by master, disconnect it + if (slv:slaved_to (mst)) then + slv:to_slavable ():unassign (mst) + else + slv:to_slavable ():assign (mst) + end + else + LuaDialog.Message ("VCA Slave Example", "Recursive VCA assignment ignored", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + end + + -- drop references + mst = nil slv = nil + collectgarbage () +end end + diff --git a/share/scripts/a_slow_mute.lua b/share/scripts/a_slow_mute.lua new file mode 100644 index 0000000000..a9ef12b754 --- /dev/null +++ b/share/scripts/a_slow_mute.lua @@ -0,0 +1,66 @@ +ardour { + ["type"] = "dsp", + name = "a-Slow-Mute", + category = "Amplifier", + license = "MIT", + author = "Ardour Team", + description = [[Mute button with slow fade in/out]] +} + +function dsp_ioconfig () + -- -1, -1 = any number of channels as long as input and output count matches + return { { audio_in = -1, audio_out = -1} } +end + + +function dsp_params () + return { { ["type"] = "input", name = "Mute", min = 0, max = 1, default = 0, toggled = true } } +end + +local cur_gain = 1 +local lpf = 0.002 -- parameter low-pass filter time-constant + +function dsp_init (rate) + lpf = 100 / rate -- interpolation time constant +end + +function low_pass_filter_param (old, new, limit) + if math.abs (old - new) < limit then + return new + else + return old + lpf * (new - old) + end +end + +-- the DSP callback function +-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray +function dsp_run (ins, outs, n_samples) + local ctrl = CtrlPorts:array() -- get control port array + local target_gain = ctrl[1] > 0 and 0.0 or 1.0; -- when muted, target_gain = 0.0; otherwise use 1.0 + local siz = n_samples -- samples remaining to process + local off = 0 -- already processed samples + local changed = false + + -- if the target gain changes, process at most 32 samples at a time, + -- and interpolate gain until the current settings match the target values + if cur_gain ~= target_gain then + changed = true + siz = 32 + end + + while n_samples > 0 do + if siz > n_samples then siz = n_samples end + if changed then + cur_gain = low_pass_filter_param (cur_gain, target_gain, 0.001) + end + + for c = 1,#ins do -- process all channels + if ins[c] ~= outs[c] then + ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz) + end + ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, cur_gain); + end + n_samples = n_samples - siz + off = off + siz + end +end diff --git a/share/scripts/access_action.lua b/share/scripts/access_action.lua new file mode 100644 index 0000000000..b50c6386d8 --- /dev/null +++ b/share/scripts/access_action.lua @@ -0,0 +1,37 @@ +ardour { + ["type"] = "EditorAction", + name = "Shortcut", + license = "MIT", + author = "me", + description = [[Trigger a keyboard shortcut. You will be prompted for the shortcut's action in the next step.]] +} + +function action_params () + local actionlist = { + { + type = "dropdown", key = "action", title = "Action", values = ArdourUI:actionlist(), + default = "Save" + } + } + + local rv = LuaDialog.Dialog ("Select Action", actionlist):run () + if not rv then -- user cancelled + return { ["x-script-abort"] = { title = "", preseeded = true} } + end + + local action = rv["action"] + local name = "Shortcut - " .. action + return { + ["action"] = { title = "Action to trigger", default = action, preseeded = true}, + ["x-script-name"] = { title = "Unique Script name", default = name, preseeded = true}, + } +end + +function factory (params) return function () + local p = params or { } + local as = assert (p["action"]) + local sp = assert (as:find('/')) + local group = assert (as:sub(0, sp - 1)) + local item = assert (as:sub(1 + sp)) + Editor:access_action (group, item) +end end diff --git a/share/scripts/add_filters.lua b/share/scripts/add_filters.lua new file mode 100644 index 0000000000..77b41c17bf --- /dev/null +++ b/share/scripts/add_filters.lua @@ -0,0 +1,54 @@ +ardour { + ["type"] = "EditorAction", + name = "Add Filters", + license = "MIT", + author = "PSmith", + description = [[Add 'HPF/LPF' Lua Processor to all Tracks]] +} + +function action_params () + return + { + ["unique"] = { title = "Only add HPF/LPF if not already present (yes/no)", default = "yes"}, + ["position"] = { title = "Insert Position from top (0,..)", default = "0"}, + } +end + + +function factory (params) + return function () + -- get configuration + local p = params or {} + local uniq = p["unique"] or "yes" + local pos = p["position"] or 0 + + -- loop over all tracks + for t in Session:get_tracks():iter() do + local insert = true; + + -- check if filters are present + if uniq ~= "no" then + local proc; + local i = 0; + repeat + -- get Nth Ardour::Processor + proc = t:nth_plugin (i) + -- check if it's a filter + if (not proc:isnil() and proc:display_name () == "a-High/Low Pass Filter") then + insert = false; + end + i = i + 1 + until proc:isnil() or insert == false + end + + -- create a new processor and insert it + if insert then + local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-High/Low Pass Filter"); + if (not a:isnil()) then + t:add_processor_by_index(a, pos, nil, true) + a = nil -- explicitly drop shared-ptr reference + end + end + end + end +end diff --git a/share/scripts/addscopes.lua b/share/scripts/addscopes.lua new file mode 100644 index 0000000000..d180f03765 --- /dev/null +++ b/share/scripts/addscopes.lua @@ -0,0 +1,75 @@ +ardour { + ["type"] = "EditorAction", + name = "Add Scopes", + license = "MIT", + author = "Ardour Team", + description = [[Add 'Inline Scope' Lua Processor to all Tracks]] +} + +function action_params () + return + { + ["unique"] = { title = "Only add Scope if non is present already (yes/no)", default = "yes"}, + ["position"] = { title = "Insert Position from top (0,..)", default = "0"}, + } +end + + +function factory (params) + return function () + -- get configuration + local p = params or {} + local uniq = p["unique"] or "yes" + local pos = p["position"] or 0 + + -- loop over all tracks + for t in Session:get_tracks():iter() do + local insert = true; + + -- check if a scope is already present + if uniq ~= "no" then + local proc; + local i = 0; + repeat + -- get Nth Ardour::Processor + proc = t:nth_plugin (i) + -- check if it's a scope + if (not proc:isnil() and proc:display_name () == "a-Inline Scope") then + insert = false; + end + i = i + 1 + until proc:isnil() or insert == false + end + + -- create a new processor and insert it + if insert then + local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-Inline Scope"); + if (not a:isnil()) then + t:add_processor_by_index(a, pos, nil, true) + ARDOUR.LuaAPI.set_processor_param (a, 0, 5) -- timescale 5sec + -- ARDOUR.LuaAPI.set_processor_param (a, 1, 1) -- logscale on + -- ARDOUR.LuaAPI.set_processor_param (a, 2, 3) -- "Max" height + a = nil -- explicitly drop shared-ptr reference + end + end + end + end +end + + +function icon (params) return function (ctx, width, height) + local wh = math.min (width, height) * .5 + local x0 = math.ceil (wh * .4) + local x1 = math.floor (wh * 1.6) + ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) + ctx:rectangle (wh * .4, wh * .4, wh * 1.2, wh * 1.2) + ctx:set_source_rgba (.7, .7, .7, 1) + ctx:fill () + ctx:set_line_width (1) + ctx:set_source_rgba (.0, .0, .0, 1) + ctx:move_to (x0, wh) + for x = x0, x1 do + ctx:line_to (x, wh - math.sin (2 * math.pi * (x-x0) / (x1-x0)) * wh * .5) + end + ctx:stroke () +end end diff --git a/share/scripts/amp4.lua b/share/scripts/amp4.lua new file mode 100644 index 0000000000..276b4a0af6 --- /dev/null +++ b/share/scripts/amp4.lua @@ -0,0 +1,119 @@ +ardour { + ["type"] = "dsp", + name = "a-Amplifier", + category = "Amplifier", + license = "MIT", + author = "Ardour Team", + description = [[Versatile +/- 20dB multichannel amplifier]] +} + +function dsp_ioconfig () + return + { + -- -1, -1 = any number of channels as long as input and output count matches + { audio_in = -1, audio_out = -1}, + } +end + + +function dsp_params () + return + { + { ["type"] = "input", name = "Gain", min = -20, max = 20, default = 0, unit="dB"}, + } +end + +local lpf = 0.02 -- parameter low-pass filter time-constant +local cur_gain = 0 -- current smoothed gain (dB) + +-- called once when plugin is instantiated +function dsp_init (rate) + lpf = 780 / rate -- interpolation time constant +end + +function low_pass_filter_param (old, new, limit) + if math.abs (old - new) < limit then + return new + else + return old + lpf * (new - old) + end +end + +-- the DSP callback function +-- "ins" and "outs" are http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray +function dsp_run (ins, outs, n_samples) + local ctrl = CtrlPorts:array() -- get control port array (read/write) + local siz = n_samples -- samples remaining to process + local off = 0 -- already processed samples + local changed = false + + -- if the gain parameter was changed, process at most 32 samples at a time + -- and interpolate gain until the current settings match the target values + if cur_gain ~= ctrl[1] then + changed = true + siz = 32 + end + + while n_samples > 0 do + if siz > n_samples then siz = n_samples end -- process at most "remaining samples" + if changed then + -- smooth gain changes above 0.02 dB difference + cur_gain = low_pass_filter_param (cur_gain, ctrl[1], 0.02) + end + + local gain = ARDOUR.DSP.dB_to_coefficient (cur_gain) -- 10 ^ (0.05 * cur_gain) + + for c = 1,#ins do -- process all channels + -- check if output and input buffers for this channel are identical + -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray + if ins[c] ~= outs[c] then + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP + ARDOUR.DSP.copy_vector (outs[c]:offset (off), ins[c]:offset (off), siz) + end + ARDOUR.DSP.apply_gain_to_buffer (outs[c]:offset (off), siz, gain); -- apply-gain, process in-place + end + n_samples = n_samples - siz + off = off + siz + end + +--[[ + if changed then + self:queue_draw () -- notify display + end +--]] +end + +------------------------------------------------------------------------------- +--- inline display + text example + +--[[ +local txt = nil -- cache pango context globally + +function render_inline (ctx, w, max_h) + local ctrl = CtrlPorts:array () -- get control ports + + if not txt then + -- allocate PangoLayout and set font + --http://manual.ardour.org/lua-scripting/class_reference/#Cairo:PangoLayout + txt = Cairo.PangoLayout (ctx, "Mono 8px") + end + + txt:set_text (string.format ("%+.2f dB", ctrl[1])); + tw, th = txt:get_pixel_size () + + local h = th + 4 -- use text-height with 4px padding + if (h > max_h) then h = max_h end -- but at most max-height + + -- clear background + ctx:rectangle (0, 0, w, h) + ctx:set_source_rgba (.2, .2, .2, 1.0) + ctx:fill () + + -- center text + ctx:set_source_rgba (.8, .8, .8, 1.0) + ctx:move_to (.5 * (w - tw), .5 * (h - th)) + txt:show_in_cairo_context (ctx) + + return {w, h} +end +--]] diff --git a/share/scripts/bounce_replace.lua b/share/scripts/bounce_replace.lua new file mode 100644 index 0000000000..da657628ab --- /dev/null +++ b/share/scripts/bounce_replace.lua @@ -0,0 +1,101 @@ +ardour { ["type"] = "EditorAction", name = "Bounce+Replace Regions", + license = "MIT", + author = "Ardour Team", + description = [[Bounce selected regions with processing and replace region]] +} + +function factory (params) return function () + -- there is currently no direct way to find the track + -- corresponding to a [selected] region + function find_track_for_region (region_id) + for route in Session:get_tracks():iter() do + local track = route:to_track(); + local pl = track:playlist () + if not pl:region_by_id (region_id):isnil () then + return track + end + end + assert (0); -- can't happen, region must be in a playlist + end + + -- get selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- prepare undo operation + Session:begin_reversible_command ("Bounce+Replace Regions") + local add_undo = false -- keep track if something has changed + + -- Iterate over Regions part of the selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- each of the items 'r' is a + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region + + local track = find_track_for_region (r:to_stateful():id()) + local playlist = track:playlist () + + -- clear existing changes, prepare "diff" of state for undo + playlist:to_stateful ():clear_changes () + + -- bounce the region with processing + local region = track:bounce_range (r:position (), r:position() + r:length (), ARDOUR.InterThreadInfo (), track:main_outs (), false); + + -- remove old region.. + playlist:remove_region (r); + -- ..and add the newly bounced one + playlist:add_region (region, r:position (), 1, false, 0, 0, false) + + -- create a diff of the performed work, add it to the session's undo stack + -- and check if it is not empty + if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then + add_undo = true + end + end + + -- all done, commit the combined Undo Operation + if add_undo then + -- the 'nil' Command here mean to use the collected diffs added above + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end + +end end + +function icon (params) return function (ctx, width, height, fg) + local wh = math.min (width, height) * .5 + local ar = wh * .2 + + ctx:set_line_width (1) + function stroke_outline (c) + ctx:set_source_rgba (0, 0, 0, 1) + ctx:stroke_preserve () + ctx:set_source_rgba (c, c, c, 1) + ctx:fill () + end + + ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) + ctx:rectangle (wh - wh * .6, wh - .7 * wh, wh * 1.2, .5 * wh) + stroke_outline (.7) + + ctx:rectangle (wh - wh * .6, wh + .1 * wh, wh * 1.2, .5 * wh) + stroke_outline (.9) + + -- arrow + ctx:set_source_rgba (0, 1, 0, 1) + ctx:set_line_width (ar * .7) + + ctx:move_to (wh, wh - .5 * wh) + ctx:rel_line_to (0, wh) + ctx:stroke () + + ctx:move_to (wh, wh + .5 * wh) + ctx:rel_line_to (-ar, -ar) + ctx:rel_line_to (2 * ar, 0) + ctx:close_path () + ctx:stroke () + + + +end end diff --git a/share/scripts/bypass_all_plugins.lua b/share/scripts/bypass_all_plugins.lua new file mode 100644 index 0000000000..913228101f --- /dev/null +++ b/share/scripts/bypass_all_plugins.lua @@ -0,0 +1,22 @@ +ardour { + ["type"] = "EditorAction", + name = "Bypass Plugins", + license = "MIT", + author = "Ardour Team", + description = [[Bypass Plugins on selected tracks]] +} + +function factory () return function () + local sel = Editor:get_selection () + for r in sel.tracks:routelist ():iter () do + local i = 0; + while 1 do -- iterate over all plugins/processors + local proc = r:nth_plugin (i) + if proc:isnil () then + break + end + proc:to_insert():deactivate() + i = i + 1 + end + end +end end diff --git a/share/scripts/create_drum_tracks.lua b/share/scripts/create_drum_tracks.lua new file mode 100644 index 0000000000..0f16f87a70 --- /dev/null +++ b/share/scripts/create_drum_tracks.lua @@ -0,0 +1,73 @@ +ardour { + ["type"] = "EditorAction", + name = "Create Drum Tracks", + author = "PSmith", + description = [[Creates 8 new tracks with representative names and colors.]] +} + +function factory () return function () + local names = { + "Kick", + "Snare", + "Hat", + "Fl Tom", + "OH L", + "OH R", + "Room 1", + "Room 2" + } + + local color = 0xff8800ff --orange + + local i = 1 + while names[i] do + local tl = Session:new_audio_track (1, 2, nil, 1, names[i], + ARDOUR.PresentationInfo.max_order, + ARDOUR.TrackMode.Normal) + + for track in tl:iter () do + track:presentation_info_ptr ():set_color (color) + end + + i = i + 1 + end --foreach track + +end end -- function factory + + +function icon (params) return function (ctx, width, height) + local x = width * .5 + local y = height * .5 + local r = math.min (x, y) * .7 + ctx:save () + ctx:translate (x, y) + ctx:scale (1, .5) + ctx:translate (-x, -y) + ctx:arc (x, y, r, 0, 2 * math.pi) + ctx:set_source_rgba (.9, .9, 1, 1) + ctx:fill () + ctx:arc (x, y, r, 0, math.pi) + ctx:arc_negative (x, y * 1.6, r, math.pi, 0) + ctx:set_source_rgba (.7, .7, .7, 1) + ctx:fill () + ctx:restore () + + ctx:set_source_rgba (.6, .4, .2, 1) + ctx:translate (x, y) + ctx:scale (.7, 1) + ctx:translate (-x, -y) + ctx:set_line_cap (Cairo.LineCap.Round) + + function drumstick (xp, lr) + ctx:set_line_width (r * .3) + ctx:move_to (x * xp, y) + ctx:close_path () + ctx:stroke () + ctx:set_line_width (r * .2) + ctx:move_to (x * xp, y) + ctx:rel_line_to (lr * x, y) + ctx:stroke () + end + drumstick (1.2, 1.2) + drumstick (0.7, -.5) +end end diff --git a/share/scripts/delete_xrun_markers.lua b/share/scripts/delete_xrun_markers.lua new file mode 100644 index 0000000000..5512e653b6 --- /dev/null +++ b/share/scripts/delete_xrun_markers.lua @@ -0,0 +1,40 @@ +ardour { ["type"] = "EditorAction", name = "Delete xrun markers", author = "Ardour Team", description = [[Delete all xrun markers in the current session]] } + +function factory () return function () + for l in Session:locations():list():iter() do + if l:is_mark() and string.find (l:name(), "^xrun%d*$") then + Session:locations():remove (l); + end + end +end end + +function icon (params) return function (ctx, width, height, fg) + local mh = height - 3.5; + local m3 = width / 3; + local m6 = width / 6; + + ctx:set_line_width (.5) + + ctx:set_source_rgba (.8, .2, .2, 1.0) + ctx:move_to (width / 2 - m6, 2) + ctx:rel_line_to (m3, 0) + ctx:rel_line_to (0, mh * 0.4) + ctx:rel_line_to (-m6, mh * 0.6) + ctx:rel_line_to (-m6, -mh * 0.6) + ctx:close_path () + ctx:fill_preserve () + ctx:set_source_rgba (.0, .0, .0, 1.0) + ctx:stroke () + + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + ctx:set_line_width (1) + + ctx:move_to (width * .2, height * .2) + ctx:line_to (width * .8, height * .8) + ctx:stroke () + + ctx:move_to (width * .8, height * .2) + ctx:line_to (width * .2, height * .8) + ctx:stroke () + +end end diff --git a/share/scripts/export_mp4chaps.lua b/share/scripts/export_mp4chaps.lua new file mode 100644 index 0000000000..61c78c3a2f --- /dev/null +++ b/share/scripts/export_mp4chaps.lua @@ -0,0 +1,78 @@ +ardour { + ["type"] = "EditorAction", + name = "Export markers as mp4chaps", + author = "Johannes Mueller", + description = [[ +Exports MP4chaps of all markers except xruns. The markers are stored in the +export directory of the session in mp4 chapter marks format. The filename +is mp4chaps.txt + +Note that there's always a chapter mark "Intro" at 00:00:00.000 as some +players can't live without it. If there are no exportable markers, the file +is not created. + +This is a bit more convenient than the export option, as one does not +have to wait for the export. +]], + license = "GPLv2" +} + +function factory (unused_params) return function () + + local fr = Session:sample_rate() + local chaps = {} + + for l in Session:locations():list():iter() do + local name = l:name() + if not l:is_mark() or string.find(name, "^xrun%d*$") then + goto next end + + local t = l:start() - Session:current_start_sample() + local h = math.floor(t / (3600*fr)) + local r = t - (h*3600*fr) + local m = math.floor(r / (60*fr)) + r = r - m*60*fr + local s = math.floor(r / fr) + r = r - s*fr + local ms = math.floor(r*1000/fr) + table.insert(chaps, string.format("%02d:%02d:%02d.%03d %s\n", h, m, s, ms, name)) + ::next:: + end + + if next(chaps) == nil then + goto out end + + table.insert(chaps, "00:00:00.000 Intro\n") + table.sort(chaps) + + file = io.open(ARDOUR.LuaAPI.build_filename (Session:path(), "export", "mp4chaps.txt"), "w") + for i, line in ipairs(chaps) do + file:write(line) + end + file:close() + + ::out:: +end end + +function icon (params) return function (ctx, width, height, fg) + local mh = height - 3.5; + local m3 = width / 3; + local m6 = width / 6; + + ctx:set_line_width (.5) + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + + ctx:move_to (width / 2 - m6, 2) + ctx:rel_line_to (m3, 0) + ctx:rel_line_to (0, mh * 0.4) + ctx:rel_line_to (-m6, mh * 0.6) + ctx:rel_line_to (-m6, -mh * 0.6) + ctx:close_path () + ctx:stroke () + + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .5) .. "px") + txt:set_text ("MP4") + local tw, th = txt:get_pixel_size () + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/faders_to_trims.lua b/share/scripts/faders_to_trims.lua new file mode 100644 index 0000000000..3fd62833f2 --- /dev/null +++ b/share/scripts/faders_to_trims.lua @@ -0,0 +1,72 @@ +ardour { + ["type"] = "EditorAction", + name = "Faders to Trims", + license = "MIT", + author = "PSmith", + description = [[Add 'Trim' plugins to all tracks. Move the fader value into the trim.]] +} + +function action_params () + return + { + } +end + + +function factory (params) + return function () + -- loop over all tracks + for t in Session:get_tracks():iter() do + + fader_value = t:gain_control():get_value() + if fader_value == 1 then + goto skip + end + if t:gain_control():automation_state() ~= ARDOUR.AutoState.Off then + goto skip + end + + -- TODO: skip MIDI tracks without or with a post-fader synth + -- (fader is MIDI-velocity) + + v = math.log(fader_value, 10) + trim_gain = 20*v + fader_pos = 0 + local proc; + local i = 0; + + repeat + -- find the fader proc + proc = t:nth_processor (i) + if (not proc:isnil() and proc:display_name () == "Fader") then + fader_pos = i + end + i = i + 1 + until proc:isnil() + + -- apply the trim + trim = t:nth_processor (fader_pos+1) + if (not trim:isnil() and trim:display_name () == "a-Amplifier") then + --existing trim found; sum the trim and the fader gain, and set the trim to that value + cur_gain = ARDOUR.LuaAPI.get_processor_param (trim, 0) + ARDOUR.LuaAPI.set_processor_param (trim, 0, trim_gain+cur_gain) + else + --create a new Trim processor, and set its value to match the fader + local a = ARDOUR.LuaAPI.new_luaproc(Session, "a-Amplifier"); + if (not a:isnil()) then + t:add_processor_by_index(a, fader_pos-1, nil, true) + ARDOUR.LuaAPI.set_processor_param (a, 0, trim_gain) + a = nil -- explicitly drop shared-ptr reference + end + end + + --zero the fader gain + t:gain_control():set_value(1, PBD.GroupControlDisposition.NoGroup) + + ::skip:: + + end --foreach track + + end --function + +end --factory diff --git a/share/scripts/jump_to_marker.lua b/share/scripts/jump_to_marker.lua new file mode 100644 index 0000000000..ea92020c83 --- /dev/null +++ b/share/scripts/jump_to_marker.lua @@ -0,0 +1,67 @@ +ardour { ["type"] = "EditorAction", name = "Jump to Marker", + license = "MIT", + author = "Ardour Team", + description = [[Jump to the first marker matching a given pattern]] +} + +function factory () return function () + local keep = false + + ::restart:: + + local dlg = LuaDialog.Dialog ("Search and Jump to Marker", + { + { type = "entry", key = "marker", default = '', title = "Marker Prefix" }, + { type = "checkbox", key = "keep", default = keep, title = "Keep Dialog Open" }, + }) + + local rv = dlg:run() + if not rv then return end + + keep = rv['keep'] + + if (rv['marker'] == "") then + if keep then goto restart end + return + end + + for l in Session:locations():list():iter() do + if l:is_mark() and string.find (l:name(), "^" .. rv['marker'] .. ".*$") then + Session:request_locate (l:start (), ARDOUR.LocateTransportDisposition.RollIfAppropriate, ARDOUR.TransportRequestSource.TRS_UI) + if keep then goto restart end + return + end + end + + LuaDialog.Message ("Jump to Marker", "No marker matching the given pattern was found.", LuaDialog.MessageType.Warning, LuaDialog.ButtonType.Close):run () + + if keep then goto restart end +end end + + +function icon (params) return function (ctx, width, height, fg) + local mh = height - 3.5; + local m3 = width / 3; + local m6 = width / 6; + + ctx:set_line_width (.5) + + -- compare to gtk2_ardour/marker.cc "Marker" + ctx:set_source_rgba (.6, .6, .6, 1.0) + ctx:move_to (width / 2 - m6, 2) + ctx:rel_line_to (m3, 0) + ctx:rel_line_to (0, mh * 0.4) + ctx:rel_line_to (-m6, mh * 0.6) + ctx:rel_line_to (-m6, -mh * 0.6) + ctx:close_path () + ctx:fill_preserve () + ctx:set_source_rgba (.0, .0, .0, 1.0) + ctx:stroke () + + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(math.min (width, height) * .5) .. "px") + txt:set_text ("txt") + local tw, th = txt:get_pixel_size () + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/lfo_automation.lua b/share/scripts/lfo_automation.lua new file mode 100644 index 0000000000..a69df1af1d --- /dev/null +++ b/share/scripts/lfo_automation.lua @@ -0,0 +1,189 @@ +ardour { + ["type"] = "EditorAction", + name = "Add LFO automation to region", + version = "0.1.1", + license = "MIT", + author = "Daniel Appelt", + description = [[Add LFO-like plugin automation to selected region]] +} + +function factory (unused_params) + return function () + -- Retrieve the first selected region + -- TODO: the following statement should do just that, no!? + -- local region = Editor:get_selection().regions:regionlist():front() + local region = nil + for r in Editor:get_selection().regions:regionlist():iter() do + if region == nil then region = r end + end + + -- Bail out if no region was selected + if region == nil then + LuaDialog.Message("Add LFO automation to region", "Please select a region first!", + LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() + return + end + + -- Identify the track the region belongs to. There really is no better way?! + local track = nil + for route in Session:get_tracks():iter() do + for r in route:to_track():playlist():region_list():iter() do + if r == region then track = route:to_track() end + end + end + + -- Get a list of all available plugin parameters on the track. This looks ugly. For the original code + -- see https://github.com/Ardour/ardour/blob/master/scripts/midi_cc_to_automation.lua + local targets = {} + local i = 0 + while true do -- iterate over all plugins on the route + if track:nth_plugin(i):isnil() then break end + + local proc = track:nth_plugin(i) -- ARDOUR.LuaAPI.plugin_automation() expects a proc not a plugin + local plug = proc:to_insert():plugin(0) + local plug_label = i .. ": " .. plug:name() -- Handle ambiguity if there are multiple plugin instances + local n = 0 -- Count control-ports separately. ARDOUR.LuaAPI.plugin_automation() only returns those. + for j = 0, plug:parameter_count() - 1 do -- Iterate over all parameters + if plug:parameter_is_control(j) then + local label = plug:parameter_label(j) + if plug:parameter_is_input(j) and label ~= "hidden" and label:sub(1,1) ~= "#" then + -- We need two return values: the plugin-instance and the parameter-id. We use a function to + -- return both values in order to avoid another sub-menu level in the dropdown. + local nn = n -- local scope for return value function + targets[plug_label] = targets[plug_label] or {} + targets[plug_label][label] = function() return {["p"] = proc, ["n"] = nn} end + end + n = n + 1 + end + end + + i = i + 1 + end + + -- Bail out if there are no plugin parameters + if next(targets) == nil then + LuaDialog.Message("Add LFO automation to region", "No plugin parameters found.", + LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() + region, track, targets = nil, nil, nil + collectgarbage() + return + end + + -- Display dialog to select (plugin and) plugin parameter, and LFO cycle type + min / max + local dialog_options = { + { type = "heading", title = "Add LFO automation to region", align = "left"}, + { type = "dropdown", key = "param", title = "Plugin parameter", values = targets }, + { type = "dropdown", key = "wave", title = "Waveform", values = { + ["Ramp up"] = 1, ["Ramp down"] = 2, ["Triangle"] = 3, ["Sine"] = 4, + ["Exp up"] = 5, ["Exp down"] = 6, ["Log up"] = 7, ["Log down"] = 8 } }, + { type = "number", key = "cycles", title = "No. of cycles", min = 1, max = 16, step = 1, digits = 0 }, + { type = "slider", key = "min", title = "Minimum in %", min = 0, max = 100, digits = 1 }, + { type = "slider", key = "max", title = "Maximum in %", min = 0, max = 100, digits = 1, default = 100 } + } + local rv = LuaDialog.Dialog("Select target", dialog_options):run() + + -- Return if the user cancelled + if not rv then + region, track, targets = nil, nil, nil + collectgarbage() + return + end + + -- Parse user response + assert(type(rv["param"]) == "function") + local pp = rv["param"]() -- evaluate function, retrieve table {["p"] = proc, ["n"] = nn} + local al, _, pd = ARDOUR.LuaAPI.plugin_automation(pp["p"], pp["n"]) + local wave = rv["wave"] + local cycles = rv["cycles"] + -- Compute minimum and maximum requested parameter values + local lower = pd.lower + rv["min"] / 100 * (pd.upper - pd.lower) + local upper = pd.lower + rv["max"] / 100 * (pd.upper - pd.lower) + track, targets, rv, pd = nil, nil, nil, nil + assert(not al:isnil()) + + -- Define lookup tables for our waves. Empty ones will be calculated in a separate step. + -- TODO: at this point we already know which one is needed, still we compute all. + local lut = { + { 0, 1 }, -- ramp up + { 1, 0 }, -- ramp down + { 0, 1, 0 }, -- triangle + {}, -- sine + {}, -- exp up + {}, -- exp down + {}, -- log up + {} -- log down + } + + -- Calculate missing look up tables + local log_min = math.exp(-2 * math.pi) + for i = 0, 20 do + -- sine + lut[4][i+1] = 0.5 * math.sin(i * math.pi / 10) + 0.5 + -- exp up + lut[5][i+1] = math.exp(-2 * math.pi + i * math.pi / 10) + -- log up + lut[7][i+1] = -math.log(1 + (i / log_min - i) / 20) / math.log(log_min) + end + -- "down" variants just need the values in reverse order + for i = 21, 1, -1 do + -- exp down + lut[6][22-i] = lut[5][i] + -- log down + lut[8][22-i] = lut[7][i] + end + + -- Initialize undo + Session:begin_reversible_command("Add LFO automation to region") + local before = al:get_state() -- save previous state (for undo) + al:clear_list() -- clear target automation-list + + local values = lut[wave] + local last = nil + for i = 0, cycles - 1 do + -- cycle length = region:length() / cycles + local cycle_start = region:position() - region:start() + i * region:length() / cycles + local offset = region:length() / cycles / (#values - 1) + + for k, v in pairs(values) do + local pos = cycle_start + (k - 1) * offset + if k == 1 and v ~= last then + -- Move event one sample further. A larger offset might be needed to avoid unwanted effects. + pos = pos + 1 + end + + if k > 1 or v ~= last then + -- Create automation point re-scaled to parameter target range. Do not create a new point + -- at cycle start if the last cycle ended on the same value. + al:add(pos, lower + v * (upper - lower), false, true) + end + last = v + end + end + + -- remove dense events + al:thin (20) -- threashold of area below curve + + -- TODO: display the modified automation lane in the time line in order to make the change visible! + + -- Save undo + -- TODO: in Ardour 5.12 this does not lead to an actual entry in the undo list?! + Session:add_command(al:memento_command(before, al:get_state())) + Session:commit_reversible_command(nil) + + region, al, lut = nil, nil, nil + collectgarbage() + end +end + +function icon (params) return function (ctx, width, height, fg) + local yc = height * .5 + local x0 = math.ceil (width * .1) + local x1 = math.floor (width * .9) + ctx:set_line_width (1) + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + ctx:move_to (x0, yc * 1.5) + for x = x0, x1 do + ctx:line_to (x, yc + math.cos (3 * math.pi * (x-x0) / (x1-x0)) * yc * .5) + end + ctx:stroke () +end end diff --git a/share/scripts/list_plugins.lua b/share/scripts/list_plugins.lua new file mode 100644 index 0000000000..6a6efe12b8 --- /dev/null +++ b/share/scripts/list_plugins.lua @@ -0,0 +1,50 @@ +ardour { ["type"] = "EditorAction", name = "List Plugins", + license = "MIT", + author = "Ardour Team", + description = [[List and count plugins used in this session]] +} + +function factory () return function () + local rv = "Plugins used in this session:\nCNT | TYPE | NAME" + local all_plugs = {} + + for r in Session:get_routes ():iter () do + if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes + local i = 0 + while true do + local proc = r:nth_plugin (i) + if proc:isnil () then break end + local pi = proc:to_insert () -- we know it's a plugin-insert (we asked for nth_plugin) + local pp = pi:plugin (0) + local id = pi:type() .. "-" .. pp:unique_id() + local cnt = 0 + if all_plugs[id] then cnt = all_plugs[id]['cnt'] end + all_plugs[id] = { name = proc:name(), ["type"] = pi:type(), id = pp:unique_id(), cnt = (cnt + 1) } + i = i + 1 + end + ::nextroute:: + end + + function plugintypestr (t) + if (t == ARDOUR.PluginType.LADSPA) then return "LADSPA" end + if (t == ARDOUR.PluginType.LV2) then return "LV2" end + if (t == ARDOUR.PluginType.AudioUnit) then return "AU" end + if (t == ARDOUR.PluginType.Windows_VST) then return "VST" end + if (t == ARDOUR.PluginType.LXVST) then return "VST" end + if (t == ARDOUR.PluginType.MacVST) then return "VST" end + if (t == ARDOUR.PluginType.Lua) then return "Lua" end + return "??" + end + + for k,v in pairs (all_plugs) do + print (string.format ("%2d * %-6s %-30s (%s)", v['cnt'], plugintypestr(v['type']), v['name'], v['id'])) + rv = rv .. "\n" .. string.format ("%2d * %-6s %s", v['cnt'], plugintypestr(v['type']), v['name']) + end + + LuaDialog.Message ("All Plugins", "" .. rv .. "", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() + + all_plugs = nil + rv = "" + collectgarbage (); + +end end diff --git a/share/scripts/ltc_reader.lua b/share/scripts/ltc_reader.lua new file mode 100644 index 0000000000..b86dc009ef --- /dev/null +++ b/share/scripts/ltc_reader.lua @@ -0,0 +1,82 @@ +ardour { + ["type"] = "dsp", + name = "LTC Reader", + category = "Utility", + author = "Ardour Team", + license = "MIT", + description = [[Linear Timecode Decoder with mixer strip inline display]] +} + +function dsp_ioconfig () + return { { audio_in = 1, audio_out = 1}, } +end + +function dsp_init (rate) + timeout = rate + samplerate = rate + ltc_reader = ARDOUR.DSP.LTCReader (rate / 25, ARDOUR.DSP.LTC_TV_STANDARD.LTC_TV_FILM_24) + self:shmem():allocate(5) +end + +function dsp_run (ins, outs, n_samples) + if ins[1] ~= outs[1] then + ARDOUR.DSP.copy_vector (outs[1]:offset (0), ins[1]:offset (0), n_samples) + end + ltc_reader:write (ins[1]:offset (0), n_samples, 0) + timeout = timeout + n_samples + local to_ui = self:shmem():to_int(0):array() + local rv + repeat + local tc + rv, tc = ltc_reader:read (0, 0, 0, 0) + if rv >= 0 then + timeout = 0 + self:shmem():atomic_set_int (0, 1) + self:shmem():atomic_set_int (1, tc[1]) + self:shmem():atomic_set_int (2, tc[2]) + self:shmem():atomic_set_int (3, tc[3]) + self:shmem():atomic_set_int (4, tc[4]) + self:queue_draw () + end + until rv < 0 + if timeout > samplerate then + if 0 ~= self:shmem():atomic_get_int (0) then + self:shmem():atomic_set_int (0, 0) + self:queue_draw () + end + end +end + +------------------------------------------------------------------------------- +-- inline UI +-- +local txt = nil -- a pango context +local vpadding = 2 + +function render_inline (ctx, displaywidth, max_h) + if not txt then + txt = Cairo.PangoLayout (ctx, "Mono 10px") + end + + local d = self:shmem():to_int(0):array() + if d[1] == 0 then + txt:set_text("--:--:--:--") + else + txt:set_text(string.format("%02d:%02d:%02d:%02d", d[2], d[3], d[4], d[5])) + end + + -- compute the size of the display + local txtwidth, lineheight = txt:get_pixel_size() + local displayheight = math.min(vpadding + (lineheight + vpadding), max_h) + + -- clear background + ctx:rectangle (0, 0, displaywidth, displayheight) + ctx:set_source_rgba (.2, .2, .2, 1.0) + ctx:fill () + ctx:set_source_rgba (.8, .8, .8, 1.0) + ctx:move_to ((displaywidth - txtwidth) / 2, 1) + txt:show_in_cairo_context (ctx) + + return {displaywidth, displayheight} +end + diff --git a/share/scripts/meter_tap.lua b/share/scripts/meter_tap.lua new file mode 100644 index 0000000000..645af522bd --- /dev/null +++ b/share/scripts/meter_tap.lua @@ -0,0 +1,42 @@ +ardour { + ["type"] = "EditorAction", + name = "Meter Tap", + author = "Ardour Lua Taskforce", + description = [[Change Metering Point for tracks in your session.]] +} + +function factory () return function () + + local dialog_options = { + { type = "label", colspan = 5, title = "" }, + { type = "radio", col = 1, colspan = 7, key = "select", title = "", values ={ ["Set All: Input"] = ARDOUR.MeterPoint.MeterInput, ["Set All: Pre Fader"] = ARDOUR.MeterPoint.MeterPreFader, ["Set All: Post Fader"] = ARDOUR.MeterPoint.MeterPostFader, ["Set All: Output"] = ARDOUR.MeterPoint.MeterOutput, ["Set All: Custom"] = ARDOUR.MeterPoint.MeterCustom}, default = "Set All: Input"}, + { type = "label", colspan = 5, title = "" }, + { type = "checkbox", col=1, colspan = 1, key = "select-tracks", default = true, title = "Selected tracks only"}, + { type = "checkbox", col=2, colspan = 1, key = "rec-tracks", default = true, title = "Record Enabled tracks only"}, + { type = "label", colspan = 5, title = "" }, + } + + local rv = LuaDialog.Dialog("Change all Meter Taps:", dialog_options):run() + if not rv then return end -- user cancelled + + local rl; + if rv['select-tracks'] then + rl = Editor:get_selection () + else + rl = Session:get_routes() + end + + local meter_point = rv['select'] + + for route in rl:iter() do + if not(route:to_track():isnil()) then + if rv['rec-tracks'] then + if route:rec_enable_control():get_value() == 1.0 then + route:to_track():set_meter_point(meter_point, false) + end + else + route:to_track():set_meter_point(meter_point, false) + end + end + end +end end diff --git a/share/scripts/midi_cc_to_automation.lua b/share/scripts/midi_cc_to_automation.lua new file mode 100644 index 0000000000..a1bab91a05 --- /dev/null +++ b/share/scripts/midi_cc_to_automation.lua @@ -0,0 +1,130 @@ +ardour { ["type"] = "EditorAction", name = "MIDI CC to Plugin Automation", + license = "MIT", + author = "Ardour Team", + description = [[Parse a given MIDI control changes (CC) from all selected MIDI regions and convert them into plugin parameter automation]] +} + +function factory () return function () + -- find possible target parameters, collect them in a nested table + -- [track-name] -> [plugin-name] -> [parameters] + -- to allow selection in a dropdown menu + local targets = {} + local have_entries = false + for r in Session:get_routes ():iter () do -- for every track/bus + if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes + local i = 0 + while true do -- iterate over all plugins on the route + local proc = r:nth_plugin (i) + if proc:isnil () then break end + local plug = proc:to_insert ():plugin (0) -- we know it's a plugin-insert (we asked for nth_plugin) + local n = 0 -- count control-ports + for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters + if plug:parameter_is_control (j) then + local label = plug:parameter_label (j) + if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then + local nn = n --local scope for return value function + -- create table parents only if needed (if there's at least one parameter) + if not targets [r:name ()] then targets [r:name ()] = {} end + -- TODO handle ambiguity if there are 2 plugins with the same name on the same track + if not targets [r:name ()][proc:display_name ()] then targets [r:name ()][proc:display_name ()] = {} end + -- we need 2 return values: the plugin-instance and the parameter-id, so we use a table (associative array) + -- however, we cannot directly use a table: the dropdown menu would expand it as another sub-menu. + -- so we produce a function that will return the table. + targets [r:name ()][proc:display_name ()][label] = function () return {["p"] = proc, ["n"] = nn} end + have_entries = true + end + n = n + 1 + end + end + i = i + 1 + end + ::nextroute:: + end + + -- bail out if there are no parameters + if not have_entries then + LuaDialog.Message ("CC to Plugin Automation", "No Plugins found", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + targets = nil + collectgarbage () + return + end + + -- create a dialog, ask user which MIDI-CC to map and to what parameter + local dialog_options = { + { type = "heading", title = "MIDI CC Source", align = "left" }, + { type = "number", key = "channel", title = "Channel", min = 1, max = 16, step = 1, digits = 0 }, + { type = "number", key = "ccparam", title = "CC Parameter", min = 0, max = 127, step = 1, digits = 0 }, + { type = "heading", title = "Target Track and Plugin", align = "left"}, + { type = "dropdown", key = "param", title = "Target Parameter", values = targets } + } + local rv = LuaDialog.Dialog ("Select Taget", dialog_options):run () + + targets = nil -- drop references (the table holds shared-pointer references to all plugins) + collectgarbage () -- and release the references immediately + + if not rv then return end -- user cancelled + + -- parse user response + + assert (type (rv["param"]) == "function") + local midi_channel = rv["channel"] - 1 -- MIDI channel 0..15 + local cc_param = rv["ccparam"] + local pp = rv["param"]() -- evaluate function, retrieve table {["p"] = proc, ["n"] = nn} + local al, _, pd = ARDOUR.LuaAPI.plugin_automation (pp["p"], pp["n"]) + rv = nil -- drop references + assert (not al:isnil ()) + assert (midi_channel >= 0 and midi_channel < 16) + assert (cc_param >= 0 and cc_param < 128) + + -- all systems go + local add_undo = false + Session:begin_reversible_command ("CC to Automation") + local before = al:get_state () -- save previous state (for undo) + al:clear_list () -- clear target automation-list + + -- for all selected MIDI regions + local sel = Editor:get_selection () + for r in sel.regions:regionlist ():iter () do + local mr = r:to_midiregion () + if mr:isnil () then goto next end + + -- get list of MIDI-CC events for the given channel and parameter + local ec = mr:control (Evoral.Parameter (ARDOUR.AutomationType.MidiCCAutomation, midi_channel, cc_param), false) + if ec:isnil () then goto next end + if ec:list ():events ():size () == 0 then goto next end + + -- MIDI events are timestamped in "bar-beat" units, we need to convert those + -- using the tempo-map, relative to the region-start + local bfc = ARDOUR.DoubleBeatsSamplesConverter (Session:tempo_map (), r:start ()) + + -- iterate over CC-events + for av in ec:list ():events ():iter () do + -- re-scale event to target range + local val = pd.lower + (pd.upper - pd.lower) * av.value / 127 + -- and add it to the target-parameter automation-list + al:add (r:position () - r:start () + bfc:to (av.when), val, false, true) + add_undo = true + end + ::next:: + end + + -- save undo + if add_undo then + local after = al:get_state () + Session:add_command (al:memento_command (before, after)) + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + LuaDialog.Message ("CC to Plugin Automation", "No data was converted. Was a MIDI-region with CC-automation selected? ", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + end + collectgarbage () +end end + +function icon (params) return function (ctx, width, height, fg) + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (height / 3) .. "px") + txt:set_text ("CC\nPA") + local tw, th = txt:get_pixel_size () + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/midi_remap.lua b/share/scripts/midi_remap.lua new file mode 100644 index 0000000000..4fb7919e23 --- /dev/null +++ b/share/scripts/midi_remap.lua @@ -0,0 +1,97 @@ +ardour { + ["type"] = "dsp", + name = "MIDI Note Mapper", + category = "Utility", + license = "MIT", + author = "Alby Musaelian", + description = [[Map arbitrary MIDI notes to others. Affects Note On/Off and polyphonic key pressure. Note that if a single note is mapped multiple times, the last mapping wins -- MIDI events are never duplicated.]] +} + +-- The number of remapping pairs to allow. Increasing this (at least in theory) +-- decreases performace, so it's set fairly low as a default. The user can +-- increase this if they have a need to. +N_REMAPINGS = 10 + +OFF_NOTE = -1 + +function dsp_ioconfig () + return { { midi_in = 1, midi_out = 1, audio_in = 0, audio_out = 0}, } +end + + +function dsp_params () + + local map_scalepoints = {} + map_scalepoints["None"] = OFF_NOTE + for note=0,127 do + local name = ARDOUR.ParameterDescriptor.midi_note_name(note) + map_scalepoints[string.format("%03d (%s)", note, name)] = note + end + + local map_params = {} + + i = 1 + for mapnum = 1,N_REMAPINGS do + -- From and to + for _,name in pairs({"| #" .. mapnum .. " Map note", "|__ to"}) do + map_params[i] = { + ["type"] = "input", + name = name, + min = -1, + max = 127, + default = OFF_NOTE, + integer = true, + enum = true, + scalepoints = map_scalepoints + } + i = i + 1 + end + end + + return map_params +end + +function dsp_run (_, _, n_samples) + assert (type(midiin) == "table") + assert (type(midiout) == "table") + local cnt = 1; + + function tx_midi (time, data) + midiout[cnt] = {} + midiout[cnt]["time"] = time; + midiout[cnt]["data"] = data; + cnt = cnt + 1; + end + + -- We build the translation table every buffer because, as far as I can tell, + -- there's no way to only rebuild it when the parameters have changed. + -- As a result, it has to be updated every buffer for the parameters to have + -- any effect. + + -- Restore translation table + local translation_table = {} + local ctrl = CtrlPorts:array() + for i=1,N_REMAPINGS*2,2 do + if not (ctrl[i] == OFF_NOTE) then + translation_table[ctrl[i]] = ctrl[i + 1] + end + end + + -- for each incoming midi event + for _,b in pairs (midiin) do + local t = b["time"] -- t = [ 1 .. n_samples ] + local d = b["data"] -- get midi-event + local event_type + if #d == 0 then event_type = -1 else event_type = d[1] >> 4 end + + if (#d == 3) and (event_type == 9 or event_type == 8 or event_type == 10) then -- note on, note off, poly. afterpressure + -- Do the mapping - 2 is note byte for these types + d[2] = translation_table[d[2]] or d[2] + if not (d[2] == OFF_NOTE) then + tx_midi (t, d) + end + else + tx_midi (t, d) + end + end +end diff --git a/share/scripts/midimon.lua b/share/scripts/midimon.lua new file mode 100644 index 0000000000..7f58fd34de --- /dev/null +++ b/share/scripts/midimon.lua @@ -0,0 +1,213 @@ +ardour { + ["type"] = "dsp", + name = "a-MIDI Monitor", + category = "Visualization", + license = "GPLv2", + author = "Ardour Team", + description = [[Display recent MIDI events inline in the mixer strip]] +} + +local maxevents = 20 +local ringsize = maxevents * 3 +local evlen = 3 +local hpadding, vpadding = 4, 2 + +function dsp_ioconfig () + return { { midi_in = 1, midi_out = 1, audio_in = -1, audio_out = -1}, } +end + +function dsp_params () + return + { + { ["type"] = "input", + name = "Font size", + doc = "Text size used by the monitor to display midi events", + min = 4, max = 12, default = 7, integer = true }, + { ["type"] = "input", + name = "Line count", + doc = "How many events will be shown at most", + min = 1, max = maxevents, default = 6, integer = true }, + { ["type"] = "input", + name = "Hexadecimal", + doc = "If enabled, values will be printed in hexadecimal notation", + min = 0, max = 1, default = 0, toggled = true }, + { ["type"] = "input", + name = "System messages", + doc = "If enabled, the monitor will show System Control and Real-Time messages", + min = 0, max = 1, default = 0, toggled = true }, + { ["type"] = "input", + name = "Numeric Notes", + doc = "If enabled, note-events displayed numerically", + min = 0, max = 1, default = 0, toggled = true }, + } +end + +function dsp_init (rate) + -- create a shmem space to hold latest midi events + -- a int representing the index of the last event, and + -- a C-table as storage for events. + self:shmem():allocate(1 + ringsize*evlen) + self:shmem():atomic_set_int(0, 1) + local buffer = self:shmem():to_int(1):array() + for i = 1, ringsize*evlen do + buffer[i] = -1 -- sentinel for empty slot + end +end + +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + local pos = self:shmem():atomic_get_int(0) + local buffer = self:shmem():to_int(1):array() + + -- passthrough all data + ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("audio")) + ARDOUR.DSP.process_map (bufs, in_map, out_map, n_samples, offset, ARDOUR.DataType ("midi")) + + -- then fill the event buffer + local ib = in_map:get (ARDOUR.DataType ("midi"), 0) -- index of 1st midi input + + if ib ~= ARDOUR.ChanMapping.Invalid then + local events = bufs:get_midi (ib):table () -- copy event list into a lua table + + -- iterate over all MIDI events + for _, e in pairs (events) do + local ev = e:buffer():array() + pos = pos % ringsize + 1 + -- copy the data + for j = 1, math.min(e:size(),evlen) do + buffer[(pos-1)*evlen + j] = ev[j] + end + -- zero unused slots + for j = e:size()+1, evlen do + buffer[(pos-1)*evlen + j] = 0 + end + end + end + + self:shmem():atomic_set_int(0, pos) + + self:queue_draw () +end + +local txt = nil -- a pango context +local cursize = 0 +local hex = nil +local format_note = nil +local show_scm = nil + +function format_note_name(b) + return string.format ("%5s", ARDOUR.ParameterDescriptor.midi_note_name (b)) +end + +function format_note_num(b) + return string.format (hex, b) +end + + +function show_midi(ctx, x, y, buffer, event) + local base = (event - 1) * evlen + if buffer[base+1] == -1 then return false end + local evtype = buffer[base + 1] >> 4 + local channel = (buffer[base + 1] & 15) + 1 -- for System Common Messages this has no use + if evtype == 8 then + txt:set_text(string.format("%02u \u{2669}Off%s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3])) + elseif evtype == 9 then + txt:set_text(string.format("%02u \u{2669}On %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3])) + elseif evtype == 10 then + txt:set_text(string.format("%02u \u{2669}KP %s" .. hex, channel, format_note(buffer[base+2]), buffer[base+3])) + elseif evtype == 11 then + txt:set_text(string.format("%02u CC " .. hex .. hex, channel, buffer[base+2], buffer[base+3])) + elseif evtype == 12 then + txt:set_text(string.format("%02u PRG " .. hex, channel, buffer[base+2])) + elseif evtype == 13 then + txt:set_text(string.format("%02u KP " .. hex, channel, buffer[base+2])) + elseif evtype == 14 then + txt:set_text(string.format("%02u PBnd" .. hex, channel, buffer[base+2] | buffer[base+3] << 7)) + elseif show_scm > 0 then -- System Common Message + local message = buffer[base + 1] & 15 + if message == 0 then + txt:set_text("-- SysEx") + elseif message == 1 then + txt:set_text(string.format("-- Time Code" .. hex, buffer[base+2])) + elseif message == 2 then + txt:set_text(string.format("-- Song Pos" .. hex, buffer[base+2] | buffer[base+3] << 7)) + elseif message == 3 then + txt:set_text(string.format("-- Select Song" .. hex, buffer[base+2])) + elseif message == 6 then + txt:set_text("-- Tune Rq") + elseif message == 8 then + txt:set_text("-- Timing") + elseif message == 10 then + txt:set_text("-- Start") + elseif message == 11 then + txt:set_text("-- Continue") + elseif message == 12 then + txt:set_text("-- Stop") + elseif message == 14 then + txt:set_text("-- Active") + elseif message == 15 then + txt:set_text("-- Reset") + else + return false + end + else + return false + end + ctx:move_to (x, y) + txt:show_in_cairo_context (ctx) + return true +end + +function render_inline (ctx, displaywidth, max_h) + local ctrl = CtrlPorts:array () + local pos = self:shmem():atomic_get_int(0) + local buffer = self:shmem():to_int(1):array() + local count = ctrl[2] + + if not txt or cursize ~= ctrl[1] then + cursize = math.floor(ctrl[1]) + txt = Cairo.PangoLayout (ctx, "Mono " .. cursize) + end + + if ctrl[3] > 0 then hex = " %02X" else hex = " %3u" end + show_scm = ctrl[4] + + if ctrl[5] > 0 then + format_note = format_note_num + else + format_note = format_note_name + end + + -- compute the size of the display + txt:set_text("0") + local _, lineheight = txt:get_pixel_size() + local displayheight = math.min(vpadding + (lineheight + vpadding) * count, max_h) + + -- compute starting position (pango anchors text at north-west corner) + local x, y = hpadding, displayheight - lineheight - vpadding + + -- clear background + ctx:rectangle (0, 0, displaywidth, displayheight) + ctx:set_source_rgba (.2, .2, .2, 1.0) + ctx:fill () + + -- color of latest event + ctx:set_source_rgba (1.0, 1.0, 1.0, 1.0) + + -- print events + for i = pos, 1, -1 do + if y < 0 then break end + if show_midi(ctx, x, y, buffer, i) then + y = y - lineheight - vpadding + ctx:set_source_rgba (.8, .8, .8, 1.0) + end + end + for i = ringsize, pos+1, -1 do + if y < 0 then break end + if show_midi(ctx, x, y, buffer, i) then + y = y - lineheight - vpadding + ctx:set_source_rgba (.8, .8, .8, 1.0) + end + end + + return {displaywidth, displayheight} +end diff --git a/share/scripts/mixer_screenshot.lua b/share/scripts/mixer_screenshot.lua new file mode 100644 index 0000000000..f8e8ded2db --- /dev/null +++ b/share/scripts/mixer_screenshot.lua @@ -0,0 +1,46 @@ +ardour { + ["type"] = "EditorAction", + name = "Mixer Screenshot", + author = "Ardour Team", + description = [[Save a screenshot of the complete mixer-window, regardless of scrollbars or visible screen area]] +} + +function factory () return function () + local rv = LuaDialog.Dialog ("Save Mixer Screenshot", + { + { type = "createfile", key = "file", title = "", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "mixer.png") }, + }):run() + + if (rv) then + if (ARDOUR.LuaAPI.file_test (rv['file'], ARDOUR.LuaAPI.FileTest.Exists)) then + local ok = LuaDialog.Message ("File Exists", "File '".. rv['file'] .. "' exists.\nReplace?", LuaDialog.MessageType.Question, LuaDialog.ButtonType.Yes_No):run () + if ok ~= LuaDialog.Response.Yes then + return + end + end + ArdourUI.mixer_screenshot (rv['file']) + end + collectgarbage () +end end + +function icon (params) return function (ctx, width, height, fg) + local wh = math.min (width, height) * .5 + ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) + + ctx:rectangle (wh * .6, wh * .6, wh * .8, wh * .8) + ctx:set_source_rgba (.1, .1, .1, 1) + ctx:fill () + + ctx:set_line_width (1) + ctx:set_source_rgba (.9, .9, .9, 1) + + ctx:move_to (wh * 0.3, wh * 0.6) + ctx:line_to (wh * 1.5, wh * 0.6) + ctx:line_to (wh * 1.5, wh * 1.7) + ctx:stroke () + + ctx:move_to (wh * 0.6, wh * 0.3) + ctx:line_to (wh * 0.6, wh * 1.4) + ctx:line_to (wh * 1.8, wh * 1.4) + ctx:stroke () +end end diff --git a/share/scripts/mixer_settings_recall.lua b/share/scripts/mixer_settings_recall.lua new file mode 100644 index 0000000000..c8f683eb98 --- /dev/null +++ b/share/scripts/mixer_settings_recall.lua @@ -0,0 +1,470 @@ +ardour { + ["type"] = "EditorAction", + name = "Recall Mixer Settings", + author = "Mixbus Team", + description = [[ + + Recalls mixer settings outined by files + created by Store Mixer Settings. + + Allows for some room to change Source + and Destination. + + ]] +} + +function factory () + + local acoraida_monicas_last_used_recall_file + + return function () + + local user_cfg = ARDOUR.user_config_directory(-1) + local local_path = ARDOUR.LuaAPI.build_filename(Session:path(), 'mixer_settings') + local global_path = ARDOUR.LuaAPI.build_filename(user_cfg, 'mixer_settings') + + local invalidate = {} + + function exists(file) + local ok, err, code = os.rename(file, file) + if not ok then + if code == 13 then -- Permission denied, but it exists + return true + end + end return ok, err + end + + function whoami() + if not pcall(function() local first_check = Session:get_mixbus(0) end) then + return "Ardour" + else + local second_check = Session:get_mixbus(11) + if second_check:isnil() then + return "Mixbus" + else + return "32C" + end + end + end + + function isdir(path) + return exists(path.."/") + end + + function get_processor_by_name(track, name) + local i = 0 + local proc = track:nth_processor(i) + repeat + if(proc:display_name() == name) then + return proc + else + i = i + 1 + end + proc = track:nth_processor(i) + until proc:isnil() + end + + function new_plugin(name, type) + local plugin = ARDOUR.LuaAPI.new_plugin(Session, name, type, "") + if not(plugin:isnil()) then return plugin end + end + + function group_by_id(id) + local id = tonumber(id) + for g in Session:route_groups():iter() do + local group_id = tonumber(g:to_stateful():id():to_s()) + if group_id == id then return g end + end + end + + function group_by_name(name) + for g in Session:route_groups():iter() do + if g:name() == name then return g end + end + end + + function route_groupid_interrogate(t) + local group = false + for g in Session:route_groups():iter() do + for r in g:route_list():iter() do + if r:name() == t:name() then group = g:to_stateful():id():to_s() end + end + end return group + end + + function route_group_interrogate(t) + for g in Session:route_groups():iter() do + for r in g:route_list():iter() do + if r:name() == t:name() then return g end + end + end + end + + function recall(debug, path, dry_run) + local file = io.open(path, "r") + assert(file, "File not found!") + local bypass_routes = {} + + local i = 0 + for l in file:lines() do + --print(i, l) + + local create_groups = dry_run["create_groups"] + local skip_line = false + + local plugin, route, group = false, false, false + local f = load(l) + + if debug then + --print('create_groups ' .. tostring(create_groups)) + print(i, string.sub(l, 0, 29), f) + end + + if f then f() end + + if instance["route_id"] then route = true end + if instance["plugin_id"] then plugin = true end + if instance["group_id"] then group = true end + + if group then + local g_id = instance["group_id"] + local routes = instance["routes"] + local name = instance["name"] + local group = group_by_id(g_id) + if not(group) then + if create_groups then + local group = Session:new_route_group(name) + for _, v in pairs(routes) do + local rt = Session:route_by_id(PBD.ID(v)) + if rt:isnil() then rt = Session:route_by_name(name) end + if not(rt:isnil()) then group:add(rt) end + end + end + end + end + + if route then + local substitution = tonumber(dry_run["destination-"..i]) + if substitution == 0 then + bypass_routes[#bypass_routes + 1] = instance["route_id"] + goto nextline + end + + local old_order = ARDOUR.ProcessorList() + local route_id = instance["route_id"] + local r_id = PBD.ID(instance["route_id"]) + local muted, soloed = instance["muted"], instance["soloed"] + local order = instance["order"] + local cache = instance["cache"] + local group = instance["group"] + local group_name = instance["group_name"] + local name = instance["route_name"] + local gc, tc, pc = instance["gain_control"], instance["trim_control"], instance["pan_control"] + local sends = instance["sends"] + + if not(substitution == instance["route_id"]) then + print('SUBSTITUTION FOR: ', name, substitution, Session:route_by_id(PBD.ID(substitution)):name()) + --bypass_routes[#bypass_routes + 1] = route_id + was_subbed = true + r_id = PBD.ID(substitution) + end + + local rt = Session:route_by_id(r_id) + if rt:isnil() then rt = Session:route_by_name(name) end + if rt:isnil() then goto nextline end + + if sends then + for i, data in pairs(sends) do + i = i-1 + for j, ctrl in pairs({ + rt:send_level_controllable(i), + rt:send_enable_controllable(i), + rt:send_pan_azimuth_controllable(i), + rt:send_pan_azimuth_enable_controllable(i), + }) do + if not(ctrl:isnil()) then + local value = data[j] + if value then + if debug then + print("Setting " .. ctrl:name() .. " to value " .. value) + end + ctrl:set_value(value, PBD.GroupControlDisposition.NoGroup) + end + end + end + end + end + + local cur_group_id = route_groupid_interrogate(rt) + if not(group) and (cur_group_id) then + local g = group_by_id(cur_group_id) + if g then g:remove(rt) end + end + + local rt_group = group_by_name(group_name) + if rt_group then rt_group:add(rt) end + + well_known = {'PRE', 'Trim', 'EQ', 'Comp', 'Fader', 'POST'} + protected_instrument = false + for k, v in pairs(order) do + local proc = Session:processor_by_id(PBD.ID(1)) + if not(was_subbed) then + proc = Session:processor_by_id(PBD.ID(v)) + end + if proc:isnil() then + for id, sub_tbl in pairs(cache) do + local name = sub_tbl[1] + local type = sub_tbl[2] + if v == id then + proc = new_plugin(name, type) + for _, control in pairs(well_known) do + if name == control then + proc = get_processor_by_name(rt, control) + invalidate[v] = proc:to_stateful():id():to_s() + goto nextproc + end + end + if not(proc) then goto nextproc end + if not(proc:isnil()) then + rt:add_processor_by_index(proc, 0, nil, true) + invalidate[v] = proc:to_stateful():id():to_s() + end + end + end + end + ::nextproc:: + if proc and not(proc:isnil()) then old_order:push_back(proc) end + if not(old_order:empty()) and not(protected_instrument) then + if not(rt:to_track():to_midi_track():isnil()) then + if not(rt:the_instrument():isnil()) then + protected_instrument = true + old_order:push_back(rt:the_instrument()) + end + end + end + end + rt:reorder_processors(old_order, nil) + if muted then rt:mute_control():set_value(1, 1) else rt:mute_control():set_value(0, 1) end + if soloed then rt:solo_control():set_value(1, 1) else rt:solo_control():set_value(0, 1) end + rt:gain_control():set_value(gc, 1) + rt:trim_control():set_value(tc, 1) + if pc ~= false and not(rt:is_master()) then rt:pan_azimuth_control():set_value(pc, 1) end + end + + if plugin then + --if the plugin is owned by a route + --we decided not to use, skip it + for _, v in pairs(bypass_routes) do + if instance["owned_by_route_id"] == v then + goto nextline + end + end + + local enable = {} + local params = instance["parameters"] + local p_id = instance["plugin_id"] + local act = instance["active"] + + for k, v in pairs(invalidate) do --invalidate any deleted plugin's id + if p_id == k then + p_id = v + end + end + + local proc = Session:processor_by_id(PBD.ID(p_id)) + if proc:isnil() then goto nextline end + local plug = proc:to_insert():plugin(0) + + for k, v in pairs(params) do + local label = plug:parameter_label(k) + if string.find(label, "Assign") or string.find(label, "Enable") then --@ToDo: Check Plugin type == LADSPA or VST? + enable[k] = v --queue any assignments/enables for after the initial parameter recalling to duck the 'in-on-change' feature + end + print(string.format("%s (Port: %s) -> %s", label, k, v)) + ARDOUR.LuaAPI.set_processor_param(proc, k, v) + end + + for k, v in pairs(enable) do + ARDOUR.LuaAPI.set_processor_param(proc, k, v) + end + if act then proc:activate() else proc:deactivate() end + end + + ::nextline:: + i = i + 1 + + end + end + + function dry_run(debug, path) + --returns a dialog-able table of + --everything we do (logically) + --in the recall function + local route_values = {['----'] = "0"} + for r in Session:get_routes():iter() do + route_values[r:name()] = r:to_stateful():id():to_s() + end + + local i = 0 + local dry_table = { + {type = "label", align="right", key="col-1-title", col=0, colspan=1, title = 'Source:'}, + {type = "label", align="left", key="col-2-title", col=1, colspan=1, title = 'Destination:'}, + } + local file = io.open(path, "r") + assert(file, "File not found!") + pad = 0 + for l in file:lines() do + local do_plugin, do_route, do_group = false, false, false + local f = load(l) + + if debug then + print(i, string.sub(l, 0, 29), f) + end + + if f then f() end + + if instance["route_id"] then do_route = true end + if instance["plugin_id"] then do_plugin = true end + if instance["group_id"] then do_group = true end + + if do_group then + local group_id = instance["group_id"] + local group_name = instance["name"] + local dlg_title, action_title = "", "" + + local group_ptr = group_by_id(group_id) + + if not(group_ptr) then + dlg_title = string.format("(Group) %s.", group_name) + --action_title = "will create and use settings" + else + dlg_title = string.format("(Group) %s.", group_ptr:name()) + --action_title = "will use group settings" + end + table.insert(dry_table, { + order=pad, type = "label", align="right", key = "group-"..i , col = 0, colspan = 1, title = dlg_title + }) + pad = pad + 1 + end + + if do_route then + local route_id = instance["route_id"] + local route_name = instance["route_name"] + local dlg_title = "" + + local route_ptr = Session:route_by_id(PBD.ID(route_id)) + + if route_ptr:isnil() then + route_ptr = Session:route_by_name(route_name) + if not(route_ptr:isnil()) then + dlg_title = string.format("%s", route_ptr:name()) + --action_title = "will use route settings" + else + dlg_title = string.format("%s", route_name) + --action_title = "will be ignored" + end + else + dlg_title = string.format("%s", route_ptr:name()) + --action_title = "will use route settings" + end + if route_ptr:isnil() then name = route_name else name = route_ptr:name() end + + table.insert(dry_table, { + order=instance['pi_order']+pad, type = "label", align="right", key = "route-"..i , col = 0, colspan = 1, title = dlg_title + }) + table.insert(dry_table, { + type = "dropdown", align="left", key = "destination-"..i, col = 1, colspan = 1, title = "", values = route_values, default = name or "----" + }) + end + i = i + 1 + end + table.insert(dry_table, { + type = "checkbox", col=0, colspan=2, align="left", key = "create_groups", default = true, title = "Create Groups if necessary?" + }) + return dry_table + end + + local global_vs_local_dlg = { + { type = "label", col=0, colspan=20, align="left", title = "" }, + { + type = "radio", col=0, colspan=20, align="left", key = "recall-dir", title = "", values = + { + ['Pick from Global Settings'] = 1, ['Pick from Local Settings'] = 2, ['Last Used Recall File'] = 3, + }, + default = 'Last Used Recall File' + }, + { type = "label", col=0, colspan=20, align="left", title = ""}, + } + + local recall_options = { + { type = "label", col=0, colspan=10, align="left", title = "" }, + { type = "file", col=0, colspan=15, align="left", key = "file", title = "Select a Settings File", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") }, + { type = "label", col=0, colspan=10, align="left", title = "" }, + } + + local gvld = LuaDialog.Dialog("Recall Mixer Settings:", global_vs_local_dlg):run() + + if not(gvld) then + return + else + if gvld['recall-dir'] == 1 then + local global_ok = isdir(global_path) + local global_default_path = ARDOUR.LuaAPI.build_filename(global_path, string.format("FactoryDefault-%s.lua", whoami())) + print(global_default_path) + if global_ok then + recall_options[2]['path'] = global_default_path + local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run() + if not(rv) then return end + local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run() + if dry_return then + acoraida_monicas_last_used_recall_file = rv['file'] + recall(false, rv['file'], dry_return) + else + return + end + else + LuaDialog.Message ("Recall Mixer Settings:", + global_path .. ' does not exist!\nPlease run Store Mixer Settings first.', + LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() + end + end + + if gvld['recall-dir'] == 2 then + local local_ok = isdir(local_path) + local local_default_path = ARDOUR.LuaAPI.build_filename(local_path, 'asdf') + print(local_default_path) + if local_ok then + recall_options[2]['path'] = local_default_path + local rv = LuaDialog.Dialog("Recall Mixer Settings:", recall_options):run() + if not(rv) then return end + local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, rv['file'])):run() + if dry_return then + acoraida_monicas_last_used_recall_file = rv['file'] + recall(true, rv['file'], dry_return) + else + return + end + else + LuaDialog.Message ("Recall Mixer Settings:", + local_path .. 'does not exist!\nPlease run Store Mixer Settings first.', + LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() + end + end + + if gvld['recall-dir'] == 3 then + if acoraida_monicas_last_used_recall_file then + local dry_return = LuaDialog.Dialog("Recall Mixer Settings:", dry_run(false, acoraida_monicas_last_used_recall_file)):run() + if dry_return then + recall(true, acoraida_monicas_last_used_recall_file, dry_return) + else + return + end + else + LuaDialog.Message ("Script has no record of last used file:", + 'Please pick a recall file and then this option will be available', + LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run() + end + end + end + +end end diff --git a/share/scripts/mixer_settings_store.lua b/share/scripts/mixer_settings_store.lua new file mode 100644 index 0000000000..1718a83ddd --- /dev/null +++ b/share/scripts/mixer_settings_store.lua @@ -0,0 +1,383 @@ +ardour { + ["type"] = "EditorAction", + name = "Store Mixer Settings", + author = "Mixbus Team", + description = [[ + + Stores the current Mixer state as a file + that can be read and recalled arbitrarily + by it's companion script, Recall Mixer Settings. + + Supports: processor settings, grouping, + mute, solo, gain, trim, pan and processor ordering, + plus re-adding certain deleted plugins. + + ]] +} + +function factory () return function () + + local user_cfg = ARDOUR.user_config_directory(-1) + local local_path = ARDOUR.LuaAPI.build_filename(Session:path(), 'mixer_settings') + local global_path = ARDOUR.LuaAPI.build_filename(user_cfg, 'mixer_settings') + + function exists(file) + local ok, err, code = os.rename(file, file) + if not ok then + if code == 13 then -- Permission denied, but it exists + return true + end + end return ok, err + end + + function whoami() + if not pcall(function() local first_check = Session:get_mixbus(0) end) then + return "Ardour" + else + local second_check = Session:get_mixbus(11) + if second_check:isnil() then + return "Mixbus" + else + return "32C" + end + end + end + + function isdir(path) + return exists(path.."/") + end + + function setup_paths() + local global_ok, local_ok = false, false + + if not(isdir(global_path)) then + global_ok, _, _ = os.execute('mkdir '.. global_path) + if global_ok == 0 then + global_ok = true + end + else + global_ok = true + end + if not(isdir(local_path)) then + local_ok, _, _ = os.execute('mkdir '.. local_path) + if local_ok == 0 then + local_ok = true + end + else + local_ok = true + end + return global_ok, local_ok + end + + function get_processor_by_name(track, name) + local i = 0 + local proc = track:nth_processor(i) + repeat + if(proc:display_name() == name) then + return proc + else + i = i + 1 + end + proc = track:nth_processor(i) + until proc:isnil() + end + + function group_by_id(id) + local id = tonumber(id) + for g in Session:route_groups():iter() do + local group_id = tonumber(g:to_stateful():id():to_s()) + if group_id == id then return g end + end + end + + function group_by_name(name) + for g in Session:route_groups():iter() do + if g:name() == name then return g end + end + end + + function route_groupid_interrogate(t) + local group = false + for g in Session:route_groups():iter() do + for r in g:route_list():iter() do + if r:name() == t:name() then group = g:to_stateful():id():to_s() end + end + end return group + end + + function route_group_interrogate(t) + for g in Session:route_groups():iter() do + for r in g:route_list():iter() do + if r:name() == t:name() then return g end + end + end + end + + function empty_last_store(path) --empty current file from last run + local file = io.open(path, "w") + --file:write(string.format("instance = { whoami = '%s' }", whoami()) + file:write("") + file:close() + end + + function mark_tracks(selected, path) + + empty_last_store(path) + + local route_string = [[instance = { + route_id = %d, + route_name = '%s', + gain_control = %s, + trim_control = %s, + pan_control = %s, + sends = {%s}, + muted = %s, + soloed = %s, + order = {%s}, + cache = {%s}, + group = %s, + group_name = '%s', + pi_order = %d + }]] + + local group_string = [[instance = { + group_id = %s, + name = '%s', + routes = {%s}, + }]] + + local processor_string = [[instance = { + plugin_id = %d, + type = %d, + display_name = '%s', + owned_by_route_name = '%s', + owned_by_route_id = %d, + parameters = {%s}, + active = %s, + }]] + + local group_route_string = " [%d] = %s," + local proc_order_string = " [%d] = %d," + local proc_cache_string = " [%d] = {'%s', %d}," + local params_string = " [%d] = %s," + + --ensure easy-to-read formatting doesn't make it through + local route_string = string.gsub(route_string, "[\n\t%s]", "") + local group_string = string.gsub(group_string, "[\n\t%s]", "") + local processor_string = string.gsub(processor_string, "[\n\t%s]", "") + + local sel = Editor:get_selection () + local groups_to_write = {} + local i = 0 + + local tracks = Session:get_stripables() + + if selected then tracks = sel.tracks:routelist() end + + for r in tracks:iter() do + local group = route_group_interrogate(r) + if group then + local already_there = false + for _, v in pairs(groups_to_write) do + if group == v then + already_there = true + end + end + if not(already_there) then + groups_to_write[#groups_to_write + 1] = group + end + end + end + + for _, g in pairs(groups_to_write) do + local tmp_str = "" + for t in g:route_list():iter() do + tmp_str = tmp_str .. string.format(group_route_string, i, t:to_stateful():id():to_s()) + i = i + 1 + end + local group_str = string.format( + group_string, + g:to_stateful():id():to_s(), + g:name(), + tmp_str + ) + + file = io.open(path, "a") + file:write(group_str, "\r\n") + file:close() + end + + for r in tracks:iter() do + if r:is_monitor () or r:is_auditioner () or not(r:to_vca():isnil()) then goto nextroute end -- skip special routes + r = r:to_route() + if r:isnil() then goto nextroute end + local order = ARDOUR.ProcessorList() + local x = 0 + repeat + local proc = r:nth_processor(x) + if not proc:isnil() then + order:push_back(proc) + end + x = x + 1 + until proc:isnil() + + + local route_group = route_group_interrogate(r) + if route_group then route_group = route_group:name() else route_group = "" end + local rid = r:to_stateful():id():to_s() + local pan = r:pan_azimuth_control() + if pan:isnil() then pan = false else pan = pan:get_value() end --sometimes a route doesn't have pan, like the master. + + -- Get send information, if any. + local send_string = "" + local i = 0 + repeat + local fmt = "{%s, %s, %s, %s}" + string.gsub(fmt, "[\n\t]", "") + local values = {} + for j, ctrl in pairs({ + r:send_level_controllable(i), + r:send_enable_controllable(i), + r:send_pan_azimuth_controllable(i), + r:send_pan_azimuth_enable_controllable(i), + }) do + if not(ctrl:isnil()) then + values[#values + 1] = ctrl:get_value() + else + values[#values + 1] = "nil" + end + end + send_string = send_string .. string.format(fmt, table.unpack(values)) + send_string = send_string .. "," + i = i + 1 + until r:send_enable_controllable(i):isnil() + + print(send_string) + + local order_nmbr = 0 + local tmp_order_str, tmp_cache_str = "", "" + for p in order:iter() do + local ptype + if not(p:to_insert():isnil()) then + ptype = p:to_insert():plugin(0):get_info().type + else + ptype = 99 + end + local pid = p:to_stateful():id():to_s() + if not(string.find(p:display_name(), "latcomp")) then + tmp_order_str = tmp_order_str .. string.format(proc_order_string, order_nmbr, pid) + tmp_cache_str = tmp_cache_str .. string.format(proc_cache_string, pid, p:display_name(), ptype) + end + order_nmbr = order_nmbr + 1 + end + + local route_str = string.format( + route_string, + rid, + r:name(), + ARDOUR.LuaAPI.ascii_dtostr(r:gain_control():get_value()), + ARDOUR.LuaAPI.ascii_dtostr(r:trim_control():get_value()), + tostring(pan), + send_string, + r:muted(), + r:soloed(), + tmp_order_str, + tmp_cache_str, + route_groupid_interrogate(r), + route_group, + r:presentation_info_ptr():order() + ) + + file = io.open(path, "a") + file:write(route_str, "\n") + file:close() + + local i = 0 + while true do + local params = {} + local proc = r:nth_plugin (i) + if proc:isnil () then break end + local active = proc:active() + local id = proc:to_stateful():id():to_s() + local plug = proc:to_insert ():plugin (0) + local ptype = proc:to_insert():plugin(0):get_info().type + local n = 0 -- count control-ports + for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters + if plug:parameter_is_control (j) then + local label = plug:parameter_label (j) + if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then + --local _, _, pd = ARDOUR.LuaAPI.plugin_automation(proc, n) + local val = ARDOUR.LuaAPI.get_processor_param(proc, j, true) + print(r:name(), "->", proc:display_name(), label, val) + params[j] = val + end + n = n + 1 + end + end + i = i + 1 + + local tmp_params_str = "" + for k, v in pairs(params) do + tmp_params_str = tmp_params_str .. string.format(params_string, k, ARDOUR.LuaAPI.ascii_dtostr(v)) + end + + local proc_str = string.format( + processor_string, + id, + ptype, + proc:display_name(), + r:name(), + r:to_stateful():id():to_s(), + tmp_params_str, + active + ) + file = io.open(path, "a") + file:write(proc_str, "\n") + file:close() + end + ::nextroute:: + end + end + + local store_options = { + { type = "label", col=0, colspan=1, align="right", title = "Name:" }, + { type = "entry", col=1, colspan=1, align="left" , key = "filename", default = Session:name(), title=""}, + { type = "label", col=0, colspan=1, align="right", title = "Location:" }, + { + type = "radio", col=1, colspan=3, align="left", key = "store-dir", title = "", values = + { + ['Global (accessible from any session)'] = 1, ['Local (this session only)'] = 2 + }, + default = 'Locally (this session only)' + }, + { type = "hseparator", title="", col=0, colspan = 3}, + { type = "label", col=0, colspan=1, align="right", title = "Selected Tracks Only:" }, + { type = "checkbox", col=1, colspan=1, align="left", key = "selected", default = false, title = ""}, + --{ type = "label", col=0, colspan=2, align="left", title = ''}, + --{ type = "label", col=0, colspan=2, align="left", title = "Global Path: " .. global_path}, + --{ type = "label", col=0, colspan=2, align="left", title = "Local Path: " .. local_path}, + } + + local global_ok, local_ok = setup_paths() + + if global_ok and local_ok then + local rv = LuaDialog.Dialog("Store Mixer Settings:", store_options):run() + + if not(rv) then return end + + local filename = rv['filename'] + if rv['store-dir'] == 1 then + local store_path = ARDOUR.LuaAPI.build_filename(global_path, string.format("%s-%s.lua", filename, whoami())) + local selected = rv['selected'] + mark_tracks(selected, store_path) + end + + if rv['store-dir'] == 2 then + local store_path = ARDOUR.LuaAPI.build_filename(local_path, string.format("%s-%s.lua", filename, whoami())) + print(store_path) + local selected = rv['selected'] + mark_tracks(selected, store_path) + end + end + +end end diff --git a/share/scripts/mute_all_tracks.lua b/share/scripts/mute_all_tracks.lua new file mode 100644 index 0000000000..e15c0530c0 --- /dev/null +++ b/share/scripts/mute_all_tracks.lua @@ -0,0 +1,21 @@ +ardour { + ["type"] = "EditorAction", + name = "Mute All Tracks", + license = "MIT", + author = "Ardour Team", + description = [[Mute All Tracks in the Session]] +} + +function factory () return function () + local ctrls = ARDOUR.ControlListPtr () -- create a list of controls to change + + for r in Session:get_tracks ():iter () do -- iterate over all tracks in the session + ctrls:push_back (r:mute_control()) -- add the track's mute-control to the list of of controls to be changed + end + + -- of more than one control is to be changed.. + if ctrls:size() > 0 then + -- do it (queue change in realtime-context) set to "1" ; here 'muted' + Session:set_controls (ctrls, 1, PBD.GroupControlDisposition.NoGroup) + end +end end diff --git a/share/scripts/new_playlist.lua b/share/scripts/new_playlist.lua new file mode 100644 index 0000000000..54563cd6a5 --- /dev/null +++ b/share/scripts/new_playlist.lua @@ -0,0 +1,17 @@ +ardour { + ["type"] = "EditorAction", + name = "New Playlist", + license = "MIT", + author = "Ardour Lua Taskforce", + description = [[Prompts and builds a new playlist for every track in the session.]] +} + +function factory () return function () + + for r in Session:get_tracks():iter() do + local rtav = Editor:rtav_from_route(r) -- lookup RTAV + Editor:new_playlists(rtav:to_timeaxisview()) + end + +collectgarbage() +end end diff --git a/share/scripts/noisegen.lua b/share/scripts/noisegen.lua new file mode 100644 index 0000000000..bf7b006160 --- /dev/null +++ b/share/scripts/noisegen.lua @@ -0,0 +1,111 @@ +ardour { + ["type"] = "dsp", + name = "NoiseGen", + category = "Instrument", + license = "MIT", + author = "Ardour Team", + description = [[Noise Generator (v-1.02)]] +} + +function dsp_params () + return + { + { ["type"] = "input", name = "White/Pink", min = 0, max = 1, default = 0, toggled = true }, + { ["type"] = "input", name = "Gain", min = -60, max = 0, default = -18, unit="dB" }, + } +end + +function dsp_ioconfig () + return { [1] = { audio_in = -1, audio_out = -1}, } +end + +local sr = 0 + +function dsp_init (rate) + sr = rate +end + +local ao = 0 +local draw = 0 + +function dsp_run (ins, outs, n_samples) + + local a = {} -- init array + local ctrl = CtrlPorts:array () + local noise = ctrl[1] or 0 + local amplitude = ARDOUR.DSP.dB_to_coefficient (ctrl[2]) or ARDOUR.DSP.dB_to_coefficient (-18) + + local b0 = 0.0 + local b1 = 0.0 + local b2 = 0.0 + local b3 = 0.0 + local b4 = 0.0 + local b5 = 0.0 + local b6 = 0.0 + + --Pink noise generation courtesy of Paul Kellet's refined method + --http://www.musicdsp.org/files/pink.txt + --If 'white' consists of uniform random numbers, + --the pink noise will have an almost gaussian distribution. + for s = 1, n_samples do + if noise == 0 then + a[s] = amplitude * 2 * (math.random() - 0.5) + end + if noise == 1 then + white = (amplitude * 0.25) * 2 * (math.random() - 0.5) + b0 = 0.99886 * b0 + white * 0.0555179; + b1 = 0.99332 * b1 + white * 0.0750759; + b2 = 0.96900 * b2 + white * 0.1538520; + b3 = 0.86650 * b3 + white * 0.3104856; + b4 = 0.55000 * b4 + white * 0.5329522; + b5 = -0.7616 * b5 - white * 0.0168980; + b6 = white * 0.115926; + a[s] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; + end + end + + if (draw > (sr/15)) then + self:queue_draw() + draw = 0 + end + + -- passes array a {} into buffer + for c = 1,#outs do + outs[c]:set_table(a, n_samples) + end + draw = draw + n_samples +end + +function render_inline (ctx, w, max_h) --inline display + local ctrl = CtrlPorts:array() + h = 30 + p = 0 + inc = 0 + ycy = 0.5 + pink = false + local amplitude = ARDOUR.DSP.dB_to_coefficient(ctrl[2]) + if ctrl[1] == 1 then pink = true end + if pink then inc = 0.7/w end + + --draw rectangle + ctx:rectangle(0, 0, w, h) + ctx:set_source_rgba(0, 0, 0, 1.0) + ctx:fill() + ctx:set_line_width(1.5) + ctx:set_source_rgba(0.8, 0.8, 0.8, 1.0) + + l_x = 0 + l_y = 0 + for x = 0,w do + if pink then ycy = 0.3 else ycy = 0.5 end --slant slightly like an actual pink noise spectrum + y = math.log(20^amplitude) * (math.random() - 0.5) - p + yc = ycy * h + ((-0.5 * h) * y) + ctx:move_to (x, yc + 3) + ctx:line_to (l_x, l_y + 3) + l_x = x + l_y = yc + ctx:stroke() + p = p + inc + end + return {w, h + 6} +end diff --git a/share/scripts/normalize_all_tracks.lua b/share/scripts/normalize_all_tracks.lua new file mode 100644 index 0000000000..2886c9b5c5 --- /dev/null +++ b/share/scripts/normalize_all_tracks.lua @@ -0,0 +1,58 @@ +ardour { ["type"] = "EditorAction", + name = "Normalize All Tracks", + license = "MIT", + author = "Ardour Team", + description = [[Normalize all regions using a common gain-factor per track.]] +} + +function factory () return function () + -- target values -- TODO: use a LuaDialog.Dialog and ask.. + local target_peak = -1 --dBFS + local target_rms = -18 --dBFS/RMS + + -- prepare undo operation + Session:begin_reversible_command ("Normalize Tracks") + local add_undo = false -- keep track if something has changed + + -- loop over all tracks in the session + for track in Session:get_tracks():iter() do + local norm = 0 -- per track gain + -- loop over all regions on track + for r in track:to_track():playlist():region_list():iter() do + -- test if it's an audio region + local ar = r:to_audioregion () + if ar:isnil () then goto next end + + local peak = ar:maximum_amplitude (nil) + local rms = ar:rms (nil) + -- check if region is silent + if (peak > 0) then + local f_rms = rms / 10 ^ (.05 * target_rms) + local f_peak = peak / 10 ^ (.05 * target_peak) + local tg = (f_peak > f_rms) and f_peak or f_rms -- max (f_peak, f_rms) + norm = (tg > norm) and tg or norm -- max (tg, norm) + end + ::next:: + end + + -- apply same gain to all regions on track + if norm > 0 then + for r in track:to_track():playlist():region_list():iter() do + local ar = r:to_audioregion () + if ar:isnil () then goto skip end + ar:to_stateful ():clear_changes () + ar:set_scale_amplitude (1 / norm) + add_undo = true + ::skip:: + end + end + end + + -- all done. now commit the combined undo operation + if add_undo then + -- the 'nil' command here means to use all collected diffs + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end +end end diff --git a/share/scripts/notch_bank.lua b/share/scripts/notch_bank.lua new file mode 100644 index 0000000000..76901920d9 --- /dev/null +++ b/share/scripts/notch_bank.lua @@ -0,0 +1,125 @@ +ardour { + ["type"] = "dsp", + name = "a-Notch Bank", + category = "Filter", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[Notch Filter Bank; useful to remove noise with a harmonic spectum (e.g, mains hum, GSM signals, charge-pump noise, etc). +Note: this plugin is not suitable to be automated, it is intended for static noise only.]] +} + +------------------------------------------------------------------ +-- this is a quick/dirty example filter: no de-click, no de-zipper +------------------------------------------------------------------- + +-- configuration +local max_stages = 100 + +-- plugin i/o ports +function dsp_ioconfig () + return + { + -- allow any number of I/O as long as port-count matches + { audio_in = -1, audio_out = -1}, + } +end + +-- plugin control ports +function dsp_params () + return + { + { ["type"] = "input", name = "Base Freq", min = 10, max = 2000, default = 100, unit="Hz", logarithmic = true }, + { ["type"] = "input", name = "Quality", min = 1.0, max = 100.0, default = 8.0, logarithmic = true }, + { ["type"] = "input", name = "Stages", min = 1.0, max = max_stages, default = 8.0, integer = true }, + } +end + +-- plugin instance state +local filters = {} -- the biquad filter instances +local chn = 0 -- configured channel count +local sample_rate = 0 -- configured sample-rate +local limit = 0 -- max number of stages (given freq & sample-rate) + +-- cached control ports (keep track of changed) +local freq = 0 +local qual = 0 + +-- dsp_init is called once when instantiating the plugin +function dsp_init (rate) + -- remember the sample-rate + sample_rate = rate +end + +-- dsp_configure is called every time when the channel-count +-- changes, and at least once at the beginning. +function dsp_configure (ins, outs) + assert (ins:n_audio () == outs:n_audio ()) + + -- explicit cleanup + filters = {} + collectgarbage () + + -- remember audio-channels + chn = ins:n_audio () + + -- set up filter instances for all channels + for c = 1, chn do + filters[c] = {} + for i = 1, max_stages do + filters[c][i] = ARDOUR.DSP.Biquad (sample_rate) + end + end +end + +-- the actual process function, called every cycle +-- ins, outs are audio-data arrays +-- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray +-- n_samples are the number of samples to process +function dsp_run (ins, outs, n_samples) + -- make sure input and output count matches... + assert (#ins == #outs) + -- ...and matches the configured number of channels + assert (#ins == chn) + + local ctrl = CtrlPorts:array () -- get control parameters as array + -- ctrl[] .. correspond to the parameters given in in dsp_params() + + -- test if the plugin-parameters have changed + if freq ~= ctrl[1] or qual ~= ctrl[2] then + -- remember current settings + freq = ctrl[1] + qual = ctrl[2] + -- calc max number of states to configure/process + limit = math.floor (sample_rate / (2 * freq)) -- at most up to SR / 2 + if limit > max_stages then limit = max_stages end + + -- re-compute the filter coefficients for all filters + for c = 1, chn do -- for each channel + for i = 1, limit do -- and for each filter stage + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:Biquad + -- and for a list of available types, see + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.DSP.Biquad.Type + -- the parameters are type, frequency, quality(bandwidth), gain + filters[c][i]:compute (ARDOUR.DSP.BiquadType.Notch, freq * i, qual * i, 0) + end + end + end + + -- limit the number of process stages + local stages = math.floor (ctrl['3']) -- current user-set parameter + if stages < 1 then stages = 1 end -- at least one stage... + if stages > limit then stages = limit end + + -- process all channels + for c = 1, chn do + -- when not processing in-place, copy the data from input to output first + if ins[c] ~= outs[c] then + ARDOUR.DSP.copy_vector (outs[c], ins[c], n_samples) + end + + -- run all stages, in-place on the output buffer + for i = 1, stages do + filters[c][i]:run (outs[c], n_samples) + end + end +end diff --git a/share/scripts/periodic_backup.lua b/share/scripts/periodic_backup.lua new file mode 100644 index 0000000000..ff274a7a08 --- /dev/null +++ b/share/scripts/periodic_backup.lua @@ -0,0 +1,47 @@ +ardour { + ["type"] = "EditorHook", + name = "Periodically Save Snapshot", + author = "Ardour Lua Task Force", + description = "Save a session-snapshot peridocally (every 15mins) named after the current date-time", +} + +-- subscribe to signals +-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal +function signals () + return LuaSignal.Set():add ({[LuaSignal.LuaTimerS] = true}) +end + +-- create callback function +function factory () + local _last_snapshot_time = 0 -- persistent variable + local _snapshot_interval = 60 * 15 -- 15 minutes + + -- callback function which invoked when signal is emitted, every 100ms + return function (signal, ref, ...) + + local now = os.time (); -- unix-time, seconds since 1970 + + -- skip initial save when script is loaded + if (_last_snapshot_time == 0) then + _last_snapshot_time = now; + end + + -- every 15 mins + if (now > _last_snapshot_time + _snapshot_interval) then + + -- don't save while recording, may interfere with recording + if Session:actively_recording() then + -- queue 30 sec after rec-stop + _last_snapshot_time = now - _snapshot_interval + 30 + return + end + + _last_snapshot_time = now + -- format date-time (avoid colon) + local snapshot_name = os.date ("%Y-%m-%d %H.%M.%S", now) + -- save session -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session + Session:save_state ("backup " .. snapshot_name, false, false, false) + end + + end +end diff --git a/share/scripts/post_export_save_hook.lua b/share/scripts/post_export_save_hook.lua new file mode 100644 index 0000000000..3b31ee320b --- /dev/null +++ b/share/scripts/post_export_save_hook.lua @@ -0,0 +1,26 @@ +ardour { + ["type"] = "EditorHook", + name = "Save Snapshot after Export", + author = "Ardour Lua Task Force", + description = "Save a session-snapshot on export, named after the export-timespan", +} + +-- subscribe to signals +-- http://manual.ardour.org/lua-scripting/class_reference/#LuaSignal.LuaSignal +function signals () + s = LuaSignal.Set() + s:add ({[LuaSignal.Exported] = true}) + return s +end + +-- create callback functions +function factory () + -- callback function which invoked when signal is emitted + return function (signal, ref, ...) + -- 'Exported' passes 2 strings: current time-span name, path to exported file + -- (see C++ libs/ardour/export_handler.cc Session::Exported ) + local timespan_name, file_path = ... + -- save session -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session + Session:save_state ("export-" .. timespan_name, false, false, false) + end +end diff --git a/share/scripts/preare_record_example.lua b/share/scripts/preare_record_example.lua new file mode 100644 index 0000000000..48e063d326 --- /dev/null +++ b/share/scripts/preare_record_example.lua @@ -0,0 +1,82 @@ +--[[ + +# Example script to prepare the Ardour session for recording + +Usually there's a certain state needed to actually start the recording. This example +script treats the situation of a podcast recording. When starting the recording the +following settings have to be ensured. + +* Session has to be recenabled +* Tracks have to be recenabled +* Gain automation have to set on write in order to record events from mute buttons +* The playhead has to be at 00:00:00.000 +* The last (failed) capture has to be cleared +* Location markers have to be cleared + +So this script automizes away the task and lets the podcast moderator by just one +action (for example triggerd by a Wiimote) prepare the session for recording. + +It can be used for example with the python script of the Linux podcasting hacks: +https://github.com/linux-podcasting-hacks/wiimote-recording-control + +Not that this script is more meant as an demo script to demonstrate the +possibilities of the Lua interface. + +--]] + +ardour { + ["type"] = "EditorAction", + name = "Prepare recording for podcast", + author = "Johannes Mueller", + description = [[ +Prepares the Ardour session for podcast recording. + +* Sets the gain automation to "Write" so that muting buttons work. +* Recenables all tracks. +* Clears all markers. +* Erases the last capture (assuming that it was a failed one) +* Rewinds the session to starting point. +* Recenables the session. +]] +} + +function factory (unused) return function() + if Session:actively_recording() then + return end + + for t in Session:get_tracks():iter() do + t:gain_control():set_automation_state(ARDOUR.AutoState.Write) + t:rec_enable_control():set_value(1, PBD.GroupControlDisposition.UseGroup) + end + + for l in Session:locations():list():iter() do + if l:is_mark() then + Session:locations():remove(l) + end + end + + Session:goto_start() + Editor:remove_last_capture() + Session:maybe_enable_record() + +end end + +function icon (params) return function (ctx, width, height) + local x = width * .5 + local y = height * .5 + local r = math.min (x, y) * .55 + + ctx:arc (x, y, r, 0, 2 * math.pi) + ctx:set_source_rgba (.9, .3, .3, 1.) + ctx:fill_preserve () + ctx:set_source_rgba (0, 0, 0, .8) + ctx:set_line_width (1) + ctx:stroke () + + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(r * 1.5) .. "px") + txt:set_text ("P") + local tw, th = txt:get_pixel_size () + ctx:set_source_rgba (0, 0, 0, 1.0) + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/remove_unknown_procs.lua b/share/scripts/remove_unknown_procs.lua new file mode 100644 index 0000000000..044e85b3b8 --- /dev/null +++ b/share/scripts/remove_unknown_procs.lua @@ -0,0 +1,44 @@ +ardour { ["type"] = "EditorAction", name = "Remove Unknown Plugins", + license = "MIT", + author = "Ardour Team", + description = [[Remove all unknown plugins/processors from all tracks and busses]] +} + +function factory (params) return function () + -- iterate over all tracks and busses (aka routes) + for route in Session:get_routes ():iter () do + -- route is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route + local i = 0; + -- we need to iterate one-by one, removing a processor invalidates the list + repeat + proc = route:nth_processor (i) + -- proc is a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Processor + -- try cast it to http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:UnknownProcessor + if not proc:isnil () and not proc:to_unknownprocessor ():isnil () then + route:remove_processor (proc, nil, true) + else + i = i + 1 + end + until proc:isnil () + end +end end + + +function icon (params) return function (ctx, width, height, fg) + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (math.min (width, height) * .5) .. "px") + txt:set_text ("Fx") + local tw, th = txt:get_pixel_size () + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:layout_cairo_path (ctx) + + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + ctx:set_line_width (3) + ctx:stroke_preserve () + + ctx:set_source_rgba (.8, .2, .2, 1) + ctx:set_line_width (2) + ctx:stroke_preserve () + + ctx:set_source_rgba (0, 0, 0, 1) + ctx:fill () +end end diff --git a/share/scripts/reset_mixer.lua b/share/scripts/reset_mixer.lua new file mode 100644 index 0000000000..faf7aad477 --- /dev/null +++ b/share/scripts/reset_mixer.lua @@ -0,0 +1,249 @@ +ardour { + ["type"] = "EditorAction", + name = "Reset Mixer", + license = "MIT", + author = "Ben Loftis, Nikolaus Gullotta, Maxime Lecoq", + description = [[Resets key Mixer settings after user-prompt (warning: this cannot be undone)]] +} + +function factory() return function() + local sp_radio_buttons = {Bypass="bypass", Remove="remove", Nothing=false} + local dlg = { + {type="label", align="left", colspan="3", title="Please select below the items you want to reset:" }, + {type="label", align="left", colspan="3", title="(Warning: this cannot be undone!)\n" }, + + {type="heading", align ="center", colspan="3", title = "Common Controls:" }, + {type="checkbox", key="fader", default=true, title="Fader" }, + {type="checkbox", key="mute", default=true, title="Mute" }, + {type="checkbox", key="solo", default=true, title="Solo" }, + {type="checkbox", key="trim", default=true, title="Trim" }, + {type="checkbox", key="pan", default=true, title="Pan (All)" }, + {type="checkbox", key="phase", default=true, title="Phase" }, + {type="checkbox", key="sends", default=true, title="Sends" }, + {type="checkbox", key="eq", default=true, title="EQ" }, + {type="checkbox", key="comp", default=true, title="Compressor" }, + + {type="heading", align="center", colspan="3", title="Processors:" }, + {type="radio", key="plugins", title="Plug-ins", values=sp_radio_buttons, default="Bypass" }, + {type="radio", key="io", title="Sends/Inserts", values=sp_radio_buttons, default="Bypass" }, + + {type="hseparator", title=""}, + + {type="heading", align="center", colspan="3", title="Misc." }, + {type="checkbox", key="auto", colspan="3", title = "Automation (switch to manual mode)" }, + {type="checkbox", key="rec", colspan="3", title = "Disable Record" }, + {type="checkbox", key="groups", colspan="3", title = "Groups" }, + {type="checkbox", key="vcas", colspan="3", title = "VCAs (unassign all)" }, + } + + function reset(ctrl, disp, auto) + local disp = disp or PBD.GroupControlDisposition.NoGroup + + if not(ctrl:isnil()) then + local pd = ctrl:desc() + ctrl:set_value(pd.normal, disp) + + if auto then + ctrl:set_automation_state(auto) + end + end + end + + function reset_eq_controls(route, disp, auto) + if route:isnil() then + return + end + + local disp = disp or PBD.GroupControlDisposition.NoGroup + + reset(route:eq_enable_controllable(), disp, auto) + + local i = 0 + repeat + for _,ctrl in pairs({ + route:eq_freq_controllable(i), + route:eq_gain_controllable(i), + route:eq_q_controllable(i), + }) do + reset(ctrl, disp, auto) + end + i = i + 1 + until route:eq_freq_controllable(i):isnil() + end + + function reset_comp_controls(route, disp, auto) + if route:isnil() then + return + end + + local disp = disp or PBD.GroupControlDisposition.NoGroup + + for _,ctrl in pairs({ + route:comp_enable_controllable(), + route:comp_makeup_controllable(), + route:comp_mode_controllable(), + route:comp_speed_controllable(), + route:comp_threshold_controllable(), + }) do + reset(ctrl, disp, auto) + end + end + + function reset_send_controls(route, disp, auto) + if route:isnil() then + return + end + + local disp = disp or PBD.GroupControlDisposition.NoGroup + + local i = 0 + repeat + for _,ctrl in pairs({ + route:send_level_controllable(i), + route:send_enable_controllable(i), + route:send_pan_azimuth_controllable(i), + route:send_pan_azimuth_enable_controllable(i), + }) do + reset(ctrl, disp, auto) + end + i = i + 1 + until route:send_enable_controllable(i):isnil() + end + + function reset_plugin_automation(plugin, state) + if plugin:to_insert():isnil() then + return + end + + local plugin = plugin:to_insert() + local pc = plugin:plugin(0):parameter_count() + for c = 0, pc do + local ac = plugin:to_automatable():automation_control(Evoral.Parameter(ARDOUR.AutomationType.PluginAutomation, 0, c), false) + if not(ac:isnil()) then + ac:set_automation_state(state) + end + end + end + + function reset_plugins(route, prefs, auto) + if route:isnil() then + return + end + + local i = 0 + local queue = {} + repeat + -- Plugins are queued to not invalidate this loop + local proc = route:nth_processor(i) + if not(proc:isnil()) then + if prefs["auto"] then + reset_plugin_automation(proc, auto) + end + if prefs["plugins"] then + local insert = proc:to_insert() + if not(insert:isnil()) then + if insert:is_channelstrip() or not(insert:display_to_user()) then + ARDOUR.LuaAPI.reset_processor_to_default(insert) + else + queue[#queue + 1] = proc + end + end + end + if prefs["io"] then + local io_proc = proc:to_ioprocessor() + if not(io_proc:isnil()) then + queue[#queue + 1] = proc + end + end + end + i = i + 1 + until proc:isnil() + + -- Deal with queue now + for _, proc in pairs(queue) do + if not(proc:to_insert():isnil()) then + if prefs["plugins"] == "remove" then + route:remove_processor(proc, nil, true) + elseif prefs["plugins"] == "bypass" then + proc:deactivate() + end + end + if not(proc:to_ioprocessor():isnil()) then + if prefs["io"] == "remove" then + route:remove_processor(proc, nil, true) + elseif prefs["io"] == "bypass" then + proc:deactivate() + end + end + end + end + + local pref = LuaDialog.Dialog("Reset Mixer", dlg):run() + + if not(pref) then goto pass_script end + assert(pref, "Dialog box was cancelled or is nil") + + for route in Session:get_routes():iter() do + local disp = PBD.GroupControlDisposition.NoGroup + local auto = nil + + if pref["auto"] then + auto = ARDOUR.AutoState.Off + end + + if pref["eq"] then reset_eq_controls(route, disp, auto) end + if pref["comp"] then reset_comp_controls(route, disp, auto) end + if pref["sends"] then reset_send_controls(route, disp, auto) end + reset_plugins(route, pref, auto) + + if pref["rec"] then + reset(route:rec_enable_control(), disp, auto) + reset(route:rec_safe_control(), disp, auto) + end + + if pref["fader"] then + reset(route:gain_control(), disp, auto) + end + + if pref["phase"] then + reset(route:phase_control(), disp, auto) + end + + if pref["trim"] then + reset(route:trim_control(), disp, auto) + end + + if pref["mute"] then + reset(route:mute_control(), disp, auto) + end + + if pref["solo"] then + reset(route:solo_control(), disp, auto) + end + + if pref["pan"] then + reset(route:pan_azimuth_control(), disp, auto) + reset(route:pan_elevation_control(), disp, auto) + reset(route:pan_frontback_control(), disp, auto) + reset(route:pan_lfe_control(), disp, auto) + reset(route:pan_width_control(), disp, auto) + end + + if pref["vcas"] then + local slave = route:to_slavable() + if not(slave:isnil()) then + for vca in Session:vca_manager():vcas():iter() do + slave:unassign(vca) + end + end + end + end + + if pref["groups"] then + for group in Session:route_groups():iter() do + Session:remove_route_group(group) + end + end + ::pass_script:: + collectgarbage() +end end \ No newline at end of file diff --git a/share/scripts/s_chanmap.lua b/share/scripts/s_chanmap.lua new file mode 100644 index 0000000000..7bc070c8cc --- /dev/null +++ b/share/scripts/s_chanmap.lua @@ -0,0 +1,34 @@ +ardour { ["type"] = "Snippet", name = "plugin channel-map dev" } + +function factory () return function () + -- first track needs to be stereo and have a stereo plugin + -- (x42-eq with spectrum display, per channel processing, + -- and pre/post visualization is very handy here) + + function checksetup (r) + -- fail if Route ID 1 is not present or not stereo + assert (r and not r:isnil()) + assert (r:n_inputs():n_audio() == 2) + -- check first Plugin and make sure it is a "Plugin Insert" + if not r:nth_plugin(0):isnil() and not r:nth_plugin(0):to_insert():isnil() then return end + -- insert x42-eq at the top. + local proc = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#stereo", ARDOUR.PluginType.LV2, ""); + r:add_processor_by_index(proc, 0, nil, true) + end + + r = Session:get_remote_nth_route(1) + checksetup (r) + pi = r:nth_plugin(0):to_insert() + + pi:set_no_inplace (true) + + cm = ARDOUR.ChanMapping() + --cm:set(ARDOUR.DataType("Audio"), 0, 0) + cm:set(ARDOUR.DataType("Audio"), 1, 0) + pi:set_input_map (0, cm) + + cm = ARDOUR.ChanMapping() + --cm:set(ARDOUR.DataType("Audio"), 0, 0) + cm:set(ARDOUR.DataType("Audio"), 1, 0) + pi:set_output_map (0, cm) +end end diff --git a/share/scripts/s_fader_automation.lua b/share/scripts/s_fader_automation.lua new file mode 100644 index 0000000000..3a9cb30eef --- /dev/null +++ b/share/scripts/s_fader_automation.lua @@ -0,0 +1,58 @@ +ardour { ["type"] = "Snippet", name = "Fader Automation" } + +function factory () return function () + local playhead = Session:transport_sample () + local samplerate = Session:nominal_sample_rate () + + -- get selected tracks + rl = Editor:get_selection ().tracks:routelist () + + -- prepare undo operation + Session:begin_reversible_command ("Fancy Fade Out") + local add_undo = false -- keep track if something has changed + + -- iterate over selected tracks + for r in rl:iter () do + local ac = r:amp ():gain_control () -- ARDOUR:AutomationControl + local al = ac:alist () -- ARDOUR:AutomationList (state, high-level) + + -- set automation state to "Touch" + ac:set_automation_state (ARDOUR.AutoState.Touch) + + -- query the value at the playhead position + local g = al:eval (playhead) + + -- get state for undo + local before = al:get_state () + + -- delete all events after the playhead... + al:truncate_end (playhead) + + -- ...and generate some new ones. + for i=0,50 do + -- use a sqrt fade-out (the shape is recognizable, and otherwise + -- not be possible to achieve with existing ardour fade shapes) + al:add (playhead + i * samplerate / 50, + g * (1 - math.sqrt (i / 50)), + false, true) + end + + -- remove dense events + al:thin (20) + + -- save undo + local after = al:get_state () + Session:add_command (al:memento_command (before, after)) + add_undo = true + + ::out:: + end + + -- all done, commit the combined Undo Operation + if add_undo then + -- the 'nil' Commend here mean to use the collected diffs added above + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end +end end diff --git a/share/scripts/s_foreach_track.lua b/share/scripts/s_foreach_track.lua new file mode 100644 index 0000000000..c1c6ed8da1 --- /dev/null +++ b/share/scripts/s_foreach_track.lua @@ -0,0 +1,10 @@ +ardour { ["type"] = "Snippet", name = "Foreach Track" } + +function factory () return function () + for r in Session:get_tracks():iter() do + print (r:name()) + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Track + -- for available methods e.g. + r:set_active (true, nil) + end +end end diff --git a/share/scripts/s_group_color.lua b/share/scripts/s_group_color.lua new file mode 100644 index 0000000000..d8c0785ed7 --- /dev/null +++ b/share/scripts/s_group_color.lua @@ -0,0 +1,11 @@ +ardour { ["type"] = "Snippet", name = "Randomize Group Colors" } + +function factory () return function () + for grb in Session:route_groups ():iter () do + local r = math.random (0, 255) + local g = math.random (0, 255) + local b = math.random (0, 255) + local rgba = (r << 24) + (g << 16) + (b << 8) + 0xff + grp:set_rgba(rgba) + end +end end diff --git a/share/scripts/s_import_files.lua b/share/scripts/s_import_files.lua new file mode 100644 index 0000000000..72cca29f8e --- /dev/null +++ b/share/scripts/s_import_files.lua @@ -0,0 +1,14 @@ +ardour { ["type"] = "Snippet", name = "Import File(s) Example" } + +function factory (params) return function () + local files = C.StringVector(); + + files:push_back("/tmp/test.wav") + + local pos = -1 + Editor:do_import (files, + Editing.ImportDistinctFiles, Editing.ImportAsTrack, ARDOUR.SrcQuality.SrcBest, + ARDOUR.MidiTrackNameSource.SMFTrackName, ARDOUR.MidiTempoMapDisposition.SMFTempoIgnore, + pos, ARDOUR.PluginInfo()) + +end end diff --git a/share/scripts/s_plugin_automation.lua b/share/scripts/s_plugin_automation.lua new file mode 100644 index 0000000000..2b04bae638 --- /dev/null +++ b/share/scripts/s_plugin_automation.lua @@ -0,0 +1,43 @@ +ardour { ["type"] = "Snippet", name = "Plugin automation" } + +function factory () return function () + -- query playhead position and session sample-rate + local playhead = Session:transport_sample () + local samplerate = Session:nominal_sample_rate () + + -- get Track/Bus with RID 3 + local r = Session:get_remote_nth_route(3) + -- make sure the track object exists + assert (not r:isnil ()) + + -- get AutomationList, ControlList and ParameterDescriptor + -- of the first plugin's first parameter + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:LuaAPI + local al, cl, pd = ARDOUR.LuaAPI.plugin_automation (r:nth_plugin (0), 0) + + if not al:isnil () then + print ("Parameter Range", pd.lower, pd.upper) + print ("Current value", cl:eval (playhead)) + + -- prepare undo operation + Session:begin_reversible_command ("Automatix") + -- remember current AutomationList state + local before = al:get_state() + + -- remove future automation + cl:truncate_end (playhead) + + -- add new data points after the playhead 1 sec, min..max + -- without guard-points, but with initial (..., false, true) + for i=0,10 do + cl:add (playhead + i * samplerate / 10, + pd.lower + math.sqrt (i / 10) * (pd.upper - pd.lower), + false, true) + end + + -- save undo + local after = al:get_state() + Session:add_command (al:memento_command(before, after)) + Session:commit_reversible_command (nil) + end +end end diff --git a/share/scripts/s_plugin_reorder.lua b/share/scripts/s_plugin_reorder.lua new file mode 100644 index 0000000000..11fb0e056a --- /dev/null +++ b/share/scripts/s_plugin_reorder.lua @@ -0,0 +1,23 @@ +ardour { ["type"] = "Snippet", name = "Plugin Order Reverse" } + +function factory () return function () + local sel = Editor:get_selection () + -- for each selected track/bus + for r in sel.tracks:routelist ():iter () do + print ("Route:", r:name ()) + local neworder = ARDOUR.ProcessorList(); -- create a PluginList + local i = 0; + repeat -- iterate over all plugins/processors + local proc = r:nth_processor (i) + if not proc:isnil () then + -- append plugin to list + neworder:push_back(proc) + end + i = i + 1 + until proc:isnil () + -- reverse list + neworder:reverse() + -- and set new order + r:reorder_processors (neworder, nil) + end +end end diff --git a/share/scripts/s_pluginutils.lua b/share/scripts/s_pluginutils.lua new file mode 100644 index 0000000000..e99bef7b93 --- /dev/null +++ b/share/scripts/s_pluginutils.lua @@ -0,0 +1,57 @@ +ardour { ["type"] = "Snippet", name = "Plugin Utils" } + +function factory () return function () + + ------------------------------------------------------------------------------- + -- List all Plugins + for p in ARDOUR.LuaAPI.list_plugins():iter() do + print (p.name, p.unique_id, p.type) + local psets = p:get_presets() + if not empty:empty() then + for pset in psets:iter() do + print (" - ", pset.label) + end + end + end + + ------------------------------------------------------------------------------- + -- add a Plugin (here LV2) to all mono tracks that contain the pattern "dru" + -- and load a plugin-preset (if it exists) + for r in Session:get_routes():iter() do + if (string.match (r:name(), "dru") and r:n_inputs():n_audio() == 1) then + local proc = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#mono", ARDOUR.PluginType.LV2, "cutbass"); + assert (not proc:isnil()) + r:add_processor_by_index(proc, 0, nil, true) + end + end + + + ------------------------------------------------------------------------------- + -- load a plugin preset + route = Session:get_remote_nth_route(2) + assert (route) + -- to 4th plugin (from top), ardour starts counting at zero + plugin = route:nth_plugin(3):to_insert():plugin(0) + assert (not plugin:isnil()) + ps = plugin:preset_by_label("cutbass") -- get preset by name + assert (ps) + print (ps.uri) + plugin:load_preset (ps) + + + ------------------------------------------------------------------------------- + -- add a LuaProcessor (here "Scope") to all tracks + for t in Session:get_tracks():iter() do + local pos = 0 -- insert at the top + + -- the following two lines are equivalent + --local proc = ARDOUR.LuaAPI.new_luaproc(Session, "a-Inline Scope"); + local proc = ARDOUR.LuaAPI.new_plugin (Session, "a-Inline Scope", ARDOUR.PluginType.Lua, ""); + assert (not proc:isnil()) + + t:add_processor_by_index(proc, pos, nil, true) + -- optionally set some parameters + ARDOUR.LuaAPI.set_processor_param (proc, 0, 5) -- timescale 5sec + end + +end end diff --git a/share/scripts/s_portengine.lua b/share/scripts/s_portengine.lua new file mode 100644 index 0000000000..ebca9ff696 --- /dev/null +++ b/share/scripts/s_portengine.lua @@ -0,0 +1,35 @@ +ardour { ["type"] = "Snippet", name = "portengine" } +function factory () return function () + + local a = Session:engine() + print ("----- Port objects from Ardour's engine ----"); + _, t = a:get_ports (ARDOUR.DataType("audio"), ARDOUR.PortList()) + -- table 't' holds argument references. t[2] is the PortList + for p in t[2]:iter() do + local lp = p:get_connected_latency_range (ARDOUR.LatencyRange(), true) + local lc = p:get_connected_latency_range (ARDOUR.LatencyRange(), false) + print (p:name(), " -- Play lat.", lp[1].min, lp[1].max, "Capt lat.", lc[1].min, lc[1].max) + end + + print ("----- Port names queries from the backend ----"); + _, t = a:get_backend_ports ("", ARDOUR.DataType("audio"), 0, C.StringVector()) + -- table 't' holds argument references. t[4] is the StringVector + for n in t[4]:iter() do + print (n) + end + + print ("----- Connections from the backend ----"); + _, t = a:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput, C.StringVector()) + for n in t[4]:iter() do + local printed_name = false; + local _, ct = a:get_connections (n, C.StringVector()) + for c in ct[2]:iter() do + if (not printed_name) then + printed_name = true; + print (n) + end + print (" ->", c) + end + end + +end end diff --git a/share/scripts/s_region_gain.lua b/share/scripts/s_region_gain.lua new file mode 100644 index 0000000000..648e76629c --- /dev/null +++ b/share/scripts/s_region_gain.lua @@ -0,0 +1,82 @@ +ardour { ["type"] = "Snippet", name = "Set Region Gain" } + +function factory () return function () + -- get Editor GUI Selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- allocate a buffer (float* in C) + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm + local cmem = ARDOUR.DSP.DspShm (8192) + + -- prepare undo operation + Session:begin_reversible_command ("Lua Region Gain") + local add_undo = false -- keep track if something has changed + + -- iterate over selected regions + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- test if it's an audio region + if r:to_audioregion ():isnil () then + goto next + end + + -- to read the Region data, we use the Readable interface of the Region + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Readable + local rd = r:to_readable () + + local n_samples = rd:readable_length () + local n_channels = rd:n_channels () + + local peak = 0 -- the audio peak to be calculated + + -- iterate over all channels in Audio Region + for c = 0, n_channels -1 do + local pos = 0 + repeat + -- read at most 8K samples of channel 'c' starting at 'pos' + local s = rd:read (cmem:to_float (0), pos, 8192, c) + pos = pos + s + -- access the raw audio data + -- http://manual.ardour.org/lua-scripting/class_reference/#C:FloatArray + local d = cmem:to_float (0):array() + -- iterate over the audio sample data + for i = 0, s do + if math.abs (d[i]) > peak then + peak = math.abs (d[i]) + end + end + until s < 8192 + assert (pos == n_samples) + end + + if (peak > 0) then + print ("Region:", r:name (), "peak:", 20 * math.log (peak) / math.log(10), "dBFS") + else + print ("Region:", r:name (), " is silent") + end + + -- normalize region + if (peak > 0) then + -- prepare for undo + r:to_stateful ():clear_changes () + -- apply gain + r:to_audioregion (): set_scale_amplitude (1 / peak) + -- save changes (if any) to undo command + if not Session:add_stateful_diff_command (r:to_statefuldestructible ()):empty () then + add_undo = true + end + end + + ::next:: + end + + -- all done. now commit the combined undo operation + if add_undo then + -- the 'nil' command here means to use all collected diffs + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end + +end end diff --git a/share/scripts/s_region_gain2.lua b/share/scripts/s_region_gain2.lua new file mode 100644 index 0000000000..cec56ca412 --- /dev/null +++ b/share/scripts/s_region_gain2.lua @@ -0,0 +1,63 @@ +ardour { ["type"] = "Snippet", name = "Normalize Regions" } + +function factory () return function () + -- get Editor GUI Selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- prepare undo operation + Session:begin_reversible_command ("Lua Normalize") + local add_undo = false -- keep track if something has changed + + -- iterate over selected regions + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- test if it's an audio region + local ar = r:to_audioregion (); + if ar:isnil () then + goto next + end + + local peak = ar:maximum_amplitude (nil); + local rms = ar:rms (nil); + + if (peak > 0) then + print ("Region:", r:name (), "peak:", 20 * math.log (peak) / math.log(10), "dBFS") + print ("Region:", r:name (), "rms :", 20 * math.log (rms) / math.log(10), "dBFS") + else + print ("Region:", r:name (), " is silent") + end + + -- normalize region + if (peak > 0) then + -- prepare for undo + r:to_stateful ():clear_changes () + -- calculate gain. + local f_rms = rms / 10 ^ (.05 * -18) -- -18dBFS/RMS + local f_peak = peak / 10 ^ (.05 * -1) -- -1dbFS/peak + -- apply gain + if (f_rms > f_peak) then + print ("Region:", r:name (), "RMS normalized by:", -20 * math.log (f_rms) / math.log(10), "dB") + ar:set_scale_amplitude (1 / f_rms) + else + print ("Region:", r:name (), "peak normalized by:", -20 * math.log (f_peak) / math.log(10), "dB") + ar:set_scale_amplitude (1 / f_peak) + end + -- save changes (if any) to undo command + if not Session:add_stateful_diff_command (r:to_statefuldestructible ()):empty () then + add_undo = true + end + end + + ::next:: + end + + -- all done. now commit the combined undo operation + if add_undo then + -- the 'nil' command here means to use all collected diffs + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end + +end end diff --git a/share/scripts/s_replaceplugin.lua b/share/scripts/s_replaceplugin.lua new file mode 100644 index 0000000000..4cef07c7ba --- /dev/null +++ b/share/scripts/s_replaceplugin.lua @@ -0,0 +1,11 @@ +ardour { ["type"] = "Snippet", name = "Replace Plugin" } + +function factory () return function () + + route = Session:get_remote_nth_route(1) + old = route:nth_plugin(0) + new = ARDOUR.LuaAPI.new_plugin(Session, "http://gareus.org/oss/lv2/fil4#stereo", ARDOUR.PluginType.LV2, ""); + route:replace_processor (old, new, nil) + old = nil new = nil -- explicitly drop references (unless they're local vars) + +end end diff --git a/share/scripts/s_selection.lua b/share/scripts/s_selection.lua new file mode 100644 index 0000000000..5dd58a43c7 --- /dev/null +++ b/share/scripts/s_selection.lua @@ -0,0 +1,60 @@ +ardour { ["type"] = "Snippet", name = "Editor Selection" } + +function factory () return function () + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + -- the Ardour Selection can include multiple items + -- (regions, tracks, ranges, markers, automation, midi-notes etc) + local sel = Editor:get_selection () + + -- + -- At the point of writing the following data items are available + -- + + -- Range selection, total span of all ranges (0, 0 if no time range is selected) + if sel.time:start () < sel.time:end_sample () then + print ("Total Range:", sel.time:start (), sel.time:end_sample ()) + end + + -- Range selection, individual ranges. + for ar in sel.time:iter () do + -- each of the items is a + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioRange + print ("Range:", ar.id, ar.start, ar._end) + end + + -- Track/Bus Selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:TrackSelection + for r in sel.tracks:routelist ():iter () do + -- each of the items is a + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route + print ("Route:", r:name ()) + end + + -- Region selection + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:RegionSelection + for r in sel.regions:regionlist ():iter () do + -- each of the items is a + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Region + print ("Region:", r:name ()) + end + + -- Markers + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:MarkerSelection + -- Note: Marker selection is not cleared and currently (Ardour-4.7) points + -- to the most recently selected marker. + for m in sel.markers:iter () do + -- each of the items is a + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOURUI::ArdourMarker + print ("Marker:", m:name (), m:position(), m:_type()) + end + + ---------------------------------------------------------- + -- The total time extents of all selected regions and ranges + local ok, ext = Editor:get_selection_extents (0, 0) + if ok then + print ("Selection Extents:", ext[1], ext[2]) + else + print ("No region or range is selected") + end + +end end diff --git a/share/scripts/s_showhide_track.lua b/share/scripts/s_showhide_track.lua new file mode 100644 index 0000000000..289367643d --- /dev/null +++ b/share/scripts/s_showhide_track.lua @@ -0,0 +1,23 @@ +ardour { ["type"] = "Snippet", name = "Show/Hide TimeAxisView" } + +function factory () return function () + -- get a route from the session by Presentation-Order + -- http://ardourman/lua-scripting/class_reference/#ARDOUR:Session + local route = Session:get_remote_nth_route(2) + assert (route) -- abort if it does not exist + print (route:name()) + + -- the GUI timeline representation of a Track/Bus is a "Route Time Axis View" Object + local rtav = Editor:rtav_from_route (route) -- lookup RTAV + + -- the show/hide state applies to any "Time Axis View", cast RTAV to TAV. + Editor:hide_track_in_display (rtav:to_timeaxisview(), false --[[true: only if selected; false: any]]) + + + -- look up the route named "Audio" + route = Session:route_by_name("Audio") + assert (route) -- abort if it does not exist + + Editor:show_track_in_display (Editor:rtav_from_route (route):to_timeaxisview(), false --[[move into view]]) + +end end diff --git a/share/scripts/s_thin_automation.lua b/share/scripts/s_thin_automation.lua new file mode 100644 index 0000000000..a86b6c71d0 --- /dev/null +++ b/share/scripts/s_thin_automation.lua @@ -0,0 +1,47 @@ +ardour { ["type"] = "Snippet", name = "Thin Fader Automation" } + +-- --TODO-- +-- For a fully fledged EditorAction this script should +-- offer a dropdown to select automation of all paramaters +-- (not just the fader) +-- see scripts/midi_cc_to_automation.lua and +-- scripts/mixer_settings_store.lua +-- Thinning Area should also be a numeric-entry or slider + +function factory () return function () + -- get selected tracks + rl = Editor:get_selection ().tracks:routelist () + + -- prepare undo operation + Session:begin_reversible_command ("Thin Automation") + local add_undo = false -- keep track if something has changed + + -- iterate over selected tracks + for r in rl:iter () do + + -- get the Fader (aka "amp") control + local ac = r:amp ():gain_control () -- ARDOUR:AutomationControl + local al = ac:alist () -- ARDOUR:AutomationList + + -- get state for undo + local before = al:get_state () + + -- remove dense events + al:thin (50) -- threashold of area below curve + + -- save undo + local after = al:get_state () + Session:add_command (al:memento_command (before, after)) + add_undo = true + + ::out:: + end + + -- all done, commit the combined Undo Operation + if add_undo then + -- the 'nil' Commend here mean to use the collected diffs added above + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end +end end diff --git a/share/scripts/s_timecode.lua b/share/scripts/s_timecode.lua new file mode 100644 index 0000000000..9f6601f903 --- /dev/null +++ b/share/scripts/s_timecode.lua @@ -0,0 +1,21 @@ +ardour { ["type"] = "Snippet", name = "Timecode" } + +function factory () return function () + + local samplerate = 48000 -- samples per second + + -- generic convert, explicitly provide Timecode (fps) and sample-rate + -- http://manual.ardour.org/lua-scripting/class_reference/#Timecode.TimecodeFormat + hh, mm, ss, ff = ARDOUR.LuaAPI.sample_to_timecode (Timecode.TimecodeFormat.TC25, samplerate, 1920) + print (ARDOUR.LuaAPI.sample_to_timecode (Timecode.TimecodeFormat.TC25, samplerate, 1920)) + + -- generic convert, explicitly provide Timecode (fps) and sample-rate + local s = ARDOUR.LuaAPI.timecode_to_sample (Timecode.TimecodeFormat.TC25, samplerate, 10, 11, 12, 13) + assert (25 * (10 * 3600 + 11 * 60 + 12 ) + 13 == s * 25 / samplerate) + + -- use session-settings: sample-rate and timecode format is taken from the + -- current session. Note that the sample-rate includes pull-up/down + print (Session:sample_to_timecode_lua (12345)) + print (Session:timecode_to_sample_lua (10, 11, 12, 13)) + +end end diff --git a/share/scripts/s_track_props.lua b/share/scripts/s_track_props.lua new file mode 100644 index 0000000000..ad983f8364 --- /dev/null +++ b/share/scripts/s_track_props.lua @@ -0,0 +1,48 @@ +ardour { ["type"] = "Snippet", name = "Track Properties" } + +function factory () return function () + --- iterate over all tracks + for t in Session:get_tracks():iter() do + -- t is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Track + + -- operate one those with "Drum" in the name + if (t:name ():find ("Drum")) then + + -- print the name, and number of audio in/out + -- see also http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:ChanCount + print (t:name (), "| Audio In:", t:n_inputs ():n_audio (), "Audio Out:", t:n_outputs ():n_audio ()) + + -- get the track's http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:PresentationInfo + pi = t:presentation_info_ptr () + + -- set the track's color to orange - hex RGBA + pi:set_color (0xff8800ff) + + -- phase invert the 1st channel + t:phase_control():set_phase_invert (1, true) + + -- solo the track -- and only the track, not other tracks grouped with it. + -- + -- Note that changing solo/mute needs to propagate implicit solo/mute. + -- These changes have to be done atomically, so that all + -- related solo/mute change simultaneously at the same time. + -- This can only be done from realtime-context, so we need to queue a session-rt + -- event using the session realtime-event dispatch mechanism: + Session:set_control (t:solo_control(), 1, PBD.GroupControlDisposition.NoGroup) + + -- unmute the track, this also examplifies how one could use lists to modify + -- multiple controllables at the same time (they should be of the same + -- paramater type - e.g. mute_control() of multiple tracks, they'll all + -- change simultaneously in rt-context) + local ctrls = ARDOUR.ControlListPtr () + ctrls:push_back (t:mute_control()) -- we could add more controls to change via push_back + Session:set_controls (ctrls, 0, PBD.GroupControlDisposition.NoGroup) + + -- add a track comment + t:set_comment ("This is a Drum Track", nil) + + -- and set the fader to -7dB == 10 ^ (0.05 * -7) + t:gain_control():set_value (10 ^ (0.05 * -7), PBD.GroupControlDisposition.NoGroup) + end + end +end end diff --git a/share/scripts/s_vamp_plugin_index.lua b/share/scripts/s_vamp_plugin_index.lua new file mode 100644 index 0000000000..b559f57e89 --- /dev/null +++ b/share/scripts/s_vamp_plugin_index.lua @@ -0,0 +1,45 @@ +ardour { ["type"] = "Snippet", name = "Vamp Plugin List" } +function factory () return function () + + local plugins = ARDOUR.LuaAPI.Vamp.list_plugins (); + for id in plugins:iter () do + local vamp = ARDOUR.LuaAPI.Vamp(id, Session:nominal_sample_rate()) + local vp = vamp:plugin () + print (" --- VAMP Plugin ---") + print ("Id:", vp:getIdentifier ()) + print ("Name:", vp:getName ()) + print ("Description:", vp:getDescription ()) + + local progs = vp:getPrograms(); + if not progs:empty () then + print ("Preset(s):") + for p in progs:iter () do + print (" *", p) + end + end + + local params = vp:getParameterDescriptors () + if not params:empty () then + print ("Parameters(s):") + for p in params:iter () do + -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:PluginBase:ParameterDescriptor + print (" * Id:", p.identifier, "Name:", p.name, "Desc:", p.description) + local i = 0; for vn in p.valueNames:iter() do + print (" ^^ ", i, " -> ", vn) + i = i + 1 + end + end + end + + local feats = vp:getOutputDescriptors () + if not feats:empty () then + print ("Output(s):") + for p in feats:iter () do + -- http://manual.ardour.org/lua-scripting/class_reference/#Vamp:Plugin:OutputDescriptor + print (" * Id:", p.identifier, "Name:", p.name, "Desc:", p.description) + end + end + + end +end end + diff --git a/share/scripts/s_whoami.lua b/share/scripts/s_whoami.lua new file mode 100644 index 0000000000..d391d65095 --- /dev/null +++ b/share/scripts/s_whoami.lua @@ -0,0 +1,22 @@ +ardour { ["type"] = "Snippet", name = "Who Am I?" } + +function factory() return function() + +function whoami() + --pcall is the lua equivalent + --of try: ... catch: ... + if not pcall(function() local first_check = Session:get_mixbus(0) end) then + return "Ardour" + else + local second_check = Session:get_mixbus(11) + if second_check:isnil() then + return "Mixbus" + else + return "32C" + end + end +end + +print(whoami()) + +end end diff --git a/share/scripts/scope.lua b/share/scripts/scope.lua new file mode 100644 index 0000000000..1952c100c4 --- /dev/null +++ b/share/scripts/scope.lua @@ -0,0 +1,229 @@ +ardour { + ["type"] = "dsp", + name = "a-Inline Scope", + category = "Visualization", + license = "MIT", + author = "Ardour Team", + description = [[Mixer strip inline waveform display]] +} + +-- return possible i/o configurations +function dsp_ioconfig () + -- -1, -1 = any number of channels as long as input and output count matches + return { [1] = { audio_in = -1, audio_out = -1}, } +end + +function dsp_params () + return + { + { ["type"] = "input", name = "Timescale", min = .1, max = 5, default = 2, unit="sec", logarithmic = true }, + { ["type"] = "input", name = "Logscale", min = 0, max = 1, default = 0, toggled = true }, + { ["type"] = "input", name = "Height (Aspect)", min = 0, max = 3, default = 1, enum = true, scalepoints = + { + ["Min"] = 0, + ["16:10"] = 1, + ["1:1"] = 2, + ["Max"] = 3 + } + }, + } +end + + +function dsp_init (rate) + -- global variables (DSP part only) + samplerate = rate + bufsiz = 6 * rate + dpy_hz = rate / 25 + dpy_wr = 0 +end + +function dsp_configure (ins, outs) + -- store configuration in global variable + audio_ins = ins:n_audio () + -- allocate shared memory area + -- this is used to speed up DSP computaton (using a C array) + -- and to share data with the GUI + self:shmem ():allocate (4 + bufsiz * audio_ins) + self:shmem ():clear () + self:shmem ():atomic_set_int (0, 0) + local cfg = self:shmem ():to_int (1):array () + cfg[1] = samplerate + cfg[2] = bufsiz + cfg[3] = audio_ins +end + +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + local shmem = self:shmem () + local write_ptr = shmem:atomic_get_int (0) + + for c = 1,audio_ins do + -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0 + local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped input buffer for given cannel + local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped output buffer for given cannel + local chn_off = 4 + bufsiz * (c - 1) + if (ib ~= ARDOUR.ChanMapping.Invalid) then + if (write_ptr + n_samples < bufsiz) then + ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), n_samples) + else + local w0 = bufsiz - write_ptr; + ARDOUR.DSP.copy_vector (shmem:to_float (write_ptr + chn_off), bufs:get_audio (ib):data (offset), w0) + ARDOUR.DSP.copy_vector (shmem:to_float (chn_off) , bufs:get_audio (ib):data (offset + w0), n_samples - w0) + end + if (ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob) then + ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples) + end + else + if (write_ptr + n_samples < bufsiz) then + ARDOUR.DSP.memset (shmem:to_float (write_ptr + chn_off), 0, n_samples) + else + local w0 = bufsiz - write_ptr; + ARDOUR.DSP.memset (shmem:to_float (write_ptr + chn_off), 0, w0) + ARDOUR.DSP.memset (shmem:to_float (chn_off) , 0, n_samples - w0) + end + end + end + -- clear unconnected inplace buffers + for c = 1,audio_ins do + local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped input buffer for given cannel + local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1); -- get id of mapped output buffer for given cannel + if (ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid) then + bufs:get_audio (ob):silence (n_samples, offset) + end + end + + write_ptr = (write_ptr + n_samples) % bufsiz + shmem:atomic_set_int (0, write_ptr) + + -- emit QueueDraw every FPS + dpy_wr = dpy_wr + n_samples + if (dpy_wr > dpy_hz) then + dpy_wr = dpy_wr % dpy_hz; + self:queue_draw () + end +end + + +-- helper function for drawing symmetric grid +function gridline (ctx, x, xr, h, val) + ctx:move_to (math.floor (.5 + x + val * xr) -.5, 1) + ctx:line_to (math.floor (.5 + x + val * xr) -.5, h - 1) + ctx:stroke () + ctx:move_to (math.floor (.5 + x - val * xr) -.5, 1) + ctx:line_to (math.floor (.5 + x - val * xr) -.5, h - 1) + ctx:stroke () +end + +function render_inline (ctx, w, max_h) + local ctrl = CtrlPorts:array () -- get control port array (read/write) + local shmem = self:shmem () -- get shared memory region + local cfg = shmem:to_int (1):array () -- "cast" into lua-table + local rate = cfg[1] + local buf_size = cfg[2] + local n_chn = cfg[3] + + -- get settings + local timescale = ctrl[1] or 1.0 -- display size in seconds + local logscale = ctrl[2] or 0; logscale = logscale > 0 -- logscale + local hmode = ctrl[3] or 1 -- height mode + + -- calc height + if hmode == 0 then + h = math.ceil (w * 10 / 16) + if (h > 44) then + h = 44 + end + elseif (hmode == 2) then + h = w + elseif (hmode == 3) then + h = max_h + else + h = math.ceil (w * 10 / 16) + end + + if (h > max_h) then + h = max_h + end + + -- display settings + local spp = math.floor (timescale * rate / (h - 2)) -- samples per pixel + local spl = spp * (h - 1) -- total number of audio samples to read + local read_ptr = (shmem:atomic_get_int (0) + buf_size - spl - 1) % buf_size -- read pointer + local xr = math.ceil ((w - 2) * (0.47 / n_chn)) -- x-axis range (per channel) + + -- clear background + ctx:rectangle (0, 0, w, h) + ctx:set_source_rgba (.2, .2, .2, 1.0) + ctx:fill () + + -- prepare drawing + ctx:set_line_width (1.0) + local dash3 = C.DoubleVector () + dash3:add ({1, 3}) + local dash4 = C.DoubleVector () + dash4:add ({1, 4}) + + -- plot every channel + for c = 1,n_chn do + local x = math.floor ((w - 2) * (c - .5) / n_chn) + 1.5 -- x-axis center for given channel + + -- draw grid -- + ctx:set_source_rgba (.5, .5, .5, 1.0) + ctx:move_to (x, 1) ctx:line_to (x, h - 1) ctx:stroke () + + ctx:set_dash (dash4, 2) + ctx:set_source_rgba (.4, .4, .4, 1.0) + if (logscale) then + gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-18)) + gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-6)) + ctx:set_dash (dash3, 2) + ctx:set_source_rgba (.5, .1, .1, 1.0) + gridline (ctx, x, xr, h, ARDOUR.DSP.log_meter(-3)) + else + gridline (ctx, x, xr, h, .1258) + gridline (ctx, x, xr, h, .5) + ctx:set_dash (dash3, 2) + ctx:set_source_rgba (.5, .1, .1, 1.0) + gridline (ctx, x, xr, h, .7079) + end + ctx:unset_dash () + ctx:set_source_rgba (.5, .1, .1, 0.7) + gridline (ctx, x, xr, h, 1) + + + -- prepare waveform display drawing + ctx:set_source_rgba (.8, .8, .8, .7) + ctx:save () + ctx:rectangle (math.floor (x - xr), 0, math.ceil (2 * xr), h) + ctx:clip () + + local chn_off = 4 + buf_size * (c - 1) + local buf_off = read_ptr; + + -- iterate over every y-axis pixel + for y = 1, h - 1 do + local s_min = 0 + local s_max = 0 + -- calc min/max values for given range + if (buf_off + spp < buf_size) then + _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, spp)) + else + local r0 = buf_size - buf_off; + _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off + buf_off), s_min, s_max, r0)) + _, s_min, s_max = table.unpack (ARDOUR.DSP.peaks (shmem:to_float (chn_off) , s_min, s_max, spp - r0)) + end + buf_off = (buf_off + spp) % buf_size; + + if (logscale) then + s_max = ARDOUR.DSP.log_meter_coeff (s_max) + s_min = - ARDOUR.DSP.log_meter_coeff (-s_min) + end + + ctx:move_to (x + s_min * xr, h - y + .5) + ctx:line_to (x + s_max * xr, h - y + .5) + end + ctx:stroke () + ctx:restore () + end + return {w, h} +end diff --git a/share/scripts/select_every_2nd_region.lua b/share/scripts/select_every_2nd_region.lua new file mode 100644 index 0000000000..30ad519483 --- /dev/null +++ b/share/scripts/select_every_2nd_region.lua @@ -0,0 +1,61 @@ +ardour { + ["type"] = "EditorAction", + name = "Region Select/2", + license = "MIT", + author = "Ardour Team", + description = [[select every 2nd region on all selected tracks]] +} + +-- select every 2nd region on all selected tracks +function factory () return function () + + local sl = ArdourUI.SelectionList () -- empty selection list + + local sel = Editor:get_selection () -- get current selection + -- for each selected track/bus.. + for route in sel.tracks:routelist ():iter () do + -- consider only tracks + local track = route:to_track () + if track:isnil() then + goto continue + end + + local skip = false; + -- iterate over all regions of the given track + for region in track:playlist():region_list():iter() do + if skip then + -- skip every 2nd region + skip = false; + else + skip = true; + -- get RegionView (GUI object to be selected) + local rv = Editor:regionview_from_region (region) + -- add it to the list of Objects to be selected + sl:push_back (rv); + end + end + ::continue:: + end + + -- set/replace current selection in the editor + Editor:set_selection (sl, ArdourUI.SelectionOp.Set); +end end + +function icon (params) return function (ctx, width, height, fg) + local wh = math.min (width, height) * .5 + ctx:translate (math.floor (width * .5 - wh), math.floor (height * .5 - wh)) + + ctx:set_line_width (1) + ctx:rectangle (wh * .25, wh * .75, wh * 1.5 , .5 * wh) + ctx:set_source_rgba (0, 0, 0, 1) + ctx:stroke_preserve () + ctx:set_source_rgba (.9, .9, .9, 1) + ctx:fill () + + ctx:set_source_rgba (1, 0, 0, 1) + ctx:rectangle (.5 + math.ceil(wh * 0.25), .5 + math.ceil(wh * .75), math.floor(wh * .5) - 1, math.floor(.5 * wh) - 1) + ctx:stroke_preserve () + + ctx:rectangle (.5 + math.ceil(wh * 1.25), .5 + math.ceil(wh * .75), math.floor(wh * .5) - 1, math.floor(.5 * wh) - 1) + ctx:stroke_preserve () +end end diff --git a/share/scripts/send_to_bus.lua b/share/scripts/send_to_bus.lua new file mode 100644 index 0000000000..95a13f95ca --- /dev/null +++ b/share/scripts/send_to_bus.lua @@ -0,0 +1,41 @@ +ardour { ["type"] = "EditorAction", name = "Send Tracks to Bus", + license = "MIT", + author = "Ardour Team", + description = [[Create a Bus and add aux-sends from all selected tracks]] +} + +function factory () return function () + -- find number of channels to use for the new bus, follow master-bus' inputs + local chn = 2 + local mst = Session:master_out (); + if not mst:isnil () then + chn = mst:n_inputs ():n_audio () + end + mst = nil -- explicitly drop reference + + local sel = Editor:get_selection () -- get selection + local tracks = ARDOUR.RouteListPtr () -- create a new list + + -- find selected *tracks*, add to tracks list + for r in sel.tracks:routelist ():iter () do + if not r:to_track ():isnil () then + tracks:push_back (r) + end + end + + if tracks:size () > 0 then + local bus = Session:new_audio_route (chn, chn, nil, 1, "", ARDOUR.PresentationInfo.Flag.AudioBus, ARDOUR.PresentationInfo.max_order) + if bus:size () > 0 then + Session:add_internal_sends (bus:front (), ARDOUR.Placement.PostFader, tracks); + end + end +end end + +function icon (params) return function (ctx, width, height, fg) + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil (math.min (width, height) * .5) .. "px") + txt:set_text ("\u{2192}B") -- "->B" + local tw, th = txt:get_pixel_size () + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/session_template_advanced.lua b/share/scripts/session_template_advanced.lua new file mode 100644 index 0000000000..1eec0349ce --- /dev/null +++ b/share/scripts/session_template_advanced.lua @@ -0,0 +1,59 @@ +ardour { + ["type"] = "SessionInit", + name = "Advanced Session", + description = [[Allows to configure master-bus and autoconnect]], + master_bus = 0 +} + +function factory () return function () + + local auto_connect_in = { + [0] = "Manually", + [1] = "automatically to physical inputs", + } + + local auto_connect_out = { + [0] = "Manually", + [1] = "automatically to physical outputs", + [2] = "automatically to master bus", + } + + local dialog_options = { + { type = "heading", title = "Customize Session: " .. Session:name () }, + { type = "number", key = "master", title = "Master bus channels", min = 0, max = 24, step = 1, digits = 0, default = 2 }, + { type = "checkbox", key = "monitor", title = "Add monitor section", default = ARDOUR.config():get_use_monitor_bus () }, + { type = "dropdown", key = "ac_input", title = "Autoconnect Inputs", + values = { + [auto_connect_in[0]] = 0, + [auto_connect_in[1]] = 1, + }, + default = auto_connect_in[ARDOUR.config():get_input_auto_connect ()] + }, + { type = "dropdown", key = "ac_output", title = "Autoconnect Outputs", + values = { + [auto_connect_out[0]] = 0, + [auto_connect_out[1]] = 1, + [auto_connect_out[2]] = 2, + }, + default = auto_connect_out[ARDOUR.config():get_output_auto_connect ()] + }, + } + + local dlg = LuaDialog.Dialog ("Template Setup", dialog_options) + local rv = dlg:run() + if (not rv) then return end + + if rv['master'] > 0 then + local count = ARDOUR.ChanCount ( ARDOUR.DataType("audio"), rv['master']) + Session:add_master_bus (count) + end + + if rv['monitor'] then + ARDOUR.config():set_use_monitor_bus (true) + end + + ARDOUR.config():set_input_auto_connect (rv['ac_input']) + ARDOUR.config():set_output_auto_connect (rv['ac_output']) + + Session:save_state(""); +end end diff --git a/share/scripts/session_template_record.lua b/share/scripts/session_template_record.lua new file mode 100644 index 0000000000..9656fd1473 --- /dev/null +++ b/share/scripts/session_template_record.lua @@ -0,0 +1,60 @@ +ardour { + ["type"] = "SessionInit", + name = "Recording Session", + description = [[Add as many mono tracks to the new session as there are physical audio inputs and optionally record-arm them.]] +} + +---- For use with templates: Session Template setup-hook +-- +-- If a script named 'template.lua' exists in a session-template folder +-- the function produced by the 'factory' function of the script is called +-- once after creating the session from the template. +-- +-- (e.g. ~/.config/ardour5/templates/Template-Name/template.lua) +-- +-- +---- For use as meta-session (specic session-setup scripts) +-- +-- Every Lua script in the script-folder of type "SessionInit" +-- is listed as implicit template in the new-session dialog. +-- The function produced by the scripts `factory` function is called +-- once after creating a new, empty session. +-- +---- For use as meta-session (general purpose Actions) +-- +-- In some cases normal action scripts can also serve as session-setup +-- To include those ActionScripts in the template-list the script needs +-- to implement an additional function +-- function session_setup () return true end; +-- The script's factory will be called without any parameters + +function factory () return function () + local e = Session:engine() + -- from the engine's POV readable/capture ports are "outputs" + local _, t = e:get_backend_ports ("", ARDOUR.DataType("audio"), ARDOUR.PortFlags.IsOutput | ARDOUR.PortFlags.IsPhysical, C.StringVector()) + -- table 't' holds argument references. t[4] is the C.StringVector (return value) + local tracks = t[4]:size(); + + local dialog_options = { + { type = "heading", title = "Customize Session: " .. Session:name () }, + { type = "number", key = "tracks", title = "Create Tracks", min = 1, max = 128, step = 1, digits = 0, default = tracks }, + { type = "checkbox", key = "recarm", default = false, title = "Record Arm Tracks" }, + } + + local dlg = LuaDialog.Dialog ("Template Setup", dialog_options) + local rv = dlg:run() + if (not rv or rv['tracks'] == 0) then + return + end + + -- create tracks + local tl = Session:new_audio_track (1, 2, nil, rv['tracks'], "", ARDOUR.PresentationInfo.max_order, ARDOUR.TrackMode.Normal) + -- and optionally record-arm them + if rv['recarm'] then + for track in tl:iter() do + track:rec_enable_control ():set_value (1, PBD.GroupControlDisposition.NoGroup) + end + end + + Session:save_state(""); +end end diff --git a/share/scripts/set_automation_mode.lua b/share/scripts/set_automation_mode.lua new file mode 100644 index 0000000000..ee99ac1a1b --- /dev/null +++ b/share/scripts/set_automation_mode.lua @@ -0,0 +1,83 @@ +ardour { + ["type"] = "EditorAction", + name = "Engage Automation", + author = "Ardour Team", + description = [[Set automation mode of various controls (fader, trim, mute, pan), for all selected tracks.]] +} + +function factory() return function() + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR.AutoState + local auto_state = ARDOUR.AutoState.Touch + local with_plugins = true + + -- Ask user which mode to use, and whether to include plugins + local dialog_options = { + { type = "label", align="left", title = "Select automation state to apply all selected tracks:" }, + { + type = "dropdown", key = "as", title="", values = + { + ["Manual"] = ARDOUR.AutoState.Off, + ["Touch"] = ARDOUR.AutoState.Touch, + ["Write"] = ARDOUR.AutoState.Write, + ["Play"] = ARDOUR.AutoState.Play, + }, + default = "Touch" + }, + { type = "checkbox", key = "plug", default = true, title = "Also set plugin controls" } + } + local rv = LuaDialog.Dialog ("Select Automation State", dialog_options):run() + if not rv then return end + auto_state = rv['as'] + with_plugins = rv['plug'] + + -- helper function to check if given ARDOUR:AutomationControl exists + function maybe_set_automation_state (ac) + if not ac:isnil() then + ac:set_automation_state (auto_state) + end + end + + -- helper function to iterate over all automatable parameters of a plugin + function set_plugin_control_mode (pi) + local pc = pi:plugin (0):parameter_count() + for c = 0, pc do + local ac = pi:to_automatable():automation_control (Evoral.Parameter (ARDOUR.AutomationType.PluginAutomation, 0, c), false) + if not ac:isnil () then + ac:set_automation_state (auto_state) + end + end + end + + -- get selected tracks + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + if sel.tracks:routelist ():empty() then + LuaDialog.Message ("Select Automation State", "No Tracks are selected.", LuaDialog.MessageType.Info, LuaDialog.ButtonType.Close):run () + return + end + + -- iterate over selected tracks + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:TrackSelection + for r in sel.tracks:routelist ():iter () do + -- r is-a http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route + -- which interhits from ARDOUR:Stripable + + -- set route's direct control + r:gain_control ():set_automation_state (auto_state) + maybe_set_automation_state (r:trim_control ()) + maybe_set_automation_state (r:mute_control ()) + maybe_set_automation_state (r:pan_azimuth_control ()) + maybe_set_automation_state (r:pan_width_control ()) + + -- for every plugin + local i = 0 + while with_plugins do + local proc = r:nth_plugin (i) + if proc:isnil () then break end + set_plugin_control_mode (proc:to_insert ()) + i = i + 1 + end + + end +end end diff --git a/share/scripts/singen.lua b/share/scripts/singen.lua new file mode 100644 index 0000000000..8b22c1257b --- /dev/null +++ b/share/scripts/singen.lua @@ -0,0 +1,94 @@ +ardour { + ["type"] = "dsp", + name = "SinGen", + category = "Instrument", + license = "MIT", + author = "Ardour Team", + description = [[Sine Wave Generator (v1.2)]] +} + +local lpf = 0 + +function dsp_params () + return + { + { ["type"] = "input", name = "Frequency", min = 20, max = 20000, default = 1000, unit="Hz", logarithmic = true }, + { ["type"] = "input", name = "Gain", min = -90, max = 0, default = -18, unit="dB" }, + } +end + +function dsp_ioconfig () + return { [1] = { audio_in = -1, audio_out = -1}, } +end + +function dsp_init (rate) + r = rate + lpf = 2048 / rate +end + +function low_pass_filter_param(old, new, limit) + if math.abs (old - new) < limit then + return new + else + return old + lpf * (new - old) + end +end + +local p = 0 +local fo = 0 +local ao = 0 + +function dsp_run (ins, outs, n_samples) + local ctrl = CtrlPorts:array() --call parameters + + local a = {} --init array + local f = ctrl[1] or 1000 + local amp = low_pass_filter_param(ao, ARDOUR.DSP.dB_to_coefficient(ctrl[2]), 0.02) + local inc = f / r + + for s = 1, n_samples do --fill table with fragments of a sine wave + p = p + inc + a[s] = amp * math.sin(p * (2 * math.pi)) + end + + for c = 1,#outs do + outs[c]:set_table(a, n_samples) --passes array into buffer + end + + if (f ~= fo) or (a ~= ao) then + self:queue_draw() + end + fo = f + ao = amp +end + +function render_inline (ctx, w, max_h) --inline display + local ctrl = CtrlPorts:array() + h = 30 + p = 0 + inc = 1/w + f = ctrl[1] / 1000 + if f < 0.5 then f = 0.5 end + if f > 8 then f = 8 end + + --draw rectangle + ctx:rectangle(0, 0, w, h) + ctx:set_source_rgba(0, 0, 0, 1.0) + ctx:fill() + ctx:set_line_width(1.5) + ctx:set_source_rgba(0.8, 0.8, 0.8, 1.0) + + l_x = 0 + l_y = 0 + for x = 0,w do + y = ARDOUR.DSP.dB_to_coefficient(ctrl[2]) * math.sin(f * (2 * math.pi * (p))) + yc = 0.5 * h + ((-0.5 * h) * y) + ctx:move_to (x, yc + 3) + ctx:line_to (l_x, l_y + 3) + l_x = x + l_y = yc + ctx:stroke() + p = p + inc + end + return {w, h + 6} +end \ No newline at end of file diff --git a/share/scripts/spectrogram.lua b/share/scripts/spectrogram.lua new file mode 100644 index 0000000000..86419682b4 --- /dev/null +++ b/share/scripts/spectrogram.lua @@ -0,0 +1,363 @@ +ardour { + ["type"] = "dsp", + name = "a-Inline Spectrogram", + category = "Visualization", + license = "MIT", + author = "Ardour Team", + description = [[Mixer strip inline spectrum display]] +} + +-- return possible i/o configurations +function dsp_ioconfig () + -- -1, -1 = any number of channels as long as input and output count matches + return { [1] = { audio_in = -1, audio_out = -1}, } +end + +function dsp_params () + return + { + { ["type"] = "input", name = "Logscale", min = 0, max = 1, default = 0, toggled = true }, + { ["type"] = "input", name = "1/f scale", min = 0, max = 1, default = 1, toggled = true }, + { ["type"] = "input", name = "FFT Size", min = 0, max = 4, default = 3, enum = true, scalepoints = + { + ["512"] = 0, + ["1024"] = 1, + ["2048"] = 2, + ["4096"] = 3, + ["8192"] = 4, + } + }, + { ["type"] = "input", name = "Height (Aspect)", min = 0, max = 3, default = 1, enum = true, scalepoints = + { + ["Min"] = 0, + ["16:10"] = 1, + ["1:1"] = 2, + ["Max"] = 3 + } + }, + { ["type"] = "input", name = "Range", min = 20, max = 160, default = 60, unit="dB"}, + { ["type"] = "input", name = "Offset", min = -40, max = 40, default = 0, unit="dB"}, + } +end + +-- symbolic names for shmem offsets +local SHMEM_RATE = 0 +local SHMEM_WRITEPTR = 1 +local SHMEM_AUDIO = 2 + +-- a C memory area. +-- It needs to be in global scope. +-- When the variable is set to nil, the allocated memory is free()ed. +-- the memory can be interpeted as float* for use in DSP, or read/write +-- to a C++ Ringbuffer instance. +-- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP:DspShm +local cmem = nil + +function dsp_init (rate) + -- global variables (DSP part only) + dpy_hz = rate / 25 + dpy_wr = 0 + + -- create a shared memory area to hold the sample rate, the write_pointer, + -- and (float) audio-data. Make it big enough to store 2s of audio which + -- should be enough. If not, the DSP will overwrite the oldest data anyway. + self:shmem ():allocate(2 + 2 * rate) + self:shmem ():clear() + self:shmem ():atomic_set_int (SHMEM_RATE, rate) + self:shmem ():atomic_set_int (SHMEM_WRITEPTR, 0) + + -- allocate memory, local mix buffer + cmem = ARDOUR.DSP.DspShm (8192) +end + +-- "dsp_runmap" uses Ardour's internal processor API, eqivalent to +-- 'connect_and_run()". There is no overhead (mapping, translating buffers). +-- The lua implementation is responsible to map all the buffers directly. +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + -- here we sum all audio input channels and then copy the data to a + -- custom-made circular table for the GUIs to process later + + local audio_ins = in_map:count (): n_audio () -- number of audio input buffers + local ccnt = 0 -- processed channel count + local mem = cmem:to_float(0) -- a "FloatArray", float* for direct C API usage from the previously allocated buffer + local rate = self:shmem ():atomic_get_int (SHMEM_RATE) + local write_ptr = self:shmem ():atomic_get_int (SHMEM_WRITEPTR) + + local ringsize = 2 * rate + local ptr_wrap = math.floor(2^50 / ringsize) * ringsize + + for c = 1,audio_ins do + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:ChanMapping + -- Note: lua starts counting at 1, ardour's ChanMapping::get() at 0 + local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped input buffer + local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) -- get index of mapped output buffer + + -- check if the input is connected to a buffer + if (ib ~= ARDOUR.ChanMapping.Invalid) then + + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:AudioBuffer + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:DSP + if c == 1 then + -- first channel, copy as-is + ARDOUR.DSP.copy_vector (mem, bufs:get_audio (ib):data (offset), n_samples) + else + -- all other channels, add to existing data. + ARDOUR.DSP.mix_buffers_no_gain (mem, bufs:get_audio (ib):data (offset), n_samples) + end + ccnt = ccnt + 1; + + -- copy data to output (if not processing in-place) + if (ob ~= ARDOUR.ChanMapping.Invalid and ib ~= ob) then + ARDOUR.DSP.copy_vector (bufs:get_audio (ob):data (offset), bufs:get_audio (ib):data (offset), n_samples) + end + end + end + + -- Clear unconnected output buffers. + -- In case we're processing in-place some buffers may be identical, + -- so this must be done *after processing*. + for c = 1,audio_ins do + local ib = in_map:get (ARDOUR.DataType ("audio"), c - 1) + local ob = out_map:get (ARDOUR.DataType ("audio"), c - 1) + if (ib == ARDOUR.ChanMapping.Invalid and ob ~= ARDOUR.ChanMapping.Invalid) then + bufs:get_audio (ob):silence (n_samples, offset) + end + end + + -- Normalize gain (1 / channel-count) + if ccnt > 1 then + ARDOUR.DSP.apply_gain_to_buffer (mem, n_samples, 1 / ccnt) + end + + -- if no channels were processed, feed silence. + if ccnt == 0 then + ARDOUR.DSP.memset (mem, 0, n_samples) + end + + -- write data to the circular table + if (write_ptr % ringsize + n_samples < ringsize) then + ARDOUR.DSP.copy_vector (self:shmem ():to_float (SHMEM_AUDIO + write_ptr % ringsize), mem, n_samples) + else + local chunk = ringsize - write_ptr % ringsize + ARDOUR.DSP.copy_vector (self:shmem ():to_float (SHMEM_AUDIO + write_ptr % ringsize), mem, chunk) + ARDOUR.DSP.copy_vector (self:shmem ():to_float (SHMEM_AUDIO), cmem:to_float (chunk), n_samples - chunk) + end + self:shmem ():atomic_set_int (SHMEM_WRITEPTR, (write_ptr + n_samples) % ptr_wrap) + + -- emit QueueDraw every FPS + -- TODO: call every FFT window-size worth of samples, at most every FPS + dpy_wr = dpy_wr + n_samples + if (dpy_wr > dpy_hz) then + dpy_wr = dpy_wr % dpy_hz + self:queue_draw () + end +end + +---------------------------------------------------------------- +-- GUI + +local fft = nil +local read_ptr = 0 +local line = 0 +local img = nil +local fft_size = 0 +local last_log = false + + +function render_inline (ctx, w, max_h) + local ctrl = CtrlPorts:array () -- get control port array (read/write) + local rate = self:shmem ():atomic_get_int (SHMEM_RATE) + if not cmem then + cmem = ARDOUR.DSP.DspShm (0) + end + + -- get settings + local logscale = ctrl[1] or 0; logscale = logscale > 0 -- x-axis logscale + local pink = ctrl[2] or 0; pink = pink > 0 -- 1/f scale + local fftsizeenum = ctrl[3] or 3 -- fft-size enum + local hmode = ctrl[4] or 1 -- height mode enum + local dbrange = ctrl[5] or 60 + local gaindb = ctrl[6] or 0 + + local fftsize + if fftsizeenum == 0 then fftsize = 512 + elseif fftsizeenum == 1 then fftsize = 1024 + elseif fftsizeenum == 2 then fftsize = 2048 + elseif fftsizeenum == 4 then fftsize = 8192 + else fftsize = 4096 + end + + if fftsize ~= fft_size then + fft_size = fftsize + fft = nil + end + + if dbrange < 20 then dbrange = 20; end + if dbrange > 160 then dbrange = 160; end + if gaindb < -40 then dbrange = -40; end + if gaindb > 40 then dbrange = 40; end + + + if not fft then + fft = ARDOUR.DSP.FFTSpectrum (fft_size, rate) + cmem:allocate (fft_size) + end + + if last_log ~= logscale then + last_log = logscale + img = nil + line = 0 + end + + -- calc height + if hmode == 0 then + h = math.ceil (w * 10 / 16) + if (h > 44) then + h = 44 + end + elseif (hmode == 2) then + h = w + elseif (hmode == 3) then + h = max_h + else + h = math.ceil (w * 10 / 16) + end + if (h > max_h) then + h = max_h + end + + -- re-create image surface + if not img or img:get_width() ~= w or img:get_height () ~= h then + img = Cairo.ImageSurface (Cairo.Format.ARGB32, w, h) + line = 0 + end + local ictx = img:context () + + local bins = fft_size / 2 - 1 -- fft bin count + local bpx = bins / w -- bins per x-pixel (linear) + local fpb = rate / fft_size -- freq-step per bin + local f_e = rate / 2 / fpb -- log-scale exponent + local f_b = w / math.log (fft_size / 2) -- inverse log-scale base + local f_l = math.log (fft_size / rate) * f_b -- inverse logscale lower-bound + + local mem = cmem:to_float (0) + + local ringsize = 2 * rate + local ptr_wrap = math.floor(2^50 / ringsize) * ringsize + + local write_ptr + function read_space() + write_ptr = self:shmem ():atomic_get_int (SHMEM_WRITEPTR) + local space = (write_ptr - read_ptr + ptr_wrap) % ptr_wrap + if space > ringsize then + -- the GUI lagged too much and unread data was overwritten + -- jump to the oldest audio still present in the ringtable + read_ptr = write_ptr - ringsize + space = ringsize + end + return space + end + + while (read_space() >= fft_size) do + -- read one window from the circular table + if (read_ptr % ringsize + fft_size < ringsize) then + ARDOUR.DSP.copy_vector (mem, self:shmem ():to_float (SHMEM_AUDIO + read_ptr % ringsize), fft_size) + else + local chunk = ringsize - read_ptr % ringsize + ARDOUR.DSP.copy_vector (mem, self:shmem ():to_float (SHMEM_AUDIO + read_ptr % ringsize), chunk) + ARDOUR.DSP.copy_vector (cmem:to_float(chunk), self:shmem ():to_float (SHMEM_AUDIO), fft_size - chunk) + end + read_ptr = (read_ptr + fft_size) % ptr_wrap + + -- process one line + fft:set_data_hann (mem, fft_size, 0) + fft:execute () + + -- draw spectrum + assert (bpx >= 1) + + -- scroll + if line == 0 then line = h - 1; else line = line - 1; end + + -- clear this line + ictx:set_source_rgba (0, 0, 0, 1) + ictx:rectangle (0, line, w, 1) + ictx:fill () + + for x = 0, w - 1 do + local pk = 0 + local b0, b1 + if logscale then + -- 20 .. 20k + b0 = math.floor (f_e ^ (x / w)) + b1 = math.floor (f_e ^ ((x + 1) / w)) + else + b0 = math.floor (x * bpx) + b1 = math.floor ((x + 1) * bpx) + end + + if b1 >= b0 and b1 <= bins and b0 >= 0 then + for i = b0, b1 do + local level = gaindb + fft:power_at_bin (i, pink and i or 1) -- pink ? i : 1 + if level > -dbrange then + local p = (dbrange + level) / dbrange + if p > pk then pk = p; end + end + end + end + if pk > 0.0 then + if pk > 1.0 then pk = 1.0; end + ictx:set_source_rgba (ARDOUR.LuaAPI.hsla_to_rgba (.70 - .72 * pk, .9, .3 + pk * .4)); + ictx:rectangle (x, line, 1, 1) + ictx:fill () + end + end + end + + -- copy image surface + if line == 0 then + img:set_as_source (ctx, 0, 0) + ctx:rectangle (0, 0, w, h) + ctx:fill () + else + local yp = h - line - 1; + img:set_as_source (ctx, 0, yp) + ctx:rectangle (0, yp, w, line) + ctx:fill () + + img:set_as_source (ctx, 0, -line) + ctx:rectangle (0, 0, w, yp) + ctx:fill () + end + + + -- draw grid on top + function x_at_freq (f) + if logscale then + return f_l + f_b * math.log (f) + else + return 2 * w * f / rate; + end + end + + function grid_freq (f) + -- draw vertical grid line + local x = .5 + math.floor (x_at_freq (f)) + ctx:move_to (x, 0) + ctx:line_to (x, h) + ctx:stroke () + end + + -- draw grid on top + local dash3 = C.DoubleVector () + dash3:add ({1, 3}) + ctx:set_line_width (1.0) + ctx:set_dash (dash3, 2) -- dotted line + ctx:set_source_rgba (.5, .5, .5, .8) + grid_freq (100) + grid_freq (1000) + grid_freq (10000) + ctx:unset_dash () + + return {w, h} +end diff --git a/share/scripts/split_all_markers.lua b/share/scripts/split_all_markers.lua new file mode 100644 index 0000000000..001c4ae432 --- /dev/null +++ b/share/scripts/split_all_markers.lua @@ -0,0 +1,122 @@ +ardour { ["type"] = "EditorAction", name = "Marker Split", + license = "MIT", + author = "Ardour Team", + description = [[Split regions on selected tracks at all locations markers]] +} + +function factory (params) return function () + + local loc = Session:locations () -- all marker locations + + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:Selection + local sel = Editor:get_selection () + + -- prepare undo operation + -- see http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Session + Session:begin_reversible_command ("Auto Region Split") + local add_undo = false -- keep track if something has changed + + -- Track/Bus Selection -- iterate over all Editor-GUI selected tracks + -- http://manual.ardour.org/lua-scripting/class_reference/#ArdourUI:TrackSelection + for r in sel.tracks:routelist ():iter () do + -- each of the items 'r' is-a + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Route + + local track = r:to_track () -- see if it's a track + if track:isnil () then + -- if not, skip it + goto continue + end + + -- get track's playlist + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Playlist + local playlist = track:playlist () + + -- clear existing changes, prepare "diff" of state for undo + playlist:to_stateful ():clear_changes () + + -- iterate over all location markers + for l in loc:list ():iter () do + if l:is_mark() then + -- get all regions on the given track's playlist (may be stacked) + for reg in playlist:regions_at (l:start ()):iter () do + playlist:split_region (reg, ARDOUR.MusicSample (l:start(), 0)) + -- the above operation will invalidate the playlist's region list: + -- split creates 2 new regions. + -- + -- Hence this script does it the way it does: the inner-most loop + -- is over playlist-regions. + end + end + end + + -- collect undo data + if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then + -- is something has changed, we need to save it at the end. + add_undo = true + end + + ::continue:: + end + + -- all done, commit the combined Undo Operation + if add_undo then + -- the 'nil' Command here mean to use the collected diffs added above + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end + +end end + + +-- render an icon for the toolbar action-button +-- this is genrally square width == height. +-- The background is set according to the theme (leave transparent when drawing). +-- A foreground color is passed as parameter 'fg' +-- +-- ctx is-a http://manual.ardour.org/lua-scripting/class_reference/#Cairo:Context +-- 2D vector graphics http://cairographics.org/ +function icon (params) return function (ctx, width, height, fg) + local mh = height - 3.5; + local m3 = width / 3; + local m6 = width / 6; + + ctx:set_line_width (.5) + + -- compare to gtk2_ardour/marker.cc "Marker" + ctx:set_source_rgba (.8, .8, .2, 1.0) + ctx:move_to (width / 2 - m6, 2) + ctx:rel_line_to (m3, 0) + ctx:rel_line_to (0, mh * 0.4) + ctx:rel_line_to (-m6, mh * 0.6) + ctx:rel_line_to (-m6, -mh * 0.6) + ctx:close_path () + ctx:fill_preserve () + ctx:set_source_rgba (.0, .0, .0, 1.0) + ctx:stroke () + + -- draw an arrow <--|--> on top, using the foreground color + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + ctx:set_line_width (1) + + ctx:move_to (width * .5, height * .4) + ctx:line_to (width * .5, height * .6) + ctx:stroke () + + ctx:move_to (2, height * .5) + ctx:line_to (width - 2, height * .5) + ctx:stroke () + + ctx:move_to (width - 2, height * .5) + ctx:rel_line_to (-m6, -m6) + ctx:rel_line_to (0, m3) + ctx:close_path () + ctx:fill () + + ctx:move_to (2, height * .5) + ctx:rel_line_to (m6, -m6) + ctx:rel_line_to (0, m3) + ctx:close_path () + ctx:fill () +end end diff --git a/share/scripts/stop_at_marker.lua b/share/scripts/stop_at_marker.lua new file mode 100644 index 0000000000..35de3510c4 --- /dev/null +++ b/share/scripts/stop_at_marker.lua @@ -0,0 +1,49 @@ +ardour { + ["type"] = "session", + name = "Stop at Marker", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An example session script which stops the transport on every location marker when rolling forward.]] +} + +function factory () + return function (n_samples) + if (not Session:transport_rolling ()) then + -- not rolling, nothing to do. + return + end + + local pos = Session:transport_sample () -- current playhead position + local loc = Session:locations () -- all marker locations + + -- find first marker after the current playhead position, ignore loop + punch ranges + -- (this only works when rolling forward, to extend this example see + -- http://manual.ardour.org/lua-scripting/class_reference/#ARDOUR:Locations ) + -- + local m = loc:first_mark_after (pos, false) + + if (m == -1) then + -- no marker was found + return + end + + -- due to `first_mark_after(pos)` "m" is always > "pos": + -- assert(pos < m) + -- + -- This callback happens from within the process callback: + -- + -- this cycle's end = next cycle start = pos + n_samples. + -- + -- Note that if "m" is exactly at cycle's end, that marker + -- will be at "pos" in the next cycle. Since we ask for + -- "first_mark_after pos", the marker would not be found. + -- + -- So even though "pos + n_samples" is barely reached, + -- we need to stop at "m" in the cycle that crosses or ends at "m". + if (pos + n_samples >= m) then + -- asking to locate to "m" ensures that playback continues at "m" + -- and the same marker will not be taken into account. + Session:request_locate (m, ARDOUR.LocateTransportDisposition.MustStop, ARDOUR.TransportRequestSource.TRS_Engine) + end + end +end diff --git a/share/scripts/store_recall_mixer.lua b/share/scripts/store_recall_mixer.lua new file mode 100644 index 0000000000..53562b7ee2 --- /dev/null +++ b/share/scripts/store_recall_mixer.lua @@ -0,0 +1,575 @@ +ardour { + ["type"] = "EditorAction", + name = "Mixer Store", + author = "Ardour Lua Taskforce", + description = [[ + Stores the current Mixer state as a file + that can be read and recalled arbitrarily. + Supports: processor settings, grouping, + mute, solo, gain, trim, pan and processor ordering, + plus re-adding certain deleted plugins. + ]] +} + +function factory() return function() + + local invalidate = {} + local path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") + + function mismatch_dialog(mismatch_str, checkbox_str) + --string.format("Track didn't match ID: %d, but did match track in session: %s", 999, 'track') + local dialog = { + { type = "label", colspan = 5, title = mismatch_str }, + { type = "checkbox", col=1, colspan = 1, key = "use", default = true, title = checkbox_str }, + } + local mismatch_return = LuaDialog.Dialog("", dialog):run() + if mismatch_return then + return mismatch_return['use'] + else + return false + end + end + + function get_processor_by_name(track, name) + local i = 0 + local proc = track:nth_processor(i) + repeat + if(proc:display_name() == name) then + return proc + else + i = i + 1 + end + proc = track:nth_processor(i) + until proc:isnil() + end + + function new_plugin(name) + for x = 0, 6 do + local plugin = ARDOUR.LuaAPI.new_plugin(Session, name, x, "") + if not(plugin:isnil()) then return plugin end + end + end + + function group_by_id(id) + local id = tonumber(id) + for g in Session:route_groups():iter() do + local group_id = tonumber(g:to_stateful():id():to_s()) + if group_id == id then return g end + end + end + + function group_by_name(name) + for g in Session:route_groups():iter() do + if g:name() == name then return g end + end + end + + function route_groupid_interrogate(t) + local group = false + for g in Session:route_groups():iter() do + for r in g:route_list():iter() do + if r:name() == t:name() then group = g:to_stateful():id():to_s() end + end + end return group + end + + function route_group_interrogate(t) + for g in Session:route_groups():iter() do + for r in g:route_list():iter() do + if r:name() == t:name() then return g end + end + end + end + + function empty_last_store() --empty current file from last run + local file = io.open(path, "w") + file:write("") + file:close() + end + + function mark_tracks(selected) + + empty_last_store() + + local route_string = [[instance = { + route_id = %d, + route_name = '%s', + gain_control = %f, + trim_control = %f, + pan_control = %s, + muted = %s, + soloed = %s, + order = {%s}, + cache = {%s}, + group = %s, + group_name = '%s' + }]] + + local group_string = [[instance = { + group_id = %s, + name = '%s', + routes = {%s}, + }]] + + local processor_string = [[instance = { + plugin_id = %d, + display_name = '%s', + owned_by_route_name = '%s', + owned_by_route_id = %d, + parameters = {%s}, + active = %s, + }]] + + local group_route_string = " [%d] = %s," + local proc_order_string = " [%d] = %d," + local proc_cache_string = " [%d] = '%s'," + local params_string = " [%d] = %f," + + --ensure easy-to-read formatting doesn't make it through + local route_string = string.gsub(route_string, "[\n\t]", "") + local group_string = string.gsub(group_string, "[\n\t]", "") + local processor_string = string.gsub(processor_string, "[\n\t]", "") + + local sel = Editor:get_selection () + local groups_to_write = {} + local i = 0 + + local tracks = Session:get_routes() + + if selected then tracks = sel.tracks:routelist() end + + for r in tracks:iter() do + local group = route_group_interrogate(r) + if group then + local already_there = false + for _, v in pairs(groups_to_write) do + if group == v then + already_there = true + end + end + if not(already_there) then + groups_to_write[#groups_to_write + 1] = group + end + end + end + + for _, g in pairs(groups_to_write) do + local tmp_str = "" + for t in g:route_list():iter() do + tmp_str = tmp_str .. string.format(group_route_string, i, t:to_stateful():id():to_s()) + i = i + 1 + end + local group_str = string.format( + group_string, + g:to_stateful():id():to_s(), + g:name(), + tmp_str + ) + + file = io.open(path, "a") + file:write(group_str, "\r\n") + file:close() + end + + for r in tracks:iter() do + if r:is_monitor () or r:is_auditioner () then goto nextroute end -- skip special routes + + local order = ARDOUR.ProcessorList() + local x = 0 + repeat + local proc = r:nth_processor(x) + if not proc:isnil() then + order:push_back(proc) + end + x = x + 1 + until proc:isnil() + + local route_group = route_group_interrogate(r) + if route_group then route_group = route_group:name() else route_group = "" end + local rid = r:to_stateful():id():to_s() + local pan = r:pan_azimuth_control() + if pan:isnil() then pan = false else pan = pan:get_value() end --sometimes a route doesn't have pan, like the master. + + local order_nmbr = 0 + local tmp_order_str, tmp_cache_str = "", "" + for p in order:iter() do + local pid = p:to_stateful():id():to_s() + if not(string.find(p:display_name(), "latcomp")) then + tmp_order_str = tmp_order_str .. string.format(proc_order_string, order_nmbr, pid) + tmp_cache_str = tmp_cache_str .. string.format(proc_cache_string, pid, p:display_name()) + end + order_nmbr = order_nmbr + 1 + end + + local route_str = string.format( + route_string, + rid, + r:name(), + r:gain_control():get_value(), + r:trim_control():get_value(), + tostring(pan), + r:muted(), + r:soloed(), + tmp_order_str, + tmp_cache_str, + route_groupid_interrogate(r), + route_group + ) + + file = io.open(path, "a") + file:write(route_str, "\n") + file:close() + + local i = 0 + while true do + local params = {} + local proc = r:nth_plugin (i) + if proc:isnil () then break end + local active = proc:active() + local id = proc:to_stateful():id():to_s() + local plug = proc:to_insert ():plugin (0) + local n = 0 -- count control-ports + for j = 0, plug:parameter_count () - 1 do -- iterate over all plugin parameters + if plug:parameter_is_control (j) then + local label = plug:parameter_label (j) + if plug:parameter_is_input (j) and label ~= "hidden" and label:sub (1,1) ~= "#" then + local _, _, pd = ARDOUR.LuaAPI.plugin_automation(proc, n) + local val = ARDOUR.LuaAPI.get_processor_param(proc, j, true) + --print(r:name(), "->", proc:display_name(), label, val) + params[n] = val + end + n = n + 1 + end + end + i = i + 1 + + local tmp_params_str = "" + for k, v in pairs(params) do + tmp_params_str = tmp_params_str .. string.format(params_string, k, v) + end + + local proc_str = string.format( + processor_string, + id, + proc:display_name(), + r:name(), + r:to_stateful():id():to_s(), + tmp_params_str, + active + ) + file = io.open(path, "a") + file:write(proc_str, "\n") + file:close() + end + ::nextroute:: + end + end + + function recall(debug, dry_run) + local file = io.open(path, "r") + assert(file, "File not found!") + local bypass_routes = {} + + local i = 0 + for l in file:lines() do + --print(i, l) + + local exec_line = dry_run["dothis-"..i] + local skip_line = false + if not(exec_line == nil) and not(exec_line) then + skip_line = true + end + + local plugin, route, group = false, false, false + local f = load(l) + + if debug then + print(i, string.sub(l, 0, 29), f) + end + + if f then f() end + + if instance["route_id"] then route = true end + if instance["plugin_id"] then plugin = true end + if instance["group_id"] then group = true end + + if group then + if skip_line then goto nextline end + + local g_id = instance["group_id"] + local routes = instance["routes"] + local name = instance["name"] + local group = group_by_id(g_id) + if not(group) then + local group = Session:new_route_group(name) + for _, v in pairs(routes) do + local rt = Session:route_by_id(PBD.ID(v)) + if rt:isnil() then rt = Session:route_by_name(name) end + if not(rt:isnil()) then group:add(rt) end + end + end + end + + if route then + local substitution = tonumber(dry_run["destination-"..i]) + if skip_line or (substitution == 0) then + bypass_routes[#bypass_routes + 1] = instance["route_id"] + goto nextline + end + + local old_order = ARDOUR.ProcessorList() + local route_id = instance["route_id"] + local r_id = PBD.ID(instance["route_id"]) + local muted, soloed = instance["muted"], instance["soloed"] + local order = instance["order"] + local cache = instance["cache"] + local group = instance["group"] + local group_name = instance["group_name"] + local name = instance["route_name"] + local gc, tc, pc = instance["gain_control"], instance["trim_control"], instance["pan_control"] + + if not(substitution == instance["route_id"]) then + print('SUBSTITUTION FOR: ', name, substitution, Session:route_by_id(PBD.ID(substitution)):name()) + --bypass_routes[#bypass_routes + 1] = route_id + was_subbed = true + r_id = PBD.ID(substitution) + end + + local rt = Session:route_by_id(r_id) + if rt:isnil() then rt = Session:route_by_name(name) end + if rt:isnil() then goto nextline end + + local cur_group_id = route_groupid_interrogate(rt) + if not(group) and (cur_group_id) then + local g = group_by_id(cur_group_id) + if g then g:remove(rt) end + end + + local rt_group = group_by_name(group_name) + if rt_group then rt_group:add(rt) end + + well_known = {'PRE', 'Trim', 'EQ', 'Comp', 'Fader', 'POST'} + + for k, v in pairs(order) do + local proc = Session:processor_by_id(PBD.ID(1)) + if not(was_subbed) then + proc = Session:processor_by_id(PBD.ID(v)) + end + if proc:isnil() then + for id, name in pairs(cache) do + if v == id then + proc = new_plugin(name) + for _, control in pairs(well_known) do + if name == control then + proc = get_processor_by_name(rt, control) + invalidate[v] = proc:to_stateful():id():to_s() + goto nextproc + end + end + if not(proc) then goto nextproc end + if not(proc:isnil()) then + rt:add_processor_by_index(proc, 0, nil, true) + invalidate[v] = proc:to_stateful():id():to_s() + end + end + end + end + ::nextproc:: + if proc and not(proc:isnil()) then old_order:push_back(proc) end + end + rt:reorder_processors(old_order, nil) + if muted then rt:mute_control():set_value(1, 1) else rt:mute_control():set_value(0, 1) end + if soloed then rt:solo_control():set_value(1, 1) else rt:solo_control():set_value(0, 1) end + rt:gain_control():set_value(gc, 1) + rt:trim_control():set_value(tc, 1) + if pc ~= false then rt:pan_azimuth_control():set_value(pc, 1) end + end + + if plugin then + if skip_line then goto nextline end + + --if the plugin is owned by a route + --we decided not to use, skip it + for _, v in pairs(bypass_routes) do + if instance["owned_by_route_id"] == v then + goto nextline + end + end + + local enable = {} + local params = instance["parameters"] + local p_id = instance["plugin_id"] + local act = instance["active"] + + for k, v in pairs(invalidate) do --invalidate any deleted plugin's id + if p_id == k then + p_id = v + end + end + + local proc = Session:processor_by_id(PBD.ID(p_id)) + if proc:isnil() then goto nextline end + local plug = proc:to_insert():plugin(0) + + for k, v in pairs(params) do + local label = plug:parameter_label(k) + if string.find(label, "Assign") or string.find(label, "Enable") then --@ToDo: Check Plugin type == LADSPA or VST? + enable[k] = v --queue any assignments/enables for after the initial parameter recalling to duck the 'in-on-change' feature + end + ARDOUR.LuaAPI.set_processor_param(proc, k, v) + end + + for k, v in pairs(enable) do + ARDOUR.LuaAPI.set_processor_param(proc, k, v) + end + if act then proc:activate() else proc:deactivate() end + end + + ::nextline:: + i = i + 1 + + end + end + + function dry_run(debug) + --returns a dialog-able table of + --everything we do (logically) + --in the recall function + local route_values = {['----'] = "0"} + for r in Session:get_routes():iter() do + route_values[r:name()] = r:to_stateful():id():to_s() + end + + local i = 0 + local dry_table = { + {type = "label", align = "left", key = "col-0-title" , col = 0, colspan = 1, title = 'Source Settings:'}, + {type = "label", align = "left", key = "col-0-title" , col = 1, colspan = 1, title = 'Actions:'}, + {type = "label", align = "left", key = "col-2-title" , col = 2, colspan = 1, title = 'Destination:'}, + {type = "label", align = "left", key = "col-2-title" , col = 3, colspan = 1, title = 'Do this?'}, + } + local file = io.open(path, "r") + assert(file, "File not found!") + + for l in file:lines() do + local do_plugin, do_route, do_group = false, false, false + local f = load(l) + + if debug then + print(i, string.sub(l, 0, 29), f) + end + + if f then f() end + + if instance["route_id"] then do_route = true end + if instance["plugin_id"] then do_plugin = true end + if instance["group_id"] then do_group = true end + + if do_group then + local group_id = instance["group_id"] + local group_name = instance["name"] + local dlg_title, action_title = "", "" + + local group_ptr = group_by_id(group_id) + + if not(group_ptr) then + new_group = Session:new_route_group(group_name) + dlg_title = string.format("Cannot Find: (Group) %s.", group_name, new_group:name()) + action_title = "will create and use settings" + else + dlg_title = string.format("Found by ID: (Group) %s.", group_ptr:name()) + action_title = "will use group settings" + end + table.insert(dry_table, { + type = "label", align = "left", key = "group-"..i , col = 0, colspan = 1, title = dlg_title + }) + table.insert(dry_table, { + type = "label", align = "left", key = "group-"..i , col = 1, colspan = 1, title = action_title + }) + table.insert(dry_table, { + type = "checkbox", col=3, colspan = 1, key = "dothis-"..i, default = true, title = "line:"..i + }) + end + + if do_route then + local route_id = instance["route_id"] + local route_name = instance["route_name"] + local dlg_title = "" + + local route_ptr = Session:route_by_id(PBD.ID(route_id)) + + if route_ptr:isnil() then + route_ptr = Session:route_by_name(route_name) + if not(route_ptr:isnil()) then + dlg_title = string.format("Found by Name: (Rotue) %s", route_ptr:name()) + action_title = "will use route settings" + else + dlg_title = string.format("Cannot Find: (Route) %s", route_name) + action_title = "will be ignored" + end + else + dlg_title = string.format("Found by ID: (Route) %s", route_ptr:name()) + action_title = "will use route settings" + end + if route_ptr:isnil() then name = route_name else name = route_ptr:name() end + table.insert(dry_table, { + type = "label", align = "left", key = "route-"..i , col = 0, colspan = 1, title = dlg_title + }) + table.insert(dry_table, { + type = "label", align = "left", key = "action-"..i , col = 1, colspan = 1, title = action_title + }) + table.insert(dry_table, { + type = "dropdown", align = "left", key = "destination-"..i, col = 2, colspan = 1, title = "", values = route_values, default = name or "----" + }) + table.insert(dry_table, { + type = "checkbox", col=3, colspan = 1, key = "dothis-"..i, default = true, title = "line"..i + }) + end + i = i + 1 + end + return dry_table + end + + local dialog_options = { + { type = "label", colspan = 5, title = "" }, + { type = "radio", col = 1, colspan = 7, key = "select", title = "", values ={ ["Store"] = "store", ["Recall"] = "recall" }, default = "Store"}, + { type = "label", colspan = 5, title = "" }, + } + + local store_options = { + { type = "label", colspan = 5, title = "" }, + { type = "checkbox", col=1, colspan = 1, key = "selected", default = false, title = "Selected tracks only"}, + { type = "entry", col=2, colspan = 10, key = "filename", default = "params", title = "Store name" }, + { type = "label", colspan = 5, title = "" }, + } + + local recall_options = { + { type = "label", colspan = 5, title = "" }, + { type = "file", col =1, colspan = 10, key = "file", title = "Select a File", path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", "params.lua") }, + { type = "label", colspan = 5, title = "" }, + } + + local rv = LuaDialog.Dialog("Mixer Store:", dialog_options):run() + + if rv then + local choice = rv["select"] + if choice == "store" then + local srv = LuaDialog.Dialog("Mixer Store:", store_options):run() + if srv then + empty_last_store() --ensures that params.lua will exist for the recall dialog + path = ARDOUR.LuaAPI.build_filename(Session:path(), "export", srv["filename"] .. ".lua") + mark_tracks(srv['selected']) + end + end + + if choice == "recall" then + local rrv = LuaDialog.Dialog("Mixer Store:", recall_options):run() + if rrv then + if rrv['file'] ~= path then path = rrv['file'] end + --recall(true) + local dry_return = LuaDialog.Dialog("Mixer Store:", dry_run(true)):run() + if dry_return then recall(true, dry_return) end + end + end + end +collectgarbage() +end end diff --git a/share/scripts/synth1.lua b/share/scripts/synth1.lua new file mode 100644 index 0000000000..de68c58e46 --- /dev/null +++ b/share/scripts/synth1.lua @@ -0,0 +1,100 @@ +ardour { + ["type"] = "dsp", + name = "Simple Synth", + category = "Instrument", + license = "MIT", + author = "Ardour Lua Task Force", + description = [[An Example Synth for Prototyping.]] +} + +function dsp_ioconfig () + return + { + -- { midi_in = 1, audio_in = 0, audio_out = -1}, -- any number of channels + -- { midi_in = 1, audio_in = 0, audio_out = 1}, -- values > 0, precisely N channels + { midi_in = 1, audio_in = 0, audio_out = 2}, -- values > 0, precisely N channels + { midi_in = 1, audio_in = 0, audio_out = 4}, -- values > 0, precisely N channels + { midi_in = 1, audio_in = 0, audio_out = 8}, -- values > 0, precisely N channels + -- { midi_in = 1, audio_in = 0, audio_out = -6}, -- values < -2, up to -N channels, here 1,..,6 + } +end + +local note_table = {} +local active_notes = {} +local phases = {} +local env = .01; + +function dsp_init (rate) + for n = 1,128 do + note_table [n] = (440 / 32) * 2^((n - 10.0) / 12.0) / rate + end + env = 100 / rate +end + +function dsp_run (ins, outs, n_samples) + -- initialize output buffer + local a = {} + for s = 1, n_samples do a[s] = 0 end + + + -- very basic synth, simple sine, basic envelope + local function synth (s_start, s_end) + for n,v in pairs (active_notes) do + local vel = v["vel"] or 0 + local tgt = v["tvel"]; + for s = s_start,s_end do + local phase = phases[n] or 0 + vel = vel + env * (tgt - vel) + a[s] = a[s] + math.sin(6.283185307 * phase) * vel / 167 + phase = phase + note_table[n] + if (phase > 1.0) then + phases[n] = phase - 2.0 + else + phases[n] = phase + end + end + if vel < 1 and tgt == 0 then + active_notes[n] = nil + else + active_notes[n]["vel"] = vel; + end + end + end + + local tme = 1 + -- parse midi messages + assert (type(midiin) == "table") -- global table of midi events (for now) + for _,b in pairs (midiin) do + local t = b["time"] -- t = [ 1 .. n_samples ] + + -- synth sound until event + synth(tme, t) + tme = t + 1 + + local d = b["data"] -- get midi-event + -- we ignore the midi channel + if (#d == 3 and (d[1] & 240) == 144) then -- note on + local n = 1 + d[2]; + active_notes[n] = active_notes[n] or {} + active_notes[n]["tvel"] = d[3] + end + if (#d == 3 and (d[1] & 240) == 128) then -- note off + local n = 1 + d[2]; + active_notes[n] = active_notes[n] or {} + active_notes[n]["tvel"] = 0 + end + if (#d == 3 and (d[1] & 240) == 176) then -- CC + if (d[2] == 120 or d[2] == 123) then -- panic + active_notes = {} + end + end + end + + -- synth rest of cycle + synth(tme, n_samples) + + -- copy + for c = 1,#outs do + outs[c]:set_table(a, n_samples) + end +end diff --git a/share/scripts/tomsloop.lua b/share/scripts/tomsloop.lua new file mode 100644 index 0000000000..d397ab9e60 --- /dev/null +++ b/share/scripts/tomsloop.lua @@ -0,0 +1,315 @@ +ardour { ["type"] = "EditorAction", name = "Tom's Loop", + license = "MIT", + author = "Ardour Team", + description = [[Bounce the loop-range of all non muted audio tracks, paste at playhead]] +} + +-- main method, every custom (i.e. non-ardour) method must be defined *inside* factory() +function factory (params) return function () + +-- when this script is called as an action, the output will be printed to the ardour log window + function print_help() + printf("See source for help.") +---[[ + print("") + print("---------------------------------------------------------------------") + print("") + print("Manual for \"Tom’s Loop\" Ardour Lua Script") + print("") + print("---------------------------------------------------------------------") + print("---------------------------------------------------------------------") + print("") + print("Table of Contents") + print("") + print("1. The first test") + print("2. Using mute and solo") + print("3. Combining region clouds to a defined length") + print("") + print("Abstract: This script for Ardour (>=4.7 git) operates on the time") + print("line. It allows to copy and combine specific portions within the loop") + print("range to a later point on the time line with one single action") + print("command. Everything that can be heard within the loop range is") + print("considered for this process, namely non-muted regions on non-muted or") + print("soloed tracks that are fully or partially inside the loop range. This") + print("still sounds a bit abstract and will be more obvious with the") + print("following example cases of use.") + print("") + print("For convenience, it’s recommended to bind the script to a keyboard") + print("shortcut in order to quickly and easily access the \"Tom’s Loop\"") + print("scripted action.") + print("") + print("-Open dialog \"Script Manager\" via menu Edit/Scripted Actions/Script") + print("Manager") + print("") + print("-In tab \"Action Scripts\", select a line and press button \"Add/Set\"") + print("") + print("-In dialog \"Add Lua Action\", select \"Tom’s Loop\" from the drop down") + print("menu and hit \"Add\"") + print("") + print("-In dialog \"Set Script Parameter\" just hit \"Add\" again") + print("") + print("-Close dialog \"Script Manager\"") + print("") + print("-Open dialog \"Bindings Editor\" via menu Window/Bindings Editor") + print("") + print("-In tab \"Editor\", expand \"Editor\", look for entry \"Tom’s loop\",") + print("select it") + print("") + print("-Hit the keyboard shortcut to assign to this scripted action") + print("") + print("-Close dialog \"Key Bindings\"") + print("") + print("An alternative way to quickly access a scripted action is to enable") + print("\"Action Script Button Visibility\" in \"Preferences/GUI\".") + print("") + print("---------------------------------------------------------------------") + print("") + print("1. The first test") + print("") + print("---------------------------------------------------------------------") + print("") + print("-Record a short sequence of audio input or import a wave file to a") + print("track to get a region") + print("") + print("-Set a loop range inside that one region") + print("") + print("-Place the playhead after the loop range, possibly after the region,") + print("non-rolling") + print("") + print(" _L====L_ V") + print(" .____|____|____________. |") + print(" |R1__|_x__|____________| |") + print("") + print("-Call \"Tom’s Loop\" via the previously created shortcut") + print("") + print("This results in a new region created at the playhead, with the length") + print("of the loop range, containing audio of the original region. The") + print("playhead moved to the end of this new region so that subsequent calls") + print("to \"Tom’s Loop\" will result in a gap less series of regions.") + print("") + print(" _L====L_ --> V") + print(" .____|____|____________. .____|") + print(" |R1__|_x__|____________| |_x__|") + print("") + print("-Repeat calling \"Tom’s Loop\"") + print("") + print("This creates multiple copies of the loop range to line up one after") + print("each other.") + print("") + print(" _L====L_ --> V") + print(" .____|____|____________. .______________|") + print(" |R1__|_x__|____________| |_x__|_x__|_x__|") + print("") + print("-Set a different loop range and call \"Tom’s Loops\" again") + print("") + print("This will create a new region with the length of the new loop range") + print("at the playhead.") + print("") + print(" _L=======L_ --> V") + print(" ._______|_______|______. .______________________|") + print(" |R1_____|_X_____|______| |_x__|_x__|_x__|_X_____|") + print("") + print("By using \"Tom’s Loop\", the loop range - which can be easily set with") + print("the handles - and the playhead it’s easy to create any sequence of") + print("existing regions on the time line. This can be useful during the") + print("arrangement phase where macro parts of the session are already") + print("temporally layed out (in the loop) but not part of the final") + print("arrangement yet. The process is non-destructive in a sense that the") + print("existing regions layout in the current loop range won’t be touched or") + print("replaced. The newly created regions are immediately visible on the") + print("time line at the playhead position.") + print("") + print("") + print("---------------------------------------------------------------------") + print("") + print("2. Using mute and solo") + print("") + print("---------------------------------------------------------------------") + print("") + print("Creating a sequence of regions like described above respects the") + print("current mute and solo state of a track. Variations of the loop are") + print("thus easy to create, further supporting the arrangement process.") + print("") + print(" _L====L_ --> V") + print(" .____|____|____________. ._________. |") + print(" |R1__|_x__|____________| |_x__|_x__| |") + print(" .__|R2|_y__|________|_. |_y__|_________|") + print(" |R3___|_z__|__________| |_z__|_z__|") + print("") + print("") + print("---------------------------------------------------------------------") + print("") + print("3. Combining region clouds to a defined length") + print("") + print("---------------------------------------------------------------------") + print("") + print("Multiple small regions say on a percussive track can be simplified") + print("for later arrangement keeping the temporal relations by combining") + print("them. Using \"Tom’s Loop\", the resulting regions will not only combine") + print("the regions but also automatically extend or shrink the new regions") + print("start and end point so that it is exactly of the wished length equal") + print("to the loop range.") + print("") + print("_L======================L_ --> V") + print(" | .____ .___. _____|_______. .______________________|") + print(" | |R1_| |R2_| |R3__|_______| |______________________|") + print("") + print("See also: Lua Action Bounce+Replace Regions") + print("") + print("") +--]] + end -- print_help() + + local n_paste = 1 + assert (n_paste > 0) + + local proc = ARDOUR.LuaAPI.nil_proc () -- bounce w/o processing + local itt = ARDOUR.InterThreadInfo () -- bounce progress info (unused) + + local loop = Session:locations ():auto_loop_location () + local playhead = Session:transport_sample () + + -- make sure we have a loop, and the playhead (edit point) is after it + if not loop then + print_help(); + print ("Error: A Loop range must be set.") + goto errorout + end + assert (loop:start () < loop:_end ()) + if loop:_end () > playhead then + print_help(); + print ("Error: The Playhead (paste point) needs to be after the loop.") + goto errorout + end + + -- prepare undo operation + Session:begin_reversible_command ("Tom's Loop") + local add_undo = false -- keep track if something has changed + + -- prefer solo'ed tracks + local soloed_track_found = false + for route in Session:get_tracks ():iter () do + if route:soloed () then + soloed_track_found = true + break + end + end + + -- count regions that are bounced + local n_regions_created = 0 + + -- loop over all tracks in the session + for route in Session:get_tracks ():iter () do + if soloed_track_found then + -- skip not soloed tracks + if not route:soloed () then + goto continue + end + end + + -- skip muted tracks (also applies to soloed + muted) + if route:muted () then + goto continue + end + + -- at this point the track is either soloed (if at least one track is soloed) + -- or not muted (if no track is soloed) + + -- test if bouncing is possible + local track = route:to_track () + if not track:bounceable (proc, false) then + goto continue + end + + -- only audio tracks + local playlist = track:playlist () + if playlist:data_type ():to_string () ~= "audio" then + goto continue + end + + -- check if there is at least one unmuted region in the loop-range + local reg_unmuted_count = 0 + for reg in playlist:regions_touched (loop:start (), loop:_end ()):iter () do + if not reg:muted() then + reg_unmuted_count = reg_unmuted_count + 1 + end + end + + if reg_unmuted_count < 1 then + goto continue + end + + -- clear existing changes, prepare "diff" of state for undo + playlist:to_stateful ():clear_changes () + + -- do the actual work + local region = track:bounce_range (loop:start (), loop:_end (), itt, proc, false) + playlist:add_region (region, playhead, n_paste, false, 0, 0, false) + + n_regions_created = n_regions_created + 1 + + -- create a diff of the performed work, add it to the session's undo stack + -- and check if it is not empty + if not Session:add_stateful_diff_command (playlist:to_statefuldestructible ()):empty () then + add_undo = true + end + + ::continue:: + end -- for all routes + + --advance playhead so it's just after the newly added regions + if n_regions_created > 0 then + Session:request_locate (playhead + loop:length() * n_paste, ARDOUR.LocateTransportDisposition.MustStop, ARDOUR.TransportRequestSource.TRS_UI) + end + + -- all done, commit the combined Undo Operation + if add_undo then + -- the 'nil' Command here mean to use the collected diffs added above + Session:commit_reversible_command (nil) + else + Session:abort_reversible_command () + end + + print ("bounced " .. n_regions_created .. " regions from loop range (" .. loop:length() .. " samples) to playhead @ sample # " .. playhead) + ::errorout:: +end -- end of anonymous action script function +end -- end of script factory + + +function icon (params) return function (ctx, width, height) + local x = width * .5 + local y = height * .5 + local r = math.min (x, y) + + ctx:set_line_width (1) + + function stroke_outline () + ctx:set_source_rgba (0, 0, 0, 1) + ctx:stroke_preserve () + ctx:set_source_rgba (1, 1, 1, 1) + ctx:fill () + end + + ctx:rectangle (x - r * .6, y - r * .05, r * .6, r * .3) + stroke_outline () + + ctx:arc (x, y, r * .61, math.pi, 0.2 * math.pi) + ctx:arc_negative (x, y, r * .35, 0.2 * math.pi, math.pi); + stroke_outline () + + function arc_arrow (rad, ang) + return x - rad * math.sin (ang * 2.0 * math.pi), y - rad * math.cos (ang * 2.0 * math.pi) + end + + ctx:move_to (arc_arrow (r * .36, .72)) + ctx:line_to (arc_arrow (r * .17, .72)) + ctx:line_to (arc_arrow (r * .56, .60)) + ctx:line_to (arc_arrow (r * .75, .72)) + ctx:line_to (arc_arrow (r * .62, .72)) + + ctx:set_source_rgba (0, 0, 0, 1) + ctx:stroke_preserve () + ctx:close_path () + ctx:set_source_rgba (1, 1, 1, 1) + ctx:fill () +end end diff --git a/share/scripts/track_organizer.lua b/share/scripts/track_organizer.lua new file mode 100644 index 0000000000..15caf2c9ff --- /dev/null +++ b/share/scripts/track_organizer.lua @@ -0,0 +1,100 @@ +ardour { + ["type"] = "EditorAction", + name = "Track Organizer", + author = "Mixbus Lua Taskforce", + description = [[Easily modifiable session overview and track property editor]] +} + +function factory () return function () + + local rbow = { ["----"] = "", ["Red"] = 0xD10000FF, ["Orange"] = 0xFF6622FF, ["Yellow"] = 0xFFDA21FF, + ["Green"] = 0x33DD00FF, ["Blue"] = 0x1133CCFF, ["Indigo"] = 0x220066FF, ["Violet"] = 0x330044FF +} + + --now starting to build our dialog + local dialog_options = { + { type = "label", colspan="4", title = "Change your Track settings here:" }, + { type = "heading", title = "Track", col = 0, colspan = 1 }, + { type = "heading", title = "Group", col = 1, colspan = 1 }, + { type = "heading", title = "Comment", col = 2, colspan = 1 }, + { type = "heading", title = "Color", col = 3, colspan = 1 }, + } + + --group option payload + --@ToDo: Add 'fake' groups for people to select, create them if they want it + local pl = {["----"] = "", ["Drums"] = "Drums", ["Bass"] = "Bass", ["Guitars"] = "Guitars", + ["Keys"] = "Keys", ["Strings"] = "Strings", ["Vox"] = "Vox" +} + + for g in Session:route_groups():iter() do + pl[g:name()] = g + end + + --helper function to find default group option + function interrogate(t) + local v = "----" + for g in Session:route_groups():iter() do + for r in g:route_list():iter() do + if r:name() == t:name() then v = g:name() end + end + end return v + end + + function find_color(t) + local c = "----" + for k, v in pairs(rbow) do + if(t:presentation_info_ptr():color() == v) then c = k end + end return c + end + + --insert an entry into our dialog_options table for each track with appropriate info + for t in Session:get_tracks():iter() do + table.insert(dialog_options, { + type = "entry", key = t:name() .. ' n', col = 0, colspan = 1, default = t:name(), title = "" --@ToDo: Shorten Names so they can still see what track they're changing? + }) --name + table.insert(dialog_options, { + type = "dropdown", key = t:name() .. ' g', col = 1, colspan = 1, title = "", values = pl, default = interrogate(t) + }) --group + table.insert(dialog_options, { + type = "entry", key = t:name() .. ' cm', col = 2, colspan = 1, default = t:comment(), title = "" + }) --comment + table.insert(dialog_options, { + type = "dropdown", key = t:name() .. ' c', col = 3, colspan = 1, title = "", values = rbow, default = find_color(t) + }) --color + end + + --run dialog_options + local rv = LuaDialog.Dialog("Track Organizer", dialog_options):run() + if not(rv) then goto script_end end + assert(rv, 'Dialog box was cancelled or is ' .. type(rv)) + + --begin track operation + for t in Session:get_tracks():iter() do + local cgrp = interrogate(t) + local name = rv[t:name() .. ' n' ] + local ngrp = rv[t:name() .. ' g' ] + local cmnt = rv[t:name() .. ' cm'] + local colr = rv[t:name() .. ' c' ] + + if t:name() ~= name then t:set_name(name) end + + if t:comment() ~= cmnt then t:set_comment(cmnt, nil) end + + if not(colr == "") then t:presentation_info_ptr():set_color(colr) end + + if type(ngrp) == "userdata" then + if cgrp ~= ngrp:name() then + ngrp:add(t) + end + end + + if type(ngrp) == "string" and not(ngrp == "") then + ngrp = Session:new_route_group(ngrp) + if cgrp ~= ngrp:name() then + ngrp:add(t) + end + end + end + ::script_end:: + collectgarbage() +end end diff --git a/share/scripts/transparent_regions.lua b/share/scripts/transparent_regions.lua new file mode 100644 index 0000000000..7db81fb7af --- /dev/null +++ b/share/scripts/transparent_regions.lua @@ -0,0 +1,28 @@ +ardour { + ["type"] = "EditorHook", + name = "Make all Regions Transparent", + author = "Ardour Lua Task Force", + description = "While the transport is looping, all regions become transparent.", +} + +function signals () + return LuaSignal.Set():add ( + { + [LuaSignal.TransportStateChange] = true, + [LuaSignal.TransportLooped] = true, + } + ) +end + +function factory () + return function (signal, ref, ...) + local all_regions = ARDOUR.RegionFactory.regions() + for _, r in all_regions:iter() do + local ar = r:to_audioregion (); + if ar:isnil () then goto next end + if ar:opaque () then + ar:set_opaque (false) + end + ::next:: + end +end end \ No newline at end of file diff --git a/share/scripts/vamp_audio_to_midi.lua b/share/scripts/vamp_audio_to_midi.lua new file mode 100644 index 0000000000..2f9101196c --- /dev/null +++ b/share/scripts/vamp_audio_to_midi.lua @@ -0,0 +1,112 @@ +ardour { + ["type"] = "EditorAction", + name = "Polyphonic Audio to MIDI", + license = "MIT", + author = "Ardour Team", +description = [[ +Analyze audio from the selected audio region to a selected MIDI region. + +A MIDI region on the target track will have to be created first (use the pen tool). + +This script uses the Polyphonic Transcription VAMP plugin from Queen Mary Univ, London. +The plugin works best at 44.1KHz input sample rate, and is tuned for piano and guitar music. Velocity is not estimated. +]] +} + +function factory () return function () + local sel = Editor:get_selection () + local sr = Session:nominal_sample_rate () + local tm = Session:tempo_map () + local vamp = ARDOUR.LuaAPI.Vamp ("libardourvampplugins:qm-transcription", sr) + local midi_region = nil + local audio_regions = {} + local start_time = Session:current_end_sample () + local end_time = Session:current_start_sample () + local max_pos = 0 + local cur_pos = 0 + for r in sel.regions:regionlist ():iter () do + if r:to_midiregion():isnil() then + local st = r:position() + local ln = r:length() + local et = st + ln + if st < start_time then + start_time = st + end + if et > end_time then + end_time = et + end + table.insert(audio_regions, r) + max_pos = max_pos + r:to_readable ():readable_length () + else + midi_region = r:to_midiregion() + end + end + + if #audio_regions == 0 then + LuaDialog.Message ("Polyphonic Audio to MIDI", "No source audio region(s) selected.\nAt least one audio-region to be analyzed need to be selected.", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run () + return + end + if not midi_region then + LuaDialog.Message ("Polyphonic Audio to MIDI", "No target MIDI region selected.\nA MIDI region, ideally empty, and extending beyond the selected audio-region(s) needs to be selected.", LuaDialog.MessageType.Error, LuaDialog.ButtonType.Close):run () + return + end + + midi_region:set_initial_position(start_time) + midi_region:set_length(end_time - start_time, 0) + + local pdialog = LuaDialog.ProgressWindow ("Audio to MIDI", true) + function progress (_, pos) + return pdialog:progress ((cur_pos + pos) / max_pos, "Analyzing") + end + + for i,ar in pairs(audio_regions) do + local a_off = ar:position () + local b_off = midi_region:quarter_note () - midi_region:start_beats () + + vamp:analyze (ar:to_readable (), 0, progress) + + if pdialog:canceled () then + goto out + end + + cur_pos = cur_pos + ar:to_readable ():readable_length () + pdialog:progress (cur_pos / max_pos, "Generating MIDI") + + local fl = vamp:plugin ():getRemainingFeatures ():at (0) + if fl and fl:size() > 0 then + local mm = midi_region:midi_source(0):model() + local midi_command = mm:new_note_diff_command ("Audio2Midi") + for f in fl:iter () do + local ft = Vamp.RealTime.realTime2Frame (f.timestamp, sr) + local fd = Vamp.RealTime.realTime2Frame (f.duration, sr) + local fn = f.values:at (0) + + local bs = tm:exact_qn_at_sample (a_off + ft, 0) + local be = tm:exact_qn_at_sample (a_off + ft + fd, 0) + + local pos = Evoral.Beats (bs - b_off) + local len = Evoral.Beats (be - bs) + local note = ARDOUR.LuaAPI.new_noteptr (1, pos, len, fn + 1, 0x7f) + midi_command:add (note) + end + mm:apply_command (Session, midi_command) + end + -- reset the plugin (prepare for next iteration) + vamp:reset () + end + + ::out:: + pdialog:done (); + pdialog = nil + vamp = nil; + collectgarbage () +end end + +function icon (params) return function (ctx, width, height, fg) + local txt = Cairo.PangoLayout (ctx, "ArdourMono ".. math.ceil(width * .7) .. "px") + txt:set_text ("\u{2669}") -- quarter note symbol UTF8 + local tw, th = txt:get_pixel_size () + ctx:set_source_rgba (ARDOUR.LuaAPI.color_to_rgba (fg)) + ctx:move_to (.5 * (width - tw), .5 * (height - th)) + txt:show_in_cairo_context (ctx) +end end diff --git a/share/scripts/voice_activate.lua b/share/scripts/voice_activate.lua new file mode 100644 index 0000000000..ef4380b9bb --- /dev/null +++ b/share/scripts/voice_activate.lua @@ -0,0 +1,53 @@ +ardour { + ["type"] = "dsp", + name = "Voice/Level Activate", + category = "Utility", + author = "Ardour Lua Task Force", + license = "MIT", + description = [[Roll the transport when the signal level on the plugin's input exceeds a given threshold.]] +} + +function dsp_ioconfig () + return + { + -- support all in/out as long as input port count equals output port count + { audio_in = -1, audio_out = -1}, + } +end + +function dsp_params () + return + { + { ["type"] = "input", name = "Threshold", min = -20, max = 0, default = -6, doc = "Threshold in dBFS for all channels" }, + { ["type"] = "output", name = "Level", min = -120, max = 0 }, + } +end + +function dsp_configure (ins, outs) + n_channels = ins:n_audio() +end + +function dsp_runmap (bufs, in_map, out_map, n_samples, offset) + local ctrl = CtrlPorts:array() -- get control port array (read/write) + + if Session:transport_rolling() then + -- don't do anything if the transport is already rolling + ctrl[2] = -math.huge -- set control output port value + return + end + + local threshold = 10 ^ (.05 * ctrl[1]) -- dBFS to coefficient + local level = -math.huge + + for c = 1,n_channels do + local b = in_map:get(ARDOUR.DataType("audio"), c - 1) -- get id of audio-buffer for the given channel + if b ~= ARDOUR.ChanMapping.Invalid then -- check if channel is mapped + local a = ARDOUR.DSP.compute_peak(bufs:get_audio(b):data(offset), n_samples, 0) -- compute digital peak + if a > threshold then + Session:request_transport_speed (1.0, true, ARDOUR.TransportRequestSource.TRS_UI) + end + if a > level then level = a end -- max level of all channels + end + end + ctrl[2] = ARDOUR.DSP.accurate_coefficient_to_dB (level) -- set control output port value +end diff --git a/share/scripts/wscript b/share/scripts/wscript new file mode 100644 index 0000000000..a820a1fc13 --- /dev/null +++ b/share/scripts/wscript @@ -0,0 +1,16 @@ +#!/usr/bin/python + +import os + +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + scripts = bld.path.ant_glob ('*.lua', excl=['^_*']) + bld.install_files (os.path.join(bld.env['DATADIR'], 'scripts'), scripts) + +def options(opt): + pass diff --git a/tools/linux_packaging/build b/tools/linux_packaging/build index ea29b1845b..359fdbc805 100755 --- a/tools/linux_packaging/build +++ b/tools/linux_packaging/build @@ -373,7 +373,7 @@ done # Lua Scripts Files # got to be careful with names here -for x in $BUILD_ROOT/../scripts/*.lua ; do +for x in $BUILD_ROOT/../share/scripts/*.lua ; do BN=$(basename $x) if test "${BN:0:1}" = "_"; then continue; diff --git a/tools/osx_packaging/osx_build b/tools/osx_packaging/osx_build index c01122ac1c..04fc0e6640 100755 --- a/tools/osx_packaging/osx_build +++ b/tools/osx_packaging/osx_build @@ -371,7 +371,7 @@ done # Lua Script Files # got to be careful with names here -for x in $BUILD_ROOT/../scripts/*.lua ; do +for x in $BUILD_ROOT/../share/scripts/*.lua ; do BN=$(basename $x) if test "${BN:0:1}" = "_"; then continue; -- cgit v1.2.3