// ----
// ---- file   : eureka_config.tks
// ----
// ---- info   : Eureka configuration file
// ----
// ---- changed: 22Jan2018, 25Jan2018, 26Jan2018, 28Jan2018, 29Jan2018, 01Feb2018, 06Feb2018
// ----          12Feb2018, 15Feb2018, 24Feb2018, 25Feb2018, 26Feb2018, 16Mar2018, 23Mar2018
// ----          30Mar2018, 03Apr2018, 09Apr2018, 30May2018, 22Jul2018, 29Nov2018, 06Dec2018
// ----          07Dec2018, 16Dec2018, 21Dec2018, 29Dec2018, 05Jan2019, 15Jan2019, 04May2019
// ----          09May2019, 10May2019, 12May2019, 29May2019, 11Jul2019, 24Jul2019, 25Jul2019
// ----          05Aug2019, 26Aug2019, 08Sep2019, 14Oct2019, 22Nov2019, 14Feb2020, 23Feb2020
// ----          03Mar2020, 22May2020, 10Jun2020, 18Aug2020, 23Aug2020, 24Aug2020, 29Aug2020
// ----          29Nov2020, 01Dec2020, 31Jan2021, 04Feb2021, 05Feb2021, 09Feb2021, 10Feb2021
// ----          26Mar2021, 09Apr2021, 11Apr2021, 09May2021, 22May2021, 31May2021, 28Jul2021
// ----          14Oct2021, 25Nov2021, 12Dec2021, 26Dec2021, 27Dec2021, 05Feb2022, 06Feb2022
// ----          27Feb2022, 08Apr2022, 13May2022, 01Jul2022, 08Jul2022, 17Jul2022, 11Aug2022
// ----          06Feb2023, 13Feb2023, 14Feb2023, 20Feb2023, 25Feb2023, 10Mar2023, 17Mar2023
// ----          25Mar2023, 30Mar2023, 21Apr2023, 12May2023, 08Jul2023, 16Jul2023, 19Jul2023
// ----          22Jul2023, 11Aug2023, 13Aug2023, 18Aug2023, 09Sep2023, 30Sep2023, 02Oct2023
// ----          04Oct2023, 27Oct2023, 02Nov2023, 03Nov2023, 04Nov2023, 05Nov2023, 10Nov2023
// ----          11Nov2023, 17Nov2023, 18Nov2023, 12Jan2024, 13Jan2024, 17Jan2024, 19Jan2024
// ----          14Feb2024, 17Feb2024, 25Apr2024, 03Jul2024, 06Jul2024, 07Jul2024, 02Aug2024
// ----          03Aug2024, 15Sep2024, 18Sep2024, 29Sep2024, 03Oct2024, 04Oct2024, 14Oct2024
// ----          19Nov2024, 22Nov2024
// ----
// ----
// ----
module MSTConfig;

use namespace ui;
use namespace st2;

// ----------------------------------------------------------------------------------------------
// (note) keep in mind that many settings are overridden in UseAutogeneratedConfig() (see below)
// (note) (the autogenerated config is updated when the settings are saved in the application)
// ----------------------------------------------------------------------------------------------

// Auto-detect keyboard layout (+auto-enable force-us-unicode if necessary)
UI.SetLocalToRawKeyTableByName("auto");

// (note) true=fast+smooth cursor movements (60hz arcade mode when repeat rate is set to 1!)
//        false=use millisec timer (~= every second frame (30Hz))
UI.SetEnableVSyncKeyRepeat(false);  // enable millisec/timer based key repeat
// UI.SetEnableVSyncKeyRepeat(true); // enable vsync/frame based key repeat

UI.ConfigureKeyRepeat(300, 29);     // millisec/timer based key repeat
UI.ConfigureKeyRepeatFrames(15, 2); // vsync/frame based key repeat

// ui::Button.b_hold_dot = true; // show dot in buttons that support hold-click events

// // UI.SetSwapStrategy(UI.SWAP_STRATEGY_EXCHANGE);
// //UI.SetSwapStrategy(UI.SWAP_STRATEGY_COPY);

// UI.b_keyboard_monitor = true;  // for demo/tutorial video recording

// true=show tooltips in window corners
// false=show tooltips near mouse cursor
UI.SetEnableWindowCornerToolTips(true);

// true=always grab mouse when editing floatparams
// false=only grab mouse in floatparam dial mode (RMB click)
UI.SetEnableFloatParamMouseGrab(true);

// Prevent blurry upscaling on 4k / UHD displays (Windows 10)
SDL.dpiAwareness = true;

// SampleView
SampleView.render_style_normal_default   = SampleView.RS_GRADIENT_AND_LINES;
SampleView.render_style_freedraw_default = SampleView.RS_LINES;
SampleView.b_render_rms_default          = true;


class STConfigBase {

   // true=don't scan samples, voice plugins, or VSTs
   //  (note) speeds up startup but sample+plugin repos won't be available
   //  (note) mainly for debugging purposes
   static boolean b_minimal_startup = false;
   // static boolean b_minimal_startup = true;


   /***
    ***
    *** Directories
    ***
    ***/

   // Main music projects path that contains
   //   audiotool_temp/
   //   eureka_data/
   //   synergy_data/
   //   recordings/
   //   samples/
   //   scales/
   // (note) not used outside this configuration file
   // (note) it is highly recommended to copy the default "mProjects/" folder to another directory
   //         so that it won't be lost when the application dir is updated / replaced
   static String my_mproj_path = "$(PROGRAM_DIR)/../mProjects";
   // static String my_mproj_path = "~/mProjects";
   // static String my_mproj_path = "f:/mProjects";

   // Root path ($(ROOT))
   static String rootpath = my_mproj_path+"/eureka_data/";

   // Temp dir ($(TMP))
   //  (note) c:/temp/ not available on Windows 11 => use custom tmp/ dir
   static String temp = rootpath + "/tmp";

   // Tuning table (.tun / .scl) directory (scanned recursively)
   static String tuning_table_rootpath = my_mproj_path+"/scales/";

   // $(PROJECT_DIR) : Project file directory
   static String project_rootpath = rootpath + "/projects/";

   // Track structure/patch directory
   static String patch_rootpath = rootpath + "/patches/";

   // Sample and plugin library cache file directory ($(CACHE_DIR))
   //  - samplecache.dat
   //  - vstcache.dat
   //  - vstcache_wl.dat
   //  - clapcache.dat
   //  - clapcache_wl.dat
   static String cache_rootpath = rootpath + "/cache/";

   // STFX patch directory
   static String stfxpatch_rootpath = rootpath + "/stfx_patches/";

   // CLAP patch directory (program/bank chunks)
   static String clappatch_rootpath = rootpath + "/clap_patches/";

   // VST patch directory (program/bank chunks)
   static String vstpatch_rootpath = rootpath + "/vst_patches/";

   // Voice plugin chain patches (PageSample "FX" tab)
   static String voicepluginchainpatch_rootpath = rootpath + "/voicepluginchain_patches/";

   // FMStack patches (PageSample "FX" tab / fm_stack voice plugin)
   static String fmstackpatch_rootpath = rootpath + "/fmstack_patches/";

   // AudioTrack recording path (project-specific)
   //  (note) used when exporting track stems to WAV files
   //  (note) $(PROJECT_NAME) will be replaced with the current project name
   //  (note) the directory will be created if it does not exist, yet
   static String audiotrack_wav_rootpath = project_rootpath+"/$(PROJECT_NAME)/tracks/";

   // Exported samples root path (project-specific)
   //  (note) used when exporting samples to WAV files
   //  (note) $(PROJECT_NAME) will be replaced with the current project name
   //  (note) the directory will be created if it does not exist, yet
   static String exported_samples_wav_rootpath = project_rootpath+"/$(PROJECT_NAME)/exported_samples/";

   // Project samples root path (project-specific)
   //  (note) used when importing sample to the project (e.g. into the sampler)
   //  (note) $(PROJECT_NAME) will be replaced with the current project name
   //  (note) the directory will be created if it does not exist, yet
   static String project_samples_wav_rootpath = project_rootpath+"/$(PROJECT_NAME)/samples/";

   // AudioTrack WAV export filename template
   //  (note) $(TAKE_NR) will be replaced with the current take nr (001..n)
   //  (note) $(PROJECT_NAME) will be replaced with the current project name
   //  (note) $(TRACK_NR) will be replaced with the current audio track nr (01..n)
   //  (note) $(TRACK_NAME) will be replaced with the current audio track name
   static String audiotrack_wav_name = "tk$(TAKE_NR)_$(TRACK_NR)_$(TRACK_NAME).wav";

   // Sample repository rootpath (other, non-project specific samples)
   //  $(SAMPLE_DIR)
   static String sample_rootpath = my_mproj_path+"/samples";

   // Preferred (relative) directory within sample_rootpath for user samples
   //  (note) can be set to username (e.g. "bsp"). default is "user".
   //  (note) overridden in eureka_config_local.tks (see Prefs dialog)
   static String sample_userpath = "user";

   // Sample preset directory
   //  (note) relative to sample_rootpath
   //  (note) .esa files will be listed in PageSample samples table "+" menu under "User"
   //  (note) (re-)scanned when menu is shown
   static String pgsample_preset_relpath = "presets/";

   // Sample category filters
   static String sample_filters_path = rootpath+"/sample_filters.ini";

   // true=load sample cache or scan samples during startup
   // static boolean b_scan_samples_or_load_cache = true;
   static boolean b_scan_samples_or_load_cache = !b_minimal_startup;

   // Sample import paths
   //  (note) [displayname, path] pairs
   //  (note) e.g. for USB-connected field recorders
   static StringArray sample_import_paths =
      Utils.IsMacOS()
      ? [
         "Tascam DR-05X", "/Volumes/Untitled/MUSIC/",
         "Zoom H4N Pro",  "/Volumes/H4N_SD/STEREO/FOLDER01/",
         "Downloads",     "/Users/bsp/Downloads/import_test/",
                         ]
      : [
         "D",  "D:/",
         "E",  "E:/",
         "F",  "F:/",
         "G",  "G:/",
         "H",  "H:/",
         "I",  "I:/",
         "J",  "J:/",
         "K",  "K:/",
         "L",  "L:/",
         "M",  "M:/",
         "N",  "N:/",
         "O",  "O:/",
         "P",  "P:/",
         "Q",  "Q:/",
         "R",  "R:/",
         "S",  "S:/",
         "T",  "T:/",
         "U",  "U:/",
         "V",  "V:/",
         "W",  "W:/",
         "X",  "X:/",
         "Y",  "Y:/",
         "Z",  "Z:/",
         ];

   // Target path for sample import (transfer)
   static String sample_import_target_path = "/Volumes/epro_data/samples_other/raw_field_recordings/imported/";

   // Sample import dialog geometry
   static Geometry4f sample_import_geo_hidpi = [0,0,0,0];
   static Geometry4f sample_import_geo_lodpi = [0,0,0,0];

   // BezierEdit curve preset directory
   //  (note) files use ".curve" suffix
   static String curve_rootpath = rootpath + "/curves/";

   // Spectral Resynthesis preset directory
   //  (note) files use ".resynth" suffix
   static String resynth_rootpath = rootpath + "/resynth/";

   // Maximum waveform size in frames (for resize_waveform cmd)
   static int max_waveform_size = 64*1024*1024;

   // Tiny Synergy Replay temporary sampleinfo export filename prefix (read by Synergy)
   static String tsr_sampleinfo_export_pathname_prefix = temp+"/eureka_tsr";

   // Path to "sox" / "sox.exe"
   static String audiotool_sox_pathname =
      // Utils.IsWindows() ? "c:\\Program Files (x86)\\sox-14-4-2\\sox.exe"
      Utils.IsWindows() ? "$(TKS_DIR)/opt/sox-14-4-2/sox.exe"
      : Utils.IsMacOS() ? "/opt/homebrew/bin/sox"  // arm64
      // : Utils.IsMacOS() ? "/usr/local/homebrew/bin/sox"  // x86_64
      // : Utils.IsMacOS() ? "$(PROGRAM_DIR)/../../opt/sox-14.4.2_5/sox"  // app dir
      : "sox"
      ;

   // Path to "rubberband" / "rubberband.exe"
   static String audiotool_rubberband_pathname =
      Utils.IsWindows() ? "$(TKS_DIR)/opt/rubberband-3.3.0-gpl-executable-windows/rubberband.exe"
      : Utils.IsMacOS() ? "/opt/homebrew/bin/rubberband"        // arm64
      // : Utils.IsMacOS() ? "/usr/local/homebrew/bin/rubberband"  // x86_64
      // : Utils.IsMacOS() ? "$(PROGRAM_DIR)/../../opt/rubberband-3.3.0/rubberband"  // app dir
      : "rubberband"
      ;

   // Path to "mpg123" / "mpg123.exe"
   static String audiotool_mpg123_pathname =
      Utils.IsWindows() ? "$(TKS_DIR)/opt/mpg123-1.31.3-static-x86-64/mpg123.exe"
      : Utils.IsMacOS() ? "/opt/homebrew/bin/mpg123"        // arm64
      // : Utils.IsMacOS() ? "/usr/local/homebrew/bin/mpg123"  // x86_64
      // : Utils.IsMacOS() ? "$(PROGRAM_DIR)/../../opt/mpg123-1.31.3/mpg123"  // app dir
      : "mpg123"
      ;

   // System text editor command
   //  (note) used for editing this config file
   static String textedit_cmd =
      Utils.IsMacOS()
      ? "/System/Applications/TextEdit.app/Contents/MacOS/TextEdit $(FILE)"
      : "notepad.exe $(FILE)"
      ;



   /***
    ***
    *** Multi-core configuration
    ***
    ***
    ***/

   // Force single-thread track rendering (false=use multithreading)
   //  (note) multi-thread rendering requires processor C-states + power-saving features like SpeedStep to be disabled
   //          (which will increase idle power consumption from ~10W to ~40W on an 18 core Intel 7980)
   static boolean b_force_single_thread = false;

   // true=ignore all CPU core / affinity mask settings and let the OS figure it out
   //  (note) should be set to false on Windows (=> enable manual CPU core assignments)
   //  (note) should be set to true on macOS (=> let the OS figure out the best thread scheduling)
   static boolean b_ignore_thread_affinity = false || Utils.IsMacOS();

   // true=allow track CPU core selection to be edited and used as CPU core group even when thread affinities are disabled
   //  (note) this allows tracks in the same group to be processed within the same worker thread
   //          audio data (e.g. FX sends) can then be passed to the next track in the CPU core group without chunk delays
   //  (note) except for the reduced latency, there do not seem to be any performance advantages on macOS
   //  (note) on Windows it is (highly) recommended to use manual track-to-CPU-core assignments
   //  (note) default=false
   static boolean b_always_allow_cpu_core_selection = false;

   // Process affinity mask. Each set bit represents one process core.
   //  (note) should include all cores used for UI/replay/track threads + cores allowed for (temporary) worker threads
   static String process_affinity_mask = "";

   // UI thread affinity mask.
   static String ui_thread_affinity_mask = "";

   // Replay thread affinity mask (light weight but needs high prio).
   static String replay_thread_affinity_mask = "";

   // Track threads affinity mask (used for track rendering).
   static String track_thread_affinity_mask = "";

   static method SetAffinity_i9_7980xe_no_ht() {
      // 18 logical cores (hyperthreading disabled in BIOS)
      //                             1   4   7   10  13  16..18
      process_affinity_mask       = ".xx xxx xxx xxx xxx xxx";
      ui_thread_affinity_mask     = "... ... ... ... ... .xx";
      replay_thread_affinity_mask = ".x. ... ... ... ... ...";
      track_thread_affinity_mask  = "..x xxx xxx xxx xxx x..";
      ;
   }

   static method SetAffinity_i7_13700H_ht() {
      // 6 performance cores with hyperthreading + 8 efficiency cores => 20 logical cores
      //  - reserve all performance cores + efficiency cores 1..5 for Eureka
      //  - reserve last efficiency core for OS
      //
      //  (note) set 'standalone_ring_size' to 3 and disable C-states and speed shift in BIOS (Asus PN64-E1)
      //
      //  (note) benchmark:  turbo boost + c-states/speed shift off, standalone_ring_size=3, ASIO buffer size=64 (Scarlett 18i20 2nd gen)
      //                     windows 11 pro
      //                     "stresstest_i713700h_fmstack_dly_rev_sends-02Oct2023"
      //                     16 instrument tracks with 8-voice FMstack + Fabfilter Pro-Q 3 + Acon Digital Dynamics each
      //                     + delay track (U-He ColourCopy) + reverb track (Acon Digital Verberate 2) + two sends per instr.track
      //                       (48 active voices, 50 active FX plugins)
      //                     ==> ~16W (20W max), ~74% load, ~25% CPU load, no underruns
      //                     w/ 14 voices per instrument track
      //                       (224 active voices, 50 active FX plugins)
      //                     ==> ~17W (20W max), ~93% load, ~25% CPU load, no underruns
      //
      //                             1  3  5  7  9  11 13 .. 20
      process_affinity_mask       = "xx xx xx xx xx xx xxxxxxx.";
      ui_thread_affinity_mask     = ".. .. .. .. .. .. .....xx.";
      replay_thread_affinity_mask = "xx .. .. .. .. .. ........";
      track_thread_affinity_mask  = ".. xx xx xx xx xx xxxxx...";
   }

   static method SetAffinity_m2_pro() {
      // 8 performance cores + 4 efficiency cores => 12 logical cores
      //  - reserve all performance cores + efficiency cores 1..5 for Eureka
      //  - reserve last efficiency core for OS
      //                             1 .. 8   9..12
      //
      //  (note) set 'standalone_ring_size' to 2 (or 3)
      //  (note) set b_ignore_thread_affinity=1 and let OS figure out the scheduling
      //
      process_affinity_mask       = "xxxxxxxx xxx.";
      ui_thread_affinity_mask     = "........ ..x.";
      replay_thread_affinity_mask = "x....... ....";
      track_thread_affinity_mask  = ".xxxxxxx xx..";
      ;
   }

   static method SetAffinity() {
      if(Utils.IsMacOS())
         SetAffinity_m2_pro();
      else
      {
         // (note) it is HIGHLY recommended to configure this properly (CPU-dependent)
         // SetAffinity_i9_7980xe_no_ht();
         SetAffinity_i7_13700H_ht();
      }
   }



   /***
    ***
    *** Window / UI setup
    ***
    ***/

   // GUI size
   //
   //   -1: custom display+cursor size
   //    0: small    (1k)
   //    1: small x2 (2k)
   //    2: large    (4k)
   //
   //   (note) also affects a number of other things (bevel borders, font+icon sizes, ..)
   //   (note) see eureka_config_local.tks
   //
   static int ui_size_preset = -1;

   // (note) this is used by Utils.KeycodeToHex() when capslock key is enabled (resp. shift is pressed)
   // (note) currently supported setting is "de". any other setting will default to a "us" layout (raw key codes).
   static String kbd_layout = "de";

   // Overall font-scaling (1.0 = optimized for 800x480 WVGA display)
   static float font_scaling = 1.0;

   // When true, disable layout caches and allow resize (should be false when running in fullscreen)
   static boolean b_ui_resizable = true;//false;

   // Overall zoom level (scales all graphics by given factors)
   //  (note) window_sx/sy resp. fullscreen_sx/sy should be scaled by the same factor
   //  (note) non-integer zoom factors will result in badly readable fonts
   //  (note) the window size will automatically be scaled accordingly
   static float zoom_x = 1;
   static float zoom_y = 1;

   // Mouse cursor zoom level
   //  (note) e.g. for Linux 4k screens
   static int cursor_zoom = 1;

   // Window width (pixels)
   //  (note) at least 960 pixels
   static int window_sx = 960;

   // Window height (pixels)
   //  (note) at least 500 pixels
   //  (note) with sy=504 and Synergy sy=600, both windows fit (vertically docked) onto Windows 10 1920x1200 screen with taskbar enabled
    static int window_sy = 504;  /// macOS

   // Override window position (pixels). -1=default pos
   //  (note) can be used to move window to secondary screen
   // static int window_x = -1;
   // static int window_y = 0;

   // static int window_x = -1920;

   // macOS with menubar and dock on the side
   static int window_x = 452;
   static int window_y = 54;

   // Dock window to Synergy window
   //  - disable window caption
   //  - move Eureka window when Synergy window is moved
   //  - show Eureka window when Synergy window becomes active
   static boolean b_window_docked = false;
   static boolean b_window_noframe_when_docked = false;
   static int dock_window_offset_x = Utils.IsMacOS() ?  0  : -8;
   static int dock_window_offset_y = Utils.IsMacOS() ? -28 : -38;

   // Start in fullscreen mode
   static boolean b_fullscreen = false;
   // static boolean b_fullscreen = true;

   // Fullscreen width (pixels)
   static int fullscreen_sx = 1920;

   // Fullscreen height (pixels)
   static int fullscreen_sy = 1200;

   // Fullscreen bits-per-pixel (16, 24, 32)
   static int fullscreen_bits = 32;

   // set fullscreen resolution to display size
   static boolean b_fullscreen_displaysize = true;

   // When true, open+close small window during startup
   //  (note) work-around for certain ATI graphics drivers (may not be needed anymore)
   static boolean b_open_prerun_window = 0 && Utils.IsWindows();

   // When true, render UI 60 times per second. When false, limit FPS to 30.
   static boolean b_force_high_framerate = true;

   // When true, synchronize buffer swap to vertical retrace (for smooth cursor movements)
   static boolean b_vsync = true;

   // When true, windows and dialogs have transparent backgrounds
   static boolean b_transparent_layers = true;

   // Post processing effect (fullscreen mode) (0=none, 1=solid, 2=scanlines)
   static int postfx_fs_mode = 2;

   // Post processing color / opacity (fullscreen mode)
   static int postfx_fs_color = #2f003863;
   // #2f005071
   // #5f000000

   // Post processing effect (window mode) (0=none)
   static int postfx_win_mode = 0;

   // post processing color / opacity (window mode)
   static int postfx_win_color = #1f001823;

   // KbdFocusSelectionMode behaviour: true=hold, false=toggle
   static boolean b_kbdfocusselectionmode_behaviour = false;

   // true=logarithmic output level sliders
   // false=linear output level sliders
   static boolean b_log_output_level_sliders = true;

   // Which UI page to start with
   //  available options: (must be prefixed with "RootForm.")
   //
   //    PAGE_AUDIO
   //
   static int start_page = RootForm.PAGE_AUDIO;

   // false=use "light" LookAndFeel (not really supported, TODO remove this)
   // true=use "dark" LookAndFeel
   static boolean b_lnf_dark = 1;




   /***
    ***
    *** Post load and plugin pooling (VST2/CLAP)
    ***
    ***/

   // true=use multi-threaded post-load initialization (VST/CLAP plugin load+init)
   // static boolean b_threaded_post_load = true;
   //  (note) can have strange side effects like the app hanging when stdout is redirected to a text file (!)
   //          (or when output is piped into another cmd, like grep)
   //          (e.g. when loading Dexed.dll, which is already excluded from threaded init)
   //           => better set this to 'false'
   static boolean b_threaded_post_load = false;

   // 1=init all lanes/mods in one thread per track (when b_threaded_post_load=true)
   // >1: start more worker threads and parallelize per-track/per-lane mod post load
   // (note) ignored when b_threaded_post_load is false
   static int threaded_post_load_mod_worker_threads = 24;

   // (note) unique_id_string sub-string matching (vendor`effect`uid`)
   // (note) ignored when b_threaded_post_load is false
   static StringArray threaded_post_load_vst_whitelist = [
      // To all who made it to this list: Well done!
      "FabFilter`FabFilter Saturn",
      "AudioThing`Outer Space",
      "Acon Digital Multiply",
      "Waldorf`Nave",
      "UVI`Falcon",
                                                          ];

   // (note) unique_id_string sub-string matching (vendor`effect`uid`)
   // (note) ignored when b_threaded_post_load is false
   static StringArray threaded_post_load_vst_blacklist = [
      //  (note) it's still possible that the crashes are an issue with this host (corrupted memory)
      //  (note) the init crashes often happen while restoring the patch data
      "Dexed",               // crashes during shutdown (works in single-thread init mode)
      "Little Plate",        // crashes during init
      "u-he`ZebraHZ",        // crashes during shutdown (also in single-thread init mode)
      "u-he`ACE",            // crashes during init
      "Modartt`Pianoteq 6",  // crashes during init
      "SparkVerb",           // crashes during shutdown (works in single-thread init mode)
      "ValhallaPlate",       // crashes during init
      "Zynaptiq`ADAPTIVERB", // crashes during init
      "PPG`Infinite",        // crashes during shutdown (works in single-thread init mode)
      "Softube`Tape",        // crashes during shutdown
      "TAL Sampler",         // extremely high CPU load during init and hangs
      "Renoise Redux",       // crashes during init
      "Waves",               // plugin UI not rendered later on (inits UI prematurely during effOpen?!)
                                                          ];

   // true=prefer multi-thread postload when (unknown) plugin is neither in white, nor in blacklist
   // false=prefer single-thread init for unknown plugins
   static boolean b_threaded_post_load_unknown = false;

   // true=re-use previously deleted instance (speeds up init/load times considerably)
   static boolean b_plugin_pooling = true;

   // plugin id_string sub-string matching (vendor`name`id`)
   //  (note) ignored when b_plugin_pooling is false
   static StringArray clap_pool_blacklist = [

                                            ];

   // unique_id_string sub-string matching (vendor`effect`uid`)
   //  (note) ignored when b_plugin_pooling is false
   static StringArray vst_pool_blacklist = [

                                            ];
      // "VeeSeeVST",

   // true=auto-hide plugin editor windows (show one editor at a time)
   // false=allow multiple VST/CLAP editor windows and keep them open until plugin instance is deleted
   // static boolean b_auto_hide_plugin_editors = false;
   static boolean b_auto_hide_plugin_editors = true;




   /***
    ***
    *** CLAP
    ***
    ***/
   // Mostly useful for debugging purposes (disable audio but keep CLAP enabled)
   static boolean b_enable_clap = true;

   // CLAP searchpath(s) (absolute path names)
   //  (note) directories will be scanned recursively
   //  (note) $CLAP_PATH environment variable can also be used to add paths
   //          (note) ';' separated list of paths (Windows)
   //          (note) ':' separated list of paths (macOS / Linux)
   //  (note) duplicate path names will be skipped
   static StringArray clap_paths =
      // macOS
      Utils.IsMacOS() ?
      [
         "/Library/Audio/Plug-Ins/CLAP",
         "~/Library/Audio/Plug-Ins/CLAP",
       ]

      // Windows
      : Utils.IsWindows() ?
      [
         "c:/Program Files/Common Files/CLAP",
         "f:/clap",
         "c:/bsp/clap",
       ]

      // Linux
      :
      [
         "/usr/lib/clap",
         "/usr/local/lib/clap",
         "~/.clap",
       ];

   static int clap_max_deepscan_jobs = Utils.IsMacOS() ? 0 : 0;

   // (note) when plugin path name matches entry (substring) it will be excluded from deep scan (and will not be available for instantiation)
   static StringArray clap_deepscan_blacklist = [
      "ACE",   // (ACE_142_preview_13207_Mac) crashes while loading state
                                                 ];

   // Enable (CLAP) plugin whitelist
   //
   //   (note) Use cases:
   //            1) minimalism: restrict plugin choices
   //            2)  stability: unknown plugins may cause crashes or graphics glitches
   //            2)      speed: increase start-up / scan speed by skipping rarely used plugins
   //
   //   (note) uses "clapcache_wl.dat" instead of "clapcache.dat" when enabled
   //           (i.e. enabling / disabling whitelisting won't necessarily cause plugin rescan)
   //
   // static boolean b_clap_deepscan_whitelist = true;
   static boolean b_clap_deepscan_whitelist = false;

   // CLAP plugin whitelist
   //
   //  (note) applied when b_vst_deepscan_whitelist = true
   //  (note) when plugin path name matches entry (substring) it will be added to deep scan (and may be available for instantiation)
   //  (note) when entry starts with "!", skip plugin and don't check subsequent entries
   //  (note) blacklist has precedence over whitelist
   //
   static StringArray clap_deepscan_whitelist =
      false/*minimal*/
      ? [
         "FabFilter",
         ]
      : [
         "FabFilter",
         ]
      ;

   // Number of concurrent threads used for scanning plugins.
   //  (note) should be 1(use multithreading but only one) or 0(single-threaded)
   //  (note) when all plugins are well-behaving, this can be set to 50 or higher (hint: they usually don't)
   static int clap_deepscan_num_threads = 0;

   static StringArray clap_deepscan_threaded_blacklist = [
                                                          ];
   // always close plugin window instead of hiding it
   static StringArray clap_hide_blacklist = [
                                            ];

   // never report transport to plugin
   static StringArray clap_transport_blacklist = [
                                                 ];

   // GenericParamDialogCLAP force-list (all platforms)
   static StringArray clap_genericparam_forced = [
                                                  ];

   // GenericParamDialogCLAP param filter
   //  (note) hashkey is used as (simple) pattern, e.g. "FabFilter" matches uniqueIDString of all FabFilter plugins
   //  (note) param names are *? wildcard patterns, e.g. "Filter ? Freq"
   static HashTable clap_genericparam_filters = #[
                                                 ];





   /***
    ***
    *** VST
    ***
    ***/

   //
   // About the plugin scanner:
   //
   //   - depending on whether 'b_vst_deepscan_whitelist' is true or false, the cache file name is 'vstcache_wl.dat' or 'vstcache.dat'
   //      - deleting it will force a rescan
   //      - as of [13Jan2024] this option is enabled by default. disable at your own risk
   //         (recommendation: add only the plugins you really need to the list)
   //
   //   - running the application with arg 'scan' will incrementally scan the next plugin libraries
   //      - the process returns 10 when the scan was aborted prematurely (=> continue when process is started again)
   //      - the process returns 0 when all plugins have been processed (blacklisted, failed, or scanned successfully)
   //
   //   - plugin scanning on macOS is quite a PITA
   //      - the maximum number of scan jobs per process run must be limited to 100 (or there will be TLS related failures/crashes)
   //      - a plugin scan (run in a separate process) can cause other processes to hang (!)
   //          => the number of simultaneous threads (which in turn call psystem() to fork a process) must be limited to 1
   //              This can happen when the plugin tries to show a dialog (e.g. missing license) when it's instantiated
   //               (note by: a plugin should do this in effEditOpen instead of effEdit open but well, it is what it is)
   //
   //  To run the plugin scan from the command line, issue (in the eureka directory)
   //    (optionally) $ rm -f vstcache.dat vstcache_wl.dat
   //    (on MacOS)   $ ../Synergy_arm64 eureka scan
   //    (on Windows) % ..\synergy.exe eureka scan
   //    (on Linux  ) $ tks eureka scan
   //

   // Mostly useful for debugging purposes (disable audio but keep VST enabled)
   static boolean b_enable_vst = true;
   // static boolean b_enable_vst = false;

   // true=rescan or load VST cache during startup
   static boolean b_vst_scan_or_load_cache = !b_minimal_startup;

   // VST searchpath(s) (absolute path names)
   //  (note) directories will be scanned recursively
   static StringArray vst_paths = Utils.IsMacOS() ?
      // macOS
      [
         "/Library/Audio/Plug-Ins/VST",
         // "~/Library/Audio/Plug-Ins/VST",
       ]
      :
      // Windows
      [
         "f:/vst_64bit",
         "c:/Program Files/Steinberg/VstPlugins",
         "c:/Program Files/VstPlugins",
         "c:/bsp/vst2",
         "c:/Program Files (x86)/VstPlugIns/Way Out Ware",
         //,"f:/Projects/research/pongasoft_bsp_vst24-data_bridge/data_bridge/vs2017/data_bridge/x64/Release/"
         //"f:/Projects/vst_eureka_x64/vst_eureka_x64/vs2017/vst_eureka_x64/x64/Release/",
         "F:/Projects/research/vst24_gl_test/M3dB/vs2017/m3db/x64/Release",
         "f:/git/VeeSeeVSTRack/vst2_bin/",
         "c:/Program Files/Acoustica Mixcraft 8/VST/",
                                    ];

   // static StringArray vst_paths = [
   //    "f:/git/Rack/vst2_bin/",
   //                                  ];

   // true= automatically perform deep scan if the number of plugins differs from the number of cache entries
   //  (i.e. after adding / removing VST plugins)
   static boolean b_vst_auto_deepscan = false;

   // limited on macOS due to dyld pthread TLS issues (see VST2Plugin.cpp)
   //  [13Jan2024] happens even when scanning in separate processes
   static int vst_max_deepscan_jobs = Utils.IsMacOS() ? 100 : 0;

   // certain plugins will crash in their "mainProc" during deep scan when loaded in this host
   //  (note) when plugin path name matches entry (substring) it will be excluded from deep scan (and will not be available for instantiation)
   static StringArray vst_deepscan_blacklist = [ "VstPlugins/SampleTank2.x",  // crashes in its mainProc()
                                                 "Nucleus 2 XT",              // can't find its datafiles and shows annoying dialog windows
                                                 "Kassiopeia!",               // AntiVir reports synthedit generated .dll as virus. false positive?
                                                 "KR-ReverbFSR120",           // openEditor() hangs in WM_CREATE
                                                 "KR-Micro-Reverb",           // crashes during deepscan but works when loaded on-demand
                                                 "LFX-1310",                  // Shows dialog "LFX-130.con\nfile can't be accessed" when effect is
                                                                              //  closed (note: file really does not exist. permission problem?)
                                                 "/buzz/",                    // filter V2 BUZZ plugins
                                                 "/winamp/",                  // filter V2 WinAMP plugin
                                                 "AriesVerb",                 // crashes in effClose if app is not run in console window (!),
                                                 "vst_eureka_x64",            // can't host itself by design
                                                 // "Piano V2",                  // Arturia Piano V2 UI hangs after 1st frame (fixed in VC8)
                                                 // "Stage-73 V",                // unstable (+goes quiet after a short while) (fixed in VC8)
                                                 // "Farfisa V",                 // causes instabilities (fixed in VC8)
                                                 "Fusion Shield",             // "not available outside Mixcraft 7" dlg
                                                 "Glass Viper",               // "not available outside Mixcraft 7" dlg
                                                 "Acoustica Instruments",     // no editor, no params (from Mixcraft 8)
                                                 "Expanded Instruments",      // no editor, no params (from Mixcraft 8)
                                                 "Studio Drums",              // no editor, no params (from Mixcraft 8)
                                                 "SideKick6",                 // no license found (from Mixcraft 8)
                                                 "Virtual Bass Amp",          // "requires a compatible Mixcraft host"
                                                 "LegacyCell",                // Korg Collection: LegacyCell (crashes)
                                                 "DUNE 3",                    // newer/latest version crashes in effClose
                                                 "Syn-X",                     // activation dialog (don't have a license)
                                                 // "SynthMaster2",           // tried to open a license dialog in effOpen (instead of effEditOpen), then hangs on macOS (=> must use "Rescan VST2" button while UI is running)
                                                 "CardinalSynth",             // FBO related OpenGL issues (macOS)
                                                 "CardinalFX",
                                                 ];

   // Enable (VST2) plugin whitelist
   //
   //   (note) Use cases:
   //            1) minimalism: restrict plugin choices
   //            2)  stability: unknown plugins may cause crashes or graphics glitches
   //            2)      speed: increase start-up / scan speed by skipping rarely used plugins
   //
   //   (note) uses "vstcache_wl.dat" instead of "vstcache.dat" when enabled
   //           (i.e. enabling / disabling whitelisting won't necessarily cause plugin rescan)
   //
   // static boolean b_vst_deepscan_whitelist = true;
   static boolean b_vst_deepscan_whitelist = false;

   // VST2 plugin whitelist
   //
   //  (note) applied when b_vst_deepscan_whitelist = true
   //  (note) when plugin path name matches entry (substring) it will be added to deep scan (and may be available for instantiation)
   //  (note) when entry starts with "!", skip plugin and don't check subsequent entries
   //  (note) blacklist has precedence over whitelist
   //
   static StringArray vst_deepscan_whitelist =
      false/*debug*/
      ? [ "Drumazon",
          ]

      : false/*minimal*/
      ? [ "!(Mono)",
          "AlborosieDubStation",                      // AudioThing
          "bx_console AMEK 9099",                     // Plugin Alliance
          "ChorX",                                    // XILS / iLok
          "Decimort2",                                // D16 Group (101 frames latency)
          "!FabFilter Micro",
          "!FabFilter One",
          "!FabFilter Simplon",
          "!FabFilter Timeless 2",
          "!FabFilter Timeless 2 (SC)",
          "FabFilter",
          "LX480",                                    // Relab Development (not in subdir) / iLok (15 frames latency)
          "MFreqShifter",                             // MeldaProduction / free
          "OuterSpace",                               // AudioThing
          "Pianoteq",
          "PSP Echo",                                 // iLok
          "PSP oldTimer",                             // iLok
          "RingX",                                    // XILS / iLok
          "RoyalCompressor",                          //
          "Softube/Drawmer 1973",                     // iLok (   4 frames latency)
          "Softube/Harmonics Analog Saturation",      // iLok (   4 frames latency)
          "Softube/Tape",                             // iLok
          "Softube/Trident A-Range",                  // iLok (   4 frames latency)
          "Softube/Tube-Tech Classic Channel mk II",  // iLok (   4 frames latency)
          "Softube/Weiss DS1-MK3",                    // iLok (8097 frames latency)
          "Sonsig Rev-A",                             // Relab Development / iLok
          "Soundtoys/EchoBoy",                        // iLok (  32 frames latency)
          "Soundtoys/MicroShift",                     // iLok (  32 frames latency)
          "Soundtoys/PhaseMistress",                  // iLok (  45 frames latency)
          "Soundtoys/SuperPlate",                     // iLok (  32 frames latency)
          "Synplant",
          "TAL-BassLine-101",
          "TAL-J-8",
          // "TAL-Pha",
          // "TAL-Reverb-4",
          "u-he/ColourCopy",                          // (  19 frames latency)
          "u-he/Satin",                               // (  22 frames latency)
          "u-he/ZRev",                                // (  16 frames latency)
          "ValhallaDelay",
          "ValhallaPlate",
          "ValhallaRoom",
          "ValhallaShimmer",
          "ValhallaUberMod",
          "ValhallaVintageVerb",
          "Vertigo VSM-3",                            // Plugin Alliance (  32 frames latency)
          "WaveBox",                                  // AudioThing (  31 frames latency)
          "Drumazon2",                                // D16 Group
          "Nepheton2",                                // D16 Group
          "Nithonat2",                                // D16 Group
          ]

      : [ "!(Mono)",
          "ABL3x",
          "Acon Digital Dynamics",                    // ( 240 frames latency)
          "Acon Digital Equalize 2",                  // ( 960 frames latency)
          "Acon Digital Limit",                       // ( 199 frames latency)
          // "Acon Digital Multiband Dynamics",       // (2464 frames latency)
          "Acon Digital Verberate 2",
          "Acon Digital Vitalize",                    // (384 frames latency)
          "AlborosieDubStation",                      // AudioThing
          // "Blackhole",                             // Eventide / iLok
          "bsp",
          "bx_2098 EQ",                               // Plugin Alliance (small UI on 4k)
          "bx_console AMEK 9099",                     // Plugin Alliance
          "bx_console SSL 9000 J",                    // Plugin Alliance
          // "bx_delay2500",                          // Plugin Alliance
          "bx_panEQ",                                 // Plugin Alliance
          "bx_limiter True Peak",                     // Plugin Alliance (320 frames latency)
          // "chipsynth OPS7",                        // Plogue
          // "chipsynth PortaFM",                     // Plogue
          "Chorus DIMENSION-D",                       // Arturia (48 frames latency)
          "Chorus JUN-6",                             // Arturia (48 frames latency)
          "ChorX",                                    // XILS / iLok
          "CS-80 V4",                                 // Arturia
          "CZ V",                                     // Arturia
          // "Chromaphone 2",                         // iLok
          "Decimort2",                                // D16 Group (101 frames latency)
          "Delay BRIGADE",                            // Arturia
          "Delay ETERNITY",                           // Arturia
          "Delay TAPE-201",                           // Arturia
          "Drumazon",                                 // D16 Group
          "Efx FRAGMENT",                             // Arturia
          "!FabFilter Micro",
          "!FabFilter One",
          "!FabFilter Simplon",
          "!FabFilter Timeless 2",
          "!FabFilter Timeless 2 (SC)",
          "FabFilter",
          "Harmonics Analog Saturation",              // Softube / iLok (4 frames latency)
          "OB-E",                                     // G-Force
          "OB-X",                                     // G-Force
          "Oddity3",                                  // G-Force
          "KaoX",                                     // XILS / iLok
          "LX480",                                    // Relab Development (not in subdir) / iLok (15 frames latency)
          "MAnalyzer",                                // MeldaProduction / free
          "MLoudnessAnalyzer",                        // MeldaProduction / free
          // "MConvolutionEZ",                        // MeldaProduction
          "MFreqShifter",                             // MeldaProduction / free
          // "MTurboReverb",                          // MeldaProduction
          // "MWaveFolder",                           // MeldaProduction
          // "MWaveShaper",                           // MeldaProduction
          "MSpectralPan",                             // MeldaProduction / free
          "MStereoScope",                             // MeldaProduction / free
          // "MeldaProduction",
          "Microtonic",                               // Sonic Charge
          "Nepheton",                                 // D16 Group
          "Nithonat",                                 // D16 Group
          "OuterSpace",                               // AudioThing
          "Permut8",                                  // Sonic Charge (14 frames latency)
          "Pianoteq",
          "Pigments",                                 // Arturia
          "PolyM",                                    // XILS / iLok
          "PSP Echo",                                 // iLok
          "PSP oldTimer",                             // iLok
          "!VSR S24",                                 // Relab Development (x64 only) / iLok
          "Relab Development",                        // iLok
          "Rev INTENSITY",                            // Arturia
          "Rev PLATE-140",                            // Arturia
          "Rev SPRING-636",                           // Arturia
          "RingX",                                    // XILS / iLok
          "RoyalCompressor",                          //
          // "SPL HawkEye",                           // Plugin Alliance
          "Softube/Bass Amp Room",                    // iLok
          "Softube/Dirty Tape",                       // iLok (  75 frames latency)
          "Softube/Drawmer 1973",                     // iLok (   4 frames latency)
          "Softube/FET Compressor Mk II",             // iLok (   4 frames latency)
          // "Softube/Heartbeat",                     // iLok
          "Softube/Metal Amp Room",                   // iLok
          "Softube/Model 72",                         // iLok
          "Softube/Model 77",                         // iLok
          "Softube/Model 80",                         // iLok (   4 frames latency)
          "Softube/Model 82",                         // iLok
          "Softube/Model 84",                         // iLok
          "Softube/Modular",                          // iLok
          "Softube/Saturation Knob",                  // iLok
          "Softube/Summit Audio Grand Channel",       // iLok (   6 frames latency)
          "Softube/Tape",                             // iLok
          "Softube/Tonelux Tilt Live",                // iLok (   0 frames latency?)
          "Softube/Transient Shaper",                 // iLok (   8 frames latency)
          "Softube/Trident A-Range",                  // iLok (   4 frames latency)
          "Softube/TSAR-1 Reverb",                    // iLok
          "Softube/Tube-Tech Classic Channel mk II",  // iLok (   4 frames latency)
          "Softube/Valley People Dyna-mite",          // iLok
          "Softube/Vintage Amp Room",                 // iLok
          "Softube/Weiss DS1-MK3",                    // iLok (8097 frames latency)
          // "Softube",                               // iLok
          // "Sonic Charge",
          "soothe",                                   // iLok
          // "Soundtoys",                             // iLok
          "Soundtoys/Crystallizer",                   // iLok (  32 frames latency)
          "Soundtoys/Decapitator",                    // iLok (  62 frames latency)
          "Soundtoys/Devil-Loc",                      // iLok (  48 frames latency (deluxe=63 frames))
          "Soundtoys/EchoBoy",                        // iLok (  32 frames latency)
          // "Soundtoys/FilterFreak1",                // iLok
          // "Soundtoys/FilterFreak2",                // iLok
          "Soundtoys/MicroShift",                     // iLok (  32 frames latency)
          // "Soundtoys/PanMan",                      // iLok
          "Soundtoys/PhaseMistress",                  // iLok (  45 frames latency)
          "Soundtoys/PrimalTap",                      // iLok (  92 frames latency)
          // "Soundtoys/Radiator",                    // iLok
          "Soundtoys/SieQ",                           // iLok (  37 frames latency)
          "Soundtoys/SuperPlate",                     // iLok (  32 frames latency)
          // "Soundtoys/Tremolator",                  // iLok
          // "Superior Drummer 3",                    // iLok
          // "DUNE3",                                 // Synapse Audio
          "Legend",                                   // Synapse Audio
          "Obsession",                                // Synapse Audio
          // "Synapse Audio",
          "Synclavier",                               // Arturia
          // "SynthMaster2",
          "TAL-BassLine-101",
          "TAL-Chorus-LX",
          "TAL-Filter-2",
          "TAL-Pha",
          "TAL-J-8",
          // "TAL-Mod",
          // "TAL-NoiseMaker",
          "TAL-Reverb-4",
          // "TAL-Sampler",
          "TAL-U-No-LX-V2",
          // "TAL-Vocoder-2",
          // "TAL",
          "TDR Kotelnikov GE",                        // Tokyo Dawn Records ( 184 frames latency)
          "TDR Limiter 6 GE",                         // Tokyo Dawn Records ( 369 frames latency)
          "TDR Nova GE",                              // Tokyo Dawn Records ( 187 frames latency)
          "TDR SlickEQ M",                            // Tokyo Dawn Records (1900 frames latency)
          "TDR VOS SlickEQ GE",                       // Tokyo Dawn Records ( 183 frames latency)
          // "TDR",                                   // Tokyo Dawn Records
          // "UltraReverb",                           // Eventide / iLok
          "Unfiltered Audio G8",                      // Plugin Alliance
          "Unfiltered Audio Silo",                    // Plugin Alliance (1400 frames latency)
          // "u-he/ACE",
          "u-he/Bazille",
          "u-he/ColourCopy",                          // (  19 frames latency)
          "u-he/Diva",
          "u-he/Repro-1",
          "u-he/Repro-5",
          "u-he/Satin",                               // (  22 frames latency)
          "u-he/ZRev",                                // (  16 frames latency)
          "u-he/Zebra2",
          // "u-he/ZebraHZ",
          // "u-he/Zebralette",
          "u-he/Zebrify",                             // (  16 frames latency)
          // "u-he",
          "ValhallaDelay",
          "ValhallaPlate",
          "ValhallaRoom",
          "ValhallaShimmer",
          "ValhallaUberMod",
          "ValhallaVintageVerb",
          // "Valhalla",
          "Vertigo VSM-3",                            // Plugin Alliance (  32 frames latency)
          "WaveBox",                                  // AudioThing (  31 frames latency)
          "XILS 4",                                   // iLok
          // "XILS 505",                              // iLok
          // "XILS V+",                               // iLok
          ];




   //  0: disable multi-threaded deep scan
   // >0: use 'n' threads for multi-threaded deep scan
   //
   //  (note) real world speed up with  4 threads: ~41sec scan time vs ~71sec (283 plugins)
   //  (note) real world speed up with  8 threads: ~41sec scan time vs ~71sec (283 plugins)
   //  (note) real world speed up with 16 threads: ~35sec scan time vs ~71sec (283 plugins)
   //  (note) real world speed up with 18 threads: ~33sec scan time vs ~71sec (283 plugins)
   //  (note) real world speed up with 32 threads: ~33sec scan time vs ~71sec (283 plugins)
   //
   //  (note) CPU usage during threaded scan is very low (seems like there's a lot of waiting going on)
   //
   //  (note) multi-threaded deep scan mostly seems to work fine on macOS (on Windows, many plugins crash)
   //          but there still are some few plugins that cause crashes so it is disabled by default
   //           ==> in some cases, a single mis-behaving plugin can cause other scan jobs/processes to hang (on macOS). weird.
   //                 ==> must scan 1 plugin at a time
   //
   // Run plugin scans in separate processes
   //  (note) workaround for plugin crashes
   static boolean b_vst_deepscan_process = 1;/// && Utils.IsMacOS();

   // Number of concurrent threads used for scanning plugins.
   //  (note) should be 1(use multithreading but only one) or 0(single-threaded)
   //  (note) when all plugins are well-behaving, this can be set to 50 or higher (hint: they usually don't)
   static int vst_deepscan_num_threads = 1;

   static StringArray vst_deepscan_threaded_blacklist = [
      "Pianoteq",  // crash after audioMasterGetVendorString, only works in single-threaded scan
      "Plogue",
      "PG-8X",
                                                          ];

   // true=re-use cache entries during deep scan (only deep-scan newly added plugins)
   //  (note) currently [12Feb2018] has issues with shell plugins (but added hack for Waves)
   static boolean b_vst_deepscan_incremental = true;

   // unique_id_string sub-string matching (vendor`effect`uid`)
   static StringArray vst_relativeparams_blacklist = [
      "VeeSeeVST",
                                            ];

   // unique_id_string sub-string matching (vendor`effect`uid`)
   static StringArray vst_closelibrary_blacklist = [
      "PolyKB",  // Shuts down application when library is closed during scan (!)
      "KaoX",    // Shuts down application when library is closed during scan (!)
                                                    ];


   // Plugins for which to enable buffered output mode by default
   //  (note) this adds vst_buffered_mode_num_chunks extra latency but may fix sound clicks
   //          for plugins that will only work properly with very larger buffer sizes
   //  (note) in practice, this fixes issues with Zynaptiq MORPH (in interweave mode)
   //          but _not_ PITCHMAP which simply overloads the CPU (my i9 can't handle it, even at 1024 sample buffers)
   static StringArray vst_buffered_mode_whitelist = [
      // "Zynaptiq`MORPH",
      "Zynaptiq`PITCHMAP",
                                                     ];

   // Number of (64 sample frame) chunks in buffered mode (default=4 => 4*64=256 sample frame processing buffers)
   static int vst_buffered_mode_num_chunks = 4;

   // always close plugin window instead of hiding it
   static StringArray vst_hide_blacklist = [
      // - opens "preset collection" window when re-showing plugin. does not happen when closing editor window instead of hiding it.
      // - weird UI framerate issues after opening+hiding multiple Softube plugins (closing window "fixes" it)
      "Softube",
                                            ];

   // never report kVstTransportPlaying to plugin
   static StringArray vst_transport_blacklist = [
      // (V2.0) cuts first played note after host sequencer has been started
      "OB-E"
                                                 ];

   // MacOS host UI redraw issue workaround list
   //  (note) re-open host window after showing plugin UI
   static StringArray vst_macos_host_ui_redraw_workaround = [
      "Arturia",          // host UI not redrawn until next resize after showing plugin editor on macOS
                                                ];

   // GenericParamDialogVST2 force-list (all platforms)
   static StringArray vst_genericparam_forced = [
                                                ];

   // GenericParamDialogVST2 force-list (macOS only)
   static StringArray vst_genericparam_forced_macos = [
      // "PSP 2445",        // plugin UI redraw issues
      // "PSP Echo",        // plugin UI redraw issues
      // "PSP oldTimer",    // plugin UI redraw issues
      // "PSP oldTimerME",  // plugin UI redraw issues
      // "PSP SpringBox",   // plugin UI redraw issues
      // "PSP InfiniStrip",  // host UI not redrawn until next resize after showing plugin editor on macOS + plugin UI redraw issues
      "PSP",
      "Delay BRIGADE",
      "Delay ETERNITY",
      "Delay TAPE-201",
      "Rev INTENSITY",
      "Rev PLATE-140",
      "Rev SPRING-636",
      "Chorus DIMENSION-D",
      "Chorus JUN-6",
                                                ];

   // GenericParamDialogVST2 param filter
   //  (note) e.g. hide Arturia helper params or reduce huge param lists to 'essential' params
   //  (note) hashkey is used as (simple) pattern, e.g. "Arturia" matches uniqueIDString of all Arturia plugins
   //  (note) param names are *? wildcard patterns, e.g. "Filter ? Freq"
   static HashTable vst_genericparam_filters = #[
      "Arturia" = ["MIDI CC helper*",
                   "MPE_PitchBend_*",
                   "MPE_Aftertouch_*",
                   "MPE_Brightness_*",
                   ],

      "FabFilter Timeless 3" = ["Filter 3*", "Filter 4*", "Filter 5*", "Filter 6*",
                                "*XLFO*",
                                "Receive Midi",
                                "XY Controller *",
                                "* EF*",
                                "Delay Tap 5 *",
                                "Delay Tap 6 *",
                                "Delay Tap 7 *",
                                "Delay Tap 8 *",
                                "Delay Tap 9 *",
                                "Delay Tap 10 *",
                                "Delay Tap 11 *",
                                "Delay Tap 12 *",
                                "Delay Tap 13 *",
                                "Delay Tap 14 *",
                                "Delay Tap 15 *",
                                "Slot * Source",
                                "Slot * Level",
                                "Slot * Bypassed",
                                "Slot * Inverted",
                                "Slot * Target",
                                "MIDI Source *",
                                "EF 2 *", "EF 3 *", "EF 4 *",
                                "EG 2 *", "EG 3 *", "EG 4 *", "EG 5 *", "EG 6 *",
                                ],

         "Valhalla" = ["Reserved*"],
                                                 ];

   // true=hide window instead of closing/destroying it when user clicks close button
   //  (note) can cause micro-underruns, depending on plugin
   //  (note) click into host window to hide plugin window, click plugin close button to destroy plugin window
   static boolean b_hide_vst_window_when_closed = false;




   /***
    ***
    *** MIDI setup
    ***
    ***/

   // Don't set this to false!
   //  (note) disabling MIDI can only be useful for debug purposes
   static boolean b_enable_midi = true;
   //static boolean b_enable_midi = false;

   // true=use LocalMIDI (built-in) loopback device emulation (recommended)
   // false=use external MIDI loopback device (e.g. loopmidi on Windows, IAC driver on macOS)
   //  (note) MUST be enabled in Linux build or app won't be able to connect to Synergy MIDI sequencer
   //  (note) see midi_in_devicenames (def="vst_a".."vst_z")
   static boolean b_enable_localmidi = true;

   // false=process all available MIDI events in first chunk
   // true=try to spread MIDI events over several chunks (experimental, can actually result in worse timing)
   // static boolean b_midi_spread_events = false;
   static boolean b_midi_spread_events = true;

   // Device used for incoming SysEx messages (sequencer<>audio process link)
   //  (note) note on/off messages recv'd on channel 1 play temp samples (current SampleView selection)
   //  (note) note+ctl messages will be forwarded to the current track (while it's being edited)
   //  (note) [deprecated] Linux: e.g. "Midi Through Port-1" (when using tkmidi_portmidi + snd_seq_dummy)
   //  (note) [deprecated] Linux: $ /sbin/modprobe snd_seq_dummy ports=16
   //  (note) Linux: must set "b_enable_localmidi = true"
   // static String sysex_in_devicename = Utils.IsLinux() ? "Midi Through Port-1" : "remote_audio";
   static String sysex_in_devicename = "remote_audio";

   // Device used for outgoing SysEx messages (sequencer<>audio process link)
   //  (note) [deprecated] Linux: e.g. "Midi Through Port-0"  (when using tkmidi_portmidi + snd_seq_dummy)
   //  (note) [deprecated] Linux: $ /sbin/modprobe snd_seq_dummy ports=16
   //  (note) Linux: must set "b_enable_localmidi = true"
   // static String sysex_out_devicename = Utils.IsLinux() ? "Midi Through Port-0" : "remote_seq";
   static String sysex_out_devicename = "remote_seq";

   // Comma separated list of MIDI in device names
   //  (note) used for receiving MIDI events from 'Synergy' sequencer (see <Synergy>STConfig.localmidi_out_devicenames)
   //  (note) up to 26 entries. will be displayed as "A", "B", "C", "D", "E", "F", "G", "H", .. 'Z'.
   //  (note) projects save device idx (A,B,C,D,..Z => 0..25), not the device name
   //  (note) see device list in the console window
   //  (note) create LocalMIDI loopback ports when b_enable_localmidi = true
   //  (note) comment or remove device entries at end of array as required
   static StringArray midi_in_devicenames =
      [/*  0 */ "vst_a",
       /*  1 */ "vst_b",
       /*  2 */ "vst_c",
       /*  3 */ "vst_d",
       /*  4 */ "vst_e",
       /*  5 */ "vst_f",
       /*  6 */ "vst_g",
       /*  7 */ "vst_h",
       /*  8 */ "vst_i",
       /*  9 */ "vst_j",
       /* 10 */ "vst_k",
       /* 11 */ "vst_l",
       /* 12 */ "vst_m",
       /* 13 */ "vst_n",
       /* 14 */ "vst_o",
       /* 15 */ "vst_p",
       // (note) q..z are disabled by default (uncomment as required)
       // (note) also see synergy_config.tks:localmidi_out_devicenames
       // /* 16 */ "vst_q",
       // /* 17 */ "vst_r",
       // /* 18 */ "vst_s",
       // /* 19 */ "vst_t",
       // /* 20 */ "vst_u",
       // /* 21 */ "vst_v",
       // /* 22 */ "vst_w",
       // /* 23 */ "vst_x",
       // /* 24 */ "vst_y",
       // /* 25 */ "vst_z",
       ]
      ;

   // Hash of hashes (key=device name, value=attribute hashtable (filter settings))
   static HashTable midi_in_filter;

   // true=check for device disconnect/reconnect every few seconds
   //  (note) this may take a few milliseconds (usually ~2) and may therefore alter the midi timing a little bit
   //  (note) this automatically reverts to false if the check takes more than 5 milliseconds
   static boolean b_midi_in_keepalive = false;

   // Hash of strings (key=device name, value="0" or "1")
   //  Enable/disable (N)RPN parser (CC#6/38/96..101), default=on
   static HashTable midi_device_param;

   // Hexdump MIDI output buffer(s) (skip clock)
   static boolean b_midi_debug_out_cmd = false;
   // static boolean b_midi_debug_out_cmd = true;

   // Hexdump MIDI output buffer(s)
   static boolean b_midi_debug_out_note = false;
   // static boolean b_midi_debug_out_note = true;

   // Output monitor history size (num buffers since last monitor UI update, per device) (def=256)
   static int output_mon_max_log_entries = 256;



   /***
    ***
    *** Audio setup
    ***
    ***/

   // Enable audio I/O
   //  (note) via SDL or PortAudio (ASIO / CoreAudio / ALSA)
   //  (note) may be set to false when debugging UI code
   //
   static boolean b_enable_audio = true;

   // true=calc audio buffers
   // false=skip callback processing (DEBUG)
   static boolean b_process_audio = true;
   static boolean b_process_tracks = true;

   // true=use SDL audio  **DEPRECATED**
   //  (note) SDL audio usually has a very high latency (>=1024 samples) (does not affect databridge mode)
   //  (note) this is merely a "last resort" type of fallback solution, e.g. when no working tkportaudio backend is available
   //  (note) please set standalone_ring_size=0 and b_force_single_thread=true when enabling this
   //
   // false=use PortAudio (low latency / multichannel support)
   //
   // maybe=use PortAudio when databridge is disabled
   //
   static boolean b_audio_sdl = maybe;

   // SDL audio buffersize (#samples)
   //  (note) only applies when SDL audio is enabled
   static int audio_sdl_bufsize = 1024;

   // Portaudio/SDL audio mixrate
   //  (note) 0=use default sample rate
   // static int audio_mixrate = 32000;
   // static int audio_mixrate = 44100;
   static int audio_mixrate = 48000;
   // static int audio_mixrate = 96000;
   // static int audio_mixrate = 0;

   // PortAudio device index
   //  (note) see device list in the console window
   //  (note) it is possible that device #0 may not even be present (seen with Akai EIE Pro driver)
   static int audio_device_index = 0;

   // PortAudio preferred device name
   //  (note) overrides the "audio_device_index" setting if the device is present
   // static String  audio_pref_device_name = "Guitar Rig Session I/O";
   // static String audio_pref_device_name = "Akai EIE Pro ASIO Driver";
   // static String audio_pref_device_name = "Fireface 802 (23861675)";
   static String audio_pref_device_name = "Loopback Audio";

   // (note) Linux Scarlett 18i20 2nd gen:
   //         in /etc/modprobe.d/alsa_scarlett.conf (create new file):
   //         options snd_usb_audio vid=0x1235 pid=0x8215 device_setup=1
   //         # see https://github.com/geoffreybennett/alsa-scarlett-gui/blob/master/USAGE.md

   // PortAudio sample format (-1=auto)
   static int audio_sample_format = -1;
   // static int audio_sample_format = PA_INT16;
   // static int audio_sample_format = PA_INT24;
   // static int audio_sample_format = PA_FLOAT32;

   // PortAudio / ASIO number of input channels
   //  (note) the maximum number of supported channels is 48 (see Audio.STREAM_MAX_INPUT_CHANNELS)
   //  (note) will be clipped to the number of actually available inputs
   //  (note) in VST ("metahost") mode, the number of inputs is always 8 (4 stereo pairs)
   static int audio_num_in = 32;

   // PortAudio / ASIO number of output channels
   //  (note) will be clipped to actual number of available channels
   //  (note) the maximum number of supported channels is 32 (see Audio.STREAM_MAX_OUTPUT_CHANNELS)
   //  (note) in VST ("metahost") mode, the number of outputs is always 16 (8 stereo pairs)
   static int audio_num_out = 32;

   // PortAudio / ASIO input channel aliases (mono+stereo pairs)
   //  (note) displayed in AudioTrack channel combobox
   static StringArray audio_in_aliases = [
      // Mono(short)  Mono(long)      Stereo(short)  Stereo(long)
      "Sy L",        "Synth L",       "Sy",          "Synth",       //  1, 1+2
      // .. overridden in eureka_config_local.tks..
                                          ];

   // PortAudio / ASIO output channel aliases (mono+stereo+shortname)
   //  (note) displayed in AudioTrack channel combobox
   static StringArray audio_out_aliases = [
      // Mono(short)  Mono(long)      Stereo(short)  Stereo(long)
      "Bs L",         "Bass L",       "Bs",          "Bass",        //  1, 1+2
      // .. overridden in eureka_config_local.tks..
                                          ];

   // true=show auto-route context menu items (track + project pages)
   static boolean b_show_autoroute_menu_tems = false;

   // Individual audio output auto-map configurations
   //  (note) used by tracks overview page context menu autoroute functions
   //  (note) left-hand side: track name (case insensitive)
   //  (note) right-hand side: audio output alias (see STConfig.audio_out_aliases)
   static HashTable track_autoroute_map_replace = #[
      "bd.l"   = "A1: BD",
      "bt.l"   = "A1: BD",
      "sd.l"   = "A2: SD",
      "cp.l"   = "A2: SD",
      "rs.l"   = "A2: SD",
      "cp.l"   = "A2: SD",
      "hh.l"   = "A5: HH",
      "ma.l"   = "A5: HH",
      "cy.l"   = "A6: CY",
      "bd"     = "A12: Drums 1",
      "bt"     = "A12: Drums 1",
      "sd"     = "A12: Drums 1",
      "cp"     = "A12: Drums 1",
      "rs"     = "A12: Drums 1",
      "lt"     = "A34: Drums 1",
      "mt"     = "A34: Drums 1",
      "ht"     = "A34: Drums 1",
      "toms"   = "A34: Drums 1",
      "hh"     = "A56: Drums 3",
      "cy"     = "A56: Drums 3",
      "ma"     = "A56: Drums 3",
      "drums"  = "A12: Drums 1",
      "bass"   = "R12: Bass",
      "keys"   = "R34: Keys",
      "lead"   = "R34: Keys",
      "pads"   = "R56: Pads",
      "final"  = "R78: Final",
                                             ];

   static HashTable track_autoroute_map_parallel = #[
                                             ];

   static HashTable track_autoroute_map_lane = #[
                                             ];

   // test tone frequency (scroll-lock key)
   // static float test_tone_freq = 440.0;
   static float test_tone_freq = Sample.BASE_FREQ_MIDDLE_C;  // 261.63Hz

   // this (ideally) should be set to false (=> use semaphores for thread communication).
   // "true" will run the cores "hot" which decreases the chance of underruns.
   // maybe=poll for <thread_spinlock_ms>, then use semaphore [experimental]
   // can be toggled at runtime in the UI (checkbox on "Audio" page)
   // static boolean b_poll = true;
   // static boolean b_poll = false;
   static boolean b_poll = maybe;

   // false=use semaphores while processing replay thread
   // true=poll while processing replay thread [experimental]
   // maybe=poll for <thread_spinlock_ms>, then use semaphore [experimental]
   // static boolean b_replay_thread_poll = false;
   // static boolean b_replay_thread_poll = maybe;
   static boolean b_replay_thread_poll = b_poll;

   // false=use semaphores while processing track threads
   // true=poll while processing track threads [experimental]
   //  - high CPU load!
   //  - does not seem to reduce underruns
   //  => better set to false
   // maybe=poll for <thread_spinlock_ms>, then use semaphore [experimental]
   // static boolean b_track_thread_poll = false;
   // static boolean b_track_thread_poll = maybe;
   // static boolean b_track_thread_poll = true;
   static boolean b_track_thread_poll = b_poll;

   // used when *_thread_poll is set to maybe
   static float thread_spinlock_ms = 0.15;

   // level reduction when "Dim" button is pressed
   static float master_dim_db = -30.0;

   // Suggested number of frames per PortAudio callback
   //  0=default size, >0=may update PortAudio buffer size
   // static int audio_frames_per_buffer = 4;
   // static int audio_frames_per_buffer = 8;
   static int audio_frames_per_buffer = Utils.IsLinux() ? 256 : Utils.IsMacOS() ? 16 : (64*4);
   // static int audio_frames_per_buffer = 24;
   // static int audio_frames_per_buffer = 32;
   // static int audio_frames_per_buffer = 48;
   // static int audio_frames_per_buffer = 64;
   // static int audio_frames_per_buffer = 0;

   // Audio buffer chunk size (def=64)
   // static int audio_chunk_size = 16;
   // static int audio_chunk_size = 32;
   // static int audio_chunk_size = 48;
   static int audio_chunk_size = 64;

   // Suggested latency (in seconds). 0=lowest possible, 0.005334=~256 frames @48kHz
   static float audio_suggested_latency = Utils.IsLinux() ? "0.005334" : 0.0f;

   // standalone mode: number of (64 sample frame) output buffers to calculate in advance (1..8)
   //  (note) only applies when b_force_single_thread=false
   //  (note) increases output latency but reduces number of underruns
   //  (note) suggested size: 4 (with ASIO buffer size set to 16 frames and process priority class realtime)
   //                     or: 4 (with ASIO buffer size set to 64 frames and process priority class normal)
   //                     or: 1 (with ASIO buffer size set to 64 frames, process priority 'realtime' (taskmgr), "Audio" power profile)
   //  (note) 0 disables ring buffer (in which case b_force_single_thread should then be set to true)
   //
   // // static int standalone_ring_size = 3;  // w/o CPU-cstates and w/o speedstep, "Audio" power profile
   // // static int standalone_ring_size = 6;
   // // static int standalone_ring_size = 1;  // "Audio" power profile, "realtime" process priority
   // // static int standalone_ring_size = 8;  // w/ CPU c-states + speedstep
   static int standalone_ring_size = 2;  // (ARM) macOS with lots of CPU-intensive tracks

   // plugin mode: number of (64 sample frame) output buffers to calculate in advance (increases output latency)
   //  (note) needed by some DAWs, e.g. Reason
   static int metahost_ring_size = 4;

   // when enabled, just store .epr project ref in plugin state (bank/program chunk)
   static boolean b_metahost_save_project_ref_only = true;

   // default input mode for new tracks (false=mono, true=stereo)
   static boolean b_stereo_input = true;

   // true=keep on processing audio+MIDI even when track, lane, or plugin is muted
   // maybe=process MIDI only (may result in hanging notes)
   // false=don't process anything (may result in hanging notes)
   static boolean b_process_when_muted = false;

   // true=process effect even when track, lane, or plugin is muted
   // false=don't process anything
   // (note) combined with b_process_when_muted
   static boolean b_process_effect_when_muted = false;

   // Number of sample frames to discard after recording was started via SYX_MSGID_RECORD_ARM followed by SYX_MSGID_SEQ_START
   //  (latency compensation)
   static int atrk_rec_skip_num_frames = 100;
   // static int atrk_rec_skip_num_frames = -80;

   // Used for AudioTracks
   static StringArray atrk_rec_skip_presets  = ["-=0", "Audio input=64", "Track output=-80"];

   // Preset list for Sample Live Recording "latency" floatparam
   static StringArray smp_liverec_skip_presets  = ["-=0", "Audio input=64", "Track output=0"];

   // Initial latency for new LiveRec sample zones (e.g. those created via Synergy Arranger)
   //  (note) see Sample.InitLiveRecZone()
   static int liverec_zone_initial_latency = 64;

   // Input/output activity (average) level threshold (def=0.02=~-34dB)
   //  (note) when audio-selecting channel (PageAudio,PageTrack)
   static float io_activity_threshold = 0.02f;



   /***
    ***
    *** Audio.data_bridge
    ***
    ***/

   // Enable databridge-only output mode (via "pongasoft and bsp`data_bridge`1970495854" VST)
   //  when enabled, replay thread waits for global databridge event, which is raised by any databridge consumer.
   //
   //  (note) load the data_bridge VST in your main DAW to connect to the Eureka audio output
   //
   //  (note) it's recommended to enable SDL audio, even though the app will just output zeros
   //          (some VST plugins depend on it, for some unknown reason)
   //
   //  (note) !!!EXPERIMENTAL!!!
   //
   static boolean b_databridge = false;
   // static boolean b_databridge = true;

   // Databridge ringbuffer size (number of 64 sample frame chunks).
   //
   // (note) higher values increase max latency
   //         - use 4 for ASIO buffer size = 128 sample frames
   //         - use 256-1 for large MME buffers (>= 1024 sample frames)
   // (note) as a rule of thumb, use (ASIO buffer size/64)*2
   //
   // static int databridge_ring_size = 256-1/*databridge_channel_t::NUM_CHUNKS*/;
   //static int databridge_ring_size = 256-1; // MME 4096 samples
   // static int databridge_ring_size = 16-1/*databridge_channel_t::NUM_CHUNKS*/;
   // static int databridge_ring_size = 4/*databridge_channel_t::NUM_CHUNKS*/; // 128 ASIO frames
   // static int databridge_ring_size = 8/*databridge_channel_t::NUM_CHUNKS*/; // 256 ASIO frames
   // static int databridge_ring_size = 64/*databridge_channel_t::NUM_CHUNKS*/; // 256 ASIO frames
   static int databridge_ring_size = 6/*databridge_channel_t::NUM_CHUNKS*/; // 128 ASIO frames + safety margin




   /***
    ***
    *** Audio.SampleView
    ***
    ***/

   // Maximum SampleView undo/redo stack size (per waveform)
   static int sampleview_undo_stack_size = 1024;

   // Maximum SampleView undo memory usage (in MBytes) (all waveforms)
   //  (if threshold is crossed, auto-purge old entries)
   //  (note) set to 0 to disable autopurge (very careful there!)
   static float sampleview_undo_autopurge_threshold = 250.0f;

   // Maximum SampleView redo memory usage (in MBytes) (all waveforms)
   //  (if threshold is crossed, auto-purge old entries)
   //  (note) set to 0 to disable autopurge (very careful there!)
   static float sampleview_redo_autopurge_threshold = 100.0f;

   // Vertical split point between audio tracks and sampleview (def=0.7)
   static float audiotrack_sampleview_split = 0.5;

   // true=Auto-export track stems when project is saved (and track audio was modified or file does not exist)
   static boolean b_audiotrack_autoexport = true;

   // true=per-zone view settings
   static boolean b_sampleview_restore_zone_view = true;



   /***
    ***
    *** Default Project Settings
    ***
    ***/



   /***
    ***
    *** Sample repositories
    ***
    ***/

   // Default waveform storage type (used in "New Instrument" dialog)
   //   true = only store sample repository relative pathname
   //  false = embed waveform data in project
   static boolean sample_external = true;



   /***
    ***
    *** Sample.Assimil8or
    ***
    ***/

   // // // Write Assimil8or HW sampler patches (.yml) (in addition to native .esa) when sample is saved via "Save" / "Save as"
   // // //  (note) zones are converted to "channels"
   // // //  (note) loops are converted to "zones", with evenly distributed "MinVoltage" (a8 zone select)
   // // static boolean b_write_yml_single = false;

   // true=auto-write Assimil8or .yml files for each project sample (stored in <project>/samples/ dir)
   //  (note) Assimil8or requires sample enumeration (prst<%03d>.yml, "Preset <n> :" line)
   static boolean b_write_yml_project = false;



   /***
    ***
    *** Audio Sample default scale / frequency table
    ***
    ***  (note) can be edited in SampleTuningDialog (PageSample=>"Tune.."=>"Set default")
    ***
    ***/

   // table name (info only)
   static String audio_default_freq_table_name = "";

   // table frequencies (Hz) (128 MIDI notes)
   //   (note) if this array does not contain 128 MIDI note entries,
   //           the equal temperament reference table is used instead.
   //   (note) sample-banks and zones may override this table (per patch)
   static FloatArray audio_default_freq_table;


   // static String audio_default_freq_table_name = "Andreas Werckmeister's temperament III (the most famous one, 1681)";
   // static FloatArray audio_default_freq_table = [8.1758, 8.61319, 9.13567, 9.68984, 10.2429, 10.9011, 11.4843, 12.2222, 12.9198, 13.6572, 14.5348, 15.3643, 16.3516, 17.2264, 18.2713, 19.3797, 20.4857, 21.8021, 22.9685, 24.4444, 25.8396, 27.3143, 29.0695, 30.7286, 32.7032, 34.4528, 36.5427, 38.7593, 40.9715, 43.6043, 45.937, 48.8889, 51.6791, 54.6286, 58.139, 61.4572, 65.4064, 68.9055, 73.0853, 77.5187, 81.9429, 87.2085, 91.874, 97.7778, 103.358, 109.257, 116.278, 122.914, 130.813, 137.811, 146.171, 155.037, 163.886, 174.417, 183.748, 195.556, 206.717, 218.514, 232.556, 245.829, 261.626, 275.622, 292.341, 310.075, 327.772, 348.834, 367.496, 391.111, 413.433, 437.029, 465.112, 491.658, 523.251, 551.244, 584.683, 620.15, 655.543, 697.668, 734.992, 782.222, 826.866, 874.058, 930.224, 983.315, 1046.5, 1102.49, 1169.37, 1240.3, 1311.09, 1395.34, 1469.98, 1564.44, 1653.73, 1748.12, 1860.45, 1966.63, 2093, 2204.98, 2338.73, 2480.6, 2622.17, 2790.67, 2939.97, 3128.89, 3307.46, 3496.23, 3720.9, 3933.26, 4186.01, 4409.95, 4677.46, 4961.2, 5244.35, 5581.35, 5879.94, 6257.78, 6614.93, 6992.46, 7441.79, 7866.52, 8372.02, 8819.9, 9354.92, 9922.39, 10488.7, 11162.7, 11759.9, 12515.6];


   /***
    ***
    *** General setup
    ***
    ***/

   // When true, print (low frequency) debug messages to console (prefixed with "[dbg]")
   //  (note) Global.Debug()
   // static boolean b_debug_lofreq = false;
   static boolean b_debug_lofreq = true;

   // When true, print (high frequency) debug messages to console (prefixed with "[trc]")
   //  (note) Global.Debug2()
   // static boolean b_debug_hifreq = false;
   static boolean b_debug_hifreq = true;

   // When true, print (realtime) debug messages to console (prefixed with "[>>>]")
   //  (note) Global.Debug3()
   static boolean b_debug_rt = false;

   // filename to load when "new project" button is clicked
   //  (note) when file does not exist, use builtin default project (warning: no mmc mappings)
   //  (note) the filename is relative to the project_rootpath
   static String newproject_template_filename = "project_template.flx";

   // When true, auto-load project (associated with Synergy sequencer project) at startup
   //  (note) same as pressing lalt-l / clicking "Load"
   static boolean b_autoload_project = true;

   // When true, save Eureka project when Synergy project is saved
   // false=Eureka project must be saved manually
   //  (note) even when disabled, the project name will still be updated
   static boolean b_remote_save = true;



   /***
    ***
    *** Tracks
    ***
    ***/

   // route new track output to track named "master", if it exists
   //  (note) if it does not, route to default (first two ASIO output channels)
   //  (note) this is also used for excluding the master output track when solo'ing other tracks
   static String track_autoroute_master_name = "master";

   // (additionally) route currently focused track output to track named "focus", if it exists
   //  (note) meant for analyzer plugins
   static String track_autoroute_focus_name = "focus";

   // TrackForm "Edit" button colorization
   static HashTable default_track_colors = #[
      "-"                     = 0,
      "Bass 1 (Blue)"         = #425074b6,
      "Bass 2 (Blue)"         = #42506bef,
      "Keys 1 (Green)"        = #3250b676,
      "Keys 2 (Green)"        = #3250cd00,
      "Pad 1 (Purple)"        = #42aa89c1,
      "Pad 2 (Purple)"        = #42ae769c,
      "Lead 1 (Olive)"        = #42a4e257,
      "Lead 2 (Olive)"        = #42ccde8a,
      "Drums 1 (Cyan)"        = #3a50cce2,
      "Drums 2 (Turquoise)"   = #32AFEEEE,
      "FX 1 (Lavender)"       = #22e58b61,
      "FX 2 (Lavender)"       = #28fc7056,
      "Audio 1 (Yellow)"      = #36eedda0,
      "Audio 2 (Yellow)"      = #36ffff50,
      "Master 1 (Cerise)"     = #57DE3163,
      "Master 2 (Cinnabar)"   = #57E44D2E,
      "Other 1 (Slate)"       = #A23e3d52,
      "Other 2 (Medium Gray)" = #32BEBEBE,
      "Other 3 (Light Gray)"  = #40D3D3D3,
      "Other 4 (Platinum)"    = #58E5E4E2,
      // // "Other 1 (Coral)"      = #32FF7F50,
      // // "Other 2 (Crayola)"    = #32EE204D,
      // // "Other 3 (Tomato)"     = #32FF6347,
      // // "Other 4 (Hibiscus)"   = #34B43757,
      // // "Other 5 (Raspberry)"  = #34D21F3C,
                                           ];

   // true=colorize tracks ("Edit" button + panel tint).
   static boolean b_colorize_tracks = true;
   // static boolean b_colorize_tracks = false;

   // LINEAR, LERP, SQRT, COS, POW
   // static int default_pan_law = Output.PAN_SQRT;  // max vol at center is ~0.7
   static int default_pan_law = Output.PAN_LINEAR;  // max vol at center is 1.0


   // Preferred MIDI device for "Create audio track" (TrackForm context menu) function
   //  (not to be confused with "AudioTracks" -- this is just a track with a sampler, used for freezing MIDI tracks
   //   in conjunction with Synergy's NT_AudioDialog)
   //  (note) 0=vst_a .. 25=vst_z
   static int preferred_audio_track_dev_idx = 7/*vst_h*/;

   // default audio-clip level (dB) (def=-18.0dB)
   //  (note) see also: Synergy STConfig.node_arranger_audio_clip_level
   static float default_audio_clip_level = -18.0;

   // number of track forms per row (0=auto)
   static int pgproject_num_tracks_per_row = 0;


   /***
    ***
    *** AudioTracks page (start)
    ***
    ***/

   // Number of seconds of silence (after active input) after which a new live_capture sample is created
   static float live_capture_silence_threshold_sec = 0.5f;

   // Silence thresholds
   static float live_capture_silence_threshold_start_lvl = 0.0015f;
   static float live_capture_silence_threshold_end_lvl   = 0.00075f;


   /***
    ***
    *** Track page
    ***
    ***/

   // Default number of visible cells (lane height)
   //  (note) the number of displayed cells is max(config.default_grid_h, track.max_used_grid_h)
   static int modgrid_default_grid_h = 11;

   // Initial state of "Preview" checkbox
   static boolean b_patch_preview = true;

   // true=always replace patch name with filename after saving
   // false=only replace when patch name is empty
   static boolean b_replace_patch_name_after_save = true;

   // true=auto-sync shadow-to-main and MPE/LPC mirror mods when VST editor window is closed
   //  (note) can be toggled via checkbox on Track page
   //  (note) manual sync can be triggered by pressing '`' (de:^) in a VST editor window
   static boolean b_mod_close_editor_autosync = false;

   // Param history size (when tracking last edited parameters)
   static int mod_param_history_size = 16;

   // true=auto-create patch variations ("save <datetime>") after manual save
   // false=don't auto-create patch variations
   static boolean b_autocreate_patch_variation_after_manual_patch_save = false;

   // true=iterate all mods when auto-assigning next "Ctl" RPN to last touched param (F12)
   //  (note) LPC lanes have their own set of ctls
   static boolean b_mod_rpnctl_iterate_all = true;

   // Maximum number of voices per sample pad (default=8)
   static int sample_pad_num_voices = 8;

   // Pad edit mode (true=edit sample when pad is already selected)
   static boolean b_sample_pad_edit_mode = true;

   // Default namespace prefix when moving/copying samples to new kit folder (default="dk")
   //  (note) e.g. when kit name is "808", the default kit namespace will be "dk:808"
   static String sample_pad_default_namespace = "dk";

   // Sample repository drumkit folder name
   //  (note) local pathname is "<sample_rootpath>/<sample_userpath>/<sample_pad_default_namespace>/<kitname>"
   //          e.g. "f:/samples/bsp/drumkit/2600/"
   static String sample_pad_drumkit_folder_name = "drumkit";

   // true= duplicate all pad samples, even when they reference the same sample
   //        (handy for further editing)
   // false= duplicate only the first sample occurence in a SamplePadKit (or just rename it, if it has no other refs)
   static boolean b_sample_pad_consolidate_autoduplicate_all_refs = true;

   // Sample Pad Kit replay variation select base note (default=C-3)
   //  (note) must not overlap with drum_layout notes (C-4..)
   //  (note) C-3=main, C#3=variation 2, D-3=variation 3, D#3=variation 4, .., G-3=variation 8
   static int sample_pad_variation_select_base_note = (3*12)/*C-3*/;

   // true=edit variation/pad follows MIDI input
   static boolean b_sample_pad_midi_follow = false;



   /***
    ***
    *** Sample page
    ***
    ***/

   // Default sample namespace names (up to 32. last 16 should be left for project-specific purposes)
   static StringArray default_sample_namespace_names = [
      "bass", "bell", "drone", "drums", "fx", "keys", "lead", "noise", "pad", "perc", "stab", "vocal", "drumkit"
                                                        ];

   // Place SampleView on top
   static boolean b_pgsample_sampleview_ontop = false;

   // Vertical split position between sample list and sample view
   static float pgsample_sample_verticalsplit = 0.7;

   // Vertical split position between samples and zones lists
   static float pgsample_smpzones_verticalsplit = 0.4;

   // Initial ADSR curve dialog geometry
   static Geometry4f pgsample_env_curve_geo_hidpi = [0,0,0,0];
   static Geometry4f pgsample_env_curve_geo_lodpi = [0,0,0,0];

   // Initial voice plugin array param dialog geometry
   static Geometry4f pgsample_voiceplugin_array_geo_hidpi = [0,0,0,0];
   static Geometry4f pgsample_voiceplugin_array_geo_lodpi = [0,0,0,0];

   // Initial Info dialog geometry
   static Geometry4f sample_info_textedit_geo_hidpi = [80, 80, 1500, 800];
   static Geometry4f sample_info_textedit_geo_lodpi = [40, 40, 700, 400];

   // Auto-find sample when typing characters (auto-focus find textfield and start search)
   static boolean b_autofind_sample = false;

   // Xfade after afterloop
   //  (note) can be toggled via zones table context menu
   static boolean b_autoloop_xfade = true;

   // true=auto-split region selection in waveform editor enables cycle mode (wheel up/down)
   // false=use mousewheel to zoom in and out
   static boolean b_pgsample_wave_region_auto_cycle_mode = false;

   // base octave for "re-init zones" synth/multisample types (default=c-3)
   static int pgsample_reinit_multi_base_note = 3*12;

   // base octave for "re-init zones" grid types (default=c-3)
   static int pgsample_reinit_grid_base_note = 3*12;

   // base octave for "re-init zones" drummap types (default=c-3)
   static int pgsample_reinit_drummap_base_note = 3*12;

   // true=prefer "names" zones table model
   // false=prefer "vel/mod range" zones table model
   static boolean b_pgsample_zone_names = false;

   // zone doubleclick mode
   //  0: do nothing (mouse hold begin/end can play sample)
   //  1: play sample (oneshot, i.e. start+immediately send note off)
   //  2: edit synth patch
   //  3: edit synth patch machine
   static int pgsample_zone_dblclick_mode = 3;

   // // // true=also show program nrs as letters (suitable for LETTER cel type in Synergy)
   // // static boolean b_pgsample_prg_letters = true;

   // Default Samples table 'Prg' column display mode
   //   PRG_DPY_PC_DEC, PRG_DPY_PC_LETTER, PRG_DPY_UNIQUE_ID, PRG_DPY_UNIQUE_ID_HEX
   //   (note) saved in project file
   static int pgsample_prg_dpy_mode = PageSample.PRG_DPY_UNIQUE_ID_HEX;

   //
   // Drum pad names (referenced by drum_pad_indicies_* arrays)
   //
   //  (note) used in DrumPadDialog (map zone(s) to pad(s))
   //  (note) see drum_layout.txt
   //  (note) also used for building path names when consolidating sample pad samples
   //
   static StringArray drum_pad_names = [
      // 0..15 ("A")
      "BD",   //  0, == BDSHRT
      "SD",   //  1, == SDSHRT
      "CP",   //  2, == CPSHRT
      "LT",   //  3, == LOW TOM
      "MT",   //  4, == MID TOM
      "HT",   //  5, == HIGH TOM
      "CH",   //  6, == HIHAT CLOSED
      "OH",   //  7, == HIHAT OPEN
      "BD2",  //  8, == BDLONG
      "SD2",  //  9, == SDLONG
      "CP2",  // 10, == CPLONG
      "MA",   // 11, == MARACAS1
      "CL",   // 12, == CLAVES1
      "CB",   // 13, == COWBELL1
      "RS",   // 14, == RIMSHOT1
      "CY",   // 15, == RC / Cymbal Ride

      // 16..31 ("B")
      "BT1",  // 16, == BASS TOM 1
      "ZAP1", // 17, == ZAP1
      "FX1",  // 18, == EFFECT 1
      "LC",   // 19, == C_OPEN
      "MC",   // 20, == C_MUFF
      "HC",   // 21, == C_SLAP
      "NOS",  // 22, == NOISE_SHORT
      "NOL",  // 23, == NOISE_LONG
      "BT2",  // 24, == BASS TOM 2
      "ZAP2", // 25, == ZAP2
      "FX2",  // 26, == EFFECT 2
      "MA2",  // 27, == MARACAS2
      "CL2",  // 28, == CLAVES2
      "CB2",  // 29, == COWBELL2
      "RS2",  // 30, == RIMSHOT2
      "CY2"   // 31, == CC / Cymbal Crash
                                                ];

   //
   // (0) Pad Layout 8x2 A (e.g. DSI Tempest)
   //
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  BD1  |  SD1  |  CP1  |  LT1  |  MT1  |  HT1  |  CH1  |  OH1  |
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  BD2  |  SD2  |  CP2  |  MA1  |  CL1  |  CB1  |  RS1  |  CY1  |
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   //  =
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  C-4  |  D-4  |  E-4  |  F-4  |  G-4  |  A-4  |  A#4  |  B-4  |
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  C-5  |  D-5  |  E-5  |  F-5  |  G-5  |  A-5  |  A#5  |  B-5  |
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   static IntArray drum_pad_indices_8x2_a = [
      0, 1,  2,  3,  4,  5,  6,  7,
      8, 9, 10, 11, 12, 13, 14, 15
                                             ];
   static IntArray drum_pad_notes_8x2_a = [
      (12*4)+0, (12*4)+2, (12*4)+4, (12*4)+5, (12*4)+7, (12*4)+9, (12*4)+10, (12*4)+11,
      (12*5)+0, (12*5)+2, (12*5)+4, (12*5)+5, (12*5)+7, (12*5)+9, (12*5)+10, (12*5)+11
                                     ];

   //
   // (0) Pad Layout 8x2 B (e.g. DSI Tempest)
   //
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  BT1  | ZAP1  |  FX1  | COPEN | CMUFF | CSLAP |NOISE_S|NOISE_L|
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  BT2  | ZAP2  |  FX2  |  MA2  |  CL2  |  CB2  |  RS2  |  CY2  |
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   //  =
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  C-6  |  D-6  |  E-6  |  F-6  |  G-6  |  A-6  |  A#6  |  B-6  |
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   // |  C-7  |  D-7  |  E-7  |  F-7  |  G-7  |  A-7  |  A#7  |  B-7  |
   // +-------+-------+-------+-------+-------+-------+-------+-------+
   static IntArray drum_pad_indices_8x2_b = [
      16+0, 16+1, 16+ 2, 16+ 3, 16+ 4, 16+ 5, 16+ 6, 16+ 7,
      16+8, 16+9, 16+10, 16+11, 16+12, 16+13, 16+14, 16+15
                                             ];
   static IntArray drum_pad_notes_8x2_b = [
      (12*6)+0, (12*6)+2, (12*6)+4, (12*6)+5, (12*6)+7, (12*6)+9, (12*6)+10, (12*6)+11,
      (12*7)+0, (12*7)+2, (12*7)+4, (12*7)+5, (12*7)+7, (12*7)+9, (12*7)+10, (12*7)+11
                                     ];

   //
   // (1) Pad Layout 4x4 A
   //
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // |   RS   |   CH   |   OH   |   CY   |   | A#5 | A#4 | B-4 | B-5 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // |   LT   |   MT   |   HT   |   CB   |   | F-4 | G-4 | A-4 | A-5 |
   // +--------+--------+--------+--------+ = +-----+-----+-----+-----+
   // | BDSHRT | SDSHRT | CPSHRT |   CL   |   | C-4 | D-4 | E-4 | G-5 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // | BDLONG | SDLONG | CPLONG |   MA   |   | C-5 | D-5 | E-5 | F-5 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   static IntArray drum_pad_indices_4x4_a = [
      14,  6,  7, 15,
       3,  4,  5, 13,
       0,  1,  2, 12,
       8,  9, 10, 11
                                             ];
   static IntArray drum_pad_notes_4x4_a = [
      (12*5)+10, (12*4)+10, (12*4)+11, (12*5)+11,
      (12*4)+5,  (12*4)+7,  (12*4)+9,  (12*5)+9,
      (12*4)+0,  (12*4)+2,  (12*4)+4,  (12*5)+7,
      (12*5)+0,  (12*5)+2,  (12*5)+4,  (12*5)+5
                                      ];

   //
   // (1) Pad Layout 4x4 B
   //
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // |  RS2   |NOISESRT|NOISELNG|  CY2   |   | A#7 | A#6 | B-6 | B-7 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // | C_OPEN | C_MUFF | C_SLAP |  CB2   |   | F-6 | G-6 | A-6 | A-7 |
   // +--------+--------+--------+--------+ = +-----+-----+-----+-----+
   // |  BT1   |  ZAP1  |  FX1   |  CL2   |   | C-6 | D-6 | E-6 | G-7 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // |  BT2   |  ZAP2  |  FX2   |  MA2   |   | C-7 | D-7 | E-7 | F-7 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   static IntArray drum_pad_indices_4x4_b = [
      30, 22, 23, 31,
      19, 20, 21, 29,
      16, 17, 18, 28,
      24, 25, 26, 27
                                             ];
   static IntArray drum_pad_notes_4x4_b = [
      (12*7)+10, (12*6)+10, (12*6)+11, (12*7)+11,
      (12*6)+5,  (12*6)+7,  (12*6)+9,  (12*7)+9,
      (12*6)+0,  (12*6)+2,  (12*6)+4,  (12*7)+7,
      (12*7)+0,  (12*7)+2,  (12*7)+4,  (12*7)+5
                                      ];


   //
   // (2) (simplified) Pad Layout 3x4 A (e.g. Akai MPK49)
   //
   // +--------+--------+--------+   +-----+-----+-----+
   // |   MA   |   CB   |   CY   |   | F-5 | A-5 | B-5 |
   // +--------+--------+--------+   +-----+-----+-----+
   // |   RS   |   CH   |   OH   |   | A#5 | A#4 | B-4 |
   // +--------+--------+--------+ = +-----+-----+-----+
   // |   LT   |   MT   |   HT   |   | F-4 | G-4 | A-4 |
   // +--------+--------+--------+   +-----+-----+-----+
   // | BDSHRT | SDSHRT | CPSHRT |   | C-4 | D-4 | E-4 |
   // +--------+--------+--------+   +-----+-----+-----+
   static IntArray drum_pad_indices_3x4_a = [
      11, 13, 15,
      14,  6,  7,
       3,  4,  5,
       0,  1,  2
                                           ];
   static IntArray drum_pad_notes_3x4_a = [
      (12*5)+5,  (12*5)+9,  (12*5)+11,
      (12*5)+10, (12*4)+10, (12*4)+11,
      (12*4)+5,  (12*4)+7,  (12*4)+9,
      (12*4)+0,  (12*4)+2,  (12*4)+4
                                      ];

   //
   // (2) (simplified) Pad Layout 3x4 B (e.g. Akai MPK49)
   //
   // +--------+--------+--------+   +-----+-----+-----+
   // |  FX1   |  FX2   |   CY2  |   | E-6 | E-7 | B-7 |
   // +--------+--------+--------+   +-----+-----+-----+
   // |  ZAP1  |NOISE_S |NOISE_L |   | D-6 | A#6 | B-6 |
   // +--------+--------+--------+ = +-----+-----+-----+
   // |   LC   |   MC   |   HC   |   | F-6 | G-6 | A-6 |
   // +--------+--------+--------+   +-----+-----+-----+
   // | BDLONG | SDLONG | CPLONG |   | C-5 | D-5 | E-5 |
   // +--------+--------+--------+   +-----+-----+-----+
   static IntArray drum_pad_indices_3x4_b = [
      16+2, 16+10, 16+15,
      16+1, 16+6,  16+7,
      16+3, 16+4,  16+5,
         8,    9,     10
                                           ];
   static IntArray drum_pad_notes_3x4_b = [
      (12*6)+4, (12*7)+4,  (12*7)+11,
      (12*6)+2, (12*6)+10, (12*6)+11,
      (12*6)+5, (12*6)+7,  (12*6)+9,
      (12*5)+0, (12*5)+2,  (12*5)+4
                                      ];


   //
   // (3) (simplified) Pad Layout 4x3  (e.g. Analog Rytm)
   //
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // |   CH   |   OH   |   CY   |   CB   |   | A#4 | B-4 | B-5 | A-5 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // |   BT   |   LT   |   MT   |   HT   | = | C-6 | F-4 | G-4 | A-4 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   // |   BD   |   SD   |   RS   |   CP   |   | C-4 | D-4 | A#5 | E-4 |
   // +--------+--------+--------+--------+   +-----+-----+-----+-----+
   static IntArray drum_pad_indices_4x3 = [
        6,  7, 15, 13,
       16,  3,  4,  5,
        0,  1, 14,  2
                                           ];
   static IntArray drum_pad_notes_4x3 = [
      (12*4)+10, (12*4)+11, (12*5)+11, (12*5)+9,
      (12*6)+0,  (12*4)+5,  (12*4)+7,  (12*4)+9,
      (12*4)+0,  (12*4)+2,  (12*5)+10, (12*4)+4
                                      ];



   //
   // (4) (simplified) Pad Layout 4x2 A  (e.g. Akai MPK Mini)
   //
   // +-------+-------+-------+-------+   +-----+-----+-----+-----+
   // |  BD1  |  SD1  |  CP1  |  CH   | = | C-4 | D-4 | E-4 | A#4 |
   // +-------+-------+-------+-------+   +-----+-----+-----+-----+
   // |  LT1  |  MT1  |  HT1  |  OH   |   | F-4 | G-4 | A-4 | B-4 |
   // +-------+-------+-------+-------+   +-----+-----+-----+-----+
   static IntArray drum_pad_indices_4x2_a = [
        0,  1,  2, 6,
        3,  4,  5, 7
                                           ];
   static IntArray drum_pad_notes_4x2_a = [
      (12*4)+0, (12*4)+2, (12*4)+4, (12*4)+10,
      (12*4)+5, (12*4)+7, (12*4)+9, (12*4)+11
                                      ];

   //
   // (4) (simplified) Pad Layout 4x2 B  (e.g. Akai MPK Mini)
   //
   // +-------+-------+-------+-------+   +-----+-----+-----+-----+
   // |  BD2  |  SD2  |  CP2  |  RS   |   | C-5 | D-5 | E-5 | A#5 |
   // +-------+-------+-------+-------+   +-----+-----+-----+-----+
   // |  MA1  |  CL1  |  CB1  |  CY   | = | F-5 | G-5 | A-5 | B-5 |
   // +-------+-------+-------+-------+   +-----+-----+-----+-----+
   static IntArray drum_pad_indices_4x2_b = [
       8,  9, 10, 14,
      11, 12, 13, 15
                                           ];
   static IntArray drum_pad_notes_4x2_b = [
      (12*5)+0, (12*5)+2, (12*5)+4, (12*5)+10,
      (12*5)+5, (12*5)+7, (12*5)+9, (12*5)+11
                                      ];

   // 0: 8x2
   // 1: 4x4
   // 2: 3x4
   // 3: 4x3
   // 4: 4x2
   static int default_drum_pad_layout = 0;

   // Initial sample browser dialog geometry
   static Geometry4f sample_browser_geo_hidpi = [0,0,0,0];
   static Geometry4f sample_browser_geo_lodpi = [0,0,0,0];

   // Sample tag / keyword categories
   //  (note) the categories are used to group the tags and present them as different multiple-selection tables in the UI
   //  (note) feel free to rename, add / move tags as required
   //  (note) the category names and tag lists are overridden in 'eureka_config_local.tks'
   static StringArray sample_tags_cat_names = [ "Instrument", "Perc", "Class", "Descriptive", "Other" ];

   // Sample tags / keywords
   //  (note) the categories are used to group the tags and present them as different multiple-selection tables in the UI
   //  (note) tags must be lowercase
   //  (note) the tag lists will be sorted (case insensitive)
   //  (note) feel free to rename, add / move tags as required
   //  (note) the category names and tag lists are overridden in 'eureka_config_local.tks'
   static StringArray *[] sample_tags = [

      // category 1 (instr type)
      [ "bass", "brass", "lead", "keys", "pad", "voice", "vocal", "chord", "fx", "bell", "mallet", /*"chime", */"perc", "stab", "drone", "chip", "field", "atmo",
        "string", "piano", "guitar", "wind", "flute", "organ",
        ],

      // category 2 (perc type)
      //   (note)  bd:bassdrum/kick sd:snaredrum rs:rimshot cp:clap hh:hihat cy:cymbal cb:cowbell co:conga bt:bass tom cl:claves
      //           ma:maracas sh:shaker ns:noise ca:cabasa gu:guiro br:brush qu:quijada tb:tambourine
      //           // tr:triangle
      [ "bd", "sd", "rs", "cp", "hh", "cy", "cb", "tom", "co", "bt", "cl", "zap",
        // "lt", "mt", "ht", "lc", "mc", "hc",
        "ma", "sh", "ns", "ca", "gu", "br", "qu", "tb",
        // "tr",
        ],

      // category 3 (sound/synth class)
      [ "mono", "glide", "poly", "stereo", "multi", "arp", "seq", "loop", "ambient", "atonal", "noise", "lofi", "transient", "aon", "cycle", "precalc", "stfx", "fmstack", "fm", "wt", "additive", "analog", "hw", "ext", "modular", "cv", "test", "wip", "util", "liverec", "morph", "kit",
        // "sample", "digital", "rm", "am", "pwm", "fb", "sync",
        ],

      // category 4 (descriptive attributes)
      //  (note) these are not so much meant for filtering but rather for informative purposes
      [ "dark", "bright", "lucid", "buzzy", "raw", "grimy", "dirty", "gritty", "clean", "cold", "warm", "detuned", "dissonant", "driven", "distorted", "harsh", "piercing", "guttural", "raspy", "mellow", "hard", "soft", "dry", "wet", "smooth", "dull", "sharp", "rich", "fat", "thin", "clicky", "round", "snappy", "punchy", "slapped", "hollow", "brittle", "solid", "acoustic", "electric", "neon", "evolving", "swirling", "undulating", "throbbing", "vibrating", "reese", "glitter", "metal", "plastic", "glassy", "airy", "woody", "wooly", "ghostly", "haunted", "ethereal", "stellar", "worn", "unstable", "rattling", "industrial", "short", "long", "wide", "distant", "experimental", "classic", "vintage", "ancient", "old", "modern", "simple", "complex", "deep", "wobble", "sweep", "acid", "bouncy", "plucked", "damped", "bowed", "bent", "echo", "delay", "chorus", "flanger", "phaser", "resonant", "reverb", "reverse",
        // "majestic", "mystic",
        // "boomy", "muddy", "cheesy",
        ],

      // category 5 (manufacturer)
      // [ "korg", "yamaha", "roland", "moog", "oberheim", "arp", "sequential", "dsi", "vermona", "novation", "elektron", "waldorf", "jomox", "mfb", ],

      // additional tags that don't fit any of the four main categories
      //  (note) patches can have arbitrary tags, this is just a baseline list that's added to the unique list of tags found during sample scan
      //  (note) tags that (also) appear in any of that cat_1..4 lists will be listed under the respective category
      //  (note) must be the last category ('other')
      [ "c64", "nes", "amiga", "atari",
        "debug", "test", "demo", "game",
        "nature", "world", "animal", "water", "sea", "fire", "swamp", "forest", "desert", "machine", "earthy", "human",
        ],
                                         ];

   // When true, (initially) show all tags
   // When false, (initially) only show tags that are used in any match
   //  (note) can be changed at runtime in the SampleTagSelectionDialog
   static boolean b_sample_tags_show_all = false;

   // Tag colorization in sample browser
   static FloatArray sample_tags_frequency_percentages = [
      0,      // 0
      0.05,   // 1
      1.00,   // 2
      2.50,   // 3
      5.00,   // 4
      10.00,  // 5
      20.00,  // 6
                                                        ];
   static IntArray sample_tags_frequency_tints = [
      #50000000,  // 0
      #20000000,  // 1
      #30ffffff,  // 2
      #8fffffff,  // 3
      #a0ffffaa,  // 4
      #b0ffbb77,  // 5
      #e0ff8f66,  // 6
                                                  ];

   // Quick-edit freq/volume/pan/aux ADSR envelope presets
   //  format: "<name>", "<a_curve_preset>:<a_rate>", "<s_curve_preset>:<s_rate>:<s_loop_flag>", "<r_curve_preset>:<r_rate>"
   //     <s_loop_flag>:  'hold' / 'loop' / 'inf'   (inf=infinite loop (freq/pan/aux envelopes))
   //  (note) curve presets are read from curve_rootpath (.curve suffix is auto-appended to preset name)

   static StringArray page_sample_quickedit_env_presets_freq =
      [
         // name             // attack               // decay/sustain               // release
         "Reset",            "sus zero:200",         "sus zero:200:hold",           "sus zero:200",
         "Atk bend",         "rls exp 3:300",        "sus zero:200:hold",           "sus zero:200",
         "Gated",            "sus const:999",        "sus const:999:hold",          "rls exp:999",
         "Chord",            "atk chord 1:400",      "dcy chord 1:30:hold",         "rls chord 1:30",
         "Percussive",       "adsr_2 min sus:400",   "sus const:100:hold",          "rls exp:120",
         "Lin atk+rls",      "ramp up:99",           "sus const:999:hold",          "ramp down:99",
         "ADSR logexp",      "ramp up log:100",      "ramp down 75 e3:100:hold",    "ramp down e3:100",
         "ADSR linear",      "ramp up:100",          "ramp down 50:100:hold",       "ramp down:100",
         "ADSR smooth",      "atk smooth 3:999",     "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR click",       "atk smooth 4b:500",    "dcy 10:300:hold",             "rls exp 3:100",
         "ADSR pluck",       "atk+dcy fast 1:700",   "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR pad",         "atk smooth 2:10",      "dcy 50:15:hold",              "rls exp 2:10",
         "Echo",             "echo 1:227",           "echo 2:127:loop",             "echo 3:33",
         "Vibrato",          "sus zero:999",         "triangle phase 0:250:inf",    "sus zero:200",
         "Log Fade",         "atk log 1:300",        "sus const:100:hold",          "rls log 2:300",
       ];

   static StringArray page_sample_quickedit_env_presets_vol =
      [
         // name             // attack               // decay/sustain               // release
         "Gated",            "sus const:999",        "sus const:999:hold",          "rls exp:999",
         "Chord",            "atk chord 1:400",      "dcy chord 1:30:hold",         "rls chord 1:30",
         "Percussive",       "adsr_2 min sus:400",   "sus const:100:hold",          "rls exp:120",
         "Lin atk+rls",      "ramp up:99",           "sus const:999:hold",          "ramp down:99",
         "ADSR logexp",      "ramp up log:100",      "ramp down 75 e3:100:hold",    "ramp down e3:100",
         "ADSR linear",      "ramp up:100",          "ramp down 50:100:hold",       "ramp down:100",
         "ADSR smooth",      "atk smooth 3:999",     "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR click",       "atk smooth 4b:500",    "dcy 10:300:hold",             "rls exp 3:100",
         "ADSR pluck",       "atk+dcy fast 1:700",   "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR pad",         "atk smooth 2:10",      "dcy 50:15:hold",              "rls exp 2:10",
         "Echo",             "echo 1:227",           "echo 2:127:loop",             "echo 3:33",
         "Tremolo",          "dcy 50:200",           "triangle phase 90:100:loop",  "rls exp 3:10",
         "Audio Clip",       "atk log 1:300",        "sus const:100:hold",          "rls log 2:300",
        ];

   static StringArray page_sample_quickedit_env_presets_pan =
      [
         // name             // attack               // decay/sustain               // release
         "Reset",            "sus zero:200",         "sus zero:200:hold",           "sus zero:200",
         "Gated",            "sus const:999",        "sus const:999:hold",          "rls exp:999",
         "Chord",            "atk chord 1:400",      "dcy chord 1:30:hold",         "rls chord 1:30",
         "Percussive",       "adsr_2 min sus:400",   "sus const:100:hold",          "rls exp:120",
         "Lin atk+rls",      "ramp up:99",           "sus const:999:loop",          "ramp down:99",
         "ADSR logexp",      "ramp up log:100",      "ramp down 75 e3:100:hold",    "ramp down e3:100",
         "ADSR linear",      "ramp up:100",          "ramp down 50:100:hold",       "ramp down:100",
         "ADSR smooth",      "atk smooth 3:999",     "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR click",       "atk smooth 4b:500",    "dcy 10:300:hold",             "rls exp 3:100",
         "ADSR pluck",       "atk+dcy fast 1:700",   "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR pad",         "atk smooth 2:10",      "dcy 50:15:hold",              "rls exp 2:10",
         "Echo",             "echo 1:227",           "echo 2:127:loop",             "echo 3:33",
         "LFO",              "ramp up bip:200",      "sineish 3b:100:inf",          "ramp down bip:70",
         "Log Fade",         "atk log 1:300",        "sus const:100:hold",          "rls log 2:300",
       ];

   static StringArray page_sample_quickedit_env_presets_aux =
      [
         // name             // attack               // decay/sustain               // release
         "Reset",            "sus zero:200",         "sus zero:200:hold",           "sus zero:200",
         "Gated",            "sus const:999",        "sus const:999:hold",          "rls exp:999",
         "Chord",            "atk chord 1:400",      "dcy chord 1:30:hold",         "rls chord 1:30",
         "Percussive",       "adsr_2 min sus:400",   "sus const:100:hold",          "rls exp:120",
         "Lin atk+rls",      "ramp up:100",          "sus const:999:loop",          "ramp down:99",
         "ADSR logexp",      "ramp up log:100",      "ramp down 75 e3:100:hold",    "ramp down e3:100",
         "ADSR linear",      "ramp up:100",          "ramp down 50:100:hold",       "ramp down:100",
         "ADSR smooth",      "atk smooth 3:999",     "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR click",       "atk smooth 4b:500",    "dcy 10:300:hold",             "rls exp 3:100",
         "ADSR pluck",       "atk+dcy fast 1:700",   "dcy 25:100:hold",             "rls exp 3:100",
         "ADSR pad",         "atk smooth 2:10",      "dcy 50:15:hold",              "rls exp 2:10",
         "Echo",             "echo 1:227",           "echo 2:127:loop",             "echo 3:33",
         "LFO",              "dcy 50:200",           "triangle phase 90:100:loop",  "rls exp 3:10",
         "Log Fade",         "atk log 1:300",        "sus const:100:hold",          "rls log 2:300",
       ];

   // Additive wavetable export resampled cycle length in frames [def=1024]
   //  (note) for cyclelen=1024 and sample_rate=48kHz, set sample zone semitone transpose to +6
   //  (note) set to 0 to use additive cycle len
   static int page_sample_additive_wt_export_cycle_len = 1024;
   // static int page_sample_additive_wt_export_cycle_len = 128;
   // static int page_sample_additive_wt_export_cycle_len = round(2*audio_mixrate / Sample.BASE_FREQ_MIDDLE_C); // 366.93039789 => 367 @48kHz


   /***
    ***
    *** Resynthesis dialog
    ***
    ***/

   // Auto-recalc timeout (in milliseconds)
   static int resynth_autocalc_delay = 500;

   // true=use multithreading (don't turn this off, the other codepath is outdated)
   static boolean b_resynth_multithreading = true;

   // true=memcpy to dst sample instead of calc'ing in place (avoid audio glitches)
   static boolean b_resynth_doublebuffer = true;

   // Size (#frames) of ringbuffer (for capturing track output) (def=20sec)
   static int resynth_ringbuf_num_frames = (48000/*audio_mixrate*/ * 20);




   /***
    ***
    *** Scope dialog
    ***
    ***/

   // Update interval (in milliseconds)
   static int scope_update_interval = 50;

   // Size (#frames) of ringbuffer (def=5sec)
   static int scope_ringbuf_num_frames = (48000/*audio_mixrate*/ * 5);

   // Initial geometry
   static Geometry4f scope_geo_hidpi = [2, 348, 986, 103];
   static Geometry4f scope_geo_lodpi = [2, 348, 986, 103];

   // Auto-show/hide when Sample page is entered/exited
   static boolean b_scope_pgsample_autoshowhide = true;




   /***
    ***
    *** Voice plugins
    ***
    ***/

   // true=scan voice plugins
   // false=skip
   // static boolean b_voice_plugins = true;
   static boolean b_voice_plugins = !b_minimal_startup;

   // global voice plugin directory
   static String global_voice_plugin_path = "$(PROGRAM_DIR)/voice_plugins/";

   // voice plugin cache directory (Cycle)
   //  - patch-specific, auto-generated plugins (.cpp + .dll/.so/.dylib)
   //  - safe to purge (plugins will be recompiled as required when project or single sample patches are loaded)
   static String cached_voice_plugin_path = cache_rootpath+"/voiceplugin_cache/";



   /***
    ***
    *** FM Stack voice plugin
    ***
    ***/

   // four-quadrant morph preset filters
   //  (note) will be sorted at runtime
   //  (note) saved to eureka_config_local.tks (when closing prefs dialog)
   static StringArray fmstack_morph_preset_filters = [
      "",
      "bd",
      "bd;bass",
      "bd;xylo",
      "bell",
      "bell;xylo",
      "sd",
      "sd;bd",
      "sd;bd;xylo",
      "hh",
      "cp",
      "bass",
      "string",
      "pad",
      "fx",
      "xylo",
      "organ",
      "drone",
      "perc",
      "tom",
      "key",
      "lead",
      "noise",
      "click",
                                                      ];




   /***
    ***
    *** Sample/VoicePlugin Synth (Cycle)
    ***
    ***/

   // Initial Dialog geometry
   static Geometry4f cycle_geo_hidpi = [0, 0, 858, 350];
   static Geometry4f cycle_geo_lodpi = [0, 0, 858, 350];

   // Internal machine editor geometry (new windows)
   //  (note) [x, y, w, h]
   static Geometry4f cycle_machine_geo_hidpi = [311, 0, 666, 348];
   static Geometry4f cycle_machine_geo_lodpi = [311, 0, 666, 348];

   // Machine editor vertical split point
   static float cycle_machine_split = 0.2;

   // Initial curve dialog geometry
   static Geometry4f cycle_curve_geo_hidpi = [0,0,0,0];
   static Geometry4f cycle_curve_geo_lodpi = [0,0,0,0];

   // Machine preset path
   static String cycle_machine_preset_path = rootpath+"/machines/";

   // Machine include path ("inc myinclude")
   static String cycle_machine_inc_path = rootpath+"/machine_inc/";

   // Select patch by current zone
   static boolean b_cycle_patch_by_zone = true;

   // Recalc interval
   //  - e.g. after/while editing bezier curve
   static int cycle_recalc_interval_delayed = 500;
   static int cycle_recalc_interval_smooth  =  50;

   // true=auto-recalc waveform (zones) when sample is loaded
   static boolean b_cycle_recalc_on_load = true;

   // true=compile patch plugins when sample is loaded (if not cached)
   static boolean b_patch_plugin_compile_on_load = true;

   // true=debug print cycle module tree
   //  (note) can be toggled at runtime via PageSample "cycle_tree_debug" page command
   static boolean b_cycle_tree_debug = false;

   // STFX plugin export: compiler command line
   //  (note) built-in path vars will be substituted (PROGRAM_DIR, ROOT, PROJECT_DIR, TKS_DIR, TMP)
   //  (note) $(SRC) refers to the auto-generated C/C++ source pathname
   //  (note) $(DST) refers to the shared object / DLL / dylib target pathname
   //
   //  (note) on Windows, please install MSYS2 <https://www.msys2.org/>, then (in MSYS2 bash console) type:
   //          $ pacman -Syu
   //          $ pacman -S base-devel gcc
   //
   //  (note) on MacOS, please install brew <https://brew.sh/> (which will also install the XCode commandline tools):
   //          $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
   //          (see https://mac.install.guide/commandlinetools/3.html)
   //
   // extra include directories for 'xinc' / user-defined native code modules:
   static String stfx_xinc = "-I$(PROGRAM_DIR)/include/xinc";
   static String stfx_cc =
      Utils.IsMacOS() ? "g++ -std=c++11 -O3 -dynamiclib -Wall -Wno-unused-function -Wno-unused-variable -I$(PROGRAM_DIR)/include "+stfx_xinc+" $(SRC) -o $(DST)"
      : "g++ -O3 -shared -Wall -Wno-unused-function -Wno-unused-variable -Wl,-soname,$(DST) $(SRC) -I$(PROGRAM_DIR)/include "+stfx_xinc+" -o $(DST)"; // Windows, Linux
   // static String stfx_cc_msys2_dir = "C:\\msys64\\mingw64\\bin";  // path to MSYS2 GCC binary
   static String stfx_cc_msys2_dir = "C:\\msys64\\ucrt64\\bin";  // path to MSYS2 GCC binary

   // true=allow native_code compilation (+reload) option
   // false=generate sources but do not compile/load them
   static boolean b_stfx_cc = true;

   // (note) called in eureka_config_local.tks
   static SetRootPath(String _path) {
      rootpath = _path;
      temp                           = rootpath + "/tmp/";
      project_rootpath               = rootpath + "/projects/";
      patch_rootpath                 = rootpath + "/patches/";
      cache_rootpath                 = rootpath + "/cache/";
      cached_voice_plugin_path       = cache_rootpath + "/voiceplugin_cache/";
      sample_filters_path            = rootpath + "/sample_filters.ini";
      stfxpatch_rootpath             = rootpath + "/stfx_patches/";
      vstpatch_rootpath              = rootpath + "/vst_patches/";
      voicepluginchainpatch_rootpath = rootpath + "/voicepluginchain_patches/";
      audiotrack_wav_rootpath        = project_rootpath+"/$(PROJECT_NAME)/tracks/";
      exported_samples_wav_rootpath  = project_rootpath+"/$(PROJECT_NAME)/exported_samples/";
      project_samples_wav_rootpath   = project_rootpath+"/$(PROJECT_NAME)/samples/";
      curve_rootpath                 = rootpath + "/curves/";
      resynth_rootpath               = rootpath + "/resynth/";
      cycle_machine_preset_path      = rootpath + "/machines/";
   }

   protected static ApplySizePreset() {
      boolean bForceSizeAndZoom = 0;  // debug
      if(0)
      {
         // debug
         bForceSizeAndZoom = 1;
         ui_size_preset = 1;
      }
      switch(ui_size_preset)
      {
         case 0:
            // LoDPI Window (>= 1024x768(XGA) screens)
            //  (note)     minimum window_sx/y: 960x600
            //  (note) recommended window_sx/y: 960x600
            //  (note)    recommended zoom_x/y: 1
            if(bForceSizeAndZoom)
            {
               window_sx = 960;
               window_sy = 600;
               zoom_x = 1;
               zoom_y = 1;
            }
            // // cursor_zoom = 1;
            UI.SetTextViewFontScaling(1.0);
            break;

         case 1:
            // Upscaled LoDPI Window (>= 1920x1200(WUXGA) screens)
            //  (note)     minimum window_sx/y: 960x600
            //  (note) recommended window_sx/y: 960x600
            //  (note)    recommended zoom_x/y: 2
            if(bForceSizeAndZoom)
            {
               window_sx = 960;
               window_sy = 600;
               zoom_x = 2;
               zoom_y = 2;
            }
            // // cursor_zoom = 2;
            UI.SetTextViewFontScaling(1.0);
            break;

         case 2:
            // "4k" HiDPI Window (font+icon-scaled) (>= 2560x1600(WQXGA) screens)
            //  (note)     minimum window_sx/y: 1800x1200
            //  (note) recommended window_sx/y: 1873x1600
            //  (note)    recommended zoom_x/y: 1
            if(bForceSizeAndZoom)
            {
               window_sx = 1873;   // (note) Synergy+Eureka windows fit side-by-side on 4k screen (with dock on the right on macOS)
               window_sy = 1600;
               zoom_x = 1;
               zoom_y = 1;
            }
            // // cursor_zoom = 1;
            UI.SetFontScaling(2.0);
            UI.SetTextViewFontScaling(1.0);
            UI.SetEnableTextViewHiDPIFont(true);
            UI.SetEnableFontSmoothing(true);  // the UI looks better with smoothing in both 2k "big" mode and actual 4k. feel free to edit.
            UI.SetIconScaling(2.0);
            UI.SetPadScaling(2.0);
            UI.SetWindowBorderScaling(2.0);
            UI.SetTableMinimumColumnWidthScaling(0.68);  // PageTrack ModMatrix table
            pgproject_num_tracks_per_row = 4;
            break;
      }
   }

   static InitPre() {
      // called in main() (before InitUI())

      ApplySizePreset();
   }

   static InitPost() {
      // called in main() (after Global.Init())
   }
}

STConfig.SetAffinity();
