Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/p3r7/norns.el

monome norns interactive development for Emacs
https://github.com/p3r7/norns.el

emacs emacs-lisp emacs-package monome monome-norns norns repl

Last synced: 7 days ago
JSON representation

monome norns interactive development for Emacs

Awesome Lists containing this project

README

        

# norns.el

logo

Interactive development environment for monome norns from Emacs.

Support for targeting remote norns instances (via TRAMP) or even Emacs running on norns itself (untested).

Several norns instances can be interacted with concurently (all buffers are namespaced by norns' hostname).

[![GNU Emacs](https://github.com/minad/corfu/blob/screenshots/emacs.svg?raw=true)](https://www.gnu.org/software/emacs/)
[![MELPA](https://melpa.org/packages/norns-badge.svg)](https://melpa.org/#/norns)

## Installation

```el
(use-package norns
:config
(add-hook 'lua-mode-hook #'norns-mode-maybe-activate)
(add-hook 'sclang-mode-mode-hook #'norns-mode-maybe-activate))
```

Additionally one can map keyboard shortcuts:

```el
(use-package norns
:bind (
:map norns-mode-map
;; NB: those keybindings are inspired by John Wiegley's
("C-c e b" . norns-load-current-script)
("C-c e r" . norns-send-selection)

:map norns-matron-repl-mode-map
("C-c e b" . norns-rerun)

:map norns-sc-repl-mode-map
("C-." . norns-sc-stop))
;; [...]
)
```

If you access a norns through a router / LAN, please set `norns-lan-domain` to your local LAN domain (defautl value is generally `lan` or `home`). Otheriwe emacs' `websocket` often has trouble connecting to the mDNS domain advertized by norns (typical error message: `websocket-open: norns.local/5555 Name or service not known`).

```el
(use-package norns
;; [...]
:init
(setq norns-lan-domain "lan")
;; [...]
)
```

## Commands

All commands (unless specified otherwise) will analyze if currently visited file is on a norns.

If it's the case, this particular norns is targeted by the command execution. Otherwise the default norns instance (configurable w/ `norns-host` / `norns-mdns-domain` is targeted instead).

A matron REPL (`*matron/*`) or SuperCollider REPL (`*norns-sc/*`) is spawned and pops in a new window.

Those behaviors can be customized by tweaking the values of `norns-access-policy` and `norns-repl-switch-on-cmd`.

#### `(norns-matron-repl)` / `(norns-sc-repl)`

Spawn and switch to matron REPL / SuperCollider REPL for norns instance.

#### `(norns-docker-matron-repl)` / `(norns-docker-sc-repl)`

Same as above but connects to a localy-running dockerized norns instance (see [winder/norns-dev](https://github.com/winder/norns-dev/)).

Requires package [docker-tramp](https://github.com/emacs-pe/docker-tramp.el) to access the container's filesystem.

If using a custom container name, tweak value of `norns-docker-container`. Likewise, you may need to adjust the value of `norns-local-mdns-domain` if your LAN domain is other than `lan`.

#### `(norns-matron-send TXT)` / `(norns-sc-send TXT)` / `(norns-send TXT)`

Prompt user to enter raw text `TXT` command and sends it to matron / SuperCollider.

Generic version (`norns-send`) auto-selects the right command according to current buffer mode.

#### `(norns-matron-send-selection)` / `(norns-sc-send-selection)` / `(norns-send-selection)`

Same as above, but acts on selection (*active region* in Emacs lingo).

#### `(norns-load-current-script)`

Ask the visited script to be loaded by the visited norns.

Will fail if currently visited file is not part of a norns script.

#### `(norns-load-script)`

Prompt user for list of available norns scripts and launch the one selected.

#### `(norns-restart)` / `(norns-reboot)`

`norns-restart` restarts all norns services. Respawns the matron and SuperCollider REPLs.

`norns-reboot` performs a full OS reboot (for when things get stuck bad).

#### `(norns-screenshot )` / `(norns-screen-dump )`

Take a screenshot of norns screen. Save it on norns itself under `norns-screenshot-folder`.

`norns-screen-dump` does the same but is lower level. It directly dumps what is in the screen memory buffer and doesn't perform scaling / quantized greyscale conversion to match how it appears IRL.

## Configuration

Several norns can be managed by this package.

Default value of `norns-access-policy` (`:current-fallback-default`) makes the execution of commands resolve to the currently visited norns instance or fallbacks to the "default" one.

To always use the default instance (absolute lookup), change this value to `:default`. Otherwise, to only try the currently visited instance (relative lookup) set it to `:current`.

The default norns instance declaration is accessible through vars `norns-host`, `norns-mdns-domain` and `norns-local-mdns-domain`. `norns-mdns-domain` is the mDNS domain advertized by norns (typically `local`) and `norns-local-mdns-domain` is the one of the local network that both norns and your computer connect to (generally `lan` or `home`).

On command execution, the matron or SuperCollider buffer (of the corresponding instance) will pop to current frame (can be disabled by setting `norns-repl-switch-on-cmd` to `nil`) w/ method `norns-repl-switch-fn` (defaults to `switch-to-buffer-other-window`).

This new window will not steal focus, but one can change that by setting `norns-repl-switch-no-focus` to `nil`.

## Advanced usages

If you want commands to interact w/ a specific norns instance independently of your current location, just define your own commands like so:

```elisp
(use-package norns
;; [...]

:config
(defun norns2-send (cmd)
(interactive "s> ")
(let ((norns-access-policy :default)
(norns-host "norns2"))
(norns-send cmd))))
```

## OSC commands

The package used to embed OSC-based commands to simulate button & encoder presses.

The `osc.el` package appears to be doing some unholy stuff (negative Unix timestamps) which somehow works on Linux x82_64 builds but does not on ARM nor WinNT. Furthermore, as it's in a `defconst` it cannot be monkeypatched.

As a result I decided to remove this dependency.

I may reintroduce it once it gets fixed.

In the meantime, you could add back the OSC-based feature by dropping that in your `init.el`:

```el
(require 'osc)

;; VARS

(defvar norns-osc-port 10111 "Default norns OSC protocol port.")

;; IO - OSC

(defun norns--osc-send (p &rest args)
"Send OSC message to current norns (w/ path P and optional ARGS)."
(let* ((default-directory (norns--location-from-access-policy))
(host (norns--core-curr-host))
(client (osc-make-client host norns-osc-port)))
(apply #'osc-send-message client p args)
(delete-process client)))

(defun norns-key (n z)
"Change state of key N to value Z (either 0 or 1) on current norns."
(norns--osc-send "/remote/key" n z))

(defun norns-key-toggle (n)
"Simulate a user key press on key N on current norns."
(norns-key n 1)
(norns-key n 0))

(defun norns-enc (n delta)
"Simulate a rotation of value DELTA on encoder N on current norns."
(norns--osc-send "/remote/enc" n delta))
```

## Implementation details

Major modes for REPLs (`norns-matron-repl-mode` / `norns-sc-repl-mode`) are based on `comint-mode`.

As those communications doesn't rely on a process (but websocket communication instead), we bind a "fake" process and handle output manually by calling `comint-output-filter` (inside of `norns--matron-output` / `norns--sc-output`).

This trick comes from `ielm`.

## Legibility

This code uses form feeds (`^L` character) as separators.

Either package [form-feed](https://github.com/wasamasa/form-feed) or [page-break-lines](https://github.com/purcell/page-break-lines) makes them appear as intended.