// ----
// ---- 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
// ----
// ----
// ----
module MSTConfig;

use namespace ui;
use namespace st2;


// (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.b_vsync_keyrepeat = false;    // enable millisec/timer based key repeat
// UI.b_vsync_keyrepeat = 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 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;
// UI.SetFontScaling(1.7);



class STConfig {

   // 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
    ***
    ***/

   // Temp dir
   static String temp = "c:/temp/";
   // static String temp = "/tmp/";

   // Root path
   static String rootpath = "$(PROGRAM_DIR)/eureka_data/";

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

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

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

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

   // Voice plugin patches (PageSample "FX" tab)
   static String voicepluginpatch_rootpath = rootpath + "/voiceplugin_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 = "$(PROGRAM_DIR)/mProjects/eureka/$(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 = "$(PROGRAM_DIR)/mProjects/eureka/$(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 = "$(PROGRAM_DIR)/mProjects/eureka/$(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 = "$(PROGRAM_DIR)/samples";

   // Preferred (relative) directory within sample_rootpath for user samples
   static String sample_userpath = "bsp";

   // 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;

   // 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 the directory that contains "sox" / "sox.exe"
   // static String audiotool_sox_pathname = "c:\Program Files (x86)\sox-14-4-2";
   // static String audiotool_sox_pathname = "$(PROGRAM_DIR)/opt/sox-14-4-2";
   static String audiotool_sox_pathname = "$(TKS_DIR)/opt/sox-14-4-2";


   /***
    ***
    *** Multi-core configuration
    ***
    ***  (note) in the default cfg,
    ***          - the first 4 cores are assigned to "other/main DAW" (e.g. Propellerhead Reason)
    ***             (use lshift-lctrl-lalt numpad -/+ to dec/inc number of used cores in Reason),
    ***          - core 5 is assigned to the Eureka+Flux UIs (low CPU usage)
    ***          - core 6 is assigned to the Eureka replay thread (low CPU usage)
    ***          - cores 7..18 are assigned to the Eureka track threads (possibly high CPU usage)
    ***
    ***/

   // Process affinity mask. Each set bit represents one process core.
   //  (note) in order to reduce/avoid underruns at low output buffer sizes, make sure
   //          that no other (CPU/GPU intensive) process is running on the same cores
   //  (note) the default setting excludes the first 5 CPU cores
   //  (note) setting this to 0 skips the SetProcessAffinityMask() call
   //  (note) setting the affinity mask to != 0 may result in underruns when certain plugin UIs are shown
   // static int process_affinity_mask = ~((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4));
   static int process_affinity_mask = ~((1<<16) | (1<<17) | (1<<1) | (1<<2));

   // CPU core idx for UI thread. -1 = use OS default.
   static int ui_thread_core = (1-1);

   // CPU core affinity mask for replay thread (light weight but needs high prio)
   static int replay_thread_core = (4-1);

   // First CPU core used for track rendering
   static int track_thread_first_core = (5-1);

   // Number of CPU cores used for track rendering
   static int track_thread_num_cores = 11; // => 5..16

   // Force single-thread track rendering
   // static boolean b_force_single_thread = false;
   static boolean b_force_single_thread = true;



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

   // (note) use the SDL_VIDEO_WINDOW_POS environment var. to position the desktop window
   //         e.g.: export SDL_VIDEO_WINDOW_POS="30,40"

   // (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;

   // If 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.0;
   static float zoom_y = 1.0;

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

   // Window height (pixels)
   //  (note) at least 600 pixels
   // static int window_sy = 480;
   static int window_sy = 600;

   // 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;

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

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

   // If 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;

   static float pg_waveforms_verticalsplit = 0.6;

   // 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_START
   //
   static int start_page = RootForm.PAGE_START;

   // false=use "light" LookAndFeel
   // true=use "dark" LookAndFeel
   static boolean b_lnf_dark = 1;



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

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

   // VST searchpath(s) (absolute path names)
   //  (note) directories will be scanned recursively
   static StringArray vst_paths = [
      "$(PROGRAM_DIR)/vst2_64bit",
      "c:/Program Files/Steinberg/VstPlugins",
      "c:/Program Files/VstPlugins",
      //,"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= *ALWAYS* load each VST plugin DLL at startup and gather name/vendor/id/version information (mainly for debugging)
   // false= only scan at first startup, then use "vstcache.dat" afterwards
   // (note) please delete the cache file to force a re-scan (usually NOT needed if vst_auto_deepscan is enabled)
   static boolean vst_always_deepscan = false;

   // true= automatically perform deep scan if the number of DLLs differs from the number of cache entries
   //  (i.e. when you've added/removed a VST plugin)
   static boolean vst_auto_deepscan = true;

   // certain plugins will crash in their "mainProc" during deep scan when loaded in this host
   //  the pathname of each DLL found is matched against the entries in this list and the plugin will be excluded
   //  from deep scan and not be available for instantiation
   //  (note) as of October 6th 2010, 362 plugins have been tested and there were issues with following ones:
   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
                                                 "Stage-73 V",                // unstable (+goes quiet after a short while)
                                                 "Farfisa V",                 // causes instabilities
                                                 "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)
                                                 ];



   //  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)
   //
   //static int vst_deepscan_num_threads = 18;
   static int vst_deepscan_num_threads = 0;

   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;
   //static boolean b_vst_deepscan_incremental = false;


   // plugins that previously required host-side workaround:
   //   -              TAL Reverb II+III : - requires messageloop workaround or effEditOpen and effEditClose will freeze entire app

   // previously problematic plugins (resp. plugins this host might not handle properly):
   //   - Modartt Pianoteq 4 STAGE (demo): - requires messageloop workaround before effEditOpen or the entire app will freeze
   //                                      - plugin crashes when e.g. "Sound Recording" or preset ComboBox is closed (dereferenced NULL pointer)
   //                                         - workaround: select an option / don't click outside of the options area
   //                                      - plugin (sometimes) crashes when [x] (close window) is clicked after the editor window is openened the first time
   //                                         ("Pianoteq Trial 4.5.1/20130416 fatal error: Internal Error [plugin\xui.cc:1971]. Please report this bug to http://www.pianoteq.com/support")
   //                                          - workaround: close the info dialog by clicking outside of it instead of the [x] button
   //                                      - minor redraw issues (e.g. in Options->Perf)
   //                                      --> the plugin only seems to work correctly when the VST window is created in the main thread
   //                                           (STē creates plugin windows and message loops in dedicated threads)
   //                                        --> fixed as of 13May2013 (VST windows are now created in main thread and use main message loop)

   // true=run VST UI in dedicated thread by default (works with most VSTs)
   // false=run VST UI in main thread (more compatible, e.g. Pianoteq)
   static boolean b_vst_uithread_default = false;

   // always use a dedicated UI thread for the following plugins:
   static StringArray vst_uithread_whitelist = [
                                                 ];

   // never use a dedicated UI thread for the following plugins:
   //  (note) OBSOLETE, REMOVE
   static StringArray vst_uithread_blacklist = [ "Pianoteq"
                                                 ];


   // true=use multi-threaded post-load initialization (VST 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;

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

                                            ];
      // "VeeSeeVST",


   // 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 (!)
                                                    ];

   // true=auto-hide plugin editor windows (show one editor at a time)
   // false=allow multiple VST 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;

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

   // 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",
                                            ];



   /***
    ***
    *** 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;

   // 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)
   static String sysex_in_devicename = "remote_audio";

   // Device used for outgoing SysEx messages (sequencer<>audio process link)
   static String sysex_out_devicename = "remote_seq";

   // 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;

   // Comma separated list of MIDI in device names
   //  (note) exactly 8 entries. will be displayed as "A", "B", "C", "D", "E", "F", "G", "H".
   //  (note) projects save device idx (A,B,C,D,.. => 0..7), not the device name
   //  (note) see device list in the console window
   static StringArray midi_in_devicenames = ["vst_a",
                                             "vst_b",
                                             "vst_c",
                                             "vst_d",
                                             "vst_e",
                                             "vst_f",
                                             "vst_g",
                                             "vst_h"
                                             ];

   // 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 (via SDL or PortAudio / ASIO)
   //
   //  (note) [28Jan2018] apparently some plugins crash or hang when this is set to false
   //                      (even in databridge mode!) (e.g. Nave, Diva) (please enable SDL audio in these cases)
   //
   static boolean b_enable_audio = true;
   // static boolean b_enable_audio = false;

   // true=use SDL audio
   //  (note) SDL audio usually has a very high latency (>=1024 samples) (does not affect databridge mode)
   //
   // false=use PortAudio (ASIO low latency / multichannel support)
   //  (note) [28Jan2018] some multi-client ASIO drivers (e.g. Focusrite) can cause system instabilities
   //          when there actually are multiple ASIO clients (seen on Win10 64-bit)
   // maybe=use PortAudio/ASIO only when databridge is disabled
   //
   // static boolean b_audio_sdl = true;
   // static boolean b_audio_sdl = false;
   static boolean b_audio_sdl = maybe;

   // SDL audio buffersize (#samples)
   //  (note) must be a multiple of 64
   //  (note) when PortAudio/ASIO output is enabled, this is ignored and the current ASIO buffer size is used
   //static int audio_sdl_bufsize = 4096;
   static int audio_sdl_bufsize = 1024;

   // Portaudio/SDL audio mixrate
   // static int     audio_mixrate = 96000;
   // static int     audio_mixrate = 48000;
   static int audio_mixrate = 44100;

   // 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 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";

   // PortAudio / ASIO number of input channels
   //  (note) the maximum number of supported channels is 32
   //          (see Audio.STREAM_MAX_INPUT_CHANNELS and Mod.Mod.MAX_IO_BUFFERS)
   //  (note) will be clipped to the number of actually available inputs
   //  (note) in VST 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 and Mod.Mod.MAX_IO_BUFFERS)
   //  (note) in VST 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        Stereo
   // //    "Aux L",       "Aux",       //  1, 1+2
   // //    "Aux R",       "",          //  2, 2+3
   // //    "Peak L",      "Peak",      //  3, 3+4
   // //    "Peak R",      "",          //  4, 4+5
   // //    "P12 L",       "P12",       //  5, 5+6
   // //    "P12 R",       "",          //  6, 6+7
   // //    "MKS-50 L",    "MKS-50",    //  7, 7+8
   // //    "MKS-50 R",    "",          //  8, 8+9
   // //    "P4MR",        "P4MR / DD", //  9, 9+10
   // //    "DD",          "",          // 10, 10+11
   // //    "M1K",         "",          // 11, 11+12
   // //    "Sub37",       "",          // 12, 12+13
   // //    "Loopback L",  "Loopback",  // 13, 13+14
   // //    "Loopback R",  "",          // 14, 14+15
   // //    "AD Drums L",  "AD Drums",  // 15, 15+16
   // //    "AD Drums R",  "",          // 16, 16+17
   // //    "AD Dm.Aux1",  "AD Dm Aux", // 17, 17+18
   // //    "AD Dm Aux2",  "",          // 18, 18+19
   // //    "AD 5 FS1R L", "AD FS1R",   // 19, 19+20
   // //    "AD 6 FS1R R", "",          // 20, 20+21
   // //    "AD Mixer L",  "AD 7/8 Mix",// 21, 21+22
   // //    "AD Mixer R",  "",          // 22, 22+23
   // //    "",            "",          // 23, 23+24
   // //    "",            "",          // 24, 24+25
   // //    "",            "",          // 25, 25+26
   // //    "",            "",          // 26, 26+27
   // //    "",            "",          // 27, 27+28
   // //    "",            "",          // 28, 28+29
   // //    "",            "",          // 29, 29+30
   // //    "",            "",          // 30, 30+31
   // //    "",            "",          // 31, 31+32
   // //    "",            "",          // 32, -
   // //                                        ];

   static StringArray audio_in_aliases = [
      // Mono        Stereo
      "Mix2 L",      "Mix2",      //  1, 1+2
      "Mix2 R",      "",          //  2, 2+3
      "Peak L",      "Peak",      //  3, 3+4
      "Peak R",      "",          //  4, 4+5
      "P12 L",       "P12",       //  5, 5+6
      "P12 R",       "",          //  6, 6+7
      "MKS-50 L",    "MKS-50",    //  7, 7+8
      "MKS-50 R",    "",          //  8, 8+9
      "P4MR",        "P4MR / DD", //  9, 9+10
      "DD",          "",          // 10, 10+11
      "M1K",         "",          // 11, 11+12
      "Sub37",       "",          // 12, 12+13
      "Loopback L",  "Loopback",  // 13, 13+14  AES
      "Loopback R",  "",          // 14, 14+15
      "AD Drums L",  "AD Drums",  // 15, 15+16
      "AD Drums R",  "",          // 16, 16+17
      "AD Dm.Aux1",  "AD Dm Aux", // 17, 17+18
      "AD Dm Aux2",  "",          // 18, 18+19
      "AD 5 FS1R L", "AD FS1R",   // 19, 19+20
      "AD 6 FS1R R", "",          // 20, 20+21
      "AD Aux3",     "AD Aux34",  // 21, 21+22
      "AD Aux4",     "",          // 22, 22+23
      "",            "",          // 23, 23+24
      "",            "",          // 24, 24+25
      "",            "",          // 25, 25+26
      "",            "",          // 26, 26+27
      "",            "",          // 27, 27+28
      "",            "",          // 28, 28+29
      "",            "",          // 29, 29+30
      "",            "",          // 30, 30+31
      "",            "",          // 31, 31+32
      "",            "",          // 32, -
                                          ];

   // PortAudio / ASIO output channel aliases (mono+stereo+shortname)
   //  (note) displayed in AudioTrack channel combobox

   static StringArray audio_out_aliases = [
      "R1: Bass L",      "R12: Bass",         "Bass ",  //  1, 1+2   (R=RME)
      "R2: Bass R",      "R2x",               "     ",  //  2, 2+3
      "R3: Keys L",      "R34: Keys",         "Keys ",  //  3, 3+4
      "R4: Keys R",      "R4x",               "     ",  //  4, 4+5
      "R5: Pad L",       "R56: Pads",         "Pads ",  //  5, 5+6
      "R6: Pad R",       "R6x",               "     ",  //  6, 6+7
      "R7: Final L",     "R78: Final",        "Final",  //  7, 7+8
      "R8: Final R",     "R8x",               "     ",  //  8, 8+9
      "Phones L",        "Phones",            "     ",  //  9, 9+10
      "Phones R",        "",                  "     ",  // 10, 10+11
      "Phones 2 L",      "Phones 2",          "     ",  // 11, 11+12
      "Phones 2 R",      "",                  "     ",  // 12, 12+13
      "AES L",           "AES",               "     ",  // 13, 13+14    RME LOOPBACK
      "AES R",           "AES",               "     ",  // 14, 14+15
      "A1: BD",          "A12: Drums 1",      "Drum1",  // 15, 15+16  (A=ADAT1)
      "A2: SD",          "A2x",               "     ",  // 16, 16+17
      "A3: Perc L",      "A34: Drums 2",      "Drum2",  // 17, 17+18
      "A4: Perc R",      "A4x",               "     ",  // 18, 18+19
      "A5: HH",          "A56: Drums 3",      "Drum3",  // 19, 19+20 / ADAT 5
      "A6: CY",          "A6x",               "     ",  // 20, 20+21 / ADAT 6
      "A7: Aux3 Ret",    "A78: Aux34 / DSR-3","Aux34",  // 21, 21+22 / ADAT 7
      "A8: Aux4 Ret",    "A8x",               "     ",  // 22, 22+23 / ADAT 8
      "Mod1",            "Mod12",             "     ",  // 23, 23+24
      "Mod2",            "Mod23",             "     ",  // 24, 24+25
      "Mod3",            "Mod34",             "     ",  // 25, 25+26
      "Mod4",            "Mod45",             "     ",  // 26, 26+27
      "Mod5",            "Mod56",             "     ",  // 27, 27+28
      "Mod6",            "Mod67",             "     ",  // 28, 28+29
      "Mod7",            "Mod78",             "     ",  // 29, 29+30
      "Mod8",            "Mod8x",             "     ",  // 30, 30+31
      "",                "",                  "     ",  // 31, 31+32
      "",                "",                  "     ",  // 32, -
                                          ];

   // 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 should be set to false
   static boolean b_poll = false;

   // 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 ASIO callback
   //  0=default size, >0=may update ASIO buffer size
   // static int audio_frames_per_buffer = 16;
   // static int audio_frames_per_buffer = 64;
   static int audio_frames_per_buffer = 0;

   // 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;

   // standalone mode: number of (64 sample frame) output buffers to calculate in advance
   //  (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)
   //  (note) set to 0 to disable ringbuffer
   // static int standalone_ring_size = 6;
   static int standalone_ring_size = 4;
   // static int standalone_ring_size = 2;

   // 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
   static int atrk_rec_skip_num_frames = 100;



   /***
    ***
    *** 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.Recorder
    ***  (note) removed (replaced by AudioTrack feature)
    ***  (todo) clean up
    ***
    ***/

   // recorder/sampleview redraw interval (millisec)
   static int audio_recorder_redrawinterval = 300;

   // enable background capturing to scratch buffer (Audio->Recorder)
   static boolean audio_recorder_capture = true;

   // scratchbuffer size (#stereo sample frames)
   // static int audio_recorder_scratchbuf_numframes = (48000*60*5); // ~5min
   // static int audio_recorder_scratchbuf_numframes = (48000*10); // ~10 sec
   static int audio_recorder_scratchbuf_numframes = (48000*60*2); // ~2min

   // auto start/stop capturing when signal exceeds minval_threshold
   static float audio_recorder_minval_threshold = 0.01f;

   // auto stop capturing when signal is below minval_threshold for "timeout" seconds
   static float audio_recorder_timeout = 3.0;

   // true=show audio recorder options by default (increases minimum recorder window height)
   static boolean b_audio_recorder_options = false;


   /***
    ***
    *** Audio.Metronome
    ***
    ***/

   // Metronome: Enable/disable metronome click
   static boolean    b_audio_metronome = false;

   // Metronome: Volume
   static float      audio_metronome_volume = 2.0f;

   // Metronome: Base note
   static int        audio_metronome_note = 84;


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

   // Maximum SampleView undo/redo stack size (per waveform)
   //  (note) this should not be a config option (pls leave unchanged)
   static int sampleview_undo_stack_size = 8192;

   // 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 = true;




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

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

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

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

   // filename of project to load during startup
   //static String autoload_filename = "projects/test.epr";
   static String autoload_filename = "";

   // true=update autoload_filename each time a project is loaded/saved
   static boolean b_autoload_filename_autoupdate = 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";

   // if true, show project comment after loading a project (if the comment is not empty)
   static boolean b_autoshowcomment = true;



   /***
    ***
    *** Tracks
    ***
    ***/
   static IntArray default_track_colors = [
      0,
      #1800004f,
      #18007f7f,
      #18005f00,
      #182f0000,
      #18003f8f,
      #1800ffff,
      #1800df2f,
      #18df3020
                                           ];

   // true=colorize tracks.
   // 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


   /***
    ***
    *** 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 = 14;

   // 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;

   // 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;

   // 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 = true;


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

   // 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 = true;

   // 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 = 0;

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

   // Samples table 'Prg' column display mode
   // int pgsample_prg_dpy_mode = PageSample.PRG_DPY_PC_DEC;
   static int pgsample_prg_dpy_mode = PageSample.PRG_DPY_UNIQUE_ID;

   //
   // Drum pad names (referenced by drum_pad_indicies_* arrays)
   //
   //  (note) used in DrumPadDialog (map zone(s) to pad(s))
   //  (note) see drum_layout.txt
   //
   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;




   /***
    ***
    *** 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 temporary ringbuffer (for capturing track output) (def=20sec)
   static int tmp_ringbuf_num_frames = (audio_mixrate * 20);




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

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




   /***
    ***
    *** Sample SynthDialog
    ***
    ***/

   // Initial Dialog geometry
   static Geometry4f sample_synth_geo = [0, 0, 821, 411];

   // Internal text editor geometry (new windows)
   //  (note) [x, y, w, h]
   // static Geometry4f textedit_geo = [0, 0, 0, 0];
   static Geometry4f sample_synth_textedit_geo = [562, 50, 666, 361];
   
   // Machine preset path
   static String sample_synth_machine_preset_path = rootpath+"/machines/";

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

   // Recalc interval
   //  - e.g. after/while editing bezier curve
   static int sample_synth_recalc_interval = 150;

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




   /***
    ***
    *** The body of the following function is auto-generated by the UI.
    *** It may override some of the manually configured configuration items
    ***
    ***/
   static UseAutogeneratedConfig() {
      //----BEGIN_AUTOGENERATED_CONFIG----
      //----END_AUTOGENERATED_CONFIG----
   }


   /***
    ***
    *** The body of the following function is auto-generated by the UI.
    *** It overrides the manually configured MIDI input/output devices
    ***
    ***/
   static UseAutogeneratedMIDIConfig() {
      //----BEGIN_AUTOGENERATED_MIDI_CONFIG----
      //----END_AUTOGENERATED_MIDI_CONFIG----
   }

}


// (note) Comment the following line if you don't want the autogenerated UI settings to override the config defaults
STConfig.UseAutogeneratedConfig();
//STConfig.UseAutogeneratedConfig();

// (note) Comment the following line if you don't want the autogenerated UI MIDI settings to override the config defaults
STConfig.UseAutogeneratedMIDIConfig();
//STConfig.UseAutogeneratedMIDIConfig();
