{:tracks-using-playlists? false,
 :carabiner {:bars true},
 :carabiner-startup-mode "Triggers",
 :window-positions {:carabiner [1529 327],
                    :waveform-detail-4 [2688 574 600 200],
                    :waveform-detail-1 [400 401 600 200],
                    :waveform-detail-3 [598 489 600 200],
                    :triggers [507 184 1005 417],
                    :overlay [818 341],
                    :nrepl [503 362],
                    :waveform-detail-2 [883 401 600 200],
                    :player-status [506 207]},
 :send-status? false,
 :triggers [{:bar true,
             :start "Start",
             :channel 1,
             :start-stop true,
             :note 127,
             :gear false,
             :stop true,
             :expressions {:tracked "(when trigger-active? (set-gm-tempo effective-tempo))",
                           :deactivation "(set-gm-tempo 0.0)"},
             :comment "",
             :outputs #beat_link_trigger.util.MidiChoice{:full-name "APC40 mkII"},
             :send true,
             :players #beat_link_trigger.util.PlayerChoice{:number -1},
             :enabled "Always",
             :message "Link"}
            {:bar true,
             :start "Start",
             :channel 1,
             :start-stop false,
             :note 127,
             :gear false,
             :stop true,
             :comment "",
             :outputs #beat_link_trigger.util.MidiChoice{:full-name "APC40 mkII"},
             :send true,
             :players #beat_link_trigger.util.PlayerChoice{:number -1},
             :enabled "Never",
             :message "Note"}],
 :expressions {:setup "(swap! globals assoc :resolume (osc/osc-client \"localhost\" 7000))\n\n;; Attempt to connect to the grandMA2 telnet command port.\n;; Edit the variable definitions below to reflect your setup.\n(try\n  (let [gm-address      \"192.168.0.98\"\n        gm-port         30000\n        gm-user         \"TSM\"\n        gm-password     \"insomni@\"\n        gm-speedmaster  \"3.3\"\n        connect-timeout 5000\n        socket-address  (InetSocketAddress. gm-address gm-port)\n        socket          (java.net.Socket.)]\n    (.connect socket socket-address connect-timeout)\n    (swap! globals assoc :gm-connection {:socket socket\n                                         :bpm-master gm-speedmaster})\n    (future (gm-response-handler))\n    (send-gm-command (str \"login \\\"\" gm-user \"\\\" \\\"\" gm-password \"\\\"\")))\n  (catch Exception e\n    (timbre/error e \"Unable to connect to grandMA2\")))",
               :shutdown "(osc/osc-close (:resolume @globals))\n\n;; Disconnect from the grandMA2 telnet command port.\n(when-let [socket (get-in @globals [:gm-connection :socket])]\n  (.close socket)\n  (swap! globals dissoc :gm-connection))",
               :shared "(defn send-cue-to-resolume\n\"Sends an OSC message to resolume triggering the supplied clip number in the\nspecified layer. Used from a show, so the show globals must also be passed.\"\n[globals layer clip]\n(osc/osc-send (:resolume @globals)\n(str \"/composition/layers/\" layer \"/clips/\" clip \"/connect\")))\n\n\n(defn gm-response-handler\n  \"A loop that reads messages from grandMA2 and responds\n  appropriately. (Currently we don’t respond in any way, but simply\n  consume responses as they arrive.)\"\n  []\n  (try\n    (loop [socket (get-in @globals [:gm-connection :socket])]\n      (when (and socket (not (.isClosed socket)))\n        (let [buffer (byte-array 1024)\n              input  (.getInputStream socket)\n              n      (.read input buffer)]\n          (when (pos? n)  ; We got data, so the socket has not yet been closed.\n            (let [message (String. buffer 0 n \"UTF-8\")]\n              (timbre/info \"Received from grandMA2:\" message)\n              ;; TODO: Here is where we would analyze and respond if needed;\n              ;;       see the ShowXPress example.\n              )\n            (recur (get-in @globals [:gm-connection :socket]))))))\n    (catch Throwable t\n      (timbre/error t \"Problem reading from grandMA2, loop aborted.\"))))\n\n(defn send-gm-command\n  \"Sends a command message to grandMA2.\"\n  [message]\n  (let [socket (get-in @globals [:gm-connection :socket])]\n    (if (and socket (not (.isClosed socket)))\n      (.write (.getOutputStream socket) (.getBytes (str message \"\\r\\n\")\n              \"UTF-8\"))\n      (timbre/warn\n       \"Cannot write to grandMA2, no open socket, discarding:\" message))))\n\n(defn set-gm-tempo\n  \"Tells grandMA2 the current tempo if it is different than the\n  value we last reported. Rounds to the nearest beat per minute\n  because the protocol does not accept any fractional values.\n  The expected way to use this is to include the following in a\n  trigger’s Tracked Update Expression:\n\n  `(when trigger-active? (set-gm-tempo effective-tempo))`\"\n  [bpm]\n  (let [bpm    (Math/round bpm)\n        master (get-in @globals [:gm-connection :bpm-master])]\n    (when-not (= bpm (get-in @globals [:gm-connection :bpm]))\n      (send-gm-command (str \"SpecialMaster \" master \" At \" bpm))\n      (swap! globals assoc-in [:gm-connection :bpm] bpm)\n      (timbre/info \"grandMA2 tempo set to\" bpm))))\n\n;; An alternate approach. You probably only want to use one of set-gm-tempo\n;; (above) and send-gm-beat (below), whichever works best in your setup.\n\n(defn send-gm-beat\n  \"Sends a learn command to grandMA2. The expected way to use this is\n  to include the following in a trigger’s Beat Expresssion:\n\n  `(when trigger-active? (send-gm-beat))`\"\n  []\n  (let [master (get-in @globals [:gm-connection :bpm-master])]\n    (send-gm-command (str \"Learn SpecialMaster \" master))))"},
 :show-player-status false,
 :beat-link-trigger-version "8.0.0",
 :waveform-style "3-Band",
 :my-settings {"jogMode" "CDJ",
               "lcdBrightness" "BRIGHTEST",
               "language" "FRENCH",
               "tempoRange" "WIDE",
               "jogRingIllumination" "BRIGHT",
               "jogWheelLcdBrightness" "BRIGHTEST",
               "discSlotIllumination" "BRIGHT",
               "ejectLoadLock" "OFF"}}
